Compare commits

..

7 Commits

Author SHA1 Message Date
Pratik Mankawde
85f583f441 Phase 5: Observability stack — spanmetrics, dashboards, runbook
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:16:03 +00:00
Pratik Mankawde
735ecd169e Phase 4: Consensus tracing — round lifecycle, proposals, validations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:15:53 +00:00
Pratik Mankawde
f504d9bf81 Phase 3: Transaction tracing — protobuf context, PeerImp, NetworkOPs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:15:41 +00:00
Pratik Mankawde
051a219703 Phase 2: Complete RPC tracing — interface, macros, attributes, tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:15:34 +00:00
Pratik Mankawde
b5fb71c135 Phase 1c: RPC layer telemetry integration
Add tracing instrumentation to the RPC request handling layer:

TracingInstrumentation.h:
- Convenience macros (XRPL_TRACE_RPC, XRPL_TRACE_TX, etc.) that
  create RAII SpanGuard objects when telemetry is enabled
- XRPL_TRACE_SET_ATTR / XRPL_TRACE_EXCEPTION for span enrichment
- Zero-overhead no-ops when XRPL_ENABLE_TELEMETRY is not defined

RPCHandler.cpp:
- Trace each RPC command with span name "rpc.command.<method>"
- Record command name, API version, role, and status as attributes
- Capture exceptions on the span for error visibility

ServerHandler.cpp:
- Trace HTTP requests ("rpc.request"), WebSocket messages
  ("rpc.ws_message"), and processRequest ("rpc.process")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:15:17 +00:00
Pratik Mankawde
252a4bb8d2 Phase 1b: Telemetry core infrastructure
Add the OpenTelemetry telemetry library and supporting infrastructure:

Build system:
- Conan opentelemetry-cpp dependency with OTLP/gRPC exporter
- CMake integration for xrpl_telemetry library target
- Levelization ordering updates

Core library (libxrpl):
- Telemetry class: provider lifecycle, span creation, sampling config
- SpanGuard: RAII span management with attribute/exception helpers
- TelemetryConfig: parse [telemetry] config section
- NullTelemetry: no-op implementation when telemetry is disabled

Application integration:
- Telemetry member in ApplicationImp with start/stop lifecycle
- getTelemetry() interface on Application
- ServiceRegistry telemetry accessor

Docker observability stack:
- OTel Collector, Jaeger, Grafana docker-compose setup
- Collector config with OTLP gRPC receiver and Jaeger exporter

Config and docs:
- Example telemetry config section in xrpld-example.cfg
- Build documentation for telemetry setup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:53:25 +00:00
Pratik Mankawde
a6a6a7c0fd Phase 1a: OpenTelemetry plan documentation
Add comprehensive planning documentation for the OpenTelemetry
distributed tracing integration:

- Tracing fundamentals and concepts
- Architecture analysis of rippled's tracing surface area
- Design decisions and trade-offs
- Implementation strategy and code samples
- Configuration reference
- Implementation phases roadmap
- Observability backend comparison
- POC task list and presentation materials

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:50:27 +00:00
296 changed files with 15843 additions and 33917 deletions

View File

@@ -1,144 +1,105 @@
---
Checks: "-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-bad-signal-to-kill-thread,
bugprone-bool-pointer-implicit-conversion,
bugprone-casting-through-void,
bugprone-chained-comparison,
bugprone-compare-pointer-to-member-virtual-function,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-empty-catch,
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-inaccurate-erase,
bugprone-incorrect-enable-if,
bugprone-incorrect-roundings,
bugprone-infinite-loop,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
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-multiple-new-in-one-expression,
bugprone-multiple-statement-macro,
bugprone-no-escape,
bugprone-non-zero-enum-to-bool-conversion,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-redundant-branch-condition,
bugprone-return-const-ref-from-parameter,
bugprone-shared-ptr-array-mismatch,
bugprone-signal-handler,
bugprone-signed-char-misuse,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-standalone-empty,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-stringview-nullptr,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memory-comparison,
bugprone-suspicious-memset-usage,
bugprone-suspicious-realloc-usage,
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-undefined-memory-manipulation,
bugprone-undelegated-constructor,
bugprone-unhandled-exception-at-new,
bugprone-unique-ptr-array-mismatch,
bugprone-unsafe-functions,
bugprone-virtual-near-miss,
cppcoreguidelines-no-suspend-with-lock,
cppcoreguidelines-virtual-class-destructor,
hicpp-ignored-remove-result,
misc-definitions-in-headers,
misc-header-include-cycle,
misc-misplaced-const,
misc-static-assert,
misc-throw-by-value-catch-by-reference,
misc-unused-alias-decls,
misc-unused-using-decls,
readability-duplicate-include,
readability-enum-initial-value,
readability-misleading-indentation,
readability-non-const-parameter,
readability-redundant-declaration,
readability-reference-to-constructed-temporary,
modernize-deprecated-headers,
modernize-make-shared,
modernize-make-unique,
performance-implicit-conversion-in-loop,
performance-move-constructor-init,
performance-trivially-destructible
bugprone-argument-comment
"
# ---
# more checks that have some issues that need to be resolved:
#
# bugprone-assert-side-effect,
# bugprone-bad-signal-to-kill-thread,
# bugprone-bool-pointer-implicit-conversion,
# bugprone-casting-through-void,
# bugprone-chained-comparison,
# bugprone-compare-pointer-to-member-virtual-function,
# bugprone-copy-constructor-init,
# bugprone-crtp-constructor-accessibility,
# bugprone-dangling-handle,
# bugprone-dynamic-static-initializers,
# bugprone-empty-catch,
# bugprone-fold-init-type,
# bugprone-forward-declaration-namespace,
# bugprone-inaccurate-erase,
# bugprone-inc-dec-in-conditions,
# bugprone-reserved-identifier,
# bugprone-incorrect-enable-if,
# bugprone-incorrect-roundings,
# bugprone-infinite-loop,
# bugprone-integer-division,
# bugprone-lambda-function-name,
# bugprone-macro-parentheses,
# 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-unused-local-non-trivial-variable,
# bugprone-switch-missing-default-case,
# bugprone-suspicious-stringview-data-usage,
# bugprone-suspicious-missing-comma,
# bugprone-pointer-arithmetic-on-polymorphic-object,
# bugprone-multi-level-implicit-pointer-conversion,
# bugprone-multiple-new-in-one-expression,
# bugprone-multiple-statement-macro,
# bugprone-no-escape,
# bugprone-non-zero-enum-to-bool-conversion,
# bugprone-optional-value-conversion,
# bugprone-parent-virtual-call,
# bugprone-pointer-arithmetic-on-polymorphic-object,
# bugprone-posix-return,
# bugprone-redundant-branch-condition,
# bugprone-reserved-identifier,
# bugprone-return-const-ref-from-parameter,
# bugprone-shared-ptr-array-mismatch,
# bugprone-signal-handler,
# bugprone-signed-char-misuse,
# bugprone-sizeof-container,
# bugprone-sizeof-expression,
# bugprone-spuriously-wake-up-functions,
# bugprone-standalone-empty,
# bugprone-string-constructor,
# bugprone-string-integer-assignment,
# bugprone-string-literal-with-embedded-nul,
# bugprone-stringview-nullptr,
# bugprone-suspicious-enum-usage,
# bugprone-suspicious-include,
# bugprone-suspicious-memory-comparison,
# bugprone-suspicious-memset-usage,
# bugprone-suspicious-missing-comma,
# bugprone-suspicious-realloc-usage,
# bugprone-suspicious-semicolon,
# bugprone-suspicious-string-compare,
# bugprone-suspicious-stringview-data-usage,
# bugprone-swapped-arguments,
# bugprone-switch-missing-default-case,
# bugprone-terminating-continue,
# bugprone-throw-keyword-missing,
# bugprone-too-small-loop-variable,
# bugprone-undefined-memory-manipulation,
# bugprone-undelegated-constructor,
# bugprone-unhandled-exception-at-new,
# bugprone-unhandled-self-assignment,
# bugprone-unique-ptr-array-mismatch,
# bugprone-unsafe-functions,
# bugprone-unused-local-non-trivial-variable,
# bugprone-unused-raii,
# bugprone-unused-return-value,
# bugprone-use-after-move,
# bugprone-unhandled-self-assignment,
# bugprone-unused-raii,
#
# cppcoreguidelines-misleading-capture-default-by-value,
# bugprone-virtual-near-miss,
# cppcoreguidelines-init-variables,
# cppcoreguidelines-misleading-capture-default-by-value,
# cppcoreguidelines-no-suspend-with-lock,
# cppcoreguidelines-pro-type-member-init,
# cppcoreguidelines-pro-type-static-cast-downcast,
# cppcoreguidelines-use-default-member-init,
# cppcoreguidelines-rvalue-reference-param-not-moved,
#
# cppcoreguidelines-use-default-member-init,
# cppcoreguidelines-virtual-class-destructor,
# hicpp-ignored-remove-result,
# llvm-namespace-comment,
# misc-const-correctness,
# misc-definitions-in-headers,
# misc-header-include-cycle,
# misc-include-cleaner,
# misc-misplaced-const,
# misc-redundant-expression,
#
# readability-avoid-nested-conditional-operator,
# readability-avoid-return-with-void-value,
# readability-braces-around-statements,
# readability-container-contains,
# readability-container-size-empty,
# readability-convert-member-functions-to-static,
# readability-const-return-type,
# readability-else-after-return,
# readability-implicit-bool-conversion,
# readability-inconsistent-declaration-parameter-name,
# readability-identifier-naming,
# readability-make-member-function-const,
# readability-math-missing-parentheses,
# readability-redundant-inline-specifier,
# readability-redundant-member-init,
# readability-redundant-casting,
# readability-redundant-string-init,
# readability-simplify-boolean-expr,
# readability-static-definition-in-anonymous-namespace,
# readability-suspicious-call-argument,
# readability-use-std-min-max,
# readability-static-accessed-through-instance,
#
# misc-static-assert,
# misc-throw-by-value-catch-by-reference,
# misc-unused-alias-decls,
# misc-unused-using-decls,
# modernize-concat-nested-namespaces,
# modernize-deprecated-headers,
# modernize-make-shared,
# modernize-make-unique,
# modernize-pass-by-value,
# modernize-type-traits,
# modernize-use-designated-initializers,
@@ -150,50 +111,79 @@ Checks: "-*,
# modernize-use-starts-ends-with,
# modernize-use-std-numbers,
# modernize-use-using,
#
# 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-duplicate-include,
# readability-else-after-return,
# readability-enum-initial-value,
# readability-implicit-bool-conversion,
# readability-inconsistent-declaration-parameter-name,
# readability-identifier-naming,
# readability-make-member-function-const,
# readability-math-missing-parentheses,
# readability-misleading-indentation,
# readability-non-const-parameter,
# readability-redundant-casting,
# readability-redundant-declaration,
# readability-redundant-inline-specifier,
# readability-redundant-member-init,
# readability-redundant-string-init,
# readability-reference-to-constructed-temporary,
# readability-simplify-boolean-expr,
# readability-static-accessed-through-instance,
# readability-static-definition-in-anonymous-namespace,
# readability-suspicious-call-argument,
# readability-use-std-min-max
#
CheckOptions:
# readability-braces-around-statements.ShortStatementLines: 2
# readability-identifier-naming.MacroDefinitionCase: UPPER_CASE
# readability-identifier-naming.ClassCase: CamelCase
# readability-identifier-naming.StructCase: CamelCase
# readability-identifier-naming.UnionCase: CamelCase
# readability-identifier-naming.EnumCase: CamelCase
# readability-identifier-naming.EnumConstantCase: CamelCase
# readability-identifier-naming.ScopedEnumConstantCase: CamelCase
# readability-identifier-naming.GlobalConstantCase: UPPER_CASE
# readability-identifier-naming.GlobalConstantPrefix: "k"
# readability-identifier-naming.GlobalVariableCase: CamelCase
# readability-identifier-naming.GlobalVariablePrefix: "g"
# readability-identifier-naming.ConstexprFunctionCase: camelBack
# readability-identifier-naming.ConstexprMethodCase: camelBack
# readability-identifier-naming.ClassMethodCase: camelBack
# readability-identifier-naming.ClassMemberCase: camelBack
# readability-identifier-naming.ClassConstantCase: UPPER_CASE
# readability-identifier-naming.ClassConstantPrefix: "k"
# readability-identifier-naming.StaticConstantCase: UPPER_CASE
# readability-identifier-naming.StaticConstantPrefix: "k"
# readability-identifier-naming.StaticVariableCase: UPPER_CASE
# readability-identifier-naming.StaticVariablePrefix: "k"
# readability-identifier-naming.ConstexprVariableCase: UPPER_CASE
# readability-identifier-naming.ConstexprVariablePrefix: "k"
# readability-identifier-naming.LocalConstantCase: camelBack
# readability-identifier-naming.LocalVariableCase: camelBack
# readability-identifier-naming.TemplateParameterCase: CamelCase
# readability-identifier-naming.ParameterCase: camelBack
# readability-identifier-naming.FunctionCase: camelBack
# readability-identifier-naming.MemberCase: camelBack
# readability-identifier-naming.PrivateMemberSuffix: _
# readability-identifier-naming.ProtectedMemberSuffix: _
# readability-identifier-naming.PublicMemberSuffix: ""
# readability-identifier-naming.FunctionIgnoredRegexp: ".*tag_invoke.*"
bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
# CheckOptions:
# readability-braces-around-statements.ShortStatementLines: 2
# readability-identifier-naming.MacroDefinitionCase: UPPER_CASE
# readability-identifier-naming.ClassCase: CamelCase
# readability-identifier-naming.StructCase: CamelCase
# readability-identifier-naming.UnionCase: CamelCase
# readability-identifier-naming.EnumCase: CamelCase
# readability-identifier-naming.EnumConstantCase: CamelCase
# readability-identifier-naming.ScopedEnumConstantCase: CamelCase
# readability-identifier-naming.GlobalConstantCase: UPPER_CASE
# readability-identifier-naming.GlobalConstantPrefix: "k"
# readability-identifier-naming.GlobalVariableCase: CamelCase
# readability-identifier-naming.GlobalVariablePrefix: "g"
# readability-identifier-naming.ConstexprFunctionCase: camelBack
# readability-identifier-naming.ConstexprMethodCase: camelBack
# readability-identifier-naming.ClassMethodCase: camelBack
# readability-identifier-naming.ClassMemberCase: camelBack
# readability-identifier-naming.ClassConstantCase: UPPER_CASE
# readability-identifier-naming.ClassConstantPrefix: "k"
# readability-identifier-naming.StaticConstantCase: UPPER_CASE
# readability-identifier-naming.StaticConstantPrefix: "k"
# readability-identifier-naming.StaticVariableCase: UPPER_CASE
# readability-identifier-naming.StaticVariablePrefix: "k"
# readability-identifier-naming.ConstexprVariableCase: UPPER_CASE
# readability-identifier-naming.ConstexprVariablePrefix: "k"
# readability-identifier-naming.LocalConstantCase: camelBack
# readability-identifier-naming.LocalVariableCase: camelBack
# readability-identifier-naming.TemplateParameterCase: CamelCase
# readability-identifier-naming.ParameterCase: camelBack
# readability-identifier-naming.FunctionCase: camelBack
# readability-identifier-naming.MemberCase: camelBack
# readability-identifier-naming.PrivateMemberSuffix: _
# readability-identifier-naming.ProtectedMemberSuffix: _
# readability-identifier-naming.PublicMemberSuffix: ""
# readability-identifier-naming.FunctionIgnoredRegexp: ".*tag_invoke.*"
# bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
# 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'
#

View File

@@ -19,7 +19,6 @@ libxrpl.nodestore > xrpl.protocol
libxrpl.protocol > xrpl.basics
libxrpl.protocol > xrpl.json
libxrpl.protocol > xrpl.protocol
libxrpl.protocol_autogen > xrpl.protocol_autogen
libxrpl.rdb > xrpl.basics
libxrpl.rdb > xrpl.rdb
libxrpl.resource > xrpl.basics
@@ -33,6 +32,8 @@ libxrpl.server > xrpl.server
libxrpl.shamap > xrpl.basics
libxrpl.shamap > xrpl.protocol
libxrpl.shamap > xrpl.shamap
libxrpl.telemetry > xrpl.basics
libxrpl.telemetry > xrpl.telemetry
libxrpl.tx > xrpl.basics
libxrpl.tx > xrpl.conditions
libxrpl.tx > xrpl.core
@@ -173,8 +174,10 @@ test.toplevel > test.csf
test.toplevel > xrpl.json
test.unit_test > xrpl.basics
tests.libxrpl > xrpl.basics
tests.libxrpl > xrpld.telemetry
tests.libxrpl > xrpl.json
tests.libxrpl > xrpl.net
tests.libxrpl > xrpl.telemetry
xrpl.conditions > xrpl.basics
xrpl.conditions > xrpl.protocol
xrpl.core > xrpl.basics
@@ -191,8 +194,6 @@ xrpl.nodestore > xrpl.basics
xrpl.nodestore > xrpl.protocol
xrpl.protocol > xrpl.basics
xrpl.protocol > xrpl.json
xrpl.protocol_autogen > xrpl.json
xrpl.protocol_autogen > xrpl.protocol
xrpl.rdb > xrpl.basics
xrpl.rdb > xrpl.core
xrpl.rdb > xrpl.protocol
@@ -209,6 +210,7 @@ xrpl.server > xrpl.shamap
xrpl.shamap > xrpl.basics
xrpl.shamap > xrpl.nodestore
xrpl.shamap > xrpl.protocol
xrpl.telemetry > xrpl.basics
xrpl.tx > xrpl.basics
xrpl.tx > xrpl.core
xrpl.tx > xrpl.ledger
@@ -218,6 +220,7 @@ xrpld.app > xrpl.basics
xrpld.app > xrpl.core
xrpld.app > xrpld.consensus
xrpld.app > xrpld.core
xrpld.app > xrpld.telemetry
xrpld.app > xrpl.json
xrpld.app > xrpl.ledger
xrpld.app > xrpl.net
@@ -227,6 +230,7 @@ xrpld.app > xrpl.rdb
xrpld.app > xrpl.resource
xrpld.app > xrpl.server
xrpld.app > xrpl.shamap
xrpld.app > xrpl.telemetry
xrpld.app > xrpl.tx
xrpld.consensus > xrpl.basics
xrpld.consensus > xrpl.json
@@ -241,6 +245,7 @@ xrpld.overlay > xrpl.basics
xrpld.overlay > xrpl.core
xrpld.overlay > xrpld.core
xrpld.overlay > xrpld.peerfinder
xrpld.overlay > xrpld.telemetry
xrpld.overlay > xrpl.json
xrpld.overlay > xrpl.protocol
xrpld.overlay > xrpl.rdb
@@ -258,6 +263,7 @@ xrpld.perflog > xrpl.json
xrpld.rpc > xrpl.basics
xrpld.rpc > xrpl.core
xrpld.rpc > xrpld.core
xrpld.rpc > xrpld.telemetry
xrpld.rpc > xrpl.json
xrpld.rpc > xrpl.ledger
xrpld.rpc > xrpl.net
@@ -268,3 +274,4 @@ xrpld.rpc > xrpl.resource
xrpld.rpc > xrpl.server
xrpld.rpc > xrpl.tx
xrpld.shamap > xrpl.shamap
xrpld.telemetry > xrpl.telemetry

View File

@@ -11,7 +11,7 @@ on:
jobs:
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
run-hooks:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@56de1bdf19639e009639a50b8d17c28ca954f267
uses: XRPLF/actions/.github/workflows/pre-commit.yml@320be44621ca2a080f05aeb15817c44b84518108
with:
runs_on: ubuntu-latest
container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-41ec7c1" }'

View File

@@ -40,18 +40,15 @@ env:
NPROC_SUBTRACT: ${{ github.event.repository.private && '1' || '2' }}
jobs:
build:
publish:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-a8c7be1
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@2cbf481018d930656e9276fcc20dc0e3a0be5b6d
with:
enable_ccache: false
- name: Get number of processors
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
id: nproc
@@ -81,23 +78,9 @@ jobs:
cmake -Donly_docs=ON ..
cmake --build . --target docs --parallel ${BUILD_NPROC}
- name: Create documentation artifact
- name: Publish documentation
if: ${{ github.event_name == 'push' }}
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with:
path: ${{ env.BUILD_DIR }}/docs/html
deploy:
if: ${{ github.event_name == 'push' }}
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ${{ env.BUILD_DIR }}/docs/html

View File

@@ -101,7 +101,7 @@ jobs:
steps:
- name: Cleanup workspace (macOS and Windows)
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4
uses: XRPLF/actions/cleanup-workspace@cf0433aa74563aead044a1e395610c96d65a37cf
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -177,7 +177,7 @@ jobs:
- name: Upload the binary (Linux)
if: ${{ github.repository_owner == 'XRPLF' && runner.os == 'Linux' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: xrpld-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/xrpld
@@ -229,21 +229,8 @@ jobs:
env:
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
run: |
set -o pipefail
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}"
- name: Show test failure summary
if: ${{ failure() && !inputs.build_only }}
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
run: |
if [ ! -f unittest.log ]; then
echo "unittest.log not found; embedded tests may not have run."
exit 0
fi
if ! grep -E "failed" unittest.log; then
echo "Log present but no failure lines found in unittest.log."
fi
- name: Debug failure (Linux)
if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }}
run: |

View File

@@ -78,13 +78,13 @@ jobs:
id: run_clang_tidy
continue-on-error: true
env:
TARGETS: ${{ inputs.files != '' && inputs.files || 'src tests' }}
FILES: ${{ inputs.files }}
run: |
run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" ${TARGETS} 2>&1 | tee clang-tidy-output.txt
run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "$BUILD_DIR" $FILES 2>&1 | tee clang-tidy-output.txt
- name: Upload clang-tidy output
if: steps.run_clang_tidy.outcome != 'success'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: clang-tidy-results
path: clang-tidy-output.txt

View File

@@ -22,8 +22,7 @@ jobs:
if: ${{ inputs.check_only_changed }}
runs-on: ubuntu-latest
outputs:
clang_tidy_config_changed: ${{ steps.changed_clang_tidy.outputs.any_changed }}
any_cpp_changed: ${{ steps.changed_files.outputs.any_changed }}
any_changed: ${{ steps.changed_files.outputs.any_changed }}
all_changed_files: ${{ steps.changed_files.outputs.all_changed_files }}
steps:
- name: Checkout repository
@@ -39,17 +38,10 @@ jobs:
**/*.ipp
separator: " "
- name: Get changed clang-tidy configuration
id: changed_clang_tidy
uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4
with:
files: |
.clang-tidy
run-clang-tidy:
needs: [determine-files]
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.any_cpp_changed == 'true' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }}
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.any_changed == 'true') }}
uses: ./.github/workflows/reusable-clang-tidy-files.yml
with:
files: ${{ (needs.determine-files.outputs.clang_tidy_config_changed == 'true' && '') || (inputs.check_only_changed && needs.determine-files.outputs.all_changed_files || '') }}
files: ${{ inputs.check_only_changed && needs.determine-files.outputs.all_changed_files || '' }}
create_issue_on_failure: ${{ inputs.create_issue_on_failure }}

View File

@@ -64,7 +64,7 @@ jobs:
steps:
- name: Cleanup workspace (macOS and Windows)
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4
uses: XRPLF/actions/cleanup-workspace@cf0433aa74563aead044a1e395610c96d65a37cf
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

3
.gitignore vendored
View File

@@ -80,6 +80,3 @@ DerivedData
# clangd cache
/.cache
# python cache
__pycache__

View File

@@ -14,21 +14,17 @@ repos:
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: trailing-whitespace
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_objects)/
- id: end-of-file-fixer
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_objects)/
- id: mixed-line-ending
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_objects)/
- id: check-merge-conflict
args: [--assume-in-merge]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: cd481d7b0bfb5c7b3090c21846317f9a8262e891 # frozen: v22.1.0
rev: 75ca4ad908dc4a99f57921f29b7e6c1521e10b26 # frozen: v21.1.8
hooks:
- id: clang-format
args: [--style=file]
"types_or": [c++, c, proto]
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_objects)/
- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: e2c2116d86a80e72e7146a06e68b7c228afc6319 # frozen: v0.6.13
@@ -37,20 +33,20 @@ repos:
additional_dependencies: [PyYAML]
- repo: https://github.com/rbubley/mirrors-prettier
rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
hooks:
- id: prettier
- repo: https://github.com/psf/black-pre-commit-mirror
rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0
rev: 831207fd435b47aeffdf6af853097e64322b4d44 # frozen: v25.12.0
hooks:
- id: black
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: a42085ade523f591dca134379a595e7859986445 # frozen: v9.7.0
rev: 1cfa010f078c354f3ffb8413616280cc28f5ba21 # frozen: v9.4.0
hooks:
- id: cspell # Spell check changed files
exclude: (.config/cspell.config.yaml|^include/xrpl/protocol_autogen/(transactions|ledger_objects)/)
exclude: .config/cspell.config.yaml
- id: cspell # Spell check the commit message
name: check commit message spelling
args:
@@ -65,15 +61,7 @@ repos:
hooks:
- id: nix-fmt
name: Format Nix files
entry: |
bash -c '
if command -v nix &> /dev/null || [ "$GITHUB_ACTIONS" = "true" ]; then
nix --extra-experimental-features "nix-command flakes" fmt "$@"
else
echo "Skipping nix-fmt: nix not installed and not in GitHub Actions"
exit 0
fi
' --
entry: nix --extra-experimental-features 'nix-command flakes' fmt
language: system
types:
- nix

View File

@@ -22,19 +22,6 @@ API version 2 is available in `rippled` version 2.0.0 and later. See [API-VERSIO
This version is supported by all `rippled` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified.
## Unreleased
This section contains changes targeting a future version.
### Additions
- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)):
- `TRANSACTION_FORMATS`: Describes the fields and their optionality for each transaction type, including common fields shared across all transactions.
- `LEDGER_ENTRY_FORMATS`: Describes the fields and their optionality for each ledger entry type, including common fields shared across all ledger entries.
- `TRANSACTION_FLAGS`: Maps transaction type names to their supported flags and flag values.
- `LEDGER_ENTRY_FLAGS`: Maps ledger entry type names to their flags and flag values.
- `ACCOUNT_SET_FLAGS`: Maps AccountSet flag names (asf flags) to their numeric values.
## XRP Ledger server version 3.1.0
[Version 3.1.0](https://github.com/XRPLF/rippled/releases/tag/3.1.0) was released on Jan 27, 2026.

View File

@@ -36,6 +36,26 @@ endif ()
# Enable ccache to speed up builds.
include(Ccache)
# make GIT_COMMIT_HASH define available to all sources
find_package(Git)
if (Git_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse
HEAD OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
if (gch)
set(GIT_COMMIT_HASH "${gch}")
message(STATUS gch: ${GIT_COMMIT_HASH})
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
endif ()
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse
--abbrev-ref HEAD OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
if (gb)
set(GIT_BRANCH "${gb}")
message(STATUS gb: ${GIT_BRANCH})
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
endif ()
endif () # git
if (thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
-DXRPL_ENABLE_THREAD_SAFETY_ANNOTATIONS)
@@ -106,6 +126,18 @@ if (rocksdb)
target_link_libraries(xrpl_libs INTERFACE RocksDB::rocksdb)
endif ()
# OpenTelemetry distributed tracing (optional).
# When ON, links against opentelemetry-cpp and defines XRPL_ENABLE_TELEMETRY
# so that tracing macros in TracingInstrumentation.h are compiled in.
# When OFF (default), all tracing code compiles to no-ops with zero overhead.
# Enable via: conan install -o telemetry=True, or cmake -Dtelemetry=ON.
option(telemetry "Enable OpenTelemetry tracing" OFF)
if (telemetry)
find_package(opentelemetry-cpp CONFIG REQUIRED)
add_compile_definitions(XRPL_ENABLE_TELEMETRY)
message(STATUS "OpenTelemetry tracing enabled")
endif ()
# Work around changes to Conan recipe for now.
if (TARGET nudb::core)
set(nudb nudb::core)

View File

@@ -251,29 +251,6 @@ pip3 install pre-commit
pre-commit install
```
## Clang-tidy
All code must pass `clang-tidy` checks according to the settings in [`.clang-tidy`](./.clang-tidy).
There is a Continuous Integration job that runs clang-tidy on pull requests. The CI will check:
- All changed C++ files (`.cpp`, `.h`, `.ipp`) when only code files are modified
- **All files in the repository** when the `.clang-tidy` configuration file is changed
This ensures that configuration changes don't introduce new warnings across the codebase.
### Running clang-tidy locally
Before running clang-tidy, you must build the project to generate required files (particularly protobuf headers). Refer to [`BUILD.md`](./BUILD.md) for build instructions.
Then run clang-tidy on your local changes:
```
run-clang-tidy -p build src tests
```
This will check all source files in the `src` and `tests` directories using the compile commands from your `build` directory.
## Contracts and instrumentation
We are using [Antithesis](https://antithesis.com/) for continuous fuzzing,

View File

@@ -0,0 +1,244 @@
# Distributed Tracing Fundamentals
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Next**: [Architecture Analysis](./01-architecture-analysis.md)
---
## What is Distributed Tracing?
Distributed tracing is a method for tracking data objects as they flow through distributed systems. In a network like XRP Ledger, a single transaction touches multiple independent nodes—each with no shared memory or logging. Distributed tracing connects these dots.
**Without tracing:** You see isolated logs on each node with no way to correlate them.
**With tracing:** You see the complete journey of a transaction or an event across all nodes it touched.
---
## Core Concepts
### 1. Trace
A **trace** represents the entire journey of a request through the system. It has a unique `trace_id` that stays constant across all nodes.
```
Trace ID: abc123
├── Node A: received transaction
├── Node B: relayed transaction
├── Node C: included in consensus
└── Node D: applied to ledger
```
### 2. Span
A **span** represents a single unit of work within a trace. Each span has:
| Attribute | Description | Example |
| ---------------- | --------------------- | -------------------------- |
| `trace_id` | Links to parent trace | `abc123` |
| `span_id` | Unique identifier | `span456` |
| `parent_span_id` | Parent span (if any) | `p_span123` |
| `name` | Operation name | `rpc.submit` |
| `start_time` | When work began | `2024-01-15T10:30:00Z` |
| `end_time` | When work completed | `2024-01-15T10:30:00.050Z` |
| `attributes` | Key-value metadata | `tx.hash=ABC...` |
| `status` | OK, ERROR MSG | `OK` |
### 3. Trace Context
**Trace context** is the data that propagates between services to link spans together. It contains:
- `trace_id` - The trace this span belongs to
- `span_id` - The current span (becomes parent for child spans)
- `trace_flags` - Sampling decisions
---
## How Spans Form a Trace
Spans have parent-child relationships forming a tree structure:
```mermaid
flowchart TB
subgraph trace["Trace: abc123"]
A["tx.submit<br/>span_id: 001<br/>50ms"] --> B["tx.validate<br/>span_id: 002<br/>5ms"]
A --> C["tx.relay<br/>span_id: 003<br/>10ms"]
A --> D["tx.apply<br/>span_id: 004<br/>30ms"]
D --> E["ledger.update<br/>span_id: 005<br/>20ms"]
end
style A fill:#0d47a1,stroke:#082f6a,color:#ffffff
style B fill:#1b5e20,stroke:#0d3d14,color:#ffffff
style C fill:#1b5e20,stroke:#0d3d14,color:#ffffff
style D fill:#1b5e20,stroke:#0d3d14,color:#ffffff
style E fill:#bf360c,stroke:#8c2809,color:#ffffff
```
The same trace visualized as a **timeline (Gantt chart)**:
```
Time → 0ms 10ms 20ms 30ms 40ms 50ms
├───────────────────────────────────────────┤
tx.submit│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│
├─────┤
tx.valid │▓▓▓▓▓│
│ ├──────────┤
tx.relay │ │▓▓▓▓▓▓▓▓▓▓│
│ ├────────────────────────────┤
tx.apply │ │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│
│ ├──────────────────┤
ledger │ │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│
```
---
## Distributed Traces Across Nodes
In distributed systems like rippled, traces span **multiple independent nodes**. The trace context must be propagated in network messages:
```mermaid
sequenceDiagram
participant Client
participant NodeA as Node A
participant NodeB as Node B
participant NodeC as Node C
Client->>NodeA: Submit TX<br/>(no trace context)
Note over NodeA: Creates new trace<br/>trace_id: abc123<br/>span: tx.receive
NodeA->>NodeB: Relay TX<br/>(trace_id: abc123, parent: 001)
Note over NodeB: Creates child span<br/>span: tx.relay<br/>parent_span_id: 001
NodeA->>NodeC: Relay TX<br/>(trace_id: abc123, parent: 001)
Note over NodeC: Creates child span<br/>span: tx.relay<br/>parent_span_id: 001
Note over NodeA,NodeC: All spans share trace_id: abc123<br/>enabling correlation across nodes
```
---
## Context Propagation
For traces to work across nodes, **trace context must be propagated** in messages.
### What's in the Context (32 bytes)
| Field | Size | Description |
| ------------- | ---------- | ------------------------------------------------------- |
| `trace_id` | 16 bytes | Identifies the entire trace (constant across all nodes) |
| `span_id` | 8 bytes | The sender's current span (becomes parent on receiver) |
| `trace_flags` | 4 bytes | Sampling decision flags |
| `trace_state` | ~0-4 bytes | Optional vendor-specific data |
### How span_id Changes at Each Hop
Only **one** `span_id` travels in the context - the sender's current span. Each node:
1. Extracts the received `span_id` and uses it as the `parent_span_id`
2. Creates a **new** `span_id` for its own span
3. Sends its own `span_id` as the parent when forwarding
```
Node A Node B Node C
────── ────── ──────
Span AAA Span BBB Span CCC
│ │ │
▼ ▼ ▼
Context out: Context out: Context out:
├─ trace_id: abc123 ├─ trace_id: abc123 ├─ trace_id: abc123
├─ span_id: AAA ──────────► ├─ span_id: BBB ──────────► ├─ span_id: CCC ──────►
└─ flags: 01 └─ flags: 01 └─ flags: 01
│ │
parent = AAA parent = BBB
```
The `trace_id` stays constant, but `span_id` **changes at every hop** to maintain the parent-child chain.
### Propagation Formats
There are two patterns:
### HTTP/RPC Headers (W3C Trace Context)
```
traceparent: 00-abc123def456-span789-01
│ │ │ │
│ │ │ └── Flags (sampled)
│ │ └── Parent span ID
│ └── Trace ID
└── Version
```
### Protocol Buffers (rippled P2P messages)
```protobuf
message TMTransaction {
bytes rawTransaction = 1;
// ... existing fields ...
// Trace context extension
bytes trace_parent = 100; // W3C traceparent
bytes trace_state = 101; // W3C tracestate
}
```
---
## Sampling
Not every trace needs to be recorded. **Sampling** reduces overhead:
### Head Sampling (at trace start)
```
Request arrives → Random 10% chance → Record or skip entire trace
```
- ✅ Low overhead
- ❌ May miss interesting traces
### Tail Sampling (after trace completes)
```
Trace completes → Collector evaluates:
- Error? → KEEP
- Slow? → KEEP
- Normal? → Sample 10%
```
- ✅ Never loses important traces
- ❌ Higher memory usage at collector
---
## Key Benefits for rippled
| Challenge | How Tracing Helps |
| ---------------------------------- | ---------------------------------------- |
| "Where is my transaction?" | Follow trace across all nodes it touched |
| "Why was consensus slow?" | See timing breakdown of each phase |
| "Which node is the bottleneck?" | Compare span durations across nodes |
| "What happened during the outage?" | Correlate errors across the network |
---
## Glossary
| Term | Definition |
| ------------------- | --------------------------------------------------------------- |
| **Trace** | Complete journey of a request, identified by `trace_id` |
| **Span** | Single operation within a trace |
| **Context** | Data propagated between services (`trace_id`, `span_id`, flags) |
| **Instrumentation** | Code that creates spans and propagates context |
| **Collector** | Service that receives, processes, and exports traces |
| **Backend** | Storage/visualization system (Jaeger, Tempo, etc.) |
| **Head Sampling** | Sampling decision at trace start |
| **Tail Sampling** | Sampling decision after trace completes |
---
_Next: [Architecture Analysis](./01-architecture-analysis.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,330 @@
# Architecture Analysis
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Design Decisions](./02-design-decisions.md) | [Implementation Strategy](./03-implementation-strategy.md)
---
## 1.1 Current rippled Architecture Overview
The rippled node software consists of several interconnected components that need instrumentation for distributed tracing:
```mermaid
flowchart TB
subgraph rippled["rippled Node"]
subgraph services["Core Services"]
RPC["RPC Server<br/>(HTTP/WS/gRPC)"]
Overlay["Overlay<br/>(P2P Network)"]
Consensus["Consensus<br/>(RCLConsensus)"]
end
JobQueue["JobQueue<br/>(Thread Pool)"]
subgraph processing["Processing Layer"]
NetworkOPs["NetworkOPs<br/>(Tx Processing)"]
LedgerMaster["LedgerMaster<br/>(Ledger Mgmt)"]
NodeStore["NodeStore<br/>(Database)"]
end
subgraph observability["Existing Observability"]
PerfLog["PerfLog<br/>(JSON)"]
Insight["Insight<br/>(StatsD)"]
Logging["Logging<br/>(Journal)"]
end
services --> JobQueue
JobQueue --> processing
end
style rippled fill:#424242,stroke:#212121,color:#ffffff
style services fill:#1565c0,stroke:#0d47a1,color:#ffffff
style processing fill:#2e7d32,stroke:#1b5e20,color:#ffffff
style observability fill:#e65100,stroke:#bf360c,color:#ffffff
```
---
## 1.2 Key Components for Instrumentation
| Component | Location | Purpose | Trace Value |
| ----------------- | ------------------------------------------ | ------------------------ | ---------------------------- |
| **Overlay** | `src/xrpld/overlay/` | P2P communication | Message propagation timing |
| **PeerImp** | `src/xrpld/overlay/detail/PeerImp.cpp` | Individual peer handling | Per-peer latency |
| **RCLConsensus** | `src/xrpld/app/consensus/RCLConsensus.cpp` | Consensus algorithm | Round timing, phase analysis |
| **NetworkOPs** | `src/xrpld/app/misc/NetworkOPs.cpp` | Transaction processing | Tx lifecycle tracking |
| **ServerHandler** | `src/xrpld/rpc/detail/ServerHandler.cpp` | RPC entry point | Request latency |
| **RPCHandler** | `src/xrpld/rpc/detail/RPCHandler.cpp` | Command execution | Per-command timing |
| **JobQueue** | `src/xrpl/core/JobQueue.h` | Async task execution | Queue wait times |
---
## 1.3 Transaction Flow Diagram
Transaction flow spans multiple nodes in the network. Each node creates linked spans to form a distributed trace:
```mermaid
sequenceDiagram
participant Client
participant PeerA as Peer A (Receive)
participant PeerB as Peer B (Relay)
participant PeerC as Peer C (Validate)
Client->>PeerA: 1. Submit TX
rect rgb(230, 245, 255)
Note over PeerA: tx.receive SPAN START
PeerA->>PeerA: HashRouter Deduplication
PeerA->>PeerA: tx.validate (child span)
end
PeerA->>PeerB: 2. Relay TX (with trace ctx)
rect rgb(230, 245, 255)
Note over PeerB: tx.receive (linked span)
end
PeerB->>PeerC: 3. Relay TX
rect rgb(230, 245, 255)
Note over PeerC: tx.receive (linked span)
PeerC->>PeerC: tx.process
end
Note over Client,PeerC: DISTRIBUTED TRACE (same trace_id: abc123)
```
### Trace Structure
```
trace_id: abc123
├── span: tx.receive (Peer A)
│ ├── span: tx.validate
│ └── span: tx.relay
├── span: tx.receive (Peer B) [parent: Peer A]
│ └── span: tx.relay
└── span: tx.receive (Peer C) [parent: Peer B]
└── span: tx.process
```
---
## 1.4 Consensus Round Flow
Consensus rounds are multi-phase operations that benefit significantly from tracing:
```mermaid
flowchart TB
subgraph round["consensus.round (root span)"]
attrs["Attributes:<br/>xrpl.consensus.ledger.seq = 12345678<br/>xrpl.consensus.mode = proposing<br/>xrpl.consensus.proposers = 35"]
subgraph open["consensus.phase.open"]
open_desc["Duration: ~3s<br/>Waiting for transactions"]
end
subgraph establish["consensus.phase.establish"]
est_attrs["proposals_received = 28<br/>disputes_resolved = 3"]
est_children["├── consensus.proposal.receive (×28)<br/>├── consensus.proposal.send (×1)<br/>└── consensus.dispute.resolve (×3)"]
end
subgraph accept["consensus.phase.accept"]
acc_attrs["transactions_applied = 150<br/>ledger.hash = DEF456..."]
acc_children["├── ledger.build<br/>└── ledger.validate"]
end
attrs --> open
open --> establish
establish --> accept
end
style round fill:#f57f17,stroke:#e65100,color:#ffffff
style open fill:#1565c0,stroke:#0d47a1,color:#ffffff
style establish fill:#2e7d32,stroke:#1b5e20,color:#ffffff
style accept fill:#c2185b,stroke:#880e4f,color:#ffffff
```
---
## 1.5 RPC Request Flow
RPC requests support W3C Trace Context headers for distributed tracing across services:
```mermaid
flowchart TB
subgraph request["rpc.request (root span)"]
http["HTTP Request<br/>POST /<br/>traceparent: 00-abc123...-def456...-01"]
attrs["Attributes:<br/>http.method = POST<br/>net.peer.ip = 192.168.1.100<br/>xrpl.rpc.command = submit"]
subgraph enqueue["jobqueue.enqueue"]
job_attr["xrpl.job.type = jtCLIENT_RPC"]
end
subgraph command["rpc.command.submit"]
cmd_attrs["xrpl.rpc.version = 2<br/>xrpl.rpc.role = user"]
cmd_children["├── tx.deserialize<br/>├── tx.validate_local<br/>└── tx.submit_to_network"]
end
response["Response: 200 OK<br/>Duration: 45ms"]
http --> attrs
attrs --> enqueue
enqueue --> command
command --> response
end
style request fill:#2e7d32,stroke:#1b5e20,color:#ffffff
style enqueue fill:#1565c0,stroke:#0d47a1,color:#ffffff
style command fill:#e65100,stroke:#bf360c,color:#ffffff
```
---
## 1.6 Key Trace Points
The following table identifies priority instrumentation points across the codebase:
| Category | Span Name | File | Method | Priority |
| --------------- | ---------------------- | -------------------- | ---------------------- | -------- |
| **Transaction** | `tx.receive` | `PeerImp.cpp` | `handleTransaction()` | High |
| **Transaction** | `tx.validate` | `NetworkOPs.cpp` | `processTransaction()` | High |
| **Transaction** | `tx.process` | `NetworkOPs.cpp` | `doTransactionSync()` | High |
| **Transaction** | `tx.relay` | `OverlayImpl.cpp` | `relay()` | Medium |
| **Consensus** | `consensus.round` | `RCLConsensus.cpp` | `startRound()` | High |
| **Consensus** | `consensus.phase.*` | `Consensus.h` | `timerEntry()` | High |
| **Consensus** | `consensus.proposal.*` | `RCLConsensus.cpp` | `peerProposal()` | Medium |
| **RPC** | `rpc.request` | `ServerHandler.cpp` | `onRequest()` | High |
| **RPC** | `rpc.command.*` | `RPCHandler.cpp` | `doCommand()` | High |
| **Peer** | `peer.connect` | `OverlayImpl.cpp` | `onHandoff()` | Low |
| **Peer** | `peer.message.*` | `PeerImp.cpp` | `onMessage()` | Low |
| **Ledger** | `ledger.acquire` | `InboundLedgers.cpp` | `acquire()` | Medium |
| **Ledger** | `ledger.build` | `RCLConsensus.cpp` | `buildLCL()` | High |
---
## 1.7 Instrumentation Priority
```mermaid
quadrantChart
title Instrumentation Priority Matrix
x-axis Low Complexity --> High Complexity
y-axis Low Value --> High Value
quadrant-1 Implement First
quadrant-2 Plan Carefully
quadrant-3 Quick Wins
quadrant-4 Consider Later
RPC Tracing: [0.3, 0.85]
Transaction Tracing: [0.65, 0.92]
Consensus Tracing: [0.75, 0.87]
Peer Message Tracing: [0.4, 0.3]
Ledger Acquisition: [0.5, 0.6]
JobQueue Tracing: [0.35, 0.5]
```
---
## 1.8 Observable Outcomes
After implementing OpenTelemetry, operators and developers will gain visibility into the following:
### 1.8.1 What You Will See: Traces
| Trace Type | Description | Example Query in Grafana/Tempo |
| -------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| **Transaction Lifecycle** | Full journey from RPC submission through validation, relay, consensus, and ledger inclusion | `{service.name="rippled" && xrpl.tx.hash="ABC123..."}` |
| **Cross-Node Propagation** | Transaction path across multiple rippled nodes with timing | `{xrpl.tx.relay_count > 0}` |
| **Consensus Rounds** | Complete round with all phases (open, establish, accept) | `{span.name=~"consensus.round.*"}` |
| **RPC Request Processing** | Individual command execution with timing breakdown | `{xrpl.rpc.command="account_info"}` |
| **Ledger Acquisition** | Peer-to-peer ledger data requests and responses | `{span.name="ledger.acquire"}` |
### 1.8.2 What You Will See: Metrics (Derived from Traces)
| Metric | Description | Dashboard Panel |
| ----------------------------- | -------------------------------------- | --------------------------- |
| **RPC Latency (p50/p95/p99)** | Response time distribution per command | Heatmap by command |
| **Transaction Throughput** | Transactions processed per second | Time series graph |
| **Consensus Round Duration** | Time to complete consensus phases | Histogram |
| **Cross-Node Latency** | Time for transaction to reach N nodes | Line chart with percentiles |
| **Error Rate** | Failed transactions/RPC calls by type | Stacked bar chart |
### 1.8.3 Concrete Dashboard Examples
**Transaction Trace View (Jaeger/Tempo):**
```
┌────────────────────────────────────────────────────────────────────────────────┐
│ Trace: abc123... (Transaction Submission) Duration: 847ms │
├────────────────────────────────────────────────────────────────────────────────┤
│ ├── rpc.request [ServerHandler] ████░░░░░░ 45ms │
│ │ └── rpc.command.submit [RPCHandler] ████░░░░░░ 42ms │
│ │ └── tx.receive [NetworkOPs] ███░░░░░░░ 35ms │
│ │ ├── tx.validate [TxQ] █░░░░░░░░░ 8ms │
│ │ └── tx.relay [Overlay] ██░░░░░░░░ 15ms │
│ │ ├── tx.receive [Node-B] █████░░░░░ 52ms │
│ │ │ └── tx.relay [Node-B] ██░░░░░░░░ 18ms │
│ │ └── tx.receive [Node-C] ██████░░░░ 65ms │
│ └── consensus.round [RCLConsensus] ████████░░ 720ms │
│ ├── consensus.phase.open ██░░░░░░░░ 180ms │
│ ├── consensus.phase.establish █████░░░░░ 480ms │
│ └── consensus.phase.accept █░░░░░░░░░ 60ms │
└────────────────────────────────────────────────────────────────────────────────┘
```
**RPC Performance Dashboard Panel:**
```
┌─────────────────────────────────────────────────────────────┐
│ RPC Command Latency (Last 1 Hour) │
├─────────────────────────────────────────────────────────────┤
│ Command │ p50 │ p95 │ p99 │ Errors │ Rate │
│──────────────────┼────────┼────────┼────────┼────────┼──────│
│ account_info │ 12ms │ 45ms │ 89ms │ 0.1% │ 150/s│
│ submit │ 35ms │ 120ms │ 250ms │ 2.3% │ 45/s│
│ ledger │ 8ms │ 25ms │ 55ms │ 0.0% │ 80/s│
│ tx │ 15ms │ 50ms │ 100ms │ 0.5% │ 60/s│
│ server_info │ 5ms │ 12ms │ 20ms │ 0.0% │ 200/s│
└─────────────────────────────────────────────────────────────┘
```
**Consensus Health Dashboard Panel:**
```mermaid
---
config:
xyChart:
width: 1200
height: 400
plotReservedSpacePercent: 50
chartOrientation: vertical
themeVariables:
xyChart:
plotColorPalette: "#3498db"
---
xychart-beta
title "Consensus Round Duration (Last 24 Hours)"
x-axis "Time of Day (Hours)" [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]
y-axis "Duration (seconds)" 1 --> 5
line [2.1, 2.3, 2.5, 2.4, 2.8, 1.6, 3.2, 3.0, 3.5, 1.3, 3.8, 3.6, 4.0, 3.2, 4.3, 4.1, 4.5, 4.3, 4.2, 2.4, 4.8, 4.6, 4.9, 4.7, 5.0, 4.9, 4.8, 2.6, 4.7, 4.5, 4.2, 4.0, 2.5, 3.7, 3.2, 3.4, 2.9, 3.1, 2.6, 2.8, 2.3, 1.5, 2.7, 2.4, 2.5, 2.3, 2.2, 2.1, 2.0]
```
### 1.8.4 Operator Actionable Insights
| Scenario | What You'll See | Action |
| --------------------- | ---------------------------------------------------------------------------- | -------------------------------- |
| **Slow RPC** | Span showing which phase is slow (parsing, execution, serialization) | Optimize specific code path |
| **Transaction Stuck** | Trace stops at validation; error attribute shows reason | Fix transaction parameters |
| **Consensus Delay** | Phase.establish taking too long; proposer attribute shows missing validators | Investigate network connectivity |
| **Memory Spike** | Large batch of spans correlating with memory increase | Tune batch_size or sampling |
| **Network Partition** | Traces missing cross-node links for specific peer | Check peer connectivity |
### 1.8.5 Developer Debugging Workflow
1. **Find Transaction**: Query by `xrpl.tx.hash` to get full trace
2. **Identify Bottleneck**: Look at span durations to find slowest component
3. **Check Attributes**: Review `xrpl.tx.validity`, `xrpl.rpc.status` for errors
4. **Correlate Logs**: Use `trace_id` to find related PerfLog entries
5. **Compare Nodes**: Filter by `service.instance.id` to compare behavior across nodes
---
_Next: [Design Decisions](./02-design-decisions.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,494 @@
# Design Decisions
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Architecture Analysis](./01-architecture-analysis.md) | [Code Samples](./04-code-samples.md)
---
## 2.1 OpenTelemetry Components
### 2.1.1 SDK Selection
**Primary Choice**: OpenTelemetry C++ SDK (`opentelemetry-cpp`)
| Component | Purpose | Required |
| --------------------------------------- | ---------------------- | ----------- |
| `opentelemetry-cpp::api` | Tracing API headers | Yes |
| `opentelemetry-cpp::sdk` | SDK implementation | Yes |
| `opentelemetry-cpp::ext` | Extensions (exporters) | Yes |
| `opentelemetry-cpp::otlp_grpc_exporter` | OTLP/gRPC export | Recommended |
| `opentelemetry-cpp::otlp_http_exporter` | OTLP/HTTP export | Alternative |
### 2.1.2 Instrumentation Strategy
**Manual Instrumentation** (recommended):
| Approach | Pros | Cons |
| ---------- | ----------------------------------------------------------------- | ------------------------------------------------------- |
| **Manual** | Precise control, optimized placement, rippled-specific attributes | More development effort |
| **Auto** | Less code, automatic coverage | Less control, potential overhead, limited customization |
---
## 2.2 Exporter Configuration
```mermaid
flowchart TB
subgraph nodes["rippled Nodes"]
node1["rippled<br/>Node 1"]
node2["rippled<br/>Node 2"]
node3["rippled<br/>Node 3"]
end
collector["OpenTelemetry<br/>Collector<br/>(sidecar or standalone)"]
subgraph backends["Observability Backends"]
jaeger["Jaeger<br/>(Dev)"]
tempo["Tempo<br/>(Prod)"]
elastic["Elastic<br/>APM"]
end
node1 -->|"OTLP/gRPC<br/>:4317"| collector
node2 -->|"OTLP/gRPC<br/>:4317"| collector
node3 -->|"OTLP/gRPC<br/>:4317"| collector
collector --> jaeger
collector --> tempo
collector --> elastic
style nodes fill:#0d47a1,stroke:#082f6a,color:#ffffff
style backends fill:#1b5e20,stroke:#0d3d14,color:#ffffff
style collector fill:#bf360c,stroke:#8c2809,color:#ffffff
```
### 2.2.1 OTLP/gRPC (Recommended)
```cpp
// Configuration for OTLP over gRPC
namespace otlp = opentelemetry::exporter::otlp;
otlp::OtlpGrpcExporterOptions opts;
opts.endpoint = "localhost:4317";
opts.use_ssl_credentials = true;
opts.ssl_credentials_cacert_path = "/path/to/ca.crt";
```
### 2.2.2 OTLP/HTTP (Alternative)
```cpp
// Configuration for OTLP over HTTP
namespace otlp = opentelemetry::exporter::otlp;
otlp::OtlpHttpExporterOptions opts;
opts.url = "http://localhost:4318/v1/traces";
opts.content_type = otlp::HttpRequestContentType::kJson; // or kBinary
```
---
## 2.3 Span Naming Conventions
### 2.3.1 Naming Schema
```
<component>.<operation>[.<sub-operation>]
```
**Examples**:
- `tx.receive` - Transaction received from peer
- `consensus.phase.establish` - Consensus establish phase
- `rpc.command.server_info` - server_info RPC command
### 2.3.2 Complete Span Catalog
```yaml
# Transaction Spans
tx:
receive: "Transaction received from network"
validate: "Transaction signature/format validation"
process: "Full transaction processing"
relay: "Transaction relay to peers"
apply: "Apply transaction to ledger"
# Consensus Spans
consensus:
round: "Complete consensus round"
phase:
open: "Open phase - collecting transactions"
establish: "Establish phase - reaching agreement"
accept: "Accept phase - applying consensus"
proposal:
receive: "Receive peer proposal"
send: "Send our proposal"
validation:
receive: "Receive peer validation"
send: "Send our validation"
# RPC Spans
rpc:
request: "HTTP/WebSocket request handling"
command:
"*": "Specific RPC command (dynamic)"
# Peer Spans
peer:
connect: "Peer connection establishment"
disconnect: "Peer disconnection"
message:
send: "Send protocol message"
receive: "Receive protocol message"
# Ledger Spans
ledger:
acquire: "Ledger acquisition from network"
build: "Build new ledger"
validate: "Ledger validation"
close: "Close ledger"
# Job Spans
job:
enqueue: "Job added to queue"
execute: "Job execution"
```
---
## 2.4 Attribute Schema
### 2.4.1 Resource Attributes (Set Once at Startup)
```cpp
// Standard OpenTelemetry semantic conventions
resource::SemanticConventions::SERVICE_NAME = "rippled"
resource::SemanticConventions::SERVICE_VERSION = BuildInfo::getVersionString()
resource::SemanticConventions::SERVICE_INSTANCE_ID = <node_public_key_base58>
// Custom rippled attributes
"xrpl.network.id" = <network_id> // e.g., 0 for mainnet
"xrpl.network.type" = "mainnet" | "testnet" | "devnet" | "standalone"
"xrpl.node.type" = "validator" | "stock" | "reporting"
"xrpl.node.cluster" = <cluster_name> // If clustered
```
### 2.4.2 Span Attributes by Category
#### Transaction Attributes
```cpp
"xrpl.tx.hash" = string // Transaction hash (hex)
"xrpl.tx.type" = string // "Payment", "OfferCreate", etc.
"xrpl.tx.account" = string // Source account (redacted in prod)
"xrpl.tx.sequence" = int64 // Account sequence number
"xrpl.tx.fee" = int64 // Fee in drops
"xrpl.tx.result" = string // "tesSUCCESS", "tecPATH_DRY", etc.
"xrpl.tx.ledger_index" = int64 // Ledger containing transaction
```
#### Consensus Attributes
```cpp
"xrpl.consensus.round" = int64 // Round number
"xrpl.consensus.phase" = string // "open", "establish", "accept"
"xrpl.consensus.mode" = string // "proposing", "observing", etc.
"xrpl.consensus.proposers" = int64 // Number of proposers
"xrpl.consensus.ledger.prev" = string // Previous ledger hash
"xrpl.consensus.ledger.seq" = int64 // Ledger sequence
"xrpl.consensus.tx_count" = int64 // Transactions in consensus set
"xrpl.consensus.duration_ms" = float64 // Round duration
```
#### RPC Attributes
```cpp
"xrpl.rpc.command" = string // Command name
"xrpl.rpc.version" = int64 // API version
"xrpl.rpc.role" = string // "admin" or "user"
"xrpl.rpc.params" = string // Sanitized parameters (optional)
```
#### Peer & Message Attributes
```cpp
"xrpl.peer.id" = string // Peer public key (base58)
"xrpl.peer.address" = string // IP:port
"xrpl.peer.latency_ms" = float64 // Measured latency
"xrpl.peer.cluster" = string // Cluster name if clustered
"xrpl.message.type" = string // Protocol message type name
"xrpl.message.size_bytes" = int64 // Message size
"xrpl.message.compressed" = bool // Whether compressed
```
#### Ledger & Job Attributes
```cpp
"xrpl.ledger.hash" = string // Ledger hash
"xrpl.ledger.index" = int64 // Ledger sequence/index
"xrpl.ledger.close_time" = int64 // Close time (epoch)
"xrpl.ledger.tx_count" = int64 // Transaction count
"xrpl.job.type" = string // Job type name
"xrpl.job.queue_ms" = float64 // Time spent in queue
"xrpl.job.worker" = int64 // Worker thread ID
```
### 2.4.3 Data Collection Summary
The following table summarizes what data is collected by category:
| Category | Attributes Collected | Purpose |
| --------------- | -------------------------------------------------------------------- | --------------------------- |
| **Transaction** | `tx.hash`, `tx.type`, `tx.result`, `tx.fee`, `ledger_index` | Trace transaction lifecycle |
| **Consensus** | `round`, `phase`, `mode`, `proposers` (public keys), `duration_ms` | Analyze consensus timing |
| **RPC** | `command`, `version`, `status`, `duration_ms` | Monitor RPC performance |
| **Peer** | `peer.id` (public key), `latency_ms`, `message.type`, `message.size` | Network topology analysis |
| **Ledger** | `ledger.hash`, `ledger.index`, `close_time`, `tx_count` | Ledger progression tracking |
| **Job** | `job.type`, `queue_ms`, `worker` | JobQueue performance |
### 2.4.4 Privacy & Sensitive Data Policy
OpenTelemetry instrumentation is designed to collect **operational metadata only**, never sensitive content.
#### Data NOT Collected
The following data is explicitly **excluded** from telemetry collection:
| Excluded Data | Reason |
| ----------------------- | ----------------------------------------- |
| **Private Keys** | Never exposed; not relevant to tracing |
| **Account Balances** | Financial data; privacy sensitive |
| **Transaction Amounts** | Financial data; privacy sensitive |
| **Raw TX Payloads** | May contain sensitive memo/data fields |
| **Personal Data** | No PII collected |
| **IP Addresses** | Configurable; excluded by default in prod |
#### Privacy Protection Mechanisms
| Mechanism | Description |
| ----------------------------- | ------------------------------------------------------------------------- |
| **Account Hashing** | `xrpl.tx.account` is hashed at collector level before storage |
| **Configurable Redaction** | Sensitive fields can be excluded via `[telemetry]` config section |
| **Sampling** | Only 10% of traces recorded by default, reducing data exposure |
| **Local Control** | Node operators have full control over what gets exported |
| **No Raw Payloads** | Transaction content is never recorded, only metadata (hash, type, result) |
| **Collector-Level Filtering** | Additional redaction/hashing can be configured at OTel Collector |
#### Collector-Level Data Protection
The OpenTelemetry Collector can be configured to hash or redact sensitive attributes before export:
```yaml
processors:
attributes:
actions:
# Hash account addresses before storage
- key: xrpl.tx.account
action: hash
# Remove IP addresses entirely
- key: xrpl.peer.address
action: delete
# Redact specific fields
- key: xrpl.rpc.params
action: delete
```
#### Configuration Options for Privacy
In `rippled.cfg`, operators can control data collection granularity:
```ini
[telemetry]
enabled=1
# Disable collection of specific components
trace_transactions=1
trace_consensus=1
trace_rpc=1
trace_peer=0 # Disable peer tracing (high volume, includes addresses)
# Redact specific attributes
redact_account=1 # Hash account addresses before export
redact_peer_address=1 # Remove peer IP addresses
```
> **Key Principle**: Telemetry collects **operational metadata** (timing, counts, hashes) — never **sensitive content** (keys, balances, amounts, raw payloads).
---
## 2.5 Context Propagation Design
### 2.5.1 Propagation Boundaries
```mermaid
flowchart TB
subgraph http["HTTP/WebSocket (RPC)"]
w3c["W3C Trace Context Headers:<br/>traceparent: 00-{trace_id}-{span_id}-{flags}<br/>tracestate: rippled=<state>"]
end
subgraph protobuf["Protocol Buffers (P2P)"]
proto["message TraceContext {<br/> bytes trace_id = 1; // 16 bytes<br/> bytes span_id = 2; // 8 bytes<br/> uint32 trace_flags = 3;<br/> string trace_state = 4;<br/>}"]
end
subgraph jobqueue["JobQueue (Internal Async)"]
job["Context captured at job creation,<br/>restored at execution<br/><br/>class Job {<br/> opentelemetry::context::Context traceContext_;<br/>};"]
end
style http fill:#0d47a1,stroke:#082f6a,color:#ffffff
style protobuf fill:#1b5e20,stroke:#0d3d14,color:#ffffff
style jobqueue fill:#bf360c,stroke:#8c2809,color:#ffffff
```
---
## 2.6 Integration with Existing Observability
### 2.6.1 Existing Frameworks Comparison
rippled already has two observability mechanisms. OpenTelemetry complements (not replaces) them:
| Aspect | PerfLog | Beast Insight (StatsD) | OpenTelemetry |
| --------------------- | ----------------------------- | ---------------------------- | ------------------------- |
| **Type** | Logging | Metrics | Distributed Tracing |
| **Data** | JSON log entries | Counters, gauges, histograms | Spans with context |
| **Scope** | Single node | Single node | **Cross-node** |
| **Output** | `perf.log` file | StatsD server | OTLP Collector |
| **Question answered** | "What happened on this node?" | "How many? How fast?" | "What was the journey?" |
| **Correlation** | By timestamp | By metric name | By `trace_id` |
| **Overhead** | Low (file I/O) | Low (UDP packets) | Low-Medium (configurable) |
### 2.6.2 What Each Framework Does Best
#### PerfLog
- **Purpose**: Detailed local event logging for RPC and job execution
- **Strengths**:
- Rich JSON output with timing data
- Already integrated in RPC handlers
- File-based, no external dependencies
- **Limitations**:
- Single-node only (no cross-node correlation)
- No parent-child relationships between events
- Manual log parsing required
```json
// Example PerfLog entry
{
"time": "2024-01-15T10:30:00.123Z",
"method": "submit",
"duration_us": 1523,
"result": "tesSUCCESS"
}
```
#### Beast Insight (StatsD)
- **Purpose**: Real-time metrics for monitoring dashboards
- **Strengths**:
- Aggregated metrics (counters, gauges, histograms)
- Low overhead (UDP, fire-and-forget)
- Good for alerting thresholds
- **Limitations**:
- No request-level detail
- No causal relationships
- Single-node perspective
```cpp
// Example StatsD usage in rippled
insight.increment("rpc.submit.count");
insight.gauge("ledger.age", age);
insight.timing("consensus.round", duration);
```
#### OpenTelemetry (NEW)
- **Purpose**: Distributed request tracing across nodes
- **Strengths**:
- **Cross-node correlation** via `trace_id`
- Parent-child span relationships
- Rich attributes per span
- Industry standard (CNCF)
- **Limitations**:
- Requires collector infrastructure
- Higher complexity than logging
```cpp
// Example OpenTelemetry span
auto span = telemetry.startSpan("tx.relay");
span->SetAttribute("tx.hash", hash);
span->SetAttribute("peer.id", peerId);
// Span automatically linked to parent via context
```
### 2.6.3 When to Use Each
| Scenario | PerfLog | StatsD | OpenTelemetry |
| --------------------------------------- | ---------- | ------ | ------------- |
| "How many TXs per second?" | ❌ | ✅ | ❌ |
| "What's the p99 RPC latency?" | ❌ | ✅ | ✅ |
| "Why was this specific TX slow?" | ⚠️ partial | ❌ | ✅ |
| "Which node delayed consensus?" | ❌ | ❌ | ✅ |
| "What happened on node X at time T?" | ✅ | ❌ | ✅ |
| "Show me the TX journey across 5 nodes" | ❌ | ❌ | ✅ |
### 2.6.4 Coexistence Strategy
```mermaid
flowchart TB
subgraph rippled["rippled Process"]
perflog["PerfLog<br/>(JSON to file)"]
insight["Beast Insight<br/>(StatsD)"]
otel["OpenTelemetry<br/>(Tracing)"]
end
perflog --> perffile["perf.log"]
insight --> statsd["StatsD Server"]
otel --> collector["OTLP Collector"]
perffile --> grafana["Grafana<br/>(Unified UI)"]
statsd --> grafana
collector --> grafana
style rippled fill:#212121,stroke:#0a0a0a,color:#ffffff
style grafana fill:#bf360c,stroke:#8c2809,color:#ffffff
```
### 2.6.5 Correlation with PerfLog
Trace IDs can be correlated with existing PerfLog entries for comprehensive debugging:
```cpp
// In RPCHandler.cpp - correlate trace with PerfLog
Status doCommand(RPC::JsonContext& context, Json::Value& result)
{
// Start OpenTelemetry span
auto span = context.app.getTelemetry().startSpan(
"rpc.command." + context.method);
// Get trace ID for correlation
auto traceId = span->GetContext().trace_id().IsValid()
? toHex(span->GetContext().trace_id())
: "";
// Use existing PerfLog with trace correlation
auto const curId = context.app.getPerfLog().currentId();
context.app.getPerfLog().rpcStart(context.method, curId);
// Future: Add trace ID to PerfLog entry
// context.app.getPerfLog().setTraceId(curId, traceId);
try {
auto ret = handler(context, result);
context.app.getPerfLog().rpcFinish(context.method, curId);
span->SetStatus(opentelemetry::trace::StatusCode::kOk);
return ret;
} catch (std::exception const& e) {
context.app.getPerfLog().rpcError(context.method, curId);
span->RecordException(e);
span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
throw;
}
}
```
---
_Previous: [Architecture Analysis](./01-architecture-analysis.md)_ | _Next: [Implementation Strategy](./03-implementation-strategy.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,451 @@
# Implementation Strategy
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Code Samples](./04-code-samples.md) | [Configuration Reference](./05-configuration-reference.md)
---
## 3.1 Directory Structure
The telemetry implementation follows rippled's existing code organization pattern:
```
include/xrpl/
├── telemetry/
│ ├── Telemetry.h # Main telemetry interface
│ ├── TelemetryConfig.h # Configuration structures
│ ├── TraceContext.h # Context propagation utilities
│ ├── SpanGuard.h # RAII span management
│ └── SpanAttributes.h # Attribute helper functions
src/libxrpl/
├── telemetry/
│ ├── Telemetry.cpp # Implementation
│ ├── TelemetryConfig.cpp # Config parsing
│ ├── TraceContext.cpp # Context serialization
│ └── NullTelemetry.cpp # No-op implementation
src/xrpld/
├── telemetry/
│ ├── TracingInstrumentation.h # Instrumentation macros
│ └── TracingInstrumentation.cpp
```
---
## 3.2 Implementation Approach
<div align="center">
```mermaid
%%{init: {'flowchart': {'nodeSpacing': 20, 'rankSpacing': 30}}}%%
flowchart TB
subgraph phase1["Phase 1: Core"]
direction LR
sdk["SDK Integration"] ~~~ interface["Telemetry Interface"] ~~~ config["Configuration"]
end
subgraph phase2["Phase 2: RPC"]
direction LR
http["HTTP Context"] ~~~ rpc["RPC Handlers"]
end
subgraph phase3["Phase 3: P2P"]
direction LR
proto["Protobuf Context"] ~~~ tx["Transaction Relay"]
end
subgraph phase4["Phase 4: Consensus"]
direction LR
consensus["Consensus Rounds"] ~~~ proposals["Proposals"]
end
phase1 --> phase2 --> phase3 --> phase4
style phase1 fill:#1565c0,stroke:#0d47a1,color:#ffffff
style phase2 fill:#2e7d32,stroke:#1b5e20,color:#ffffff
style phase3 fill:#e65100,stroke:#bf360c,color:#ffffff
style phase4 fill:#c2185b,stroke:#880e4f,color:#ffffff
```
</div>
### Key Principles
1. **Minimal Intrusion**: Instrumentation should not alter existing control flow
2. **Zero-Cost When Disabled**: Use compile-time flags and no-op implementations
3. **Backward Compatibility**: Protocol Buffer extensions use high field numbers
4. **Graceful Degradation**: Tracing failures must not affect node operation
---
## 3.3 Performance Overhead Summary
| Metric | Overhead | Notes |
| ------------- | ---------- | ----------------------------------- |
| CPU | 1-3% | Span creation and attribute setting |
| Memory | 2-5 MB | Batch buffer for pending spans |
| Network | 10-50 KB/s | Compressed OTLP export to collector |
| Latency (p99) | <2% | With proper sampling configuration |
---
## 3.4 Detailed CPU Overhead Analysis
### 3.4.1 Per-Operation Costs
| Operation | Time (ns) | Frequency | Impact |
| --------------------- | --------- | ---------------------- | ---------- |
| Span creation | 200-500 | Every traced operation | Low |
| Span end | 100-200 | Every traced operation | Low |
| SetAttribute (string) | 80-120 | 3-5 per span | Low |
| SetAttribute (int) | 40-60 | 2-3 per span | Negligible |
| AddEvent | 50-80 | 0-2 per span | Negligible |
| Context injection | 150-250 | Per outgoing message | Low |
| Context extraction | 100-180 | Per incoming message | Low |
| GetCurrent context | 10-20 | Thread-local access | Negligible |
### 3.4.2 Transaction Processing Overhead
<div align="center">
```mermaid
%%{init: {'pie': {'textPosition': 0.75}}}%%
pie showData
"tx.receive (800ns)" : 800
"tx.validate (500ns)" : 500
"tx.relay (500ns)" : 500
"Context inject (600ns)" : 600
```
**Transaction Tracing Overhead (~2.4μs total)**
</div>
**Overhead percentage**: 2.4 μs / 200 μs (avg tx processing) = **~1.2%**
### 3.4.3 Consensus Round Overhead
| Operation | Count | Cost (ns) | Total |
| ---------------------- | ----- | --------- | ---------- |
| consensus.round span | 1 | ~1000 | ~1 μs |
| consensus.phase spans | 3 | ~700 | ~2.1 μs |
| proposal.receive spans | ~20 | ~600 | ~12 μs |
| proposal.send spans | ~3 | ~600 | ~1.8 μs |
| Context operations | ~30 | ~200 | ~6 μs |
| **TOTAL** | | | **~23 μs** |
**Overhead percentage**: 23 μs / 3s (typical round) = **~0.0008%** (negligible)
### 3.4.4 RPC Request Overhead
| Operation | Cost (ns) |
| ---------------- | ------------ |
| rpc.request span | ~700 |
| rpc.command span | ~600 |
| Context extract | ~250 |
| Context inject | ~200 |
| **TOTAL** | **~1.75 μs** |
- Fast RPC (1ms): 1.75 μs / 1ms = **~0.175%**
- Slow RPC (100ms): 1.75 μs / 100ms = **~0.002%**
---
## 3.5 Memory Overhead Analysis
### 3.5.1 Static Memory
| Component | Size | Allocated |
| ------------------------ | ----------- | ---------- |
| TracerProvider singleton | ~64 KB | At startup |
| BatchSpanProcessor | ~128 KB | At startup |
| OTLP exporter | ~256 KB | At startup |
| Propagator registry | ~8 KB | At startup |
| **Total static** | **~456 KB** | |
### 3.5.2 Dynamic Memory
| Component | Size per unit | Max units | Peak |
| -------------------- | ------------- | ---------- | ----------- |
| Active span | ~200 bytes | 1000 | ~200 KB |
| Queued span (export) | ~500 bytes | 2048 | ~1 MB |
| Attribute storage | ~50 bytes | 5 per span | Included |
| Context storage | ~64 bytes | Per thread | ~6.4 KB |
| **Total dynamic** | | | **~1.2 MB** |
### 3.5.3 Memory Growth Characteristics
```mermaid
---
config:
xyChart:
width: 700
height: 400
---
xychart-beta
title "Memory Usage vs Span Rate"
x-axis "Spans/second" [0, 200, 400, 600, 800, 1000]
y-axis "Memory (MB)" 0 --> 6
line [1, 1.8, 2.6, 3.4, 4.2, 5]
```
**Notes**:
- Memory increases linearly with span rate
- Batch export prevents unbounded growth
- Queue size is configurable (default 2048 spans)
- At queue limit, oldest spans are dropped (not blocked)
---
## 3.6 Network Overhead Analysis
### 3.6.1 Export Bandwidth
| Sampling Rate | Spans/sec | Bandwidth | Notes |
| ------------- | --------- | --------- | ---------------- |
| 100% | ~500 | ~250 KB/s | Development only |
| 10% | ~50 | ~25 KB/s | Staging |
| 1% | ~5 | ~2.5 KB/s | Production |
| Error-only | ~1 | ~0.5 KB/s | Minimal overhead |
### 3.6.2 Trace Context Propagation
| Message Type | Context Size | Messages/sec | Overhead |
| ---------------------- | ------------ | ------------ | ----------- |
| TMTransaction | 32 bytes | ~100 | ~3.2 KB/s |
| TMProposeSet | 32 bytes | ~10 | ~320 B/s |
| TMValidation | 32 bytes | ~50 | ~1.6 KB/s |
| **Total P2P overhead** | | | **~5 KB/s** |
---
## 3.7 Optimization Strategies
### 3.7.1 Sampling Strategies
```mermaid
flowchart TD
trace["New Trace"]
trace --> errors{"Is Error?"}
errors -->|Yes| sample["SAMPLE"]
errors -->|No| consensus{"Is Consensus?"}
consensus -->|Yes| sample
consensus -->|No| slow{"Is Slow?"}
slow -->|Yes| sample
slow -->|No| prob{"Random < 10%?"}
prob -->|Yes| sample
prob -->|No| drop["DROP"]
style sample fill:#4caf50,stroke:#388e3c,color:#fff
style drop fill:#f44336,stroke:#c62828,color:#fff
```
### 3.7.2 Batch Tuning Recommendations
| Environment | Batch Size | Batch Delay | Max Queue |
| ------------------ | ---------- | ----------- | --------- |
| Low-latency | 128 | 1000ms | 512 |
| High-throughput | 1024 | 10000ms | 8192 |
| Memory-constrained | 256 | 2000ms | 512 |
### 3.7.3 Conditional Instrumentation
```cpp
// Compile-time feature flag
#ifndef XRPL_ENABLE_TELEMETRY
// Zero-cost when disabled
#define XRPL_TRACE_SPAN(t, n) ((void)0)
#endif
// Runtime component filtering
if (telemetry.shouldTracePeer())
{
XRPL_TRACE_SPAN(telemetry, "peer.message.receive");
// ... instrumentation
}
// No overhead when component tracing disabled
```
---
## 3.8 Links to Detailed Documentation
- **[Code Samples](./04-code-samples.md)**: Complete implementation code for all components
- **[Configuration Reference](./05-configuration-reference.md)**: Configuration options and collector setup
- **[Implementation Phases](./06-implementation-phases.md)**: Detailed timeline and milestones
---
## 3.9 Code Intrusiveness Assessment
This section provides a detailed assessment of how intrusive the OpenTelemetry integration is to the existing rippled codebase.
### 3.9.1 Files Modified Summary
| Component | Files Modified | Lines Added | Lines Changed | Architectural Impact |
| --------------------- | -------------- | ----------- | ------------- | -------------------- |
| **Core Telemetry** | 5 new files | ~800 | 0 | None (new module) |
| **Application Init** | 2 files | ~30 | ~5 | Minimal |
| **RPC Layer** | 3 files | ~80 | ~20 | Minimal |
| **Transaction Relay** | 4 files | ~120 | ~40 | Low |
| **Consensus** | 3 files | ~100 | ~30 | Low-Medium |
| **Protocol Buffers** | 1 file | ~25 | 0 | Low |
| **CMake/Build** | 3 files | ~50 | ~10 | Minimal |
| **Total** | **~21 files** | **~1,205** | **~105** | **Low** |
### 3.9.2 Detailed File Impact
```mermaid
pie title Code Changes by Component
"New Telemetry Module" : 800
"Transaction Relay" : 160
"Consensus" : 130
"RPC Layer" : 100
"Application Init" : 35
"Protocol Buffers" : 25
"Build System" : 60
```
#### New Files (No Impact on Existing Code)
| File | Lines | Purpose |
| ---------------------------------------------- | ----- | -------------------- |
| `include/xrpl/telemetry/Telemetry.h` | ~160 | Main interface |
| `include/xrpl/telemetry/SpanGuard.h` | ~120 | RAII wrapper |
| `include/xrpl/telemetry/TraceContext.h` | ~80 | Context propagation |
| `src/xrpld/telemetry/TracingInstrumentation.h` | ~60 | Macros |
| `src/libxrpl/telemetry/Telemetry.cpp` | ~200 | Implementation |
| `src/libxrpl/telemetry/TelemetryConfig.cpp` | ~60 | Config parsing |
| `src/libxrpl/telemetry/NullTelemetry.cpp` | ~40 | No-op implementation |
#### Modified Files (Existing Rippled Code)
| File | Lines Added | Lines Changed | Risk Level |
| ------------------------------------------------- | ----------- | ------------- | ---------- |
| `src/xrpld/app/main/Application.cpp` | ~15 | ~3 | Low |
| `include/xrpl/app/main/Application.h` | ~5 | ~2 | Low |
| `src/xrpld/rpc/detail/ServerHandler.cpp` | ~40 | ~10 | Low |
| `src/xrpld/rpc/handlers/*.cpp` | ~30 | ~8 | Low |
| `src/xrpld/overlay/detail/PeerImp.cpp` | ~60 | ~15 | Medium |
| `src/xrpld/overlay/detail/OverlayImpl.cpp` | ~30 | ~10 | Medium |
| `src/xrpld/app/consensus/RCLConsensus.cpp` | ~50 | ~15 | Medium |
| `src/xrpld/app/consensus/RCLConsensusAdaptor.cpp` | ~40 | ~12 | Medium |
| `src/xrpld/core/JobQueue.cpp` | ~20 | ~5 | Low |
| `src/xrpld/overlay/detail/ripple.proto` | ~25 | 0 | Low |
| `CMakeLists.txt` | ~40 | ~8 | Low |
| `cmake/FindOpenTelemetry.cmake` | ~50 | 0 | None (new) |
### 3.9.3 Risk Assessment by Component
<div align="center">
**Do First** ↖ ↗ **Plan Carefully**
```mermaid
quadrantChart
title Code Intrusiveness Risk Matrix
x-axis Low Risk --> High Risk
y-axis Low Value --> High Value
RPC Tracing: [0.2, 0.8]
Transaction Relay: [0.5, 0.9]
Consensus Tracing: [0.7, 0.95]
Peer Message Tracing: [0.8, 0.4]
JobQueue Context: [0.4, 0.5]
Ledger Acquisition: [0.5, 0.6]
```
**Optional** ↙ ↘ **Avoid**
</div>
#### Risk Level Definitions
| Risk Level | Definition | Mitigation |
| ---------- | ---------------------------------------------------------------- | ---------------------------------- |
| **Low** | Additive changes only; no modification to existing logic | Standard code review |
| **Medium** | Minor modifications to existing functions; clear boundaries | Comprehensive unit tests |
| **High** | Changes to core logic or data structures; potential side effects | Integration tests + staged rollout |
### 3.9.4 Architectural Impact Assessment
| Aspect | Impact | Justification |
| -------------------- | ------- | --------------------------------------------------------------------- |
| **Data Flow** | None | Tracing is purely observational; no business logic changes |
| **Threading Model** | Minimal | Context propagation uses thread-local storage (standard OTel pattern) |
| **Memory Model** | Low | Bounded queues prevent unbounded growth; RAII ensures cleanup |
| **Network Protocol** | Low | Optional fields in protobuf (high field numbers); backward compatible |
| **Configuration** | None | New config section; existing configs unaffected |
| **Build System** | Low | Optional CMake flag; builds work without OpenTelemetry |
| **Dependencies** | Low | OpenTelemetry SDK is optional; null implementation when disabled |
### 3.9.5 Backward Compatibility
| Compatibility | Status | Notes |
| --------------- | ------- | ----------------------------------------------------- |
| **Config File** | ✅ Full | New `[telemetry]` section is optional |
| **Protocol** | ✅ Full | Optional protobuf fields with high field numbers |
| **Build** | ✅ Full | `XRPL_ENABLE_TELEMETRY=OFF` produces identical binary |
| **Runtime** | ✅ Full | `enabled=0` produces zero overhead |
| **API** | ✅ Full | No changes to public RPC or P2P APIs |
### 3.9.6 Rollback Strategy
If issues are discovered after deployment:
1. **Immediate**: Set `enabled=0` in config and restart (zero code change)
2. **Quick**: Rebuild with `XRPL_ENABLE_TELEMETRY=OFF`
3. **Complete**: Revert telemetry commits (clean separation makes this easy)
### 3.9.7 Code Change Examples
**Minimal RPC Instrumentation (Low Intrusiveness):**
```cpp
// Before
void ServerHandler::onRequest(...) {
auto result = processRequest(req);
send(result);
}
// After (only ~10 lines added)
void ServerHandler::onRequest(...) {
XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.request"); // +1 line
XRPL_TRACE_SET_ATTR("xrpl.rpc.command", command); // +1 line
auto result = processRequest(req);
XRPL_TRACE_SET_ATTR("xrpl.rpc.status", status); // +1 line
send(result);
}
```
**Consensus Instrumentation (Medium Intrusiveness):**
```cpp
// Before
void RCLConsensusAdaptor::startRound(...) {
// ... existing logic
}
// After (context storage required)
void RCLConsensusAdaptor::startRound(...) {
XRPL_TRACE_CONSENSUS(app_.getTelemetry(), "consensus.round");
XRPL_TRACE_SET_ATTR("xrpl.consensus.ledger.seq", seq);
// Store context for child spans in phase transitions
currentRoundContext_ = _xrpl_guard_->context(); // New member variable
// ... existing logic unchanged
}
```
---
_Previous: [Design Decisions](./02-design-decisions.md)_ | _Next: [Code Samples](./04-code-samples.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,982 @@
# Code Samples
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Implementation Strategy](./03-implementation-strategy.md) | [Configuration Reference](./05-configuration-reference.md)
---
## 4.1 Core Interfaces
### 4.1.1 Main Telemetry Interface
```cpp
// include/xrpl/telemetry/Telemetry.h
#pragma once
#include <xrpl/telemetry/TelemetryConfig.h>
#include <opentelemetry/trace/tracer.h>
#include <opentelemetry/trace/span.h>
#include <opentelemetry/context/context.h>
#include <memory>
#include <string>
#include <string_view>
namespace xrpl {
namespace telemetry {
/**
* Main telemetry interface for OpenTelemetry integration.
*
* This class provides the primary API for distributed tracing in rippled.
* It manages the OpenTelemetry SDK lifecycle and provides convenience
* methods for creating spans and propagating context.
*/
class Telemetry
{
public:
/**
* Configuration for the telemetry system.
*/
struct Setup
{
bool enabled = false;
std::string serviceName = "rippled";
std::string serviceVersion;
std::string serviceInstanceId; // Node public key
// Exporter configuration
std::string exporterType = "otlp_grpc"; // "otlp_grpc", "otlp_http", "none"
std::string exporterEndpoint = "localhost:4317";
bool useTls = false;
std::string tlsCertPath;
// Sampling configuration
double samplingRatio = 1.0; // 1.0 = 100% sampling
// Batch processor settings
std::uint32_t batchSize = 512;
std::chrono::milliseconds batchDelay{5000};
std::uint32_t maxQueueSize = 2048;
// Network attributes
std::uint32_t networkId = 0;
std::string networkType = "mainnet";
// Component filtering
bool traceTransactions = true;
bool traceConsensus = true;
bool traceRpc = true;
bool tracePeer = false; // High volume, disabled by default
bool traceLedger = true;
};
virtual ~Telemetry() = default;
// ═══════════════════════════════════════════════════════════════════════
// LIFECYCLE
// ═══════════════════════════════════════════════════════════════════════
/** Start the telemetry system (call after configuration) */
virtual void start() = 0;
/** Stop the telemetry system (flushes pending spans) */
virtual void stop() = 0;
/** Check if telemetry is enabled */
virtual bool isEnabled() const = 0;
// ═══════════════════════════════════════════════════════════════════════
// TRACER ACCESS
// ═══════════════════════════════════════════════════════════════════════
/** Get the tracer for creating spans */
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer>
getTracer(std::string_view name = "rippled") = 0;
// ═══════════════════════════════════════════════════════════════════════
// SPAN CREATION (Convenience Methods)
// ═══════════════════════════════════════════════════════════════════════
/** Start a new span with default options */
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
startSpan(
std::string_view name,
opentelemetry::trace::SpanKind kind =
opentelemetry::trace::SpanKind::kInternal) = 0;
/** Start a span as child of given context */
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
startSpan(
std::string_view name,
opentelemetry::context::Context const& parentContext,
opentelemetry::trace::SpanKind kind =
opentelemetry::trace::SpanKind::kInternal) = 0;
// ═══════════════════════════════════════════════════════════════════════
// CONTEXT PROPAGATION
// ═══════════════════════════════════════════════════════════════════════
/** Serialize context for network transmission */
virtual std::string serializeContext(
opentelemetry::context::Context const& ctx) = 0;
/** Deserialize context from network data */
virtual opentelemetry::context::Context deserializeContext(
std::string const& serialized) = 0;
// ═══════════════════════════════════════════════════════════════════════
// COMPONENT FILTERING
// ═══════════════════════════════════════════════════════════════════════
/** Check if transaction tracing is enabled */
virtual bool shouldTraceTransactions() const = 0;
/** Check if consensus tracing is enabled */
virtual bool shouldTraceConsensus() const = 0;
/** Check if RPC tracing is enabled */
virtual bool shouldTraceRpc() const = 0;
/** Check if peer message tracing is enabled */
virtual bool shouldTracePeer() const = 0;
};
// Factory functions
std::unique_ptr<Telemetry>
make_Telemetry(
Telemetry::Setup const& setup,
beast::Journal journal);
Telemetry::Setup
setup_Telemetry(
Section const& section,
std::string const& nodePublicKey,
std::string const& version);
} // namespace telemetry
} // namespace xrpl
```
---
## 4.2 RAII Span Guard
```cpp
// include/xrpl/telemetry/SpanGuard.h
#pragma once
#include <opentelemetry/trace/span.h>
#include <opentelemetry/trace/scope.h>
#include <opentelemetry/trace/status.h>
#include <string_view>
#include <exception>
namespace xrpl {
namespace telemetry {
/**
* RAII guard for OpenTelemetry spans.
*
* Automatically ends the span on destruction and makes it the current
* span in the thread-local context.
*/
class SpanGuard
{
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span_;
opentelemetry::trace::Scope scope_;
public:
/**
* Construct guard with span.
* The span becomes the current span in thread-local context.
*/
explicit SpanGuard(
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span)
: span_(std::move(span))
, scope_(span_)
{
}
// Non-copyable, non-movable
SpanGuard(SpanGuard const&) = delete;
SpanGuard& operator=(SpanGuard const&) = delete;
SpanGuard(SpanGuard&&) = delete;
SpanGuard& operator=(SpanGuard&&) = delete;
~SpanGuard()
{
if (span_)
span_->End();
}
/** Access the underlying span */
opentelemetry::trace::Span& span() { return *span_; }
opentelemetry::trace::Span const& span() const { return *span_; }
/** Set span status to OK */
void setOk()
{
span_->SetStatus(opentelemetry::trace::StatusCode::kOk);
}
/** Set span status with code and description */
void setStatus(
opentelemetry::trace::StatusCode code,
std::string_view description = "")
{
span_->SetStatus(code, std::string(description));
}
/** Set an attribute on the span */
template<typename T>
void setAttribute(std::string_view key, T&& value)
{
span_->SetAttribute(
opentelemetry::nostd::string_view(key.data(), key.size()),
std::forward<T>(value));
}
/** Add an event to the span */
void addEvent(std::string_view name)
{
span_->AddEvent(std::string(name));
}
/** Record an exception on the span */
void recordException(std::exception const& e)
{
span_->RecordException(e);
span_->SetStatus(
opentelemetry::trace::StatusCode::kError,
e.what());
}
/** Get the current trace context */
opentelemetry::context::Context context() const
{
return opentelemetry::context::RuntimeContext::GetCurrent();
}
};
/**
* No-op span guard for when tracing is disabled.
* Provides the same interface but does nothing.
*/
class NullSpanGuard
{
public:
NullSpanGuard() = default;
void setOk() {}
void setStatus(opentelemetry::trace::StatusCode, std::string_view = "") {}
template<typename T>
void setAttribute(std::string_view, T&&) {}
void addEvent(std::string_view) {}
void recordException(std::exception const&) {}
};
} // namespace telemetry
} // namespace xrpl
```
---
## 4.3 Instrumentation Macros
```cpp
// src/xrpld/telemetry/TracingInstrumentation.h
#pragma once
#include <xrpl/telemetry/Telemetry.h>
#include <xrpl/telemetry/SpanGuard.h>
namespace xrpl {
namespace telemetry {
// ═══════════════════════════════════════════════════════════════════════════
// INSTRUMENTATION MACROS
// ═══════════════════════════════════════════════════════════════════════════
#ifdef XRPL_ENABLE_TELEMETRY
// Start a span that is automatically ended when guard goes out of scope
#define XRPL_TRACE_SPAN(telemetry, name) \
auto _xrpl_span_ = (telemetry).startSpan(name); \
::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_)
// Start a span with specific kind
#define XRPL_TRACE_SPAN_KIND(telemetry, name, kind) \
auto _xrpl_span_ = (telemetry).startSpan(name, kind); \
::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_)
// Conditional span based on component
#define XRPL_TRACE_TX(telemetry, name) \
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
if ((telemetry).shouldTraceTransactions()) { \
_xrpl_guard_.emplace((telemetry).startSpan(name)); \
}
#define XRPL_TRACE_CONSENSUS(telemetry, name) \
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
if ((telemetry).shouldTraceConsensus()) { \
_xrpl_guard_.emplace((telemetry).startSpan(name)); \
}
#define XRPL_TRACE_RPC(telemetry, name) \
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
if ((telemetry).shouldTraceRpc()) { \
_xrpl_guard_.emplace((telemetry).startSpan(name)); \
}
// Set attribute on current span (if exists)
#define XRPL_TRACE_SET_ATTR(key, value) \
if (_xrpl_guard_.has_value()) { \
_xrpl_guard_->setAttribute(key, value); \
}
// Record exception on current span
#define XRPL_TRACE_EXCEPTION(e) \
if (_xrpl_guard_.has_value()) { \
_xrpl_guard_->recordException(e); \
}
#else // XRPL_ENABLE_TELEMETRY not defined
#define XRPL_TRACE_SPAN(telemetry, name) ((void)0)
#define XRPL_TRACE_SPAN_KIND(telemetry, name, kind) ((void)0)
#define XRPL_TRACE_TX(telemetry, name) ((void)0)
#define XRPL_TRACE_CONSENSUS(telemetry, name) ((void)0)
#define XRPL_TRACE_RPC(telemetry, name) ((void)0)
#define XRPL_TRACE_SET_ATTR(key, value) ((void)0)
#define XRPL_TRACE_EXCEPTION(e) ((void)0)
#endif // XRPL_ENABLE_TELEMETRY
} // namespace telemetry
} // namespace xrpl
```
---
## 4.4 Protocol Buffer Extensions
### 4.4.1 TraceContext Message Definition
Add to `src/xrpld/overlay/detail/ripple.proto`:
```protobuf
// Trace context for distributed tracing across nodes
// Uses W3C Trace Context format internally
message TraceContext {
// 16-byte trace identifier (required for valid context)
bytes trace_id = 1;
// 8-byte span identifier of parent span
bytes span_id = 2;
// Trace flags (bit 0 = sampled)
uint32 trace_flags = 3;
// W3C tracestate header value for vendor-specific data
string trace_state = 4;
}
// Extend existing messages with optional trace context
// High field numbers (1000+) to avoid conflicts
message TMTransaction {
// ... existing fields ...
// Optional trace context for distributed tracing
optional TraceContext trace_context = 1001;
}
message TMProposeSet {
// ... existing fields ...
optional TraceContext trace_context = 1001;
}
message TMValidation {
// ... existing fields ...
optional TraceContext trace_context = 1001;
}
message TMGetLedger {
// ... existing fields ...
optional TraceContext trace_context = 1001;
}
message TMLedgerData {
// ... existing fields ...
optional TraceContext trace_context = 1001;
}
```
### 4.4.2 Context Serialization/Deserialization
```cpp
// include/xrpl/telemetry/TraceContext.h
#pragma once
#include <opentelemetry/context/context.h>
#include <opentelemetry/trace/span_context.h>
#include <protocol/messages.h> // Generated protobuf
#include <optional>
#include <string>
namespace xrpl {
namespace telemetry {
/**
* Utilities for trace context serialization and propagation.
*/
class TraceContextPropagator
{
public:
/**
* Extract trace context from Protocol Buffer message.
* Returns empty context if no trace info present.
*/
static opentelemetry::context::Context
extract(protocol::TraceContext const& proto);
/**
* Inject current trace context into Protocol Buffer message.
*/
static void
inject(
opentelemetry::context::Context const& ctx,
protocol::TraceContext& proto);
/**
* Extract trace context from HTTP headers (for RPC).
* Supports W3C Trace Context (traceparent, tracestate).
*/
static opentelemetry::context::Context
extractFromHeaders(
std::function<std::optional<std::string>(std::string_view)> headerGetter);
/**
* Inject trace context into HTTP headers (for RPC responses).
*/
static void
injectToHeaders(
opentelemetry::context::Context const& ctx,
std::function<void(std::string_view, std::string_view)> headerSetter);
};
// ═══════════════════════════════════════════════════════════════════════════
// IMPLEMENTATION
// ═══════════════════════════════════════════════════════════════════════════
inline opentelemetry::context::Context
TraceContextPropagator::extract(protocol::TraceContext const& proto)
{
using namespace opentelemetry::trace;
if (proto.trace_id().size() != 16 || proto.span_id().size() != 8)
return opentelemetry::context::Context{}; // Invalid, return empty
// Construct TraceId and SpanId from bytes
TraceId traceId(reinterpret_cast<uint8_t const*>(proto.trace_id().data()));
SpanId spanId(reinterpret_cast<uint8_t const*>(proto.span_id().data()));
TraceFlags flags(static_cast<uint8_t>(proto.trace_flags()));
// Create SpanContext from extracted data
SpanContext spanContext(traceId, spanId, flags, /* remote = */ true);
// Create context with extracted span as parent
return opentelemetry::context::Context{}.SetValue(
opentelemetry::trace::kSpanKey,
opentelemetry::nostd::shared_ptr<Span>(
new DefaultSpan(spanContext)));
}
inline void
TraceContextPropagator::inject(
opentelemetry::context::Context const& ctx,
protocol::TraceContext& proto)
{
using namespace opentelemetry::trace;
// Get current span from context
auto span = GetSpan(ctx);
if (!span)
return;
auto const& spanCtx = span->GetContext();
if (!spanCtx.IsValid())
return;
// Serialize trace_id (16 bytes)
auto const& traceId = spanCtx.trace_id();
proto.set_trace_id(traceId.Id().data(), TraceId::kSize);
// Serialize span_id (8 bytes)
auto const& spanId = spanCtx.span_id();
proto.set_span_id(spanId.Id().data(), SpanId::kSize);
// Serialize flags
proto.set_trace_flags(spanCtx.trace_flags().flags());
// Note: tracestate not implemented yet
}
} // namespace telemetry
} // namespace xrpl
```
---
## 4.5 Module-Specific Instrumentation
### 4.5.1 Transaction Relay Instrumentation
```cpp
// src/xrpld/overlay/detail/PeerImp.cpp (modified)
#include <xrpl/telemetry/TracingInstrumentation.h>
void
PeerImp::handleTransaction(
std::shared_ptr<protocol::TMTransaction> const& m)
{
// Extract trace context from incoming message
opentelemetry::context::Context parentCtx;
if (m->has_trace_context())
{
parentCtx = telemetry::TraceContextPropagator::extract(
m->trace_context());
}
// Start span as child of remote span (cross-node link)
auto span = app_.getTelemetry().startSpan(
"tx.receive",
parentCtx,
opentelemetry::trace::SpanKind::kServer);
telemetry::SpanGuard guard(span);
try
{
// Parse and validate transaction
SerialIter sit(makeSlice(m->rawtransaction()));
auto stx = std::make_shared<STTx const>(sit);
// Add transaction attributes
guard.setAttribute("xrpl.tx.hash", to_string(stx->getTransactionID()));
guard.setAttribute("xrpl.tx.type", stx->getTxnType());
guard.setAttribute("xrpl.peer.id", remote_address_.to_string());
// Check if we've seen this transaction (HashRouter)
auto const [flags, suppressed] =
app_.getHashRouter().addSuppressionPeer(
stx->getTransactionID(),
id_);
if (suppressed)
{
guard.setAttribute("xrpl.tx.suppressed", true);
guard.addEvent("tx.duplicate");
return; // Already processing this transaction
}
// Create child span for validation
{
auto validateSpan = app_.getTelemetry().startSpan("tx.validate");
telemetry::SpanGuard validateGuard(validateSpan);
auto [validity, reason] = checkTransaction(stx);
validateGuard.setAttribute("xrpl.tx.validity",
validity == Validity::Valid ? "valid" : "invalid");
if (validity != Validity::Valid)
{
validateGuard.setStatus(
opentelemetry::trace::StatusCode::kError,
reason);
return;
}
}
// Relay to other peers (capture context for propagation)
auto ctx = guard.context();
// Create child span for relay
auto relaySpan = app_.getTelemetry().startSpan(
"tx.relay",
ctx,
opentelemetry::trace::SpanKind::kClient);
{
telemetry::SpanGuard relayGuard(relaySpan);
// Inject context into outgoing message
protocol::TraceContext protoCtx;
telemetry::TraceContextPropagator::inject(
relayGuard.context(), protoCtx);
// Relay to other peers
app_.overlay().relay(
stx->getTransactionID(),
*m,
protoCtx, // Pass trace context
exclusions);
relayGuard.setAttribute("xrpl.tx.relay_count",
static_cast<int64_t>(relayCount));
}
guard.setOk();
}
catch (std::exception const& e)
{
guard.recordException(e);
JLOG(journal_.warn()) << "Transaction handling failed: " << e.what();
}
}
```
### 4.5.2 Consensus Instrumentation
```cpp
// src/xrpld/app/consensus/RCLConsensus.cpp (modified)
#include <xrpl/telemetry/TracingInstrumentation.h>
void
RCLConsensusAdaptor::startRound(
NetClock::time_point const& now,
RCLCxLedger::ID const& prevLedgerHash,
RCLCxLedger const& prevLedger,
hash_set<NodeID> const& peers,
bool proposing)
{
XRPL_TRACE_CONSENSUS(app_.getTelemetry(), "consensus.round");
XRPL_TRACE_SET_ATTR("xrpl.consensus.ledger.prev", to_string(prevLedgerHash));
XRPL_TRACE_SET_ATTR("xrpl.consensus.ledger.seq",
static_cast<int64_t>(prevLedger.seq() + 1));
XRPL_TRACE_SET_ATTR("xrpl.consensus.proposers",
static_cast<int64_t>(peers.size()));
XRPL_TRACE_SET_ATTR("xrpl.consensus.mode",
proposing ? "proposing" : "observing");
// Store trace context for use in phase transitions
currentRoundContext_ = _xrpl_guard_.has_value()
? _xrpl_guard_->context()
: opentelemetry::context::Context{};
// ... existing implementation ...
}
ConsensusPhase
RCLConsensusAdaptor::phaseTransition(ConsensusPhase newPhase)
{
// Create span for phase transition
auto span = app_.getTelemetry().startSpan(
"consensus.phase." + to_string(newPhase),
currentRoundContext_);
telemetry::SpanGuard guard(span);
guard.setAttribute("xrpl.consensus.phase", to_string(newPhase));
guard.addEvent("phase.enter");
auto const startTime = std::chrono::steady_clock::now();
try
{
auto result = doPhaseTransition(newPhase);
auto const duration = std::chrono::steady_clock::now() - startTime;
guard.setAttribute("xrpl.consensus.phase_duration_ms",
std::chrono::duration<double, std::milli>(duration).count());
guard.setOk();
return result;
}
catch (std::exception const& e)
{
guard.recordException(e);
throw;
}
}
void
RCLConsensusAdaptor::peerProposal(
NetClock::time_point const& now,
RCLCxPeerPos const& proposal)
{
// Extract trace context from proposal message
opentelemetry::context::Context parentCtx;
if (proposal.hasTraceContext())
{
parentCtx = telemetry::TraceContextPropagator::extract(
proposal.traceContext());
}
auto span = app_.getTelemetry().startSpan(
"consensus.proposal.receive",
parentCtx,
opentelemetry::trace::SpanKind::kServer);
telemetry::SpanGuard guard(span);
guard.setAttribute("xrpl.consensus.proposer",
toBase58(TokenType::NodePublic, proposal.nodeId()));
guard.setAttribute("xrpl.consensus.round",
static_cast<int64_t>(proposal.proposal().proposeSeq()));
// ... existing implementation ...
guard.setOk();
}
```
### 4.5.3 RPC Handler Instrumentation
```cpp
// src/xrpld/rpc/detail/ServerHandler.cpp (modified)
#include <xrpl/telemetry/TracingInstrumentation.h>
void
ServerHandler::onRequest(
http_request_type&& req,
std::function<void(http_response_type&&)>&& send)
{
// Extract trace context from HTTP headers (W3C Trace Context)
auto parentCtx = telemetry::TraceContextPropagator::extractFromHeaders(
[&req](std::string_view name) -> std::optional<std::string> {
auto it = req.find(boost::beast::http::field{
std::string(name)});
if (it != req.end())
return std::string(it->value());
return std::nullopt;
});
// Start request span
auto span = app_.getTelemetry().startSpan(
"rpc.request",
parentCtx,
opentelemetry::trace::SpanKind::kServer);
telemetry::SpanGuard guard(span);
// Add HTTP attributes
guard.setAttribute("http.method", std::string(req.method_string()));
guard.setAttribute("http.target", std::string(req.target()));
guard.setAttribute("http.user_agent",
std::string(req[boost::beast::http::field::user_agent]));
auto const startTime = std::chrono::steady_clock::now();
try
{
// Parse and process request
auto const& body = req.body();
Json::Value jv;
Json::Reader reader;
if (!reader.parse(body, jv))
{
guard.setStatus(
opentelemetry::trace::StatusCode::kError,
"Invalid JSON");
sendError(send, "Invalid JSON");
return;
}
// Extract command name
std::string command = jv.isMember("command")
? jv["command"].asString()
: jv.isMember("method")
? jv["method"].asString()
: "unknown";
guard.setAttribute("xrpl.rpc.command", command);
// Create child span for command execution
auto cmdSpan = app_.getTelemetry().startSpan(
"rpc.command." + command);
{
telemetry::SpanGuard cmdGuard(cmdSpan);
// Execute RPC command
auto result = processRequest(jv);
// Record result attributes
if (result.isMember("status"))
{
cmdGuard.setAttribute("xrpl.rpc.status",
result["status"].asString());
}
if (result["status"].asString() == "error")
{
cmdGuard.setStatus(
opentelemetry::trace::StatusCode::kError,
result.isMember("error_message")
? result["error_message"].asString()
: "RPC error");
}
else
{
cmdGuard.setOk();
}
}
auto const duration = std::chrono::steady_clock::now() - startTime;
guard.setAttribute("http.duration_ms",
std::chrono::duration<double, std::milli>(duration).count());
// Inject trace context into response headers
http_response_type resp;
telemetry::TraceContextPropagator::injectToHeaders(
guard.context(),
[&resp](std::string_view name, std::string_view value) {
resp.set(std::string(name), std::string(value));
});
guard.setOk();
send(std::move(resp));
}
catch (std::exception const& e)
{
guard.recordException(e);
JLOG(journal_.error()) << "RPC request failed: " << e.what();
sendError(send, e.what());
}
}
```
### 4.5.4 JobQueue Context Propagation
```cpp
// src/xrpld/core/JobQueue.h (modified)
#include <opentelemetry/context/context.h>
class Job
{
// ... existing members ...
// Captured trace context at job creation
opentelemetry::context::Context traceContext_;
public:
// Constructor captures current trace context
Job(JobType type, std::function<void()> func, ...)
: type_(type)
, func_(std::move(func))
, traceContext_(opentelemetry::context::RuntimeContext::GetCurrent())
// ... other initializations ...
{
}
// Get trace context for restoration during execution
opentelemetry::context::Context const&
traceContext() const { return traceContext_; }
};
// src/xrpld/core/JobQueue.cpp (modified)
void
Worker::run()
{
while (auto job = getJob())
{
// Restore trace context from job creation
auto token = opentelemetry::context::RuntimeContext::Attach(
job->traceContext());
// Start execution span
auto span = app_.getTelemetry().startSpan("job.execute");
telemetry::SpanGuard guard(span);
guard.setAttribute("xrpl.job.type", to_string(job->type()));
guard.setAttribute("xrpl.job.queue_ms", job->queueTimeMs());
guard.setAttribute("xrpl.job.worker", workerId_);
try
{
job->execute();
guard.setOk();
}
catch (std::exception const& e)
{
guard.recordException(e);
JLOG(journal_.error()) << "Job execution failed: " << e.what();
}
}
}
```
---
## 4.6 Span Flow Visualization
<div align="center">
```mermaid
flowchart TB
subgraph Client["External Client"]
submit["Submit TX"]
end
subgraph NodeA["rippled Node A"]
rpcA["rpc.request"]
cmdA["rpc.command.submit"]
txRecvA["tx.receive"]
txValA["tx.validate"]
txRelayA["tx.relay"]
end
subgraph NodeB["rippled Node B"]
txRecvB["tx.receive"]
txValB["tx.validate"]
txRelayB["tx.relay"]
end
subgraph NodeC["rippled Node C"]
txRecvC["tx.receive"]
consensusC["consensus.round"]
phaseC["consensus.phase.establish"]
end
submit --> rpcA
rpcA --> cmdA
cmdA --> txRecvA
txRecvA --> txValA
txValA --> txRelayA
txRelayA -.->|"TraceContext"| txRecvB
txRecvB --> txValB
txValB --> txRelayB
txRelayB -.->|"TraceContext"| txRecvC
txRecvC --> consensusC
consensusC --> phaseC
style Client fill:#334155,stroke:#1e293b,color:#fff
style NodeA fill:#1e3a8a,stroke:#172554,color:#fff
style NodeB fill:#064e3b,stroke:#022c22,color:#fff
style NodeC fill:#78350f,stroke:#451a03,color:#fff
style submit fill:#e2e8f0,stroke:#cbd5e1,color:#1e293b
style rpcA fill:#1d4ed8,stroke:#1e40af,color:#fff
style cmdA fill:#1d4ed8,stroke:#1e40af,color:#fff
style txRecvA fill:#047857,stroke:#064e3b,color:#fff
style txValA fill:#047857,stroke:#064e3b,color:#fff
style txRelayA fill:#047857,stroke:#064e3b,color:#fff
style txRecvB fill:#047857,stroke:#064e3b,color:#fff
style txValB fill:#047857,stroke:#064e3b,color:#fff
style txRelayB fill:#047857,stroke:#064e3b,color:#fff
style txRecvC fill:#047857,stroke:#064e3b,color:#fff
style consensusC fill:#fef3c7,stroke:#fde68a,color:#1e293b
style phaseC fill:#fef3c7,stroke:#fde68a,color:#1e293b
```
</div>
---
_Previous: [Implementation Strategy](./03-implementation-strategy.md)_ | _Next: [Configuration Reference](./05-configuration-reference.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,936 @@
# Configuration Reference
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Code Samples](./04-code-samples.md) | [Implementation Phases](./06-implementation-phases.md)
---
## 5.1 rippled Configuration
### 5.1.1 Configuration File Section
Add to `cfg/xrpld-example.cfg`:
```ini
# ═══════════════════════════════════════════════════════════════════════════════
# TELEMETRY (OpenTelemetry Distributed Tracing)
# ═══════════════════════════════════════════════════════════════════════════════
#
# Enables distributed tracing for transaction flow, consensus, and RPC calls.
# Traces are exported to an OpenTelemetry Collector using OTLP protocol.
#
# [telemetry]
#
# # Enable/disable telemetry (default: 0 = disabled)
# enabled=1
#
# # Exporter type: "otlp_grpc" (default), "otlp_http", or "none"
# exporter=otlp_grpc
#
# # OTLP endpoint (default: localhost:4317 for gRPC, localhost:4318 for HTTP)
# endpoint=localhost:4317
#
# # Use TLS for exporter connection (default: 0)
# use_tls=0
#
# # Path to CA certificate for TLS (optional)
# # tls_ca_cert=/path/to/ca.crt
#
# # Sampling ratio: 0.0-1.0 (default: 1.0 = 100% sampling)
# # Use lower values in production to reduce overhead
# sampling_ratio=0.1
#
# # Batch processor settings
# batch_size=512 # Spans per batch (default: 512)
# batch_delay_ms=5000 # Max delay before sending batch (default: 5000)
# max_queue_size=2048 # Max queued spans (default: 2048)
#
# # Component-specific tracing (default: all enabled except peer)
# trace_transactions=1 # Transaction relay and processing
# trace_consensus=1 # Consensus rounds and proposals
# trace_rpc=1 # RPC request handling
# trace_peer=0 # Peer messages (high volume, disabled by default)
# trace_ledger=1 # Ledger acquisition and building
#
# # Service identification (automatically detected if not specified)
# # service_name=rippled
# # service_instance_id=<node_public_key>
[telemetry]
enabled=0
```
### 5.1.2 Configuration Options Summary
| Option | Type | Default | Description |
| --------------------- | ------ | ---------------- | ----------------------------------------- |
| `enabled` | bool | `false` | Enable/disable telemetry |
| `exporter` | string | `"otlp_grpc"` | Exporter type: otlp_grpc, otlp_http, none |
| `endpoint` | string | `localhost:4317` | OTLP collector endpoint |
| `use_tls` | bool | `false` | Enable TLS for exporter connection |
| `tls_ca_cert` | string | `""` | Path to CA certificate file |
| `sampling_ratio` | float | `1.0` | Sampling ratio (0.0-1.0) |
| `batch_size` | uint | `512` | Spans per export batch |
| `batch_delay_ms` | uint | `5000` | Max delay before sending batch (ms) |
| `max_queue_size` | uint | `2048` | Maximum queued spans |
| `trace_transactions` | bool | `true` | Enable transaction tracing |
| `trace_consensus` | bool | `true` | Enable consensus tracing |
| `trace_rpc` | bool | `true` | Enable RPC tracing |
| `trace_peer` | bool | `false` | Enable peer message tracing (high volume) |
| `trace_ledger` | bool | `true` | Enable ledger tracing |
| `service_name` | string | `"rippled"` | Service name for traces |
| `service_instance_id` | string | `<node_pubkey>` | Instance identifier |
---
## 5.2 Configuration Parser
```cpp
// src/libxrpl/telemetry/TelemetryConfig.cpp
#include <xrpl/telemetry/Telemetry.h>
#include <xrpl/basics/Log.h>
namespace xrpl {
namespace telemetry {
Telemetry::Setup
setup_Telemetry(
Section const& section,
std::string const& nodePublicKey,
std::string const& version)
{
Telemetry::Setup setup;
// Basic settings
setup.enabled = section.value_or("enabled", false);
setup.serviceName = section.value_or("service_name", "rippled");
setup.serviceVersion = version;
setup.serviceInstanceId = section.value_or(
"service_instance_id", nodePublicKey);
// Exporter settings
setup.exporterType = section.value_or("exporter", "otlp_grpc");
if (setup.exporterType == "otlp_grpc")
setup.exporterEndpoint = section.value_or("endpoint", "localhost:4317");
else if (setup.exporterType == "otlp_http")
setup.exporterEndpoint = section.value_or("endpoint", "localhost:4318");
setup.useTls = section.value_or("use_tls", false);
setup.tlsCertPath = section.value_or("tls_ca_cert", "");
// Sampling
setup.samplingRatio = section.value_or("sampling_ratio", 1.0);
if (setup.samplingRatio < 0.0 || setup.samplingRatio > 1.0)
{
Throw<std::runtime_error>(
"telemetry.sampling_ratio must be between 0.0 and 1.0");
}
// Batch processor
setup.batchSize = section.value_or("batch_size", 512u);
setup.batchDelay = std::chrono::milliseconds{
section.value_or("batch_delay_ms", 5000u)};
setup.maxQueueSize = section.value_or("max_queue_size", 2048u);
// Component filtering
setup.traceTransactions = section.value_or("trace_transactions", true);
setup.traceConsensus = section.value_or("trace_consensus", true);
setup.traceRpc = section.value_or("trace_rpc", true);
setup.tracePeer = section.value_or("trace_peer", false);
setup.traceLedger = section.value_or("trace_ledger", true);
return setup;
}
} // namespace telemetry
} // namespace xrpl
```
---
## 5.3 Application Integration
### 5.3.1 ApplicationImp Changes
```cpp
// src/xrpld/app/main/Application.cpp (modified)
#include <xrpl/telemetry/Telemetry.h>
class ApplicationImp : public Application
{
// ... existing members ...
// Telemetry (must be constructed early, destroyed late)
std::unique_ptr<telemetry::Telemetry> telemetry_;
public:
ApplicationImp(...)
{
// Initialize telemetry early (before other components)
auto telemetrySection = config_->section("telemetry");
auto telemetrySetup = telemetry::setup_Telemetry(
telemetrySection,
toBase58(TokenType::NodePublic, nodeIdentity_.publicKey()),
BuildInfo::getVersionString());
// Set network attributes
telemetrySetup.networkId = config_->NETWORK_ID;
telemetrySetup.networkType = [&]() {
if (config_->NETWORK_ID == 0) return "mainnet";
if (config_->NETWORK_ID == 1) return "testnet";
if (config_->NETWORK_ID == 2) return "devnet";
return "custom";
}();
telemetry_ = telemetry::make_Telemetry(
telemetrySetup,
logs_->journal("Telemetry"));
// ... rest of initialization ...
}
void start() override
{
// Start telemetry first
if (telemetry_)
telemetry_->start();
// ... existing start code ...
}
void stop() override
{
// ... existing stop code ...
// Stop telemetry last (to capture shutdown spans)
if (telemetry_)
telemetry_->stop();
}
telemetry::Telemetry& getTelemetry() override
{
assert(telemetry_);
return *telemetry_;
}
};
```
### 5.3.2 Application Interface Addition
```cpp
// include/xrpl/app/main/Application.h (modified)
namespace telemetry { class Telemetry; }
class Application
{
public:
// ... existing virtual methods ...
/** Get the telemetry system for distributed tracing */
virtual telemetry::Telemetry& getTelemetry() = 0;
};
```
---
## 5.4 CMake Integration
### 5.4.1 Find OpenTelemetry Module
```cmake
# cmake/FindOpenTelemetry.cmake
# Find OpenTelemetry C++ SDK
#
# This module defines:
# OpenTelemetry_FOUND - System has OpenTelemetry
# OpenTelemetry::api - API library target
# OpenTelemetry::sdk - SDK library target
# OpenTelemetry::otlp_grpc_exporter - OTLP gRPC exporter target
# OpenTelemetry::otlp_http_exporter - OTLP HTTP exporter target
find_package(opentelemetry-cpp CONFIG QUIET)
if(opentelemetry-cpp_FOUND)
set(OpenTelemetry_FOUND TRUE)
# Create imported targets if not already created by config
if(NOT TARGET OpenTelemetry::api)
add_library(OpenTelemetry::api ALIAS opentelemetry-cpp::api)
endif()
if(NOT TARGET OpenTelemetry::sdk)
add_library(OpenTelemetry::sdk ALIAS opentelemetry-cpp::sdk)
endif()
if(NOT TARGET OpenTelemetry::otlp_grpc_exporter)
add_library(OpenTelemetry::otlp_grpc_exporter ALIAS
opentelemetry-cpp::otlp_grpc_exporter)
endif()
else()
# Try pkg-config fallback
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(OTEL opentelemetry-cpp QUIET)
if(OTEL_FOUND)
set(OpenTelemetry_FOUND TRUE)
# Create imported targets from pkg-config
add_library(OpenTelemetry::api INTERFACE IMPORTED)
target_include_directories(OpenTelemetry::api INTERFACE
${OTEL_INCLUDE_DIRS})
endif()
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OpenTelemetry
REQUIRED_VARS OpenTelemetry_FOUND)
```
### 5.4.2 CMakeLists.txt Changes
```cmake
# CMakeLists.txt (additions)
# ═══════════════════════════════════════════════════════════════════════════════
# TELEMETRY OPTIONS
# ═══════════════════════════════════════════════════════════════════════════════
option(XRPL_ENABLE_TELEMETRY
"Enable OpenTelemetry distributed tracing support" OFF)
if(XRPL_ENABLE_TELEMETRY)
find_package(OpenTelemetry REQUIRED)
# Define compile-time flag
add_compile_definitions(XRPL_ENABLE_TELEMETRY)
message(STATUS "OpenTelemetry tracing: ENABLED")
else()
message(STATUS "OpenTelemetry tracing: DISABLED")
endif()
# ═══════════════════════════════════════════════════════════════════════════════
# TELEMETRY LIBRARY
# ═══════════════════════════════════════════════════════════════════════════════
if(XRPL_ENABLE_TELEMETRY)
add_library(xrpl_telemetry
src/libxrpl/telemetry/Telemetry.cpp
src/libxrpl/telemetry/TelemetryConfig.cpp
src/libxrpl/telemetry/TraceContext.cpp
)
target_include_directories(xrpl_telemetry
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(xrpl_telemetry
PUBLIC
OpenTelemetry::api
OpenTelemetry::sdk
OpenTelemetry::otlp_grpc_exporter
PRIVATE
xrpl_basics
)
# Add to main library dependencies
target_link_libraries(xrpld PRIVATE xrpl_telemetry)
else()
# Create null implementation library
add_library(xrpl_telemetry
src/libxrpl/telemetry/NullTelemetry.cpp
)
target_include_directories(xrpl_telemetry
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
)
endif()
```
---
## 5.5 OpenTelemetry Collector Configuration
### 5.5.1 Development Configuration
```yaml
# otel-collector-dev.yaml
# Minimal configuration for local development
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 100
exporters:
# Console output for debugging
logging:
verbosity: detailed
sampling_initial: 5
sampling_thereafter: 200
# Jaeger for trace visualization
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [logging, jaeger]
```
### 5.5.2 Production Configuration
```yaml
# otel-collector-prod.yaml
# Production configuration with filtering, sampling, and multiple backends
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
tls:
cert_file: /etc/otel/server.crt
key_file: /etc/otel/server.key
ca_file: /etc/otel/ca.crt
processors:
# Memory limiter to prevent OOM
memory_limiter:
check_interval: 1s
limit_mib: 1000
spike_limit_mib: 200
# Batch processing for efficiency
batch:
timeout: 5s
send_batch_size: 512
send_batch_max_size: 1024
# Tail-based sampling (keep errors and slow traces)
tail_sampling:
decision_wait: 10s
num_traces: 100000
expected_new_traces_per_sec: 1000
policies:
# Always keep error traces
- name: errors
type: status_code
status_code:
status_codes: [ERROR]
# Keep slow consensus rounds (>5s)
- name: slow-consensus
type: latency
latency:
threshold_ms: 5000
# Keep slow RPC requests (>1s)
- name: slow-rpc
type: and
and:
and_sub_policy:
- name: rpc-spans
type: string_attribute
string_attribute:
key: xrpl.rpc.command
values: [".*"]
enabled_regex_matching: true
- name: latency
type: latency
latency:
threshold_ms: 1000
# Probabilistic sampling for the rest
- name: probabilistic
type: probabilistic
probabilistic:
sampling_percentage: 10
# Attribute processing
attributes:
actions:
# Hash sensitive data
- key: xrpl.tx.account
action: hash
# Add deployment info
- key: deployment.environment
value: production
action: upsert
exporters:
# Grafana Tempo for long-term storage
otlp/tempo:
endpoint: tempo.monitoring:4317
tls:
insecure: false
ca_file: /etc/otel/tempo-ca.crt
# Elastic APM for correlation with logs
otlp/elastic:
endpoint: apm.elastic:8200
headers:
Authorization: "Bearer ${ELASTIC_APM_TOKEN}"
extensions:
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
service:
extensions: [health_check, zpages]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, attributes, batch]
exporters: [otlp/tempo, otlp/elastic]
```
---
## 5.6 Docker Compose Development Environment
```yaml
# docker-compose-telemetry.yaml
version: "3.8"
services:
# OpenTelemetry Collector
otel-collector:
image: otel/opentelemetry-collector-contrib:0.92.0
container_name: otel-collector
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-dev.yaml:/etc/otel-collector-config.yaml:ro
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "13133:13133" # Health check
depends_on:
- jaeger
# Jaeger for trace visualization
jaeger:
image: jaegertracing/all-in-one:1.53
container_name: jaeger
environment:
- COLLECTOR_OTLP_ENABLED=true
ports:
- "16686:16686" # UI
- "14250:14250" # gRPC
# Grafana for dashboards
grafana:
image: grafana/grafana:10.2.3
container_name: grafana
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
ports:
- "3000:3000"
depends_on:
- jaeger
# Prometheus for metrics (optional, for correlation)
prometheus:
image: prom/prometheus:v2.48.1
container_name: prometheus
volumes:
- ./prometheus.yaml:/etc/prometheus/prometheus.yml:ro
ports:
- "9090:9090"
networks:
default:
name: rippled-telemetry
```
---
## 5.7 Configuration Architecture
```mermaid
flowchart TB
subgraph config["Configuration Sources"]
cfgFile["xrpld.cfg<br/>[telemetry] section"]
cmake["CMake<br/>XRPL_ENABLE_TELEMETRY"]
end
subgraph init["Initialization"]
parse["setup_Telemetry()"]
factory["make_Telemetry()"]
end
subgraph runtime["Runtime Components"]
tracer["TracerProvider"]
exporter["OTLP Exporter"]
processor["BatchProcessor"]
end
subgraph collector["Collector Pipeline"]
recv["Receivers"]
proc["Processors"]
exp["Exporters"]
end
cfgFile --> parse
cmake -->|"compile flag"| parse
parse --> factory
factory --> tracer
tracer --> processor
processor --> exporter
exporter -->|"OTLP"| recv
recv --> proc
proc --> exp
style config fill:#e3f2fd,stroke:#1976d2
style runtime fill:#e8f5e9,stroke:#388e3c
style collector fill:#fff3e0,stroke:#ff9800
```
---
## 5.8 Grafana Integration
Step-by-step instructions for integrating rippled traces with Grafana.
### 5.8.1 Data Source Configuration
#### Tempo (Recommended)
```yaml
# grafana/provisioning/datasources/tempo.yaml
apiVersion: 1
datasources:
- name: Tempo
type: tempo
access: proxy
url: http://tempo:3200
jsonData:
httpMethod: GET
tracesToLogs:
datasourceUid: loki
tags: ["service.name", "xrpl.tx.hash"]
mappedTags: [{ key: "trace_id", value: "traceID" }]
mapTagNamesEnabled: true
filterByTraceID: true
serviceMap:
datasourceUid: prometheus
nodeGraph:
enabled: true
search:
hide: false
lokiSearch:
datasourceUid: loki
```
#### Jaeger
```yaml
# grafana/provisioning/datasources/jaeger.yaml
apiVersion: 1
datasources:
- name: Jaeger
type: jaeger
access: proxy
url: http://jaeger:16686
jsonData:
tracesToLogs:
datasourceUid: loki
tags: ["service.name"]
```
#### Elastic APM
```yaml
# grafana/provisioning/datasources/elastic-apm.yaml
apiVersion: 1
datasources:
- name: Elasticsearch-APM
type: elasticsearch
access: proxy
url: http://elasticsearch:9200
database: "apm-*"
jsonData:
esVersion: "8.0.0"
timeField: "@timestamp"
logMessageField: message
logLevelField: log.level
```
### 5.8.2 Dashboard Provisioning
```yaml
# grafana/provisioning/dashboards/dashboards.yaml
apiVersion: 1
providers:
- name: "rippled-dashboards"
orgId: 1
folder: "rippled"
folderUid: "rippled"
type: file
disableDeletion: false
updateIntervalSeconds: 30
options:
path: /var/lib/grafana/dashboards/rippled
```
### 5.8.3 Example Dashboard: RPC Performance
```json
{
"title": "rippled RPC Performance",
"uid": "rippled-rpc-performance",
"panels": [
{
"title": "RPC Latency by Command",
"type": "heatmap",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && span.xrpl.rpc.command != \"\"} | histogram_over_time(duration) by (span.xrpl.rpc.command)"
}
],
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
},
{
"title": "RPC Error Rate",
"type": "timeseries",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && status.code=error} | rate() by (span.xrpl.rpc.command)"
}
],
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
},
{
"title": "Top 10 Slowest RPC Commands",
"type": "table",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && span.xrpl.rpc.command != \"\"} | avg(duration) by (span.xrpl.rpc.command) | topk(10)"
}
],
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 8 }
},
{
"title": "Recent Traces",
"type": "table",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\"}"
}
],
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 16 }
}
]
}
```
### 5.8.4 Example Dashboard: Transaction Tracing
```json
{
"title": "rippled Transaction Tracing",
"uid": "rippled-tx-tracing",
"panels": [
{
"title": "Transaction Throughput",
"type": "stat",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"tx.receive\"} | rate()"
}
],
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }
},
{
"title": "Cross-Node Relay Count",
"type": "timeseries",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"tx.relay\"} | avg(span.xrpl.tx.relay_count)"
}
],
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }
},
{
"title": "Transaction Validation Errors",
"type": "table",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"tx.validate\" && status.code=error}"
}
],
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }
}
]
}
```
### 5.8.5 TraceQL Query Examples
Common queries for rippled traces:
```
# Find all traces for a specific transaction hash
{resource.service.name="rippled" && span.xrpl.tx.hash="ABC123..."}
# Find slow RPC commands (>100ms)
{resource.service.name="rippled" && name=~"rpc.command.*"} | duration > 100ms
# Find consensus rounds taking >5 seconds
{resource.service.name="rippled" && name="consensus.round"} | duration > 5s
# Find failed transactions with error details
{resource.service.name="rippled" && name="tx.validate" && status.code=error}
# Find transactions relayed to many peers
{resource.service.name="rippled" && name="tx.relay"} | span.xrpl.tx.relay_count > 10
# Compare latency across nodes
{resource.service.name="rippled" && name="rpc.command.account_info"} | avg(duration) by (resource.service.instance.id)
```
### 5.8.6 Correlation with PerfLog
To correlate OpenTelemetry traces with existing PerfLog data:
**Step 1: Configure Loki to ingest PerfLog**
```yaml
# promtail-config.yaml
scrape_configs:
- job_name: rippled-perflog
static_configs:
- targets:
- localhost
labels:
job: rippled
__path__: /var/log/rippled/perf*.log
pipeline_stages:
- json:
expressions:
trace_id: trace_id
ledger_seq: ledger_seq
tx_hash: tx_hash
- labels:
trace_id:
ledger_seq:
tx_hash:
```
**Step 2: Add trace_id to PerfLog entries**
Modify PerfLog to include trace_id when available:
```cpp
// In PerfLog output, add trace_id from current span context
void logPerf(Json::Value& entry) {
auto span = opentelemetry::trace::GetSpan(
opentelemetry::context::RuntimeContext::GetCurrent());
if (span && span->GetContext().IsValid()) {
char traceIdHex[33];
span->GetContext().trace_id().ToLowerBase16(traceIdHex);
entry["trace_id"] = std::string(traceIdHex, 32);
}
// ... existing logging
}
```
**Step 3: Configure Grafana trace-to-logs link**
In Tempo data source configuration, set up the derived field:
```yaml
jsonData:
tracesToLogs:
datasourceUid: loki
tags: ["trace_id", "xrpl.tx.hash"]
filterByTraceID: true
filterBySpanID: false
```
### 5.8.7 Correlation with Insight/StatsD Metrics
To correlate traces with existing Beast Insight metrics:
**Step 1: Export Insight metrics to Prometheus**
```yaml
# prometheus.yaml
scrape_configs:
- job_name: "rippled-statsd"
static_configs:
- targets: ["statsd-exporter:9102"]
```
**Step 2: Add exemplars to metrics**
OpenTelemetry SDK automatically adds exemplars (trace IDs) to metrics when using the Prometheus exporter. This links metrics spikes to specific traces.
**Step 3: Configure Grafana metric-to-trace link**
```yaml
# In Prometheus data source
jsonData:
exemplarTraceIdDestinations:
- name: trace_id
datasourceUid: tempo
```
**Step 4: Dashboard panel with exemplars**
```json
{
"title": "RPC Latency with Trace Links",
"type": "timeseries",
"datasource": "Prometheus",
"targets": [
{
"expr": "histogram_quantile(0.99, rate(rippled_rpc_duration_seconds_bucket[5m]))",
"exemplar": true
}
]
}
```
This allows clicking on metric data points to jump directly to the related trace.
---
_Previous: [Code Samples](./04-code-samples.md)_ | _Next: [Implementation Phases](./06-implementation-phases.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,543 @@
# Implementation Phases
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Configuration Reference](./05-configuration-reference.md) | [Observability Backends](./07-observability-backends.md)
---
## 6.1 Phase Overview
```mermaid
gantt
title OpenTelemetry Implementation Timeline
dateFormat YYYY-MM-DD
axisFormat Week %W
section Phase 1
Core Infrastructure :p1, 2024-01-01, 2w
SDK Integration :p1a, 2024-01-01, 4d
Telemetry Interface :p1b, after p1a, 3d
Configuration & CMake :p1c, after p1b, 3d
Unit Tests :p1d, after p1c, 2d
section Phase 2
RPC Tracing :p2, after p1, 2w
HTTP Context Extraction :p2a, after p1, 2d
RPC Handler Instrumentation :p2b, after p2a, 4d
WebSocket Support :p2c, after p2b, 2d
Integration Tests :p2d, after p2c, 2d
section Phase 3
Transaction Tracing :p3, after p2, 2w
Protocol Buffer Extension :p3a, after p2, 2d
PeerImp Instrumentation :p3b, after p3a, 3d
Relay Context Propagation :p3c, after p3b, 3d
Multi-node Tests :p3d, after p3c, 2d
section Phase 4
Consensus Tracing :p4, after p3, 2w
Consensus Round Spans :p4a, after p3, 3d
Proposal Handling :p4b, after p4a, 3d
Validation Tests :p4c, after p4b, 4d
section Phase 5
Documentation & Deploy :p5, after p4, 1w
```
---
## 6.2 Phase 1: Core Infrastructure (Weeks 1-2)
**Objective**: Establish foundational telemetry infrastructure
### Tasks
| Task | Description | Effort | Risk |
| ---- | ----------------------------------------------------- | ------ | ------ |
| 1.1 | Add OpenTelemetry C++ SDK to Conan/CMake | 2d | Low |
| 1.2 | Implement `Telemetry` interface and factory | 2d | Low |
| 1.3 | Implement `SpanGuard` RAII wrapper | 1d | Low |
| 1.4 | Implement configuration parser | 1d | Low |
| 1.5 | Integrate into `ApplicationImp` | 1d | Medium |
| 1.6 | Add conditional compilation (`XRPL_ENABLE_TELEMETRY`) | 1d | Low |
| 1.7 | Create `NullTelemetry` no-op implementation | 0.5d | Low |
| 1.8 | Unit tests for core infrastructure | 1.5d | Low |
**Total Effort**: 10 days (2 developers)
### Exit Criteria
- [ ] OpenTelemetry SDK compiles and links
- [ ] Telemetry can be enabled/disabled via config
- [ ] Basic span creation works
- [ ] No performance regression when disabled
- [ ] Unit tests passing
---
## 6.3 Phase 2: RPC Tracing (Weeks 3-4)
**Objective**: Complete tracing for all RPC operations
### Tasks
| Task | Description | Effort | Risk |
| ---- | -------------------------------------------------- | ------ | ------ |
| 2.1 | Implement W3C Trace Context HTTP header extraction | 1d | Low |
| 2.2 | Instrument `ServerHandler::onRequest()` | 1d | Low |
| 2.3 | Instrument `RPCHandler::doCommand()` | 2d | Medium |
| 2.4 | Add RPC-specific attributes | 1d | Low |
| 2.5 | Instrument WebSocket handler | 1d | Medium |
| 2.6 | Integration tests for RPC tracing | 2d | Low |
| 2.7 | Performance benchmarks | 1d | Low |
| 2.8 | Documentation | 1d | Low |
**Total Effort**: 10 days
### Exit Criteria
- [ ] All RPC commands traced
- [ ] Trace context propagates from HTTP headers
- [ ] WebSocket and HTTP both instrumented
- [ ] <1ms overhead per RPC call
- [ ] Integration tests passing
---
## 6.4 Phase 3: Transaction Tracing (Weeks 5-6)
**Objective**: Trace transaction lifecycle across network
### Tasks
| Task | Description | Effort | Risk |
| ---- | --------------------------------------------- | ------ | ------ |
| 3.1 | Define `TraceContext` Protocol Buffer message | 1d | Low |
| 3.2 | Implement protobuf context serialization | 1d | Low |
| 3.3 | Instrument `PeerImp::handleTransaction()` | 2d | Medium |
| 3.4 | Instrument `NetworkOPs::submitTransaction()` | 1d | Medium |
| 3.5 | Instrument HashRouter integration | 1d | Medium |
| 3.6 | Implement relay context propagation | 2d | High |
| 3.7 | Integration tests (multi-node) | 2d | Medium |
| 3.8 | Performance benchmarks | 1d | Low |
**Total Effort**: 11 days
### Exit Criteria
- [ ] Transaction traces span across nodes
- [ ] Trace context in Protocol Buffer messages
- [ ] HashRouter deduplication visible in traces
- [ ] Multi-node integration tests passing
- [ ] <5% overhead on transaction throughput
---
## 6.5 Phase 4: Consensus Tracing (Weeks 7-8)
**Objective**: Full observability into consensus rounds
### Tasks
| Task | Description | Effort | Risk |
| ---- | ---------------------------------------------- | ------ | ------ |
| 4.1 | Instrument `RCLConsensusAdaptor::startRound()` | 1d | Medium |
| 4.2 | Instrument phase transitions | 2d | Medium |
| 4.3 | Instrument proposal handling | 2d | High |
| 4.4 | Instrument validation handling | 1d | Medium |
| 4.5 | Add consensus-specific attributes | 1d | Low |
| 4.6 | Correlate with transaction traces | 1d | Medium |
| 4.7 | Multi-validator integration tests | 2d | High |
| 4.8 | Performance validation | 1d | Medium |
**Total Effort**: 11 days
### Exit Criteria
- [ ] Complete consensus round traces
- [ ] Phase transitions visible
- [ ] Proposals and validations traced
- [ ] No impact on consensus timing
- [ ] Multi-validator test network validated
---
## 6.6 Phase 5: Documentation & Deployment (Week 9)
**Objective**: Production readiness
### Tasks
| Task | Description | Effort | Risk |
| ---- | ----------------------------- | ------ | ---- |
| 5.1 | Operator runbook | 1d | Low |
| 5.2 | Grafana dashboards | 1d | Low |
| 5.3 | Alert definitions | 0.5d | Low |
| 5.4 | Collector deployment examples | 0.5d | Low |
| 5.5 | Developer documentation | 1d | Low |
| 5.6 | Training materials | 0.5d | Low |
| 5.7 | Final integration testing | 0.5d | Low |
**Total Effort**: 5 days
---
## 6.7 Risk Assessment
```mermaid
quadrantChart
title Risk Assessment Matrix
x-axis Low Impact --> High Impact
y-axis Low Likelihood --> High Likelihood
quadrant-1 Monitor Closely
quadrant-2 Mitigate Immediately
quadrant-3 Accept Risk
quadrant-4 Plan Mitigation
SDK Compatibility: [0.25, 0.2]
Protocol Changes: [0.75, 0.65]
Performance Overhead: [0.65, 0.45]
Context Propagation: [0.5, 0.5]
Memory Leaks: [0.8, 0.2]
```
### Risk Details
| Risk | Likelihood | Impact | Mitigation |
| ------------------------------------ | ---------- | ------ | --------------------------------------- |
| Protocol changes break compatibility | Medium | High | Use high field numbers, optional fields |
| Performance overhead unacceptable | Medium | Medium | Sampling, conditional compilation |
| Context propagation complexity | Medium | Medium | Phased rollout, extensive testing |
| SDK compatibility issues | Low | Medium | Pin SDK version, fallback to no-op |
| Memory leaks in long-running nodes | Low | High | Memory profiling, bounded queues |
---
## 6.8 Success Metrics
| Metric | Target | Measurement |
| ------------------------ | ------------------------------ | --------------------- |
| Trace coverage | >95% of transactions | Sampling verification |
| CPU overhead | <3% | Benchmark tests |
| Memory overhead | <5 MB | Memory profiling |
| Latency impact (p99) | <2% | Performance tests |
| Trace completeness | >99% spans with required attrs | Validation script |
| Cross-node trace linkage | >90% of multi-hop transactions | Integration tests |
---
## 6.9 Effort Summary
<div align="center">
```mermaid
%%{init: {'pie': {'textPosition': 0.75}}}%%
pie showData
"Phase 1: Core Infrastructure" : 10
"Phase 2: RPC Tracing" : 10
"Phase 3: Transaction Tracing" : 11
"Phase 4: Consensus Tracing" : 11
"Phase 5: Documentation" : 5
```
**Total Effort Distribution (47 developer-days)**
</div>
### Resource Requirements
| Phase | Developers | Duration | Total Effort |
| --------- | ---------- | ----------- | ------------ |
| 1 | 2 | 2 weeks | 10 days |
| 2 | 1-2 | 2 weeks | 10 days |
| 3 | 2 | 2 weeks | 11 days |
| 4 | 2 | 2 weeks | 11 days |
| 5 | 1 | 1 week | 5 days |
| **Total** | **2** | **9 weeks** | **47 days** |
---
## 6.10 Quick Wins and Crawl-Walk-Run Strategy
This section outlines a prioritized approach to maximize ROI with minimal initial investment.
### 6.10.1 Crawl-Walk-Run Overview
<div align="center">
```mermaid
flowchart TB
subgraph crawl["🐢 CRAWL (Week 1-2)"]
direction LR
c1[Core SDK Setup] ~~~ c2[RPC Tracing Only] ~~~ c3[Single Node]
end
subgraph walk["🚶 WALK (Week 3-5)"]
direction LR
w1[Transaction Tracing] ~~~ w2[Cross-Node Context] ~~~ w3[Basic Dashboards]
end
subgraph run["🏃 RUN (Week 6-9)"]
direction LR
r1[Consensus Tracing] ~~~ r2[Full Correlation] ~~~ r3[Production Deploy]
end
crawl --> walk --> run
style crawl fill:#1b5e20,stroke:#0d3d14,color:#fff
style walk fill:#bf360c,stroke:#8c2809,color:#fff
style run fill:#0d47a1,stroke:#082f6a,color:#fff
style c1 fill:#1b5e20,stroke:#0d3d14,color:#fff
style c2 fill:#1b5e20,stroke:#0d3d14,color:#fff
style c3 fill:#1b5e20,stroke:#0d3d14,color:#fff
style w1 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style w2 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style w3 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style r1 fill:#0d47a1,stroke:#082f6a,color:#fff
style r2 fill:#0d47a1,stroke:#082f6a,color:#fff
style r3 fill:#0d47a1,stroke:#082f6a,color:#fff
```
</div>
### 6.10.2 Quick Wins (Immediate Value)
| Quick Win | Effort | Value | When to Deploy |
| ------------------------------ | -------- | ------ | -------------- |
| **RPC Command Tracing** | 2 days | High | Week 2 |
| **RPC Latency Histograms** | 0.5 days | High | Week 2 |
| **Error Rate Dashboard** | 0.5 days | Medium | Week 2 |
| **Transaction Submit Tracing** | 1 day | High | Week 3 |
| **Consensus Round Duration** | 1 day | Medium | Week 6 |
### 6.10.3 CRAWL Phase (Weeks 1-2)
**Goal**: Get basic tracing working with minimal code changes.
**What You Get**:
- RPC request/response traces for all commands
- Latency breakdown per RPC command
- Error visibility with stack traces
- Basic Grafana dashboard
**Code Changes**: ~15 lines in `ServerHandler.cpp`, ~40 lines in new telemetry module
**Why Start Here**:
- RPC is the lowest-risk, highest-visibility component
- Immediate value for debugging client issues
- No cross-node complexity
- Single file modification to existing code
### 6.10.4 WALK Phase (Weeks 3-5)
**Goal**: Add transaction lifecycle tracing across nodes.
**What You Get**:
- End-to-end transaction traces from submit to relay
- Cross-node correlation (see transaction path)
- HashRouter deduplication visibility
- Relay latency metrics
**Code Changes**: ~120 lines across 4 files, plus protobuf extension
**Why Do This Second**:
- Builds on RPC tracing (transactions submitted via RPC)
- Moderate complexity (requires context propagation)
- High value for debugging transaction issues
### 6.10.5 RUN Phase (Weeks 6-9)
**Goal**: Full observability including consensus.
**What You Get**:
- Complete consensus round visibility
- Phase transition timing
- Validator proposal tracking
- Full end-to-end traces (client → RPC → TX → consensus → ledger)
**Code Changes**: ~100 lines across 3 consensus files
**Why Do This Last**:
- Highest complexity (consensus is critical path)
- Requires thorough testing
- Lower relative value (consensus issues are rarer)
### 6.10.6 ROI Prioritization Matrix
```mermaid
quadrantChart
title Implementation ROI Matrix
x-axis Low Effort --> High Effort
y-axis Low Value --> High Value
quadrant-1 Quick Wins - Do First
quadrant-2 Major Projects - Plan Carefully
quadrant-3 Nice to Have - Optional
quadrant-4 Time Sinks - Avoid
RPC Tracing: [0.15, 0.9]
TX Submit Trace: [0.25, 0.85]
TX Relay Trace: [0.5, 0.8]
Consensus Trace: [0.7, 0.75]
Peer Message Trace: [0.85, 0.3]
Ledger Acquire: [0.55, 0.5]
```
---
## 6.11 Definition of Done
Clear, measurable criteria for each phase.
### 6.11.1 Phase 1: Core Infrastructure
| Criterion | Measurement | Target |
| --------------- | ---------------------------------------------------------- | ---------------------------- |
| SDK Integration | `cmake --build` succeeds with `-DXRPL_ENABLE_TELEMETRY=ON` | ✅ Compiles |
| Runtime Toggle | `enabled=0` produces zero overhead | <0.1% CPU difference |
| Span Creation | Unit test creates and exports span | Span appears in Jaeger |
| Configuration | All config options parsed correctly | Config validation tests pass |
| Documentation | Developer guide exists | PR approved |
**Definition of Done**: All criteria met, PR merged, no regressions in CI.
### 6.11.2 Phase 2: RPC Tracing
| Criterion | Measurement | Target |
| ------------------ | ---------------------------------- | -------------------------- |
| Coverage | All RPC commands instrumented | 100% of commands |
| Context Extraction | traceparent header propagates | Integration test passes |
| Attributes | Command, status, duration recorded | Validation script confirms |
| Performance | RPC latency overhead | <1ms p99 |
| Dashboard | Grafana dashboard deployed | Screenshot in docs |
**Definition of Done**: RPC traces visible in Jaeger/Tempo for all commands, dashboard shows latency distribution.
### 6.11.3 Phase 3: Transaction Tracing
| Criterion | Measurement | Target |
| ---------------- | ------------------------------- | ---------------------------------- |
| Local Trace | Submit validate TxQ traced | Single-node test passes |
| Cross-Node | Context propagates via protobuf | Multi-node test passes |
| Relay Visibility | relay_count attribute correct | Spot check 100 txs |
| HashRouter | Deduplication visible in trace | Duplicate txs show suppressed=true |
| Performance | TX throughput overhead | <5% degradation |
**Definition of Done**: Transaction traces span 3+ nodes in test network, performance within bounds.
### 6.11.4 Phase 4: Consensus Tracing
| Criterion | Measurement | Target |
| -------------------- | ----------------------------- | ------------------------- |
| Round Tracing | startRound creates root span | Unit test passes |
| Phase Visibility | All phases have child spans | Integration test confirms |
| Proposer Attribution | Proposer ID in attributes | Spot check 50 rounds |
| Timing Accuracy | Phase durations match PerfLog | <5% variance |
| No Consensus Impact | Round timing unchanged | Performance test passes |
**Definition of Done**: Consensus rounds fully traceable, no impact on consensus timing.
### 6.11.5 Phase 5: Production Deployment
| Criterion | Measurement | Target |
| ------------ | ---------------------------- | -------------------------- |
| Collector HA | Multiple collectors deployed | No single point of failure |
| Sampling | Tail sampling configured | 10% base + errors + slow |
| Retention | Data retained per policy | 7 days hot, 30 days warm |
| Alerting | Alerts configured | Error spike, high latency |
| Runbook | Operator documentation | Approved by ops team |
| Training | Team trained | Session completed |
**Definition of Done**: Telemetry running in production, operators trained, alerts active.
### 6.11.6 Success Metrics Summary
| Phase | Primary Metric | Secondary Metric | Deadline |
| ------- | ---------------------- | --------------------------- | ------------- |
| Phase 1 | SDK compiles and runs | Zero overhead when disabled | End of Week 2 |
| Phase 2 | 100% RPC coverage | <1ms latency overhead | End of Week 4 |
| Phase 3 | Cross-node traces work | <5% throughput impact | End of Week 6 |
| Phase 4 | Consensus fully traced | No consensus timing impact | End of Week 8 |
| Phase 5 | Production deployment | Operators trained | End of Week 9 |
---
## 6.12 Recommended Implementation Order
Based on ROI analysis, implement in this exact order:
```mermaid
flowchart TB
subgraph week1["Week 1"]
t1[1. OpenTelemetry SDK<br/>Conan/CMake integration]
t2[2. Telemetry interface<br/>SpanGuard, config]
end
subgraph week2["Week 2"]
t3[3. RPC ServerHandler<br/>instrumentation]
t4[4. Basic Jaeger setup<br/>for testing]
end
subgraph week3["Week 3"]
t5[5. Transaction submit<br/>tracing]
t6[6. Grafana dashboard<br/>v1]
end
subgraph week4["Week 4"]
t7[7. Protobuf context<br/>extension]
t8[8. PeerImp tx.relay<br/>instrumentation]
end
subgraph week5["Week 5"]
t9[9. Multi-node<br/>integration tests]
t10[10. Performance<br/>benchmarks]
end
subgraph week6_8["Weeks 6-8"]
t11[11. Consensus<br/>instrumentation]
t12[12. Full integration<br/>testing]
end
subgraph week9["Week 9"]
t13[13. Production<br/>deployment]
t14[14. Documentation<br/>& training]
end
t1 --> t2 --> t3 --> t4
t4 --> t5 --> t6
t6 --> t7 --> t8
t8 --> t9 --> t10
t10 --> t11 --> t12
t12 --> t13 --> t14
style week1 fill:#1b5e20,stroke:#0d3d14,color:#fff
style week2 fill:#1b5e20,stroke:#0d3d14,color:#fff
style week3 fill:#bf360c,stroke:#8c2809,color:#fff
style week4 fill:#bf360c,stroke:#8c2809,color:#fff
style week5 fill:#bf360c,stroke:#8c2809,color:#fff
style week6_8 fill:#0d47a1,stroke:#082f6a,color:#fff
style week9 fill:#4a148c,stroke:#2e0d57,color:#fff
style t1 fill:#1b5e20,stroke:#0d3d14,color:#fff
style t2 fill:#1b5e20,stroke:#0d3d14,color:#fff
style t3 fill:#1b5e20,stroke:#0d3d14,color:#fff
style t4 fill:#1b5e20,stroke:#0d3d14,color:#fff
style t5 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style t6 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style t7 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style t8 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style t9 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style t10 fill:#ffe0b2,stroke:#ffcc80,color:#1e293b
style t11 fill:#0d47a1,stroke:#082f6a,color:#fff
style t12 fill:#0d47a1,stroke:#082f6a,color:#fff
style t13 fill:#4a148c,stroke:#2e0d57,color:#fff
style t14 fill:#4a148c,stroke:#2e0d57,color:#fff
```
---
_Previous: [Configuration Reference](./05-configuration-reference.md)_ | _Next: [Observability Backends](./07-observability-backends.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,595 @@
# Observability Backend Recommendations
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Implementation Phases](./06-implementation-phases.md) | [Appendix](./08-appendix.md)
---
## 7.1 Development/Testing Backends
| Backend | Pros | Cons | Use Case |
| ---------- | ------------------- | ----------------- | ----------------- |
| **Jaeger** | Easy setup, good UI | Limited retention | Local dev, CI |
| **Zipkin** | Simple, lightweight | Basic features | Quick prototyping |
### Quick Start with Jaeger
```bash
# Start Jaeger with OTLP support
docker run -d --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/all-in-one:latest
```
---
## 7.2 Production Backends
| Backend | Pros | Cons | Use Case |
| ----------------- | ----------------------------------------- | ------------------ | --------------------------- |
| **Grafana Tempo** | Cost-effective, Grafana integration | Newer project | Most production deployments |
| **Elastic APM** | Full observability stack, log correlation | Resource intensive | Existing Elastic users |
| **Honeycomb** | Excellent query, high cardinality | SaaS cost | Deep debugging needs |
| **Datadog APM** | Full platform, easy setup | SaaS cost | Enterprise with budget |
### Backend Selection Flowchart
```mermaid
flowchart TD
start[Select Backend] --> budget{Budget<br/>Constraints?}
budget -->|Yes| oss[Open Source]
budget -->|No| saas{Prefer<br/>SaaS?}
oss --> existing{Existing<br/>Stack?}
existing -->|Grafana| tempo[Grafana Tempo]
existing -->|Elastic| elastic[Elastic APM]
existing -->|None| tempo
saas -->|Yes| enterprise{Enterprise<br/>Support?}
saas -->|No| oss
enterprise -->|Yes| datadog[Datadog APM]
enterprise -->|No| honeycomb[Honeycomb]
tempo --> final[Configure Collector]
elastic --> final
honeycomb --> final
datadog --> final
style start fill:#0f172a,stroke:#020617,color:#fff
style budget fill:#334155,stroke:#1e293b,color:#fff
style oss fill:#1e293b,stroke:#0f172a,color:#fff
style existing fill:#334155,stroke:#1e293b,color:#fff
style saas fill:#334155,stroke:#1e293b,color:#fff
style enterprise fill:#334155,stroke:#1e293b,color:#fff
style final fill:#0f172a,stroke:#020617,color:#fff
style tempo fill:#1b5e20,stroke:#0d3d14,color:#fff
style elastic fill:#bf360c,stroke:#8c2809,color:#fff
style honeycomb fill:#0d47a1,stroke:#082f6a,color:#fff
style datadog fill:#4a148c,stroke:#2e0d57,color:#fff
```
---
## 7.3 Recommended Production Architecture
```mermaid
flowchart TB
subgraph validators["Validator Nodes"]
v1[rippled<br/>Validator 1]
v2[rippled<br/>Validator 2]
end
subgraph stock["Stock Nodes"]
s1[rippled<br/>Stock 1]
s2[rippled<br/>Stock 2]
end
subgraph collector["OTel Collector Cluster"]
c1[Collector<br/>DC1]
c2[Collector<br/>DC2]
end
subgraph backends["Storage Backends"]
tempo[(Grafana<br/>Tempo)]
elastic[(Elastic<br/>APM)]
archive[(S3/GCS<br/>Archive)]
end
subgraph ui["Visualization"]
grafana[Grafana<br/>Dashboards]
end
v1 -->|OTLP| c1
v2 -->|OTLP| c1
s1 -->|OTLP| c2
s2 -->|OTLP| c2
c1 --> tempo
c1 --> elastic
c2 --> tempo
c2 --> archive
tempo --> grafana
elastic --> grafana
style validators fill:#b71c1c,stroke:#7f1d1d,color:#ffffff
style stock fill:#0d47a1,stroke:#082f6a,color:#ffffff
style collector fill:#bf360c,stroke:#8c2809,color:#ffffff
style backends fill:#1b5e20,stroke:#0d3d14,color:#ffffff
style ui fill:#4a148c,stroke:#2e0d57,color:#ffffff
```
---
## 7.4 Architecture Considerations
### 7.4.1 Collector Placement
| Strategy | Description | Pros | Cons |
| ------------- | -------------------- | ------------------------ | ----------------------- |
| **Sidecar** | Collector per node | Isolation, simple config | Resource overhead |
| **DaemonSet** | Collector per host | Shared resources | Complexity |
| **Gateway** | Central collector(s) | Centralized processing | Single point of failure |
**Recommendation**: Use **Gateway** pattern with regional collectors for rippled networks:
- One collector cluster per datacenter/region
- Tail-based sampling at collector level
- Multiple export destinations for redundancy
### 7.4.2 Sampling Strategy
```mermaid
flowchart LR
subgraph head["Head Sampling (Node)"]
hs[10% probabilistic]
end
subgraph tail["Tail Sampling (Collector)"]
ts1[Keep all errors]
ts2[Keep slow >5s]
ts3[Keep 10% rest]
end
head --> tail
ts1 --> final[Final Traces]
ts2 --> final
ts3 --> final
style head fill:#0d47a1,stroke:#082f6a,color:#fff
style tail fill:#1b5e20,stroke:#0d3d14,color:#fff
style hs fill:#0d47a1,stroke:#082f6a,color:#fff
style ts1 fill:#1b5e20,stroke:#0d3d14,color:#fff
style ts2 fill:#1b5e20,stroke:#0d3d14,color:#fff
style ts3 fill:#1b5e20,stroke:#0d3d14,color:#fff
style final fill:#bf360c,stroke:#8c2809,color:#fff
```
### 7.4.3 Data Retention
| Environment | Hot Storage | Warm Storage | Cold Archive |
| ----------- | ----------- | ------------ | ------------ |
| Development | 24 hours | N/A | N/A |
| Staging | 7 days | N/A | N/A |
| Production | 7 days | 30 days | many years |
---
## 7.5 Integration Checklist
- [ ] Choose primary backend (Tempo recommended for cost/features)
- [ ] Deploy collector cluster with high availability
- [ ] Configure tail-based sampling for error/latency traces
- [ ] Set up Grafana dashboards for trace visualization
- [ ] Configure alerts for trace anomalies
- [ ] Establish data retention policies
- [ ] Test trace correlation with logs and metrics
---
## 7.6 Grafana Dashboard Examples
Pre-built dashboards for rippled observability.
### 7.6.1 Consensus Health Dashboard
```json
{
"title": "rippled Consensus Health",
"uid": "rippled-consensus-health",
"tags": ["rippled", "consensus", "tracing"],
"panels": [
{
"title": "Consensus Round Duration",
"type": "timeseries",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"consensus.round\"} | avg(duration) by (resource.service.instance.id)"
}
],
"fieldConfig": {
"defaults": {
"unit": "ms",
"thresholds": {
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 4000 },
{ "color": "red", "value": 5000 }
]
}
}
},
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
},
{
"title": "Phase Duration Breakdown",
"type": "barchart",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=~\"consensus.phase.*\"} | avg(duration) by (name)"
}
],
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
},
{
"title": "Proposers per Round",
"type": "stat",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"consensus.round\"} | avg(span.xrpl.consensus.proposers)"
}
],
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 8 }
},
{
"title": "Recent Slow Rounds (>5s)",
"type": "table",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"consensus.round\"} | duration > 5s"
}
],
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 12 }
}
]
}
```
### 7.6.2 Node Overview Dashboard
```json
{
"title": "rippled Node Overview",
"uid": "rippled-node-overview",
"panels": [
{
"title": "Active Nodes",
"type": "stat",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\"} | count_over_time() by (resource.service.instance.id) | count()"
}
],
"gridPos": { "h": 4, "w": 4, "x": 0, "y": 0 }
},
{
"title": "Total Transactions (1h)",
"type": "stat",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"tx.receive\"} | count()"
}
],
"gridPos": { "h": 4, "w": 4, "x": 4, "y": 0 }
},
{
"title": "Error Rate",
"type": "gauge",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && status.code=error} | rate() / {resource.service.name=\"rippled\"} | rate() * 100"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"max": 10,
"thresholds": {
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 1 },
{ "color": "red", "value": 5 }
]
}
}
},
"gridPos": { "h": 4, "w": 4, "x": 8, "y": 0 }
},
{
"title": "Service Map",
"type": "nodeGraph",
"datasource": "Tempo",
"gridPos": { "h": 12, "w": 12, "x": 12, "y": 0 }
}
]
}
```
### 7.6.3 Alert Rules
```yaml
# grafana/provisioning/alerting/rippled-alerts.yaml
apiVersion: 1
groups:
- name: rippled-tracing-alerts
folder: rippled
interval: 1m
rules:
- uid: consensus-slow
title: Consensus Round Slow
condition: A
data:
- refId: A
datasourceUid: tempo
model:
queryType: traceql
query: '{resource.service.name="rippled" && name="consensus.round"} | avg(duration) > 5s'
for: 5m
annotations:
summary: Consensus rounds taking >5 seconds
description: "Consensus duration: {{ $value }}ms"
labels:
severity: warning
- uid: rpc-error-spike
title: RPC Error Rate Spike
condition: B
data:
- refId: B
datasourceUid: tempo
model:
queryType: traceql
query: '{resource.service.name="rippled" && name=~"rpc.command.*" && status.code=error} | rate() > 0.05'
for: 2m
annotations:
summary: RPC error rate >5%
labels:
severity: critical
- uid: tx-throughput-drop
title: Transaction Throughput Drop
condition: C
data:
- refId: C
datasourceUid: tempo
model:
queryType: traceql
query: '{resource.service.name="rippled" && name="tx.receive"} | rate() < 10'
for: 10m
annotations:
summary: Transaction throughput below threshold
labels:
severity: warning
```
---
## 7.7 PerfLog and Insight Correlation
How to correlate OpenTelemetry traces with existing rippled observability.
### 7.7.1 Correlation Architecture
```mermaid
flowchart TB
subgraph rippled["rippled Node"]
otel[OpenTelemetry<br/>Spans]
perflog[PerfLog<br/>JSON Logs]
insight[Beast Insight<br/>StatsD Metrics]
end
subgraph collectors["Data Collection"]
otelc[OTel Collector]
promtail[Promtail/Fluentd]
statsd[StatsD Exporter]
end
subgraph storage["Storage"]
tempo[(Tempo)]
loki[(Loki)]
prom[(Prometheus)]
end
subgraph grafana["Grafana"]
traces[Trace View]
logs[Log View]
metrics[Metrics View]
corr[Correlation<br/>Panel]
end
otel -->|OTLP| otelc --> tempo
perflog -->|JSON| promtail --> loki
insight -->|StatsD| statsd --> prom
tempo --> traces
loki --> logs
prom --> metrics
traces --> corr
logs --> corr
metrics --> corr
style rippled fill:#0d47a1,stroke:#082f6a,color:#fff
style collectors fill:#bf360c,stroke:#8c2809,color:#fff
style storage fill:#1b5e20,stroke:#0d3d14,color:#fff
style grafana fill:#4a148c,stroke:#2e0d57,color:#fff
style otel fill:#0d47a1,stroke:#082f6a,color:#fff
style perflog fill:#0d47a1,stroke:#082f6a,color:#fff
style insight fill:#0d47a1,stroke:#082f6a,color:#fff
style otelc fill:#bf360c,stroke:#8c2809,color:#fff
style promtail fill:#bf360c,stroke:#8c2809,color:#fff
style statsd fill:#bf360c,stroke:#8c2809,color:#fff
style tempo fill:#1b5e20,stroke:#0d3d14,color:#fff
style loki fill:#1b5e20,stroke:#0d3d14,color:#fff
style prom fill:#1b5e20,stroke:#0d3d14,color:#fff
style traces fill:#4a148c,stroke:#2e0d57,color:#fff
style logs fill:#4a148c,stroke:#2e0d57,color:#fff
style metrics fill:#4a148c,stroke:#2e0d57,color:#fff
style corr fill:#4a148c,stroke:#2e0d57,color:#fff
```
### 7.7.2 Correlation Fields
| Source | Field | Link To | Purpose |
| ----------- | --------------------------- | ------------- | -------------------------- |
| **Trace** | `trace_id` | Logs | Find log entries for trace |
| **Trace** | `xrpl.tx.hash` | Logs, Metrics | Find TX-related data |
| **Trace** | `xrpl.consensus.ledger.seq` | Logs | Find ledger-related logs |
| **PerfLog** | `trace_id` (new) | Traces | Jump to trace from log |
| **PerfLog** | `ledger_seq` | Traces | Find consensus trace |
| **Insight** | `exemplar.trace_id` | Traces | Jump from metric spike |
### 7.7.3 Example: Debugging a Slow Transaction
**Step 1: Find the trace**
```
# In Grafana Explore with Tempo
{resource.service.name="rippled" && span.xrpl.tx.hash="ABC123..."}
```
**Step 2: Get the trace_id from the trace view**
```
Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736
```
**Step 3: Find related PerfLog entries**
```
# In Grafana Explore with Loki
{job="rippled"} |= "4bf92f3577b34da6a3ce929d0e0e4736"
```
**Step 4: Check Insight metrics for the time window**
```
# In Grafana with Prometheus
rate(rippled_tx_applied_total[1m])
@ timestamp_from_trace
```
### 7.7.4 Unified Dashboard Example
```json
{
"title": "rippled Unified Observability",
"uid": "rippled-unified",
"panels": [
{
"title": "Transaction Latency (Traces)",
"type": "timeseries",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\" && name=\"tx.receive\"} | histogram_over_time(duration)"
}
],
"gridPos": { "h": 6, "w": 8, "x": 0, "y": 0 }
},
{
"title": "Transaction Rate (Metrics)",
"type": "timeseries",
"datasource": "Prometheus",
"targets": [
{
"expr": "rate(rippled_tx_received_total[5m])",
"legendFormat": "{{ instance }}"
}
],
"fieldConfig": {
"defaults": {
"links": [
{
"title": "View traces",
"url": "/explore?left={\"datasource\":\"Tempo\",\"query\":\"{resource.service.name=\\\"rippled\\\" && name=\\\"tx.receive\\\"}\"}"
}
]
}
},
"gridPos": { "h": 6, "w": 8, "x": 8, "y": 0 }
},
{
"title": "Recent Logs",
"type": "logs",
"datasource": "Loki",
"targets": [
{
"expr": "{job=\"rippled\"} | json"
}
],
"gridPos": { "h": 6, "w": 8, "x": 16, "y": 0 }
},
{
"title": "Trace Search",
"type": "table",
"datasource": "Tempo",
"targets": [
{
"queryType": "traceql",
"query": "{resource.service.name=\"rippled\"}"
}
],
"fieldConfig": {
"overrides": [
{
"matcher": { "id": "byName", "options": "traceID" },
"properties": [
{
"id": "links",
"value": [
{
"title": "View trace",
"url": "/explore?left={\"datasource\":\"Tempo\",\"query\":\"${__value.raw}\"}"
},
{
"title": "View logs",
"url": "/explore?left={\"datasource\":\"Loki\",\"query\":\"{job=\\\"rippled\\\"} |= \\\"${__value.raw}\\\"\"}"
}
]
}
]
}
]
},
"gridPos": { "h": 12, "w": 24, "x": 0, "y": 6 }
}
]
}
```
---
_Previous: [Implementation Phases](./06-implementation-phases.md)_ | _Next: [Appendix](./08-appendix.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,133 @@
# Appendix
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
> **Related**: [Observability Backends](./07-observability-backends.md)
---
## 8.1 Glossary
| Term | Definition |
| --------------------- | ---------------------------------------------------------- |
| **Span** | A unit of work with start/end time, name, and attributes |
| **Trace** | A collection of spans representing a complete request flow |
| **Trace ID** | 128-bit unique identifier for a trace |
| **Span ID** | 64-bit unique identifier for a span within a trace |
| **Context** | Carrier for trace/span IDs across boundaries |
| **Propagator** | Component that injects/extracts context |
| **Sampler** | Decides which traces to record |
| **Exporter** | Sends spans to backend |
| **Collector** | Receives, processes, and forwards telemetry |
| **OTLP** | OpenTelemetry Protocol (wire format) |
| **W3C Trace Context** | Standard HTTP headers for trace propagation |
| **Baggage** | Key-value pairs propagated across service boundaries |
| **Resource** | Entity producing telemetry (service, host, etc.) |
| **Instrumentation** | Code that creates telemetry data |
### rippled-Specific Terms
| Term | Definition |
| ----------------- | -------------------------------------------------- |
| **Overlay** | P2P network layer managing peer connections |
| **Consensus** | XRP Ledger consensus algorithm (RCL) |
| **Proposal** | Validator's suggested transaction set for a ledger |
| **Validation** | Validator's signature on a closed ledger |
| **HashRouter** | Component for transaction deduplication |
| **JobQueue** | Thread pool for asynchronous task execution |
| **PerfLog** | Existing performance logging system in rippled |
| **Beast Insight** | Existing metrics framework in rippled |
---
## 8.2 Span Hierarchy Visualization
```mermaid
flowchart TB
subgraph trace["Trace: Transaction Lifecycle"]
rpc["rpc.submit<br/>(entry point)"]
validate["tx.validate"]
relay["tx.relay<br/>(parent span)"]
subgraph peers["Peer Spans"]
p1["peer.send<br/>Peer A"]
p2["peer.send<br/>Peer B"]
p3["peer.send<br/>Peer C"]
end
consensus["consensus.round"]
apply["tx.apply"]
end
rpc --> validate
validate --> relay
relay --> p1
relay --> p2
relay --> p3
p1 -.->|"context propagation"| consensus
consensus --> apply
style trace fill:#0f172a,stroke:#020617,color:#fff
style peers fill:#1e3a8a,stroke:#172554,color:#fff
style rpc fill:#1d4ed8,stroke:#1e40af,color:#fff
style validate fill:#047857,stroke:#064e3b,color:#fff
style relay fill:#047857,stroke:#064e3b,color:#fff
style p1 fill:#0e7490,stroke:#155e75,color:#fff
style p2 fill:#0e7490,stroke:#155e75,color:#fff
style p3 fill:#0e7490,stroke:#155e75,color:#fff
style consensus fill:#fef3c7,stroke:#fde68a,color:#1e293b
style apply fill:#047857,stroke:#064e3b,color:#fff
```
---
## 8.3 References
### OpenTelemetry Resources
1. [OpenTelemetry C++ SDK](https://github.com/open-telemetry/opentelemetry-cpp)
2. [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/)
3. [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/)
4. [OTLP Protocol Specification](https://opentelemetry.io/docs/specs/otlp/)
### Standards
5. [W3C Trace Context](https://www.w3.org/TR/trace-context/)
6. [W3C Baggage](https://www.w3.org/TR/baggage/)
7. [Protocol Buffers](https://protobuf.dev/)
### rippled Resources
8. [rippled Source Code](https://github.com/XRPLF/rippled)
9. [XRP Ledger Documentation](https://xrpl.org/docs/)
10. [rippled Overlay README](https://github.com/XRPLF/rippled/blob/develop/src/xrpld/overlay/README.md)
11. [rippled RPC README](https://github.com/XRPLF/rippled/blob/develop/src/xrpld/rpc/README.md)
12. [rippled Consensus README](https://github.com/XRPLF/rippled/blob/develop/src/xrpld/app/consensus/README.md)
---
## 8.4 Version History
| Version | Date | Author | Changes |
| ------- | ---------- | ------ | --------------------------------- |
| 1.0 | 2026-02-12 | - | Initial implementation plan |
| 1.1 | 2026-02-13 | - | Refactored into modular documents |
---
## 8.5 Document Index
| Document | Description |
| ---------------------------------------------------------------- | ------------------------------------------ |
| [OpenTelemetryPlan.md](./OpenTelemetryPlan.md) | Master overview and executive summary |
| [01-architecture-analysis.md](./01-architecture-analysis.md) | rippled architecture and trace points |
| [02-design-decisions.md](./02-design-decisions.md) | SDK selection, exporters, span conventions |
| [03-implementation-strategy.md](./03-implementation-strategy.md) | Directory structure, performance analysis |
| [04-code-samples.md](./04-code-samples.md) | C++ code examples for all components |
| [05-configuration-reference.md](./05-configuration-reference.md) | rippled config, CMake, Collector configs |
| [06-implementation-phases.md](./06-implementation-phases.md) | Timeline, tasks, risks, success metrics |
| [07-observability-backends.md](./07-observability-backends.md) | Backend selection and architecture |
| [08-appendix.md](./08-appendix.md) | Glossary, references, version history |
---
_Previous: [Observability Backends](./07-observability-backends.md)_ | _Back to: [Overview](./OpenTelemetryPlan.md)_

View File

@@ -0,0 +1,190 @@
# [OpenTelemetry](00-tracing-fundamentals.md) Distributed Tracing Implementation Plan for rippled (xrpld)
## Executive Summary
This document provides a comprehensive implementation plan for integrating OpenTelemetry distributed tracing into the rippled XRP Ledger node software. The plan addresses the unique challenges of a decentralized peer-to-peer system where trace context must propagate across network boundaries between independent nodes.
### Key Benefits
- **End-to-end transaction visibility**: Track transactions from submission through consensus to ledger inclusion
- **Consensus round analysis**: Understand timing and behavior of consensus phases across validators
- **RPC performance insights**: Identify slow handlers and optimize response times
- **Network topology understanding**: Visualize message propagation patterns between peers
- **Incident debugging**: Correlate events across distributed nodes during issues
### Estimated Performance Overhead
| Metric | Overhead | Notes |
| ------------- | ---------- | ----------------------------------- |
| CPU | 1-3% | Span creation and attribute setting |
| Memory | 2-5 MB | Batch buffer for pending spans |
| Network | 10-50 KB/s | Compressed OTLP export to collector |
| Latency (p99) | <2% | With proper sampling configuration |
---
## Document Structure
This implementation plan is organized into modular documents for easier navigation:
<div align="center">
```mermaid
flowchart TB
overview["📋 OpenTelemetryPlan.md<br/>(This Document)"]
subgraph analysis["Analysis & Design"]
arch["01-architecture-analysis.md"]
design["02-design-decisions.md"]
end
subgraph impl["Implementation"]
strategy["03-implementation-strategy.md"]
code["04-code-samples.md"]
config["05-configuration-reference.md"]
end
subgraph deploy["Deployment & Planning"]
phases["06-implementation-phases.md"]
backends["07-observability-backends.md"]
appendix["08-appendix.md"]
end
overview --> analysis
overview --> impl
overview --> deploy
arch --> design
design --> strategy
strategy --> code
code --> config
config --> phases
phases --> backends
backends --> appendix
style overview fill:#1b5e20,stroke:#0d3d14,color:#fff,stroke-width:2px
style analysis fill:#0d47a1,stroke:#082f6a,color:#fff
style impl fill:#bf360c,stroke:#8c2809,color:#fff
style deploy fill:#4a148c,stroke:#2e0d57,color:#fff
style arch fill:#0d47a1,stroke:#082f6a,color:#fff
style design fill:#0d47a1,stroke:#082f6a,color:#fff
style strategy fill:#bf360c,stroke:#8c2809,color:#fff
style code fill:#bf360c,stroke:#8c2809,color:#fff
style config fill:#bf360c,stroke:#8c2809,color:#fff
style phases fill:#4a148c,stroke:#2e0d57,color:#fff
style backends fill:#4a148c,stroke:#2e0d57,color:#fff
style appendix fill:#4a148c,stroke:#2e0d57,color:#fff
```
</div>
---
## Table of Contents
| Section | Document | Description |
| ------- | ---------------------------------------------------------- | ---------------------------------------------------------------------- |
| **1** | [Architecture Analysis](./01-architecture-analysis.md) | rippled component analysis, trace points, instrumentation priorities |
| **2** | [Design Decisions](./02-design-decisions.md) | SDK selection, exporters, span naming, attributes, context propagation |
| **3** | [Implementation Strategy](./03-implementation-strategy.md) | Directory structure, key principles, performance optimization |
| **4** | [Code Samples](./04-code-samples.md) | Complete C++ implementation examples for all components |
| **5** | [Configuration Reference](./05-configuration-reference.md) | rippled config, CMake integration, Collector configurations |
| **6** | [Implementation Phases](./06-implementation-phases.md) | 5-phase timeline, tasks, risks, success metrics |
| **7** | [Observability Backends](./07-observability-backends.md) | Backend selection guide and production architecture |
| **8** | [Appendix](./08-appendix.md) | Glossary, references, version history |
---
## 1. Architecture Analysis
The rippled node consists of several key components that require instrumentation for comprehensive distributed tracing. The main areas include the RPC server (HTTP/WebSocket), Overlay P2P network, Consensus mechanism (RCLConsensus), JobQueue for async task execution, and existing observability infrastructure (PerfLog, Insight/StatsD, Journal logging).
Key trace points span across transaction submission via RPC, peer-to-peer message propagation, consensus round execution, and ledger building. The implementation prioritizes high-value, low-risk components first: RPC handlers provide immediate value with minimal risk, while consensus tracing requires careful implementation to avoid timing impacts.
➡️ **[Read full Architecture Analysis](./01-architecture-analysis.md)**
---
## 2. Design Decisions
The OpenTelemetry C++ SDK is selected for its CNCF backing, active development, and native performance characteristics. Traces are exported via OTLP/gRPC (primary) or OTLP/HTTP (fallback) to an OpenTelemetry Collector, which provides flexible routing and sampling.
Span naming follows a hierarchical `<component>.<operation>` convention (e.g., `rpc.submit`, `tx.relay`, `consensus.round`). Context propagation uses W3C Trace Context headers for HTTP and embedded Protocol Buffer fields for P2P messages. The implementation coexists with existing PerfLog and Insight observability systems through correlation IDs.
**Data Collection & Privacy**: Telemetry collects only operational metadata (timing, counts, hashes) — never sensitive content (private keys, balances, amounts, raw payloads). Privacy protection includes account hashing, configurable redaction, sampling, and collector-level filtering. Node operators retain full control(not penned down in this document yet) over what data is exported.
➡️ **[Read full Design Decisions](./02-design-decisions.md)**
---
## 3. Implementation Strategy
The telemetry code is organized under `include/xrpl/telemetry/` for headers and `src/libxrpl/telemetry/` for implementation. Key principles include RAII-based span management via `SpanGuard`, conditional compilation with `XRPL_ENABLE_TELEMETRY`, and minimal runtime overhead through batch processing and efficient sampling.
Performance optimization strategies include probabilistic head sampling (10% default), tail-based sampling at the collector for errors and slow traces, batch export to reduce network overhead, and conditional instrumentation that compiles to no-ops when disabled.
➡️ **[Read full Implementation Strategy](./03-implementation-strategy.md)**
---
## 4. Code Samples
Complete C++ implementation examples are provided for all telemetry components:
- `Telemetry.h` - Core interface for tracer access and span creation
- `SpanGuard.h` - RAII wrapper for automatic span lifecycle management
- `TracingInstrumentation.h` - Macros for conditional instrumentation
- Protocol Buffer extensions for trace context propagation
- Module-specific instrumentation (RPC, Consensus, P2P, JobQueue)
➡️ **[View all Code Samples](./04-code-samples.md)**
---
## 5. Configuration Reference
Configuration is handled through the `[telemetry]` section in `xrpld.cfg` with options for enabling/disabling, exporter selection, endpoint configuration, sampling ratios, and component-level filtering. CMake integration includes a `XRPL_ENABLE_TELEMETRY` option for compile-time control.
OpenTelemetry Collector configurations are provided for development (with Jaeger) and production (with tail-based sampling, Tempo, and Elastic APM). Docker Compose examples enable quick local development environment setup.
➡️ **[View full Configuration Reference](./05-configuration-reference.md)**
---
## 6. Implementation Phases
The implementation spans 9 weeks across 5 phases:
| Phase | Duration | Focus | Key Deliverables |
| ----- | --------- | ------------------- | --------------------------------------------------- |
| 1 | Weeks 1-2 | Core Infrastructure | SDK integration, Telemetry interface, Configuration |
| 2 | Weeks 3-4 | RPC Tracing | HTTP context extraction, Handler instrumentation |
| 3 | Weeks 5-6 | Transaction Tracing | Protocol Buffer context, Relay propagation |
| 4 | Weeks 7-8 | Consensus Tracing | Round spans, Proposal/validation tracing |
| 5 | Week 9 | Documentation | Runbook, Dashboards, Training |
**Total Effort**: 47 developer-days with 2 developers
➡️ **[View full Implementation Phases](./06-implementation-phases.md)**
---
## 7. Observability Backends
For development and testing, Jaeger provides easy setup with a good UI. For production deployments, Grafana Tempo is recommended for its cost-effectiveness and Grafana integration, while Elastic APM is ideal for organizations with existing Elastic infrastructure.
The recommended production architecture uses a gateway collector pattern with regional collectors performing tail-based sampling, routing traces to multiple backends (Tempo for primary storage, Elastic for log correlation, S3/GCS for long-term archive).
➡️ **[View Observability Backend Recommendations](./07-observability-backends.md)**
---
## 8. Appendix
The appendix contains a glossary of OpenTelemetry and rippled-specific terms, references to external documentation and specifications, version history for this implementation plan, and a complete document index.
➡️ **[View Appendix](./08-appendix.md)**
---
_This document provides a comprehensive implementation plan for integrating OpenTelemetry distributed tracing into the rippled XRP Ledger node software. For detailed information on any section, follow the links to the corresponding sub-documents._

View File

@@ -0,0 +1,610 @@
# OpenTelemetry POC Task List
> **Goal**: Build a minimal end-to-end proof of concept that demonstrates distributed tracing in rippled. A successful POC will show RPC request traces flowing from rippled through an OTel Collector into Jaeger, viewable in a browser UI.
>
> **Scope**: RPC tracing only (highest value, lowest risk per the [CRAWL phase](./06-implementation-phases.md#6102-quick-wins-immediate-value) in the implementation phases). No cross-node P2P context propagation or consensus tracing in the POC.
### Related Plan Documents
| Document | Relevance to POC |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [00-tracing-fundamentals.md](./00-tracing-fundamentals.md) | Core concepts: traces, spans, context propagation, sampling |
| [01-architecture-analysis.md](./01-architecture-analysis.md) | RPC request flow (§1.5), key trace points (§1.6), instrumentation priority (§1.7) |
| [02-design-decisions.md](./02-design-decisions.md) | SDK selection (§2.1), exporter config (§2.2), span naming (§2.3), attribute schema (§2.4), coexistence with PerfLog/Insight (§2.6) |
| [03-implementation-strategy.md](./03-implementation-strategy.md) | Directory structure (§3.1), key principles (§3.2), performance overhead (§3.3-3.6), conditional compilation (§3.7.3), code intrusiveness (§3.9) |
| [04-code-samples.md](./04-code-samples.md) | Telemetry interface (§4.1), SpanGuard (§4.2), macros (§4.3), RPC instrumentation (§4.5.3) |
| [05-configuration-reference.md](./05-configuration-reference.md) | rippled config (§5.1), config parser (§5.2), Application integration (§5.3), CMake (§5.4), Collector config (§5.5), Docker Compose (§5.6), Grafana (§5.8) |
| [06-implementation-phases.md](./06-implementation-phases.md) | Phase 1 core tasks (§6.2), Phase 2 RPC tasks (§6.3), quick wins (§6.10), definition of done (§6.11) |
| [07-observability-backends.md](./07-observability-backends.md) | Jaeger dev setup (§7.1), Grafana dashboards (§7.6), alert rules (§7.6.3) |
---
## Task 0: Docker Observability Stack Setup
**Objective**: Stand up the backend infrastructure to receive, store, and display traces.
**What to do**:
- Create `docker/telemetry/docker-compose.yml` in the repo with three services:
1. **OpenTelemetry Collector** (`otel/opentelemetry-collector-contrib:latest`)
- Expose ports `4317` (OTLP gRPC) and `4318` (OTLP HTTP)
- Expose port `13133` (health check)
- Mount a config file `docker/telemetry/otel-collector-config.yaml`
2. **Jaeger** (`jaegertracing/all-in-one:latest`)
- Expose port `16686` (UI) and `14250` (gRPC collector)
- Set env `COLLECTOR_OTLP_ENABLED=true`
3. **Grafana** (`grafana/grafana:latest`) — optional but useful
- Expose port `3000`
- Enable anonymous admin access for local dev (`GF_AUTH_ANONYMOUS_ENABLED=true`, `GF_AUTH_ANONYMOUS_ORG_ROLE=Admin`)
- Provision Jaeger as a data source via `docker/telemetry/grafana/provisioning/datasources/jaeger.yaml`
- Create `docker/telemetry/otel-collector-config.yaml`:
```yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 100
exporters:
logging:
verbosity: detailed
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [logging, otlp/jaeger]
```
- Create Grafana Jaeger datasource provisioning file at `docker/telemetry/grafana/provisioning/datasources/jaeger.yaml`:
```yaml
apiVersion: 1
datasources:
- name: Jaeger
type: jaeger
access: proxy
url: http://jaeger:16686
```
**Verification**: Run `docker compose -f docker/telemetry/docker-compose.yml up -d`, then:
- `curl http://localhost:13133` returns healthy (Collector)
- `http://localhost:16686` opens Jaeger UI (no traces yet)
- `http://localhost:3000` opens Grafana (optional)
**Reference**:
- [05-configuration-reference.md §5.5](./05-configuration-reference.md) — Collector config (dev YAML with Jaeger exporter)
- [05-configuration-reference.md §5.6](./05-configuration-reference.md) — Docker Compose development environment
- [07-observability-backends.md §7.1](./07-observability-backends.md) — Jaeger quick start and backend selection
- [05-configuration-reference.md §5.8](./05-configuration-reference.md) — Grafana datasource provisioning and dashboards
---
## Task 1: Add OpenTelemetry C++ SDK Dependency
**Objective**: Make `opentelemetry-cpp` available to the build system.
**What to do**:
- Edit `conanfile.py` to add `opentelemetry-cpp` as an **optional** dependency. The gRPC otel plugin flag (`"grpc/*:otel_plugin": False`) in the existing conanfile may need to remain false — we pull the OTel SDK separately.
- Add a Conan option: `with_telemetry = [True, False]` defaulting to `False`
- When `with_telemetry` is `True`, add `opentelemetry-cpp` to `self.requires()`
- Required OTel Conan components: `opentelemetry-cpp` (which bundles api, sdk, and exporters). If the package isn't in Conan Center, consider using `FetchContent` in CMake or building from source as a fallback.
- Edit `CMakeLists.txt`:
- Add option: `option(XRPL_ENABLE_TELEMETRY "Enable OpenTelemetry tracing" OFF)`
- When ON, `find_package(opentelemetry-cpp CONFIG REQUIRED)` and add compile definition `XRPL_ENABLE_TELEMETRY`
- When OFF, do nothing (zero build impact)
- Verify the build succeeds with `-DXRPL_ENABLE_TELEMETRY=OFF` (no regressions) and with `-DXRPL_ENABLE_TELEMETRY=ON` (SDK links successfully).
**Key files**:
- `conanfile.py`
- `CMakeLists.txt`
**Reference**:
- [05-configuration-reference.md §5.4](./05-configuration-reference.md) — CMake integration, `FindOpenTelemetry.cmake`, `XRPL_ENABLE_TELEMETRY` option
- [03-implementation-strategy.md §3.2](./03-implementation-strategy.md) — Key principle: zero-cost when disabled via compile-time flags
- [02-design-decisions.md §2.1](./02-design-decisions.md) — SDK selection rationale and required OTel components
---
## Task 2: Create Core Telemetry Interface and NullTelemetry
**Objective**: Define the `Telemetry` abstract interface and a no-op implementation so the rest of the codebase can reference telemetry without hard-depending on the OTel SDK.
**What to do**:
- Create `include/xrpl/telemetry/Telemetry.h`:
- Define `namespace xrpl::telemetry`
- Define `struct Telemetry::Setup` holding: `enabled`, `exporterEndpoint`, `samplingRatio`, `serviceName`, `serviceVersion`, `serviceInstanceId`, `traceRpc`, `traceTransactions`, `traceConsensus`, `tracePeer`
- Define abstract `class Telemetry` with:
- `virtual void start() = 0;`
- `virtual void stop() = 0;`
- `virtual bool isEnabled() const = 0;`
- `virtual nostd::shared_ptr<Tracer> getTracer(string_view name = "rippled") = 0;`
- `virtual nostd::shared_ptr<Span> startSpan(string_view name, SpanKind kind = kInternal) = 0;`
- `virtual nostd::shared_ptr<Span> startSpan(string_view name, Context const& parentContext, SpanKind kind = kInternal) = 0;`
- `virtual bool shouldTraceRpc() const = 0;`
- `virtual bool shouldTraceTransactions() const = 0;`
- `virtual bool shouldTraceConsensus() const = 0;`
- Factory: `std::unique_ptr<Telemetry> make_Telemetry(Setup const&, beast::Journal);`
- Config parser: `Telemetry::Setup setup_Telemetry(Section const&, std::string const& nodePublicKey, std::string const& version);`
- Create `include/xrpl/telemetry/SpanGuard.h`:
- RAII guard that takes an `nostd::shared_ptr<Span>`, creates a `Scope`, and calls `span->End()` in destructor.
- Convenience: `setAttribute()`, `setOk()`, `setStatus()`, `addEvent()`, `recordException()`, `context()`
- See [04-code-samples.md](./04-code-samples.md) §4.2 for the full implementation.
- Create `src/libxrpl/telemetry/NullTelemetry.cpp`:
- Implements `Telemetry` with all no-ops.
- `isEnabled()` returns `false`, `startSpan()` returns a noop span.
- This is used when `XRPL_ENABLE_TELEMETRY` is OFF or `enabled=0` in config.
- Guard all OTel SDK headers behind `#ifdef XRPL_ENABLE_TELEMETRY`. The `NullTelemetry` implementation should compile without the OTel SDK present.
**Key new files**:
- `include/xrpl/telemetry/Telemetry.h`
- `include/xrpl/telemetry/SpanGuard.h`
- `src/libxrpl/telemetry/NullTelemetry.cpp`
**Reference**:
- [04-code-samples.md §4.1](./04-code-samples.md) — Full `Telemetry` interface with `Setup` struct, lifecycle, tracer access, span creation, and component filtering methods
- [04-code-samples.md §4.2](./04-code-samples.md) — Full `SpanGuard` RAII implementation and `NullSpanGuard` no-op class
- [03-implementation-strategy.md §3.1](./03-implementation-strategy.md) — Directory structure: `include/xrpl/telemetry/` for headers, `src/libxrpl/telemetry/` for implementation
- [03-implementation-strategy.md §3.7.3](./03-implementation-strategy.md) — Conditional instrumentation and zero-cost compile-time disabled pattern
---
## Task 3: Implement OTel-Backed Telemetry
**Objective**: Implement the real `Telemetry` class that initializes the OTel SDK, configures the OTLP exporter and batch processor, and creates tracers/spans.
**What to do**:
- Create `src/libxrpl/telemetry/Telemetry.cpp` (compiled only when `XRPL_ENABLE_TELEMETRY=ON`):
- `class TelemetryImpl : public Telemetry` that:
- In `start()`: creates a `TracerProvider` with:
- Resource attributes: `service.name`, `service.version`, `service.instance.id`
- An `OtlpGrpcExporter` pointed at `setup.exporterEndpoint` (default `localhost:4317`)
- A `BatchSpanProcessor` with configurable batch size and delay
- A `TraceIdRatioBasedSampler` using `setup.samplingRatio`
- Sets the global `TracerProvider`
- In `stop()`: calls `ForceFlush()` then shuts down the provider
- In `startSpan()`: delegates to `getTracer()->StartSpan(name, ...)`
- `shouldTraceRpc()` etc. read from `Setup` fields
- Create `src/libxrpl/telemetry/TelemetryConfig.cpp`:
- `setup_Telemetry()` parses the `[telemetry]` config section from `xrpld.cfg`
- Maps config keys: `enabled`, `exporter`, `endpoint`, `sampling_ratio`, `trace_rpc`, `trace_transactions`, `trace_consensus`, `trace_peer`
- Wire `make_Telemetry()` factory:
- If `setup.enabled` is true AND `XRPL_ENABLE_TELEMETRY` is defined: return `TelemetryImpl`
- Otherwise: return `NullTelemetry`
- Add telemetry source files to CMake. When `XRPL_ENABLE_TELEMETRY=ON`, compile `Telemetry.cpp` and `TelemetryConfig.cpp` and link against `opentelemetry-cpp::api`, `opentelemetry-cpp::sdk`, `opentelemetry-cpp::otlp_grpc_exporter`. When OFF, compile only `NullTelemetry.cpp`.
**Key new files**:
- `src/libxrpl/telemetry/Telemetry.cpp`
- `src/libxrpl/telemetry/TelemetryConfig.cpp`
**Key modified files**:
- `CMakeLists.txt` (add telemetry library target)
**Reference**:
- [04-code-samples.md §4.1](./04-code-samples.md) — `Telemetry` interface that `TelemetryImpl` must implement
- [05-configuration-reference.md §5.2](./05-configuration-reference.md) — `setup_Telemetry()` config parser implementation
- [02-design-decisions.md §2.2](./02-design-decisions.md) — OTLP/gRPC exporter config (endpoint, TLS options)
- [02-design-decisions.md §2.4.1](./02-design-decisions.md) — Resource attributes: `service.name`, `service.version`, `service.instance.id`, `xrpl.network.id`
- [03-implementation-strategy.md §3.4](./03-implementation-strategy.md) — Per-operation CPU costs and overhead budget for span creation
- [03-implementation-strategy.md §3.5](./03-implementation-strategy.md) — Memory overhead: static (~456 KB) and dynamic (~1.2 MB) budgets
---
## Task 4: Integrate Telemetry into Application Lifecycle
**Objective**: Wire the `Telemetry` object into `Application` so all components can access it.
**What to do**:
- Edit `src/xrpld/app/main/Application.h`:
- Forward-declare `namespace xrpl::telemetry { class Telemetry; }`
- Add pure virtual method: `virtual telemetry::Telemetry& getTelemetry() = 0;`
- Edit `src/xrpld/app/main/Application.cpp` (the `ApplicationImp` class):
- Add member: `std::unique_ptr<telemetry::Telemetry> telemetry_;`
- In the constructor, after config is loaded and node identity is known:
```cpp
auto const telemetrySection = config_->section("telemetry");
auto telemetrySetup = telemetry::setup_Telemetry(
telemetrySection,
toBase58(TokenType::NodePublic, nodeIdentity_.publicKey()),
BuildInfo::getVersionString());
telemetry_ = telemetry::make_Telemetry(telemetrySetup, logs_->journal("Telemetry"));
```
- In `start()`: call `telemetry_->start()` early
- In `stop()` or destructor: call `telemetry_->stop()` late (to flush pending spans)
- Implement `getTelemetry()` override: return `*telemetry_`
- Add `[telemetry]` section to the example config `cfg/rippled-example.cfg`:
```ini
# [telemetry]
# enabled=1
# endpoint=localhost:4317
# sampling_ratio=1.0
# trace_rpc=1
```
**Key modified files**:
- `src/xrpld/app/main/Application.h`
- `src/xrpld/app/main/Application.cpp`
- `cfg/rippled-example.cfg` (or equivalent example config)
**Reference**:
- [05-configuration-reference.md §5.3](./05-configuration-reference.md) — `ApplicationImp` changes: member declaration, constructor init, `start()`/`stop()` wiring, `getTelemetry()` override
- [05-configuration-reference.md §5.1](./05-configuration-reference.md) — `[telemetry]` config section format and all option defaults
- [03-implementation-strategy.md §3.9.2](./03-implementation-strategy.md) — File impact assessment: `Application.cpp` ~15 lines added, ~3 changed (Low risk)
---
## Task 5: Create Instrumentation Macros
**Objective**: Define convenience macros that make instrumenting code one-liners, and that compile to zero-cost no-ops when telemetry is disabled.
**What to do**:
- Create `src/xrpld/telemetry/TracingInstrumentation.h`:
- When `XRPL_ENABLE_TELEMETRY` is defined:
```cpp
#define XRPL_TRACE_SPAN(telemetry, name) \
auto _xrpl_span_ = (telemetry).startSpan(name); \
::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_)
#define XRPL_TRACE_RPC(telemetry, name) \
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
if ((telemetry).shouldTraceRpc()) { \
_xrpl_guard_.emplace((telemetry).startSpan(name)); \
}
#define XRPL_TRACE_SET_ATTR(key, value) \
if (_xrpl_guard_.has_value()) { \
_xrpl_guard_->setAttribute(key, value); \
}
#define XRPL_TRACE_EXCEPTION(e) \
if (_xrpl_guard_.has_value()) { \
_xrpl_guard_->recordException(e); \
}
```
- When `XRPL_ENABLE_TELEMETRY` is NOT defined, all macros expand to `((void)0)`
**Key new file**:
- `src/xrpld/telemetry/TracingInstrumentation.h`
**Reference**:
- [04-code-samples.md §4.3](./04-code-samples.md) — Full macro definitions for `XRPL_TRACE_SPAN`, `XRPL_TRACE_RPC`, `XRPL_TRACE_CONSENSUS`, `XRPL_TRACE_SET_ATTR`, `XRPL_TRACE_EXCEPTION` with both enabled and disabled branches
- [03-implementation-strategy.md §3.7.3](./03-implementation-strategy.md) — Conditional instrumentation pattern: compile-time `#ifndef` and runtime `shouldTrace*()` checks
- [03-implementation-strategy.md §3.9.7](./03-implementation-strategy.md) — Before/after code examples showing minimal intrusiveness (~1-3 lines per instrumentation point)
---
## Task 6: Instrument RPC ServerHandler
**Objective**: Add tracing to the HTTP RPC entry point so every incoming RPC request creates a span.
**What to do**:
- Edit `src/xrpld/rpc/detail/ServerHandler.cpp`:
- `#include` the `TracingInstrumentation.h` header
- In `ServerHandler::onRequest(Session& session)`:
- At the top of the method, add: `XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.request");`
- After the RPC command name is extracted, set attribute: `XRPL_TRACE_SET_ATTR("xrpl.rpc.command", command);`
- After the response status is known, set: `XRPL_TRACE_SET_ATTR("http.status_code", static_cast<int64_t>(statusCode));`
- Wrap error paths with: `XRPL_TRACE_EXCEPTION(e);`
- In `ServerHandler::processRequest(...)`:
- Add a child span: `XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.process");`
- Set method attribute: `XRPL_TRACE_SET_ATTR("xrpl.rpc.method", request_method);`
- In `ServerHandler::onWSMessage(...)` (WebSocket path):
- Add: `XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.ws.message");`
- The goal is to see spans like:
```
rpc.request
└── rpc.process
```
in Jaeger for every HTTP RPC call.
**Key modified file**:
- `src/xrpld/rpc/detail/ServerHandler.cpp` (~15-25 lines added)
**Reference**:
- [04-code-samples.md §4.5.3](./04-code-samples.md) — Complete `ServerHandler::onRequest()` instrumented code sample with W3C header extraction, span creation, attribute setting, and error handling
- [01-architecture-analysis.md §1.5](./01-architecture-analysis.md) — RPC request flow diagram: HTTP request -> attributes -> jobqueue.enqueue -> rpc.command -> response
- [01-architecture-analysis.md §1.6](./01-architecture-analysis.md) — Key trace points table: `rpc.request` in `ServerHandler.cpp::onRequest()` (Priority: High)
- [02-design-decisions.md §2.3](./02-design-decisions.md) — Span naming convention: `rpc.request`, `rpc.command.*`
- [02-design-decisions.md §2.4.2](./02-design-decisions.md) — RPC span attributes: `xrpl.rpc.command`, `xrpl.rpc.version`, `xrpl.rpc.role`, `xrpl.rpc.params`
- [03-implementation-strategy.md §3.9.2](./03-implementation-strategy.md) — File impact: `ServerHandler.cpp` ~40 lines added, ~10 changed (Low risk)
---
## Task 7: Instrument RPC Command Execution
**Objective**: Add per-command tracing inside the RPC handler so each command (e.g., `submit`, `account_info`, `server_info`) gets its own child span.
**What to do**:
- Edit `src/xrpld/rpc/detail/RPCHandler.cpp`:
- `#include` the `TracingInstrumentation.h` header
- In `doCommand(RPC::JsonContext& context, Json::Value& result)`:
- At the top: `XRPL_TRACE_RPC(context.app.getTelemetry(), "rpc.command." + context.method);`
- Set attributes:
- `XRPL_TRACE_SET_ATTR("xrpl.rpc.command", context.method);`
- `XRPL_TRACE_SET_ATTR("xrpl.rpc.version", static_cast<int64_t>(context.apiVersion));`
- `XRPL_TRACE_SET_ATTR("xrpl.rpc.role", (context.role == Role::ADMIN) ? "admin" : "user");`
- On success: `XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "success");`
- On error: `XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "error");` and set the error message
- After this, traces in Jaeger should look like:
```
rpc.request (xrpl.rpc.command=account_info)
└── rpc.process
└── rpc.command.account_info (xrpl.rpc.version=2, xrpl.rpc.role=user, xrpl.rpc.status=success)
```
**Key modified file**:
- `src/xrpld/rpc/detail/RPCHandler.cpp` (~15-20 lines added)
**Reference**:
- [04-code-samples.md §4.5.3](./04-code-samples.md) — `ServerHandler::onRequest()` code sample (includes child span pattern for `rpc.command.*`)
- [02-design-decisions.md §2.3](./02-design-decisions.md) — Span naming: `rpc.command.*` pattern with dynamic command name (e.g., `rpc.command.server_info`)
- [02-design-decisions.md §2.4.2](./02-design-decisions.md) — RPC attribute schema: `xrpl.rpc.command`, `xrpl.rpc.version`, `xrpl.rpc.role`, `xrpl.rpc.status`
- [01-architecture-analysis.md §1.6](./01-architecture-analysis.md) — Key trace points table: `rpc.command.*` in `RPCHandler.cpp::doCommand()` (Priority: High)
- [02-design-decisions.md §2.6.5](./02-design-decisions.md) — Correlation with PerfLog: how `doCommand()` can link trace_id with existing PerfLog entries
- [03-implementation-strategy.md §3.4.4](./03-implementation-strategy.md) — RPC request overhead budget: ~1.75 μs total per request
---
## Task 8: Build, Run, and Verify End-to-End
**Objective**: Prove the full pipeline works: rippled emits traces -> OTel Collector receives them -> Jaeger displays them.
**What to do**:
1. **Start the Docker stack**:
```bash
docker compose -f docker/telemetry/docker-compose.yml up -d
```
Verify Collector health: `curl http://localhost:13133`
2. **Build rippled with telemetry**:
```bash
# Adjust for your actual build workflow
conan install . --build=missing -o with_telemetry=True
cmake --preset default -DXRPL_ENABLE_TELEMETRY=ON
cmake --build --preset default
```
3. **Configure rippled**:
Add to `rippled.cfg` (or your local test config):
```ini
[telemetry]
enabled=1
endpoint=localhost:4317
sampling_ratio=1.0
trace_rpc=1
```
4. **Start rippled** in standalone mode:
```bash
./rippled --conf rippled.cfg -a --start
```
5. **Generate RPC traffic**:
```bash
# server_info
curl -s -X POST http://localhost:5005 \
-H "Content-Type: application/json" \
-d '{"method":"server_info","params":[{}]}'
# ledger
curl -s -X POST http://localhost:5005 \
-H "Content-Type: application/json" \
-d '{"method":"ledger","params":[{"ledger_index":"current"}]}'
# account_info (will error in standalone, that's fine — we trace errors too)
curl -s -X POST http://localhost:5005 \
-H "Content-Type: application/json" \
-d '{"method":"account_info","params":[{"account":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"}]}'
```
6. **Verify in Jaeger**:
- Open `http://localhost:16686`
- Select service `rippled` from the dropdown
- Click "Find Traces"
- Confirm you see traces with spans: `rpc.request` -> `rpc.process` -> `rpc.command.server_info`
- Click into a trace and verify attributes: `xrpl.rpc.command`, `xrpl.rpc.status`, `xrpl.rpc.version`
7. **Verify zero-overhead when disabled**:
- Rebuild with `XRPL_ENABLE_TELEMETRY=OFF`, or set `enabled=0` in config
- Run the same RPC calls
- Confirm no new traces appear and no errors in rippled logs
**Verification Checklist**:
- [ ] Docker stack starts without errors
- [ ] rippled builds with `-DXRPL_ENABLE_TELEMETRY=ON`
- [ ] rippled starts and connects to OTel Collector (check rippled logs for telemetry messages)
- [ ] Traces appear in Jaeger UI under service "rippled"
- [ ] Span hierarchy is correct (parent-child relationships)
- [ ] Span attributes are populated (`xrpl.rpc.command`, `xrpl.rpc.status`, etc.)
- [ ] Error spans show error status and message
- [ ] Building with `XRPL_ENABLE_TELEMETRY=OFF` produces no regressions
- [ ] Setting `enabled=0` at runtime produces no traces and no errors
**Reference**:
- [06-implementation-phases.md §6.11.1](./06-implementation-phases.md) — Phase 1 definition of done: SDK compiles, runtime toggle works, span creation verified in Jaeger, config validation passes
- [06-implementation-phases.md §6.11.2](./06-implementation-phases.md) — Phase 2 definition of done: 100% RPC coverage, traceparent propagation, <1ms p99 overhead, dashboard deployed
- [06-implementation-phases.md §6.8](./06-implementation-phases.md) — Success metrics: trace coverage >95%, CPU overhead <3%, memory <5 MB, latency impact <2%
- [03-implementation-strategy.md §3.9.5](./03-implementation-strategy.md) — Backward compatibility: config optional, protocol unchanged, `XRPL_ENABLE_TELEMETRY=OFF` produces identical binary
- [01-architecture-analysis.md §1.8](./01-architecture-analysis.md) — Observable outcomes: what traces, metrics, and dashboards to expect
---
## Task 9: Document POC Results and Next Steps
**Objective**: Capture findings, screenshots, and remaining work for the team.
**What to do**:
- Take screenshots of Jaeger showing:
- The service list with "rippled"
- A trace with the full span tree
- Span detail view showing attributes
- Document any issues encountered (build issues, SDK quirks, missing attributes)
- Note performance observations (build time impact, any noticeable runtime overhead)
- Write a short summary of what the POC proves and what it doesn't cover yet:
- **Proves**: OTel SDK integrates with rippled, OTLP export works, RPC traces visible
- **Doesn't cover**: Cross-node P2P context propagation, consensus tracing, protobuf trace context, W3C traceparent header extraction, tail-based sampling, production deployment
- Outline next steps (mapping to the full plan phases):
- [Phase 2](./06-implementation-phases.md) completion: [W3C header extraction](./02-design-decisions.md) (§2.5), WebSocket tracing, all [RPC handlers](./01-architecture-analysis.md) (§1.6)
- [Phase 3](./06-implementation-phases.md): [Protobuf `TraceContext` message](./04-code-samples.md) (§4.4), [transaction relay tracing](./04-code-samples.md) (§4.5.1) across nodes
- [Phase 4](./06-implementation-phases.md): [Consensus round and phase tracing](./04-code-samples.md) (§4.5.2)
- [Phase 5](./06-implementation-phases.md): [Production collector config](./05-configuration-reference.md) (§5.5.2), [Grafana dashboards](./07-observability-backends.md) (§7.6), [alerting](./07-observability-backends.md) (§7.6.3)
**Reference**:
- [06-implementation-phases.md §6.1](./06-implementation-phases.md) — Full 5-phase timeline overview and Gantt chart
- [06-implementation-phases.md §6.10](./06-implementation-phases.md) — Crawl-Walk-Run strategy: POC is the CRAWL phase, next steps are WALK and RUN
- [06-implementation-phases.md §6.12](./06-implementation-phases.md) — Recommended implementation order (14 steps across 9 weeks)
- [03-implementation-strategy.md §3.9](./03-implementation-strategy.md) — Code intrusiveness assessment and risk matrix for each remaining component
- [07-observability-backends.md §7.2](./07-observability-backends.md) — Production backend selection (Tempo, Elastic APM, Honeycomb, Datadog)
- [02-design-decisions.md §2.5](./02-design-decisions.md) — Context propagation design: W3C HTTP headers, protobuf P2P, JobQueue internal
- [00-tracing-fundamentals.md](./00-tracing-fundamentals.md) — Reference for team onboarding on distributed tracing concepts
---
## Summary
| Task | Description | New Files | Modified Files | Depends On |
| ---- | ------------------------------------ | --------- | -------------- | ---------- |
| 0 | Docker observability stack | 4 | 0 | — |
| 1 | OTel C++ SDK dependency | 0 | 2 | — |
| 2 | Core Telemetry interface + NullImpl | 3 | 0 | 1 |
| 3 | OTel-backed Telemetry implementation | 2 | 1 | 1, 2 |
| 4 | Application lifecycle integration | 0 | 3 | 2, 3 |
| 5 | Instrumentation macros | 1 | 0 | 2 |
| 6 | Instrument RPC ServerHandler | 0 | 1 | 4, 5 |
| 7 | Instrument RPC command execution | 0 | 1 | 4, 5 |
| 8 | End-to-end verification | 0 | 0 | 0-7 |
| 9 | Document results and next steps | 1 | 0 | 8 |
**Parallel work**: Tasks 0 and 1 can run in parallel. Tasks 2 and 5 have no dependency on each other. Tasks 6 and 7 can be done in parallel once Tasks 4 and 5 are complete.
---
## Next Steps (Post-POC)
### Metrics Pipeline for Grafana Dashboards
The current POC exports **traces only**. Grafana's Explore view can query Jaeger for individual traces, but time-series charts (latency histograms, request throughput, error rates) require a **metrics pipeline**. To enable this:
1. **Add a `spanmetrics` connector** to the OTel Collector config that derives RED metrics (Rate, Errors, Duration) from trace spans automatically:
```yaml
connectors:
spanmetrics:
histogram:
explicit:
buckets: [1ms, 5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 5s]
dimensions:
- name: xrpl.rpc.command
- name: xrpl.rpc.status
exporters:
prometheus:
endpoint: 0.0.0.0:8889
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug, otlp/jaeger, spanmetrics]
metrics:
receivers: [spanmetrics]
exporters: [prometheus]
```
2. **Add Prometheus** to the Docker Compose stack to scrape the collector's metrics endpoint.
3. **Add Prometheus as a Grafana datasource** and build dashboards for:
- RPC request latency (p50/p95/p99) by command
- RPC throughput (requests/sec) by command
- Error rate by command
- Span duration distribution
### Additional Instrumentation
- **W3C `traceparent` header extraction** in `ServerHandler` to support cross-service context propagation from external callers
- **WebSocket RPC tracing** in `ServerHandler::onWSMessage()`
- **Transaction relay tracing** across nodes using protobuf `TraceContext` messages
- **Consensus round and phase tracing** for validator coordination visibility
- **Ledger close tracing** to measure close-to-validated latency
### Production Hardening
- **Tail-based sampling** in the OTel Collector to reduce volume while retaining error/slow traces
- **TLS configuration** for the OTLP exporter in production deployments
- **Resource limits** on the batch processor queue to prevent unbounded memory growth
- **Health monitoring** for the telemetry pipeline itself (collector lag, export failures)
### POC Lessons Learned
Issues encountered during POC implementation that inform future work:
| Issue | Resolution | Impact on Future Work |
| -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Conan lockfile rejected `opentelemetry-cpp/1.18.0` | Used `--lockfile=""` to bypass | Lockfile must be regenerated when adding new dependencies |
| Conan package only builds OTLP HTTP exporter, not gRPC | Switched from gRPC to HTTP exporter (`localhost:4318/v1/traces`) | HTTP exporter is the default; gRPC requires custom Conan profile |
| CMake target `opentelemetry-cpp::api` etc. don't exist in Conan package | Use umbrella target `opentelemetry-cpp::opentelemetry-cpp` | Conan targets differ from upstream CMake targets |
| OTel Collector `logging` exporter deprecated | Renamed to `debug` exporter | Use `debug` in all collector configs going forward |
| Macro parameter `telemetry` collided with `::xrpl::telemetry::` namespace | Renamed macro params to `_tel_obj_`, `_span_name_` | Avoid common words as macro parameter names |
| `opentelemetry::trace::Scope` creates new context on move | Store scope as member, create once in constructor | SpanGuard move semantics need care with Scope lifecycle |
| `TracerProviderFactory::Create` returns `unique_ptr<sdk::TracerProvider>`, not `nostd::shared_ptr` | Use `std::shared_ptr` member, wrap in `nostd::shared_ptr` for global provider | OTel SDK factory return types don't match API provider types |

View File

@@ -0,0 +1,220 @@
# Phase 2: RPC Tracing Completion Task List
> **Goal**: Complete full RPC tracing coverage with W3C Trace Context propagation, unit tests, and performance validation. Build on the POC foundation to achieve production-quality RPC observability.
>
> **Scope**: W3C header extraction, TraceContext propagation utilities, unit tests for core telemetry, integration tests for RPC tracing, and performance benchmarks.
>
> **Branch**: `pratik/otel-phase2-rpc-tracing` (from `pratik/OpenTelemetry_and_DistributedTracing_planning`)
### Related Plan Documents
| Document | Relevance |
| ------------------------------------------------------------ | ------------------------------------------------------------- |
| [04-code-samples.md](./04-code-samples.md) | TraceContextPropagator (§4.4.2), RPC instrumentation (§4.5.3) |
| [02-design-decisions.md](./02-design-decisions.md) | W3C Trace Context (§2.5), span attributes (§2.4.2) |
| [06-implementation-phases.md](./06-implementation-phases.md) | Phase 2 tasks (§6.3), definition of done (§6.11.2) |
---
## Task 2.1: Implement W3C Trace Context HTTP Header Extraction
**Objective**: Extract `traceparent` and `tracestate` headers from incoming HTTP RPC requests so external callers can propagate their trace context into rippled.
**What to do**:
- Create `include/xrpl/telemetry/TraceContextPropagator.h`:
- `extractFromHeaders(headerGetter)` - extract W3C traceparent/tracestate from HTTP headers
- `injectToHeaders(ctx, headerSetter)` - inject trace context into response headers
- Use OTel's `TextMapPropagator` with `W3CTraceContextPropagator` for standards compliance
- Only compiled when `XRPL_ENABLE_TELEMETRY` is defined
- Create `src/libxrpl/telemetry/TraceContextPropagator.cpp`:
- Implement a simple `TextMapCarrier` adapter for HTTP headers
- Use `opentelemetry::context::propagation::GlobalTextMapPropagator` for extraction/injection
- Register the W3C propagator in `TelemetryImpl::start()`
- Modify `src/xrpld/rpc/detail/ServerHandler.cpp`:
- In the HTTP request handler, extract parent context from headers before creating span
- Pass extracted context to `startSpan()` as parent
- Inject trace context into response headers
**Key new files**:
- `include/xrpl/telemetry/TraceContextPropagator.h`
- `src/libxrpl/telemetry/TraceContextPropagator.cpp`
**Key modified files**:
- `src/xrpld/rpc/detail/ServerHandler.cpp`
- `src/libxrpl/telemetry/Telemetry.cpp` (register W3C propagator)
**Reference**:
- [04-code-samples.md §4.4.2](./04-code-samples.md) — TraceContextPropagator with extractFromHeaders/injectToHeaders
- [02-design-decisions.md §2.5](./02-design-decisions.md) — W3C Trace Context propagation design
---
## Task 2.2: Add XRPL_TRACE_PEER Macro
**Objective**: Add the missing peer-tracing macro for future Phase 3 use and ensure macro completeness.
**What to do**:
- Edit `src/xrpld/telemetry/TracingInstrumentation.h`:
- Add `XRPL_TRACE_PEER(_tel_obj_, _span_name_)` macro that checks `shouldTracePeer()`
- Add `XRPL_TRACE_LEDGER(_tel_obj_, _span_name_)` macro (for future ledger tracing)
- Ensure disabled variants expand to `((void)0)`
**Key modified file**:
- `src/xrpld/telemetry/TracingInstrumentation.h`
---
## Task 2.3: Add shouldTraceLedger() to Telemetry Interface
**Objective**: The `Setup` struct has a `traceLedger` field but there's no corresponding virtual method. Add it for interface completeness.
**What to do**:
- Edit `include/xrpl/telemetry/Telemetry.h`:
- Add `virtual bool shouldTraceLedger() const = 0;`
- Update all implementations:
- `src/libxrpl/telemetry/Telemetry.cpp` (TelemetryImpl, NullTelemetryOtel)
- `src/libxrpl/telemetry/NullTelemetry.cpp` (NullTelemetry)
**Key modified files**:
- `include/xrpl/telemetry/Telemetry.h`
- `src/libxrpl/telemetry/Telemetry.cpp`
- `src/libxrpl/telemetry/NullTelemetry.cpp`
---
## Task 2.4: Unit Tests for Core Telemetry Infrastructure
**Objective**: Add unit tests for the core telemetry abstractions to validate correctness and catch regressions.
**What to do**:
- Create `src/test/telemetry/Telemetry_test.cpp`:
- Test NullTelemetry: verify all methods return expected no-op values
- Test Setup defaults: verify all Setup fields have correct defaults
- Test setup_Telemetry config parser: verify parsing of [telemetry] section
- Test enabled/disabled factory paths
- Test shouldTrace\* methods respect config flags
- Create `src/test/telemetry/SpanGuard_test.cpp`:
- Test SpanGuard RAII lifecycle (span ends on destruction)
- Test move constructor works correctly
- Test setAttribute, setOk, setStatus, addEvent, recordException
- Test context() returns valid context
- Add test files to CMake build
**Key new files**:
- `src/test/telemetry/Telemetry_test.cpp`
- `src/test/telemetry/SpanGuard_test.cpp`
**Reference**:
- [06-implementation-phases.md §6.11.1](./06-implementation-phases.md) — Phase 1 exit criteria (unit tests passing)
---
## Task 2.5: Enhance RPC Span Attributes
**Objective**: Add additional attributes to RPC spans per the semantic conventions defined in the plan.
**What to do**:
- Edit `src/xrpld/rpc/detail/ServerHandler.cpp`:
- Add `http.method` attribute for HTTP requests
- Add `http.status_code` attribute for responses
- Add `net.peer.ip` attribute for client IP (if available)
- Edit `src/xrpld/rpc/detail/RPCHandler.cpp`:
- Add `xrpl.rpc.duration_ms` attribute on completion
- Add error message attribute on failure: `xrpl.rpc.error_message`
**Key modified files**:
- `src/xrpld/rpc/detail/ServerHandler.cpp`
- `src/xrpld/rpc/detail/RPCHandler.cpp`
**Reference**:
- [02-design-decisions.md §2.4.2](./02-design-decisions.md) — RPC attribute schema
---
## Task 2.6: Build Verification and Performance Baseline
**Objective**: Verify the build succeeds with and without telemetry, and establish a performance baseline.
**What to do**:
1. Build with `telemetry=ON` and verify no compilation errors
2. Build with `telemetry=OFF` and verify no regressions
3. Run existing unit tests to verify no breakage
4. Document any build issues in lessons.md
**Verification Checklist**:
- [ ] `conan install . --build=missing -o telemetry=True` succeeds
- [ ] `cmake --preset default -Dtelemetry=ON` configures correctly
- [ ] Build succeeds with telemetry ON
- [ ] Build succeeds with telemetry OFF
- [ ] Existing tests pass with telemetry ON
- [ ] Existing tests pass with telemetry OFF
---
## Summary
| Task | Description | New Files | Modified Files | Depends On |
| ---- | ------------------------------------------- | --------- | -------------- | ---------- |
| 2.1 | W3C Trace Context header extraction | 2 | 2 | POC |
| 2.2 | Add XRPL_TRACE_PEER/LEDGER macros | 0 | 1 | POC |
| 2.3 | Add shouldTraceLedger() interface method | 0 | 3 | POC |
| 2.4 | Unit tests for core telemetry | 2 | 1 | POC |
| 2.5 | Enhanced RPC span attributes | 0 | 2 | POC |
| 2.6 | Build verification and performance baseline | 0 | 0 | 2.1-2.5 |
**Parallel work**: Tasks 2.1, 2.2, 2.3 can run in parallel. Task 2.4 depends on 2.3. Task 2.5 can run in parallel with 2.4. Task 2.6 depends on all others.
---
## Known Issues / Future Work
### Thread safety of TelemetryImpl::stop() vs startSpan()
`TelemetryImpl::stop()` resets `sdkProvider_` (a `std::shared_ptr`) without
synchronization. `getTracer()` reads the same member from RPC handler threads.
This is a data race if any thread calls `startSpan()` concurrently with `stop()`.
**Current mitigation**: `Application::stop()` shuts down `serverHandler_`,
`overlay_`, and `jobQueue_` before calling `telemetry_->stop()`, so no callers
remain. See comments in `Telemetry.cpp:stop()` and `Application.cpp`.
**TODO**: Add an `std::atomic<bool> stopped_` flag checked in `getTracer()` to
make this robust against future shutdown order changes.
### Macro incompatibility: XRPL_TRACE_SPAN vs XRPL_TRACE_SET_ATTR
`XRPL_TRACE_SPAN` and `XRPL_TRACE_SPAN_KIND` declare `_xrpl_guard_` as a bare
`SpanGuard`, but `XRPL_TRACE_SET_ATTR` and `XRPL_TRACE_EXCEPTION` call
`_xrpl_guard_.has_value()` which requires `std::optional<SpanGuard>`. Using
`XRPL_TRACE_SPAN` followed by `XRPL_TRACE_SET_ATTR` in the same scope would
fail to compile.
**Current mitigation**: No call site currently uses `XRPL_TRACE_SPAN` — all
production code uses the conditional macros (`XRPL_TRACE_RPC`, `XRPL_TRACE_TX`,
etc.) which correctly wrap the guard in `std::optional`.
**TODO**: Either make `XRPL_TRACE_SPAN`/`XRPL_TRACE_SPAN_KIND` also wrap in
`std::optional`, or document that `XRPL_TRACE_SET_ATTR` is only compatible with
the conditional macros.

View File

@@ -0,0 +1,263 @@
# Phase 3: Transaction Tracing Task List
> **Goal**: Trace the full transaction lifecycle from RPC submission through peer relay, including cross-node context propagation via Protocol Buffer extensions. This is the WALK phase that demonstrates true distributed tracing.
>
> **Scope**: Protocol Buffer `TraceContext` message, context serialization, PeerImp transaction instrumentation, NetworkOPs processing instrumentation, HashRouter visibility, and multi-node relay context propagation.
>
> **Branch**: `pratik/otel-phase3-tx-tracing` (from `pratik/otel-phase2-rpc-tracing`)
### Related Plan Documents
| Document | Relevance |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| [04-code-samples.md](./04-code-samples.md) | TraceContext protobuf (§4.4.1), PeerImp instrumentation (§4.5.1), context serialization (§4.4.2) |
| [01-architecture-analysis.md](./01-architecture-analysis.md) | Transaction flow (§1.3), key trace points (§1.6) |
| [06-implementation-phases.md](./06-implementation-phases.md) | Phase 3 tasks (§6.4), definition of done (§6.11.3) |
| [02-design-decisions.md](./02-design-decisions.md) | Context propagation design (§2.5), attribute schema (§2.4.3) |
---
## Task 3.1: Define TraceContext Protocol Buffer Message
**Objective**: Add trace context fields to the P2P protocol messages so trace IDs can propagate across nodes.
**What to do**:
- Edit `include/xrpl/proto/xrpl.proto` (or `src/ripple/proto/ripple.proto`, wherever the proto is):
- Add `TraceContext` message definition:
```protobuf
message TraceContext {
bytes trace_id = 1; // 16-byte trace identifier
bytes span_id = 2; // 8-byte span identifier
uint32 trace_flags = 3; // bit 0 = sampled
string trace_state = 4; // W3C tracestate value
}
```
- Add `optional TraceContext trace_context = 1001;` to:
- `TMTransaction`
- `TMProposeSet` (for Phase 4 use)
- `TMValidation` (for Phase 4 use)
- Use high field numbers (1001+) to avoid conflicts with existing fields
- Regenerate protobuf C++ code
**Key modified files**:
- `include/xrpl/proto/xrpl.proto` (or equivalent)
**Reference**:
- [04-code-samples.md §4.4.1](./04-code-samples.md) — TraceContext message definition
- [02-design-decisions.md §2.5.2](./02-design-decisions.md) — Protocol buffer context propagation design
---
## Task 3.2: Implement Protobuf Context Serialization
**Objective**: Create utilities to serialize/deserialize OTel trace context to/from protobuf `TraceContext` messages.
**What to do**:
- Create `include/xrpl/telemetry/TraceContextPropagator.h` (extend from Phase 2 if exists, or add protobuf methods):
- Add protobuf-specific methods:
- `static Context extractFromProtobuf(protocol::TraceContext const& proto)` — reconstruct OTel context from protobuf fields
- `static void injectToProtobuf(Context const& ctx, protocol::TraceContext& proto)` — serialize current span context into protobuf fields
- Both methods guard behind `#ifdef XRPL_ENABLE_TELEMETRY`
- Create/extend `src/libxrpl/telemetry/TraceContextPropagator.cpp`:
- Implement extraction: read trace_id (16 bytes), span_id (8 bytes), trace_flags from protobuf, construct `SpanContext`, wrap in `Context`
- Implement injection: get current span from context, serialize its TraceId, SpanId, and TraceFlags into protobuf fields
**Key new/modified files**:
- `include/xrpl/telemetry/TraceContextPropagator.h`
- `src/libxrpl/telemetry/TraceContextPropagator.cpp`
**Reference**:
- [04-code-samples.md §4.4.2](./04-code-samples.md) — Full extract/inject implementation
---
## Task 3.3: Instrument PeerImp Transaction Handling
**Objective**: Add trace spans to the peer-level transaction receive and relay path.
**What to do**:
- Edit `src/xrpld/overlay/detail/PeerImp.cpp`:
- In `onMessage(TMTransaction)` / `handleTransaction()`:
- Extract parent trace context from incoming `TMTransaction::trace_context` field (if present)
- Create `tx.receive` span as child of extracted context (or new root if none)
- Set attributes: `xrpl.tx.hash`, `xrpl.peer.id`, `xrpl.tx.status`
- On HashRouter suppression (duplicate): set `xrpl.tx.suppressed=true`, add `tx.duplicate` event
- Wrap validation call with child span `tx.validate`
- Wrap relay with `tx.relay` span
- When relaying to peers:
- Inject current trace context into outgoing `TMTransaction::trace_context`
- Set `xrpl.tx.relay_count` attribute
- Include `TracingInstrumentation.h` and use `XRPL_TRACE_TX` macro
**Key modified files**:
- `src/xrpld/overlay/detail/PeerImp.cpp`
**Reference**:
- [04-code-samples.md §4.5.1](./04-code-samples.md) — Full PeerImp instrumentation example
- [01-architecture-analysis.md §1.3](./01-architecture-analysis.md) — Transaction flow diagram
- [01-architecture-analysis.md §1.6](./01-architecture-analysis.md) — tx.receive trace point
---
## Task 3.4: Instrument NetworkOPs Transaction Processing
**Objective**: Trace the transaction processing pipeline in NetworkOPs, covering both sync and async paths.
**What to do**:
- Edit `src/xrpld/app/misc/NetworkOPs.cpp`:
- In `processTransaction()`:
- Create `tx.process` span
- Set attributes: `xrpl.tx.hash`, `xrpl.tx.type`, `xrpl.tx.local` (whether from RPC or peer)
- Record whether sync or async path is taken
- In `doTransactionAsync()`:
- Capture parent context before queuing
- Create `tx.queue` span with queue depth attribute
- Add event when transaction is dequeued for processing
- In `doTransactionSync()`:
- Create `tx.process_sync` span
- Record result (applied, queued, rejected)
**Key modified files**:
- `src/xrpld/app/misc/NetworkOPs.cpp`
**Reference**:
- [01-architecture-analysis.md §1.6](./01-architecture-analysis.md) — tx.validate and tx.process trace points
- [02-design-decisions.md §2.4.3](./02-design-decisions.md) — Transaction attribute schema
---
## Task 3.5: Instrument HashRouter for Dedup Visibility
**Objective**: Make transaction deduplication visible in traces by recording HashRouter decisions as span attributes/events.
**What to do**:
- Edit `src/xrpld/overlay/detail/PeerImp.cpp` (in handleTransaction):
- After calling `HashRouter::shouldProcess()` or `addSuppressionPeer()`:
- Record `xrpl.tx.suppressed` attribute (true/false)
- Record `xrpl.tx.flags` showing current HashRouter state (SAVED, TRUSTED, etc.)
- Add `tx.first_seen` or `tx.duplicate` event
- This is NOT a modification to HashRouter itself — just recording its decisions as span attributes in the existing PeerImp instrumentation from Task 3.3.
**Key modified files**:
- `src/xrpld/overlay/detail/PeerImp.cpp` (same changes as 3.3, logically grouped)
---
## Task 3.6: Context Propagation in Transaction Relay
**Objective**: Ensure trace context flows correctly when transactions are relayed between peers, creating linked spans across nodes.
**What to do**:
- Verify the relay path injects trace context:
- When `PeerImp` relays a transaction, the `TMTransaction` message should carry `trace_context`
- When a remote peer receives it, the context is extracted and used as parent
- Test context propagation:
- Manually verify with 2+ node setup that trace IDs match across nodes
- Confirm parent-child span relationships are correct in Jaeger
- Handle edge cases:
- Missing trace context (older peers): create new root span
- Corrupted trace context: log warning, create new root span
- Sampled-out traces: respect trace flags
**Key modified files**:
- `src/xrpld/overlay/detail/PeerImp.cpp`
- `src/xrpld/overlay/detail/OverlayImpl.cpp` (if relay method needs context param)
**Reference**:
- [02-design-decisions.md §2.5](./02-design-decisions.md) — Context propagation design
- [04-code-samples.md §4.5.1](./04-code-samples.md) — Relay context injection pattern
---
## Task 3.7: Build Verification and Testing
**Objective**: Verify all Phase 3 changes compile and work correctly.
**What to do**:
1. Build with `telemetry=ON` — verify no compilation errors
2. Build with `telemetry=OFF` — verify no regressions
3. Run existing unit tests
4. Verify protobuf regeneration produces correct C++ code
5. Document any issues encountered
**Verification Checklist**:
- [ ] Protobuf changes generate valid C++
- [ ] Build succeeds with telemetry ON
- [ ] Build succeeds with telemetry OFF
- [ ] Existing tests pass
- [ ] No undefined symbols from new telemetry calls
---
## Summary
| Task | Description | New Files | Modified Files | Depends On |
| ---- | ----------------------------------- | --------- | -------------- | ---------- |
| 3.1 | TraceContext protobuf message | 0 | 1 | Phase 2 |
| 3.2 | Protobuf context serialization | 1-2 | 0 | 3.1 |
| 3.3 | PeerImp transaction instrumentation | 0 | 1 | 3.2 |
| 3.4 | NetworkOPs transaction processing | 0 | 1 | Phase 2 |
| 3.5 | HashRouter dedup visibility | 0 | 1 | 3.3 |
| 3.6 | Relay context propagation | 0 | 1-2 | 3.3, 3.5 |
| 3.7 | Build verification and testing | 0 | 0 | 3.1-3.6 |
**Parallel work**: Tasks 3.1 and 3.4 can start in parallel. Task 3.2 depends on 3.1. Tasks 3.3 and 3.5 depend on 3.2. Task 3.6 depends on 3.3 and 3.5.
**Exit Criteria** (from [06-implementation-phases.md §6.11.3](./06-implementation-phases.md)):
- [ ] Transaction traces span across nodes
- [ ] Trace context in Protocol Buffer messages
- [ ] HashRouter deduplication visible in traces
- [ ] <5% overhead on transaction throughput
---
## Known Issues / Future Work
### Propagation utilities not yet wired into P2P flow
`extractFromProtobuf()` and `injectToProtobuf()` in `TraceContextPropagator.h`
are implemented and tested but not called from production code. To enable
cross-node distributed traces:
- Call `injectToProtobuf()` in `PeerImp` when sending `TMTransaction` /
`TMProposeSet` messages
- Call `extractFromProtobuf()` in the corresponding message handlers to
reconstruct the parent span context, then pass it to `startSpan()` as the
parent
This was deferred to validate single-node tracing performance first.
### Unused trace_state proto field
The `TraceContext.trace_state` field (field 4) in `xrpl.proto` is reserved for
W3C `tracestate` vendor-specific key-value pairs but is not read or written by
`TraceContextPropagator`. Wire it when cross-vendor trace propagation is needed.
No wire cost since proto `optional` fields are zero-cost when absent.

View File

@@ -0,0 +1,221 @@
# Phase 4: Consensus Tracing Task List
> **Goal**: Full observability into consensus rounds — track round lifecycle, phase transitions, proposal handling, and validation. This is the RUN phase that completes the distributed tracing story.
>
> **Scope**: RCLConsensus instrumentation for round starts, phase transitions (open/establish/accept), proposal send/receive, validation handling, and correlation with transaction traces from Phase 3.
>
> **Branch**: `pratik/otel-phase4-consensus-tracing` (from `pratik/otel-phase3-tx-tracing`)
### Related Plan Documents
| Document | Relevance |
| ------------------------------------------------------------ | ----------------------------------------------------------- |
| [04-code-samples.md](./04-code-samples.md) | Consensus instrumentation (§4.5.2), consensus span patterns |
| [01-architecture-analysis.md](./01-architecture-analysis.md) | Consensus round flow (§1.4), key trace points (§1.6) |
| [06-implementation-phases.md](./06-implementation-phases.md) | Phase 4 tasks (§6.5), definition of done (§6.11.4) |
| [02-design-decisions.md](./02-design-decisions.md) | Consensus attribute schema (§2.4.4) |
---
## Task 4.1: Instrument Consensus Round Start
**Objective**: Create a root span for each consensus round that captures the round's key parameters.
**What to do**:
- Edit `src/xrpld/app/consensus/RCLConsensus.cpp`:
- In `RCLConsensus::startRound()` (or the Adaptor's startRound):
- Create `consensus.round` span using `XRPL_TRACE_CONSENSUS` macro
- Set attributes:
- `xrpl.consensus.ledger.prev` — previous ledger hash
- `xrpl.consensus.ledger.seq` — target ledger sequence
- `xrpl.consensus.proposers` — number of trusted proposers
- `xrpl.consensus.mode` — "proposing" or "observing"
- Store the span context for use by child spans in phase transitions
- Add a member to hold current round trace context:
- `opentelemetry::context::Context currentRoundContext_` (guarded by `#ifdef`)
- Updated at round start, used by phase transition spans
**Key modified files**:
- `src/xrpld/app/consensus/RCLConsensus.cpp`
- `src/xrpld/app/consensus/RCLConsensus.h` (add context member)
**Reference**:
- [04-code-samples.md §4.5.2](./04-code-samples.md) — startRound instrumentation example
- [01-architecture-analysis.md §1.4](./01-architecture-analysis.md) — Consensus round flow
---
## Task 4.2: Instrument Phase Transitions
**Objective**: Create child spans for each consensus phase (open, establish, accept) to show timing breakdown.
**What to do**:
- Edit `src/xrpld/app/consensus/RCLConsensus.cpp`:
- Identify where phase transitions occur (the `Consensus<Adaptor>` template drives this)
- For each phase entry:
- Create span as child of `currentRoundContext_`: `consensus.phase.open`, `consensus.phase.establish`, `consensus.phase.accept`
- Set `xrpl.consensus.phase` attribute
- Add `phase.enter` event at start, `phase.exit` event at end
- Record phase duration in milliseconds
- In the `onClose` adaptor method:
- Create `consensus.ledger_close` span
- Set attributes: close_time, mode, transaction count in initial position
- Note: The Consensus template class in `include/xrpl/consensus/Consensus.h` drives phase transitions — check if instrumentation goes there or in the Adaptor
**Key modified files**:
- `src/xrpld/app/consensus/RCLConsensus.cpp`
- Possibly `include/xrpl/consensus/Consensus.h` (for template-level phase tracking)
**Reference**:
- [04-code-samples.md §4.5.2](./04-code-samples.md) — phaseTransition instrumentation
---
## Task 4.3: Instrument Proposal Handling
**Objective**: Trace proposal send and receive to show validator coordination.
**What to do**:
- Edit `src/xrpld/app/consensus/RCLConsensus.cpp`:
- In `Adaptor::propose()`:
- Create `consensus.proposal.send` span
- Set attributes: `xrpl.consensus.round` (proposal sequence), proposal hash
- Inject trace context into outgoing `TMProposeSet::trace_context` (from Phase 3 protobuf)
- In `Adaptor::peerProposal()` (or wherever peer proposals are received):
- Extract trace context from incoming `TMProposeSet::trace_context`
- Create `consensus.proposal.receive` span as child of extracted context
- Set attributes: `xrpl.consensus.proposer` (node ID), `xrpl.consensus.round`
- In `Adaptor::share(RCLCxPeerPos)`:
- Create `consensus.proposal.relay` span for relaying peer proposals
**Key modified files**:
- `src/xrpld/app/consensus/RCLConsensus.cpp`
**Reference**:
- [04-code-samples.md §4.5.2](./04-code-samples.md) — peerProposal instrumentation
- [02-design-decisions.md §2.4.4](./02-design-decisions.md) — Consensus attribute schema
---
## Task 4.4: Instrument Validation Handling
**Objective**: Trace validation send and receive to show ledger validation flow.
**What to do**:
- Edit `src/xrpld/app/consensus/RCLConsensus.cpp` (or the validation handler):
- When sending our validation:
- Create `consensus.validation.send` span
- Set attributes: validated ledger hash, sequence, signing time
- When receiving a peer validation:
- Extract trace context from `TMValidation::trace_context` (if present)
- Create `consensus.validation.receive` span
- Set attributes: `xrpl.consensus.validator` (node ID), ledger hash
**Key modified files**:
- `src/xrpld/app/consensus/RCLConsensus.cpp`
- `src/xrpld/app/misc/NetworkOPs.cpp` (if validation handling is here)
---
## Task 4.5: Add Consensus-Specific Attributes
**Objective**: Enrich consensus spans with detailed attributes for debugging and analysis.
**What to do**:
- Review all consensus spans and ensure they include:
- `xrpl.consensus.ledger.seq` — target ledger sequence number
- `xrpl.consensus.round` — consensus round number
- `xrpl.consensus.mode` — proposing/observing/wrongLedger
- `xrpl.consensus.phase` — current phase name
- `xrpl.consensus.phase_duration_ms` — time spent in phase
- `xrpl.consensus.proposers` — number of trusted proposers
- `xrpl.consensus.tx_count` — transactions in proposed set
- `xrpl.consensus.disputes` — number of disputed transactions
- `xrpl.consensus.converge_percent` — convergence percentage
**Key modified files**:
- `src/xrpld/app/consensus/RCLConsensus.cpp`
---
## Task 4.6: Correlate Transaction and Consensus Traces
**Objective**: Link transaction traces from Phase 3 with consensus traces so you can follow a transaction from submission through consensus into the ledger.
**What to do**:
- In `onClose()` or `onAccept()`:
- When building the consensus position, link the round span to individual transaction spans using span links (if OTel SDK supports it) or events
- At minimum, record the transaction hashes included in the consensus set as span events: `tx.included` with `xrpl.tx.hash` attribute
- In `processTransactionSet()` (NetworkOPs):
- If the consensus round span context is available, create child spans for each transaction applied to the ledger
**Key modified files**:
- `src/xrpld/app/consensus/RCLConsensus.cpp`
- `src/xrpld/app/misc/NetworkOPs.cpp`
---
## Task 4.7: Build Verification and Testing
**Objective**: Verify all Phase 4 changes compile and don't affect consensus timing.
**What to do**:
1. Build with `telemetry=ON` — verify no compilation errors
2. Build with `telemetry=OFF` — verify no regressions (critical for consensus code)
3. Run existing consensus-related unit tests
4. Verify that all macros expand to no-ops when disabled
5. Check that no consensus-critical code paths are affected by instrumentation overhead
**Verification Checklist**:
- [ ] Build succeeds with telemetry ON
- [ ] Build succeeds with telemetry OFF
- [ ] Existing consensus tests pass
- [ ] No new includes in consensus headers when telemetry is OFF
- [ ] Phase timing instrumentation doesn't use blocking operations
---
## Summary
| Task | Description | New Files | Modified Files | Depends On |
| ---- | ------------------------------------- | --------- | -------------- | ------------- |
| 4.1 | Consensus round start instrumentation | 0 | 2 | Phase 3 |
| 4.2 | Phase transition instrumentation | 0 | 1-2 | 4.1 |
| 4.3 | Proposal handling instrumentation | 0 | 1 | 4.1 |
| 4.4 | Validation handling instrumentation | 0 | 1-2 | 4.1 |
| 4.5 | Consensus-specific attributes | 0 | 1 | 4.2, 4.3, 4.4 |
| 4.6 | Transaction-consensus correlation | 0 | 2 | 4.2, Phase 3 |
| 4.7 | Build verification and testing | 0 | 0 | 4.1-4.6 |
**Parallel work**: Tasks 4.2, 4.3, and 4.4 can run in parallel after 4.1 is complete. Task 4.5 depends on all three. Task 4.6 depends on 4.2 and Phase 3.
**Exit Criteria** (from [06-implementation-phases.md §6.11.4](./06-implementation-phases.md)):
- [ ] Complete consensus round traces
- [ ] Phase transitions visible
- [ ] Proposals and validations traced
- [ ] No impact on consensus timing

View File

@@ -0,0 +1,221 @@
# Phase 5: Integration Test Task List
> **Goal**: End-to-end verification of the complete telemetry pipeline using a
> 6-node consensus network. Proves that RPC, transaction, and consensus spans
> flow through the observability stack (otel-collector, Jaeger, Prometheus,
> Grafana) under realistic conditions.
>
> **Scope**: Integration test script, manual testing plan, 6-node local network
> setup, Jaeger/Prometheus/Grafana verification.
>
> **Branch**: `pratik/otel-phase5-docs-deployment`
### Related Plan Documents
| Document | Relevance |
| ---------------------------------------------------------------- | ------------------------------------------ |
| [07-observability-backends.md](./07-observability-backends.md) | Jaeger, Grafana, Prometheus setup |
| [05-configuration-reference.md](./05-configuration-reference.md) | Collector config, Docker Compose |
| [06-implementation-phases.md](./06-implementation-phases.md) | Phase 5 tasks, definition of done |
| [Phase5_taskList.md](./Phase5_taskList.md) | Phase 5 main task list (5.6 = integration) |
---
## Task IT.1: Create Integration Test Script
**Objective**: Automated bash script that stands up a 6-node xrpld network
with telemetry, exercises all span categories, and verifies data in
Jaeger/Prometheus.
**What to do**:
- Create `docker/telemetry/integration-test.sh`:
- Prerequisites check (docker, xrpld binary, curl, jq)
- Start observability stack via `docker compose`
- Generate 6 validator key pairs via temp standalone xrpld
- Generate 6 node configs + shared `validators.txt`
- Start 6 xrpld nodes in consensus mode (`--start`, no `-a`)
- Wait for all nodes to reach `"proposing"` state (120s timeout)
**Key new file**: `docker/telemetry/integration-test.sh`
**Verification**:
- [ ] Script starts without errors
- [ ] All 6 nodes reach "proposing" state
- [ ] Observability stack is healthy (otel-collector, Jaeger, Prometheus, Grafana)
---
## Task IT.2: RPC Span Verification (Phase 2)
**Objective**: Verify RPC spans flow through the telemetry pipeline.
**What to do**:
- Send `server_info`, `server_state`, `ledger` RPCs to node1 (port 5005)
- Wait for batch export (5s)
- Query Jaeger API for:
- `rpc.request` spans (ServerHandler::onRequest)
- `rpc.process` spans (ServerHandler::processRequest)
- `rpc.command.server_info` spans (callMethod)
- `rpc.command.server_state` spans (callMethod)
- `rpc.command.ledger` spans (callMethod)
- Verify `xrpl.rpc.command` attribute present on `rpc.command.*` spans
**Verification**:
- [ ] Jaeger shows `rpc.request` traces
- [ ] Jaeger shows `rpc.process` traces
- [ ] Jaeger shows `rpc.command.*` traces with correct attributes
---
## Task IT.3: Transaction Span Verification (Phase 3)
**Objective**: Verify transaction spans flow through the telemetry pipeline.
**What to do**:
- Get genesis account sequence via `account_info` RPC
- Submit Payment transaction using genesis seed (`snoPBrXtMeMyMHUVTgbuqAfg1SUTb`)
- Wait for consensus inclusion (10s)
- Query Jaeger API for:
- `tx.process` spans (NetworkOPsImp::processTransaction) on submitting node
- `tx.receive` spans (PeerImp::handleTransaction) on peer nodes
- Verify `xrpl.tx.hash` attribute on `tx.process` spans
- Verify `xrpl.peer.id` attribute on `tx.receive` spans
**Verification**:
- [ ] Jaeger shows `tx.process` traces with `xrpl.tx.hash`
- [ ] Jaeger shows `tx.receive` traces with `xrpl.peer.id`
---
## Task IT.4: Consensus Span Verification (Phase 4)
**Objective**: Verify consensus spans flow through the telemetry pipeline.
**What to do**:
- Consensus runs automatically in 6-node network
- Query Jaeger API for:
- `consensus.proposal.send` (Adaptor::propose)
- `consensus.ledger_close` (Adaptor::onClose)
- `consensus.accept` (Adaptor::onAccept)
- `consensus.validation.send` (Adaptor::validate)
- Verify attributes:
- `xrpl.consensus.mode` on `consensus.ledger_close`
- `xrpl.consensus.proposers` on `consensus.accept`
- `xrpl.consensus.ledger.seq` on `consensus.validation.send`
**Verification**:
- [ ] Jaeger shows `consensus.ledger_close` traces with `xrpl.consensus.mode`
- [ ] Jaeger shows `consensus.accept` traces with `xrpl.consensus.proposers`
- [ ] Jaeger shows `consensus.proposal.send` traces
- [ ] Jaeger shows `consensus.validation.send` traces
---
## Task IT.5: Spanmetrics Verification (Phase 5)
**Objective**: Verify spanmetrics connector derives RED metrics from spans.
**What to do**:
- Query Prometheus for `traces_span_metrics_calls_total`
- Query Prometheus for `traces_span_metrics_duration_milliseconds_count`
- Verify Grafana loads at `http://localhost:3000`
**Verification**:
- [ ] Prometheus returns non-empty results for `traces_span_metrics_calls_total`
- [ ] Prometheus returns non-empty results for duration histogram
- [ ] Grafana UI accessible with dashboards visible
---
## Task IT.6: Manual Testing Plan
**Objective**: Document how to run tests manually for future reference.
**What to do**:
- Create `docker/telemetry/TESTING.md` with:
- Prerequisites section
- Single-node standalone test (quick verification)
- 6-node consensus test (full verification)
- Expected span catalog (all 12 span names with attributes)
- Verification queries (Jaeger API, Prometheus API)
- Troubleshooting guide
**Key new file**: `docker/telemetry/TESTING.md`
**Verification**:
- [ ] Document covers both single-node and multi-node testing
- [ ] All 12 span names documented with source file and attributes
- [ ] Troubleshooting section covers common failure modes
---
## Task IT.7: Run and Verify
**Objective**: Execute the integration test and validate results.
**What to do**:
- Run `docker/telemetry/integration-test.sh` locally
- Debug any failures
- Leave stack running for manual verification
- Share URLs:
- Jaeger: `http://localhost:16686`
- Grafana: `http://localhost:3000`
- Prometheus: `http://localhost:9090`
**Verification**:
- [ ] Script completes with all checks passing
- [ ] Jaeger UI shows rippled service with all expected span names
- [ ] Grafana dashboards load and show data
---
## Task IT.8: Commit
**Objective**: Commit all new files to Phase 5 branch.
**What to do**:
- Run `pcc` (pre-commit checks)
- Commit 3 new files to `pratik/otel-phase5-docs-deployment`
**Verification**:
- [ ] `pcc` passes
- [ ] Commit created on Phase 5 branch
---
## Summary
| Task | Description | New Files | Depends On |
| ---- | ----------------------------- | --------- | ---------- |
| IT.1 | Integration test script | 1 | Phase 5 |
| IT.2 | RPC span verification | 0 | IT.1 |
| IT.3 | Transaction span verification | 0 | IT.1 |
| IT.4 | Consensus span verification | 0 | IT.1 |
| IT.5 | Spanmetrics verification | 0 | IT.1 |
| IT.6 | Manual testing plan | 1 | -- |
| IT.7 | Run and verify | 0 | IT.1-IT.6 |
| IT.8 | Commit | 0 | IT.7 |
**Exit Criteria**:
- [ ] All 6 xrpld nodes reach "proposing" state
- [ ] All 11 expected span names visible in Jaeger
- [ ] Spanmetrics available in Prometheus
- [ ] Grafana dashboards show data
- [ ] Manual testing plan document complete

View File

@@ -0,0 +1,241 @@
# Phase 5: Documentation & Deployment Task List
> **Goal**: Production readiness — Grafana dashboards, spanmetrics pipeline, operator runbook, alert definitions, and final integration testing. This phase ensures the telemetry system is useful and maintainable in production.
>
> **Scope**: Grafana dashboard definitions, OTel Collector spanmetrics connector, Prometheus integration, alert rules, operator documentation, and production-ready Docker Compose stack.
>
> **Branch**: `pratik/otel-phase5-docs-deployment` (from `pratik/otel-phase4-consensus-tracing`)
### Related Plan Documents
| Document | Relevance |
| ---------------------------------------------------------------- | -------------------------------------------------------------------------- |
| [07-observability-backends.md](./07-observability-backends.md) | Jaeger setup (§7.1), Grafana dashboards (§7.6), alerts (§7.6.3) |
| [05-configuration-reference.md](./05-configuration-reference.md) | Collector config (§5.5), production config (§5.5.2), Docker Compose (§5.6) |
| [06-implementation-phases.md](./06-implementation-phases.md) | Phase 5 tasks (§6.6), definition of done (§6.11.5) |
---
## Task 5.1: Add Spanmetrics Connector to OTel Collector
**Objective**: Derive RED metrics (Rate, Errors, Duration) from trace spans automatically, enabling Grafana time-series dashboards.
**What to do**:
- Edit `docker/telemetry/otel-collector-config.yaml`:
- Add `spanmetrics` connector:
```yaml
connectors:
spanmetrics:
histogram:
explicit:
buckets: [1ms, 5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 5s]
dimensions:
- name: xrpl.rpc.command
- name: xrpl.rpc.status
- name: xrpl.consensus.phase
- name: xrpl.tx.type
```
- Add `prometheus` exporter:
```yaml
exporters:
prometheus:
endpoint: 0.0.0.0:8889
```
- Wire the pipeline:
```yaml
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug, otlp/jaeger, spanmetrics]
metrics:
receivers: [spanmetrics]
exporters: [prometheus]
```
- Edit `docker/telemetry/docker-compose.yml`:
- Expose port `8889` on the collector for Prometheus scraping
- Add Prometheus service
- Add Prometheus as Grafana datasource
**Key modified files**:
- `docker/telemetry/otel-collector-config.yaml`
- `docker/telemetry/docker-compose.yml`
**Key new files**:
- `docker/telemetry/prometheus.yml` (Prometheus scrape config)
- `docker/telemetry/grafana/provisioning/datasources/prometheus.yaml`
**Reference**:
- [POC_taskList.md §Next Steps](./POC_taskList.md) — Metrics pipeline for Grafana dashboards
---
## Task 5.2: Create Grafana Dashboards
**Objective**: Provide pre-built Grafana dashboards for RPC performance, transaction lifecycle, and consensus health.
**What to do**:
- Create `docker/telemetry/grafana/provisioning/dashboards/dashboards.yaml` (provisioning config)
- Create dashboard JSON files:
1. **RPC Performance Dashboard** (`rpc-performance.json`):
- RPC request latency (p50/p95/p99) by command — histogram panel
- RPC throughput (requests/sec) by command — time series
- RPC error rate by command — bar gauge
- Top slowest RPC commands — table
2. **Transaction Overview Dashboard** (`transaction-overview.json`):
- Transaction processing rate — time series
- Transaction latency distribution — histogram
- Suppression rate (duplicates) — stat panel
- Transaction processing path (sync vs async) — pie chart
3. **Consensus Health Dashboard** (`consensus-health.json`):
- Consensus round duration — time series
- Phase duration breakdown (open/establish/accept) — stacked bar
- Proposals sent/received per round — stat panel
- Consensus mode distribution (proposing/observing) — pie chart
- Store dashboards in `docker/telemetry/grafana/dashboards/`
**Key new files**:
- `docker/telemetry/grafana/provisioning/dashboards/dashboards.yaml`
- `docker/telemetry/grafana/dashboards/rpc-performance.json`
- `docker/telemetry/grafana/dashboards/transaction-overview.json`
- `docker/telemetry/grafana/dashboards/consensus-health.json`
**Reference**:
- [07-observability-backends.md §7.6](./07-observability-backends.md) — Grafana dashboard specifications
- [01-architecture-analysis.md §1.8.3](./01-architecture-analysis.md) — Dashboard panel examples
---
## Task 5.3: Define Alert Rules
**Objective**: Create alert definitions for key telemetry anomalies.
**What to do**:
- Create `docker/telemetry/grafana/provisioning/alerting/alerts.yaml`:
- **RPC Latency Alert**: p99 latency > 1s for any command over 5 minutes
- **RPC Error Rate Alert**: Error rate > 5% for any command over 5 minutes
- **Consensus Duration Alert**: Round duration > 10s (warn), > 30s (critical)
- **Transaction Processing Alert**: Processing rate drops below threshold
- **Telemetry Pipeline Health**: No spans received for > 2 minutes
**Key new files**:
- `docker/telemetry/grafana/provisioning/alerting/alerts.yaml`
**Reference**:
- [07-observability-backends.md §7.6.3](./07-observability-backends.md) — Alert rule definitions
---
## Task 5.4: Production Collector Configuration
**Objective**: Create a production-ready OTel Collector configuration with tail-based sampling and resource limits.
**What to do**:
- Create `docker/telemetry/otel-collector-config-production.yaml`:
- Tail-based sampling policy:
- Always sample errors and slow traces
- 10% base sampling rate for normal traces
- Always sample first trace for each unique RPC command
- Resource limits:
- Memory limiter processor (80% of available memory)
- Queued retry for export failures
- TLS configuration for production endpoints
- Health check endpoint
**Key new files**:
- `docker/telemetry/otel-collector-config-production.yaml`
**Reference**:
- [05-configuration-reference.md §5.5.2](./05-configuration-reference.md) — Production collector config
---
## Task 5.5: Operator Runbook
**Objective**: Create operator documentation for managing the telemetry system in production.
**What to do**:
- Create `docs/telemetry-runbook.md`:
- **Setup**: How to enable telemetry in rippled
- **Configuration**: All config options with descriptions
- **Collector Deployment**: Docker Compose vs. Kubernetes vs. bare metal
- **Troubleshooting**: Common issues and resolutions
- No traces appearing
- High memory usage from telemetry
- Collector connection failures
- Sampling configuration tuning
- **Performance Tuning**: Batch size, queue size, sampling ratio guidelines
- **Upgrading**: How to upgrade OTel SDK and Collector versions
**Key new files**:
- `docs/telemetry-runbook.md`
---
## Task 5.6: Final Integration Testing
**Objective**: Validate the complete telemetry stack end-to-end.
**What to do**:
1. Start full Docker stack (Collector, Jaeger, Grafana, Prometheus)
2. Build rippled with `telemetry=ON`
3. Run in standalone mode with telemetry enabled
4. Generate RPC traffic and verify traces in Jaeger
5. Verify dashboards populate in Grafana
6. Verify alerts trigger correctly
7. Test telemetry OFF path (no regressions)
8. Run full test suite
**Verification Checklist**:
- [ ] Docker stack starts without errors
- [ ] Traces appear in Jaeger with correct hierarchy
- [ ] Grafana dashboards show metrics derived from spans
- [ ] Prometheus scrapes spanmetrics successfully
- [ ] Alerts can be triggered by simulated conditions
- [ ] Build succeeds with telemetry ON and OFF
- [ ] Full test suite passes
---
## Summary
| Task | Description | New Files | Modified Files | Depends On |
| ---- | ---------------------------------- | --------- | -------------- | ---------- |
| 5.1 | Spanmetrics connector + Prometheus | 2 | 2 | Phase 4 |
| 5.2 | Grafana dashboards | 4 | 0 | 5.1 |
| 5.3 | Alert definitions | 1 | 0 | 5.1 |
| 5.4 | Production collector config | 1 | 0 | Phase 4 |
| 5.5 | Operator runbook | 1 | 0 | Phase 4 |
| 5.6 | Final integration testing | 0 | 0 | 5.1-5.5 |
**Parallel work**: Tasks 5.1, 5.4, and 5.5 can run in parallel. Tasks 5.2 and 5.3 depend on 5.1. Task 5.6 depends on all others.
**Exit Criteria** (from [06-implementation-phases.md §6.11.5](./06-implementation-phases.md)):
- [ ] Dashboards deployed and showing data
- [ ] Alerts configured and tested
- [ ] Operator documentation complete
- [ ] Production collector config ready
- [ ] Full test suite passes

View File

@@ -1529,3 +1529,46 @@ validators.txt
# set to ssl_verify to 0.
[ssl_verify]
1
#-------------------------------------------------------------------------------
#
# 11. Telemetry (OpenTelemetry Tracing)
#
#-------------------------------------------------------------------------------
#
# Enables distributed tracing via OpenTelemetry. Requires building with
# -DXRPL_ENABLE_TELEMETRY=ON (telemetry Conan option).
#
# [telemetry]
#
# enabled=0
#
# Enable or disable telemetry at runtime. Default: 0 (disabled).
#
# endpoint=http://localhost:4318/v1/traces
#
# The OpenTelemetry Collector endpoint (OTLP/HTTP). Default: http://localhost:4318/v1/traces.
#
# exporter=otlp_http
#
# Exporter type: otlp_http. Default: otlp_http.
#
# sampling_ratio=1.0
#
# Fraction of traces to sample (0.0 to 1.0). Default: 1.0 (all traces).
#
# trace_rpc=1
#
# Enable RPC request tracing. Default: 1.
#
# trace_transactions=1
#
# Enable transaction lifecycle tracing. Default: 1.
#
# trace_consensus=1
#
# Enable consensus round tracing. Default: 1.
#
# trace_peer=0
#
# Enable peer message tracing (high volume). Default: 0.
#

View File

@@ -1,21 +0,0 @@
include_guard()
set(GIT_BUILD_BRANCH "")
set(GIT_COMMIT_HASH "")
find_package(Git)
if (NOT Git_FOUND)
message(WARNING "Git not found. Git branch and commit hash will be empty.")
return()
endif ()
set(GIT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.git)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse --abbrev-ref HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE GIT_BUILD_BRANCH)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE GIT_COMMIT_HASH)
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")

View File

@@ -58,12 +58,6 @@ include(target_link_modules)
add_module(xrpl beast)
target_link_libraries(xrpl.libxrpl.beast PUBLIC xrpl.imports.main)
include(GitInfo)
add_module(xrpl git)
target_compile_definitions(xrpl.libxrpl.git PRIVATE GIT_COMMIT_HASH="${GIT_COMMIT_HASH}"
GIT_BUILD_BRANCH="${GIT_BUILD_BRANCH}")
target_link_libraries(xrpl.libxrpl.git PUBLIC xrpl.imports.main)
# Level 02
add_module(xrpl basics)
target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast)
@@ -77,27 +71,18 @@ target_link_libraries(xrpl.libxrpl.crypto PUBLIC xrpl.libxrpl.basics)
# Level 04
add_module(xrpl protocol)
target_link_libraries(xrpl.libxrpl.protocol PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.git
xrpl.libxrpl.json)
target_link_libraries(xrpl.libxrpl.protocol PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.json)
# Level 05
## Set up code generation for protocol_autogen module
include(XrplProtocolAutogen)
setup_protocol_autogen()
add_module(xrpl protocol_autogen)
target_link_libraries(xrpl.libxrpl.protocol_autogen PUBLIC xrpl.libxrpl.protocol)
# Level 06
add_module(xrpl core)
target_link_libraries(xrpl.libxrpl.core PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json
xrpl.libxrpl.protocol xrpl.libxrpl.protocol_autogen)
xrpl.libxrpl.protocol)
# Level 07
# Level 06
add_module(xrpl resource)
target_link_libraries(xrpl.libxrpl.resource PUBLIC xrpl.libxrpl.protocol)
# Level 08
# Level 07
add_module(xrpl net)
target_link_libraries(xrpl.libxrpl.net PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json
xrpl.libxrpl.protocol xrpl.libxrpl.resource)
@@ -126,7 +111,6 @@ target_link_libraries(
PUBLIC xrpl.libxrpl.basics
xrpl.libxrpl.json
xrpl.libxrpl.protocol
xrpl.libxrpl.protocol_autogen
xrpl.libxrpl.rdb
xrpl.libxrpl.server
xrpl.libxrpl.shamap
@@ -135,6 +119,17 @@ target_link_libraries(
add_module(xrpl tx)
target_link_libraries(xrpl.libxrpl.tx PUBLIC xrpl.libxrpl.ledger)
# Telemetry module — OpenTelemetry distributed tracing support.
# Sources: include/xrpl/telemetry/ (headers), src/libxrpl/telemetry/ (impl).
# When telemetry=ON, links the Conan-provided umbrella target
# opentelemetry-cpp::opentelemetry-cpp (individual component targets like
# ::api, ::sdk are not available in the Conan package).
add_module(xrpl telemetry)
target_link_libraries(xrpl.libxrpl.telemetry PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.beast)
if (telemetry)
target_link_libraries(xrpl.libxrpl.telemetry PUBLIC opentelemetry-cpp::opentelemetry-cpp)
endif ()
add_library(xrpl.libxrpl)
set_target_properties(xrpl.libxrpl PROPERTIES OUTPUT_NAME xrpl)
@@ -151,17 +146,16 @@ target_link_modules(
conditions
core
crypto
git
json
ledger
net
nodestore
protocol
protocol_autogen
rdb
resource
server
shamap
telemetry
tx)
# All headers in libxrpl are in modules.

View File

@@ -23,17 +23,16 @@ install(TARGETS common
xrpl.libxrpl.conditions
xrpl.libxrpl.core
xrpl.libxrpl.crypto
xrpl.libxrpl.git
xrpl.libxrpl.json
xrpl.libxrpl.rdb
xrpl.libxrpl.ledger
xrpl.libxrpl.net
xrpl.libxrpl.nodestore
xrpl.libxrpl.protocol
xrpl.libxrpl.protocol_autogen
xrpl.libxrpl.resource
xrpl.libxrpl.server
xrpl.libxrpl.shamap
xrpl.libxrpl.telemetry
xrpl.libxrpl.tx
antithesis-sdk-cpp
EXPORT XrplExports

View File

@@ -1,155 +0,0 @@
#[===================================================================[
Protocol Autogen - Code generation for protocol wrapper classes
#]===================================================================]
# Options for code generation
set(CODEGEN_VENV_DIR
""
CACHE PATH
"Path to Python virtual environment for code generation. If provided, automatic venv setup is skipped."
)
# Function to set up code generation for protocol_autogen module
# This runs at configure time to generate C++ wrapper classes from macro files
function (setup_protocol_autogen)
# Directory paths
set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol/detail")
set(AUTOGEN_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol_autogen")
set(SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts")
# Input macro files
set(TRANSACTIONS_MACRO "${MACRO_DIR}/transactions.macro")
set(LEDGER_ENTRIES_MACRO "${MACRO_DIR}/ledger_entries.macro")
set(SFIELDS_MACRO "${MACRO_DIR}/sfields.macro")
# Python scripts
set(GENERATE_TX_SCRIPT "${SCRIPTS_DIR}/generate_tx_classes.py")
set(GENERATE_LEDGER_SCRIPT "${SCRIPTS_DIR}/generate_ledger_classes.py")
set(REQUIREMENTS_FILE "${SCRIPTS_DIR}/requirements.txt")
# Create output directories
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/transactions")
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/ledger_objects")
# Find Python3 - check if already found by Conan or find it ourselves
if (NOT Python3_EXECUTABLE)
find_package(Python3 COMPONENTS Interpreter QUIET)
endif ()
if (NOT Python3_EXECUTABLE)
# Try finding python3 executable directly
find_program(Python3_EXECUTABLE NAMES python3 python)
endif ()
if (NOT Python3_EXECUTABLE)
message(FATAL_ERROR "Python3 not found. Code generation cannot proceed.")
return()
endif ()
message(STATUS "Using Python3 for code generation: ${Python3_EXECUTABLE}")
# Set up Python virtual environment for code generation
if (CODEGEN_VENV_DIR)
# User-provided venv - skip automatic setup
set(VENV_DIR "${CODEGEN_VENV_DIR}")
message(STATUS "Using user-provided Python venv: ${VENV_DIR}")
else ()
# Use default venv in build directory
set(VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv")
endif ()
# Determine the Python executable path in the venv
if (WIN32)
set(VENV_PYTHON "${VENV_DIR}/Scripts/python.exe")
set(VENV_PIP "${VENV_DIR}/Scripts/pip.exe")
else ()
set(VENV_PYTHON "${VENV_DIR}/bin/python")
set(VENV_PIP "${VENV_DIR}/bin/pip")
endif ()
# Only auto-setup venv if not user-provided
if (NOT CODEGEN_VENV_DIR)
# Check if venv needs to be created or updated
set(VENV_NEEDS_UPDATE FALSE)
if (NOT EXISTS "${VENV_PYTHON}")
set(VENV_NEEDS_UPDATE TRUE)
message(STATUS "Creating Python virtual environment for code generation...")
elseif ("${REQUIREMENTS_FILE}" IS_NEWER_THAN "${VENV_DIR}/.requirements_installed")
set(VENV_NEEDS_UPDATE TRUE)
message(STATUS "Updating Python virtual environment (requirements changed)...")
endif ()
# Create/update virtual environment if needed
if (VENV_NEEDS_UPDATE)
message(STATUS "Setting up Python virtual environment at ${VENV_DIR}")
execute_process(COMMAND ${Python3_EXECUTABLE} -m venv "${VENV_DIR}"
RESULT_VARIABLE VENV_RESULT ERROR_VARIABLE VENV_ERROR)
if (NOT VENV_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to create virtual environment: ${VENV_ERROR}")
endif ()
message(STATUS "Installing Python dependencies...")
execute_process(COMMAND ${VENV_PIP} install --upgrade pip
RESULT_VARIABLE PIP_UPGRADE_RESULT OUTPUT_QUIET
ERROR_VARIABLE PIP_UPGRADE_ERROR)
if (NOT PIP_UPGRADE_RESULT EQUAL 0)
message(WARNING "Failed to upgrade pip: ${PIP_UPGRADE_ERROR}")
endif ()
execute_process(COMMAND ${VENV_PIP} install -r "${REQUIREMENTS_FILE}"
RESULT_VARIABLE PIP_INSTALL_RESULT ERROR_VARIABLE PIP_INSTALL_ERROR)
if (NOT PIP_INSTALL_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to install Python dependencies: ${PIP_INSTALL_ERROR}")
endif ()
# Mark requirements as installed
file(TOUCH "${VENV_DIR}/.requirements_installed")
message(STATUS "Python virtual environment ready")
endif ()
endif ()
# Generate transaction classes at configure time
message(STATUS "Generating transaction classes from transactions.macro...")
execute_process(COMMAND ${VENV_PYTHON} "${GENERATE_TX_SCRIPT}" "${TRANSACTIONS_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/transactions" --sfields-macro
"${SFIELDS_MACRO}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE TX_GEN_RESULT
OUTPUT_VARIABLE TX_GEN_OUTPUT
ERROR_VARIABLE TX_GEN_ERROR)
if (NOT TX_GEN_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to generate transaction classes:\n${TX_GEN_ERROR}")
else ()
message(STATUS "Transaction classes generated successfully")
endif ()
# Generate ledger entry classes at configure time
message(STATUS "Generating ledger entry classes from ledger_entries.macro...")
execute_process(COMMAND ${VENV_PYTHON} "${GENERATE_LEDGER_SCRIPT}" "${LEDGER_ENTRIES_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/ledger_objects" --sfields-macro
"${SFIELDS_MACRO}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE LEDGER_GEN_RESULT
OUTPUT_VARIABLE LEDGER_GEN_OUTPUT
ERROR_VARIABLE LEDGER_GEN_ERROR)
if (NOT LEDGER_GEN_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to generate ledger entry classes:\n${LEDGER_GEN_ERROR}")
else ()
message(STATUS "Ledger entry classes generated successfully")
endif ()
# Register dependencies so CMake reconfigures when these files change
set_property(DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS
"${TRANSACTIONS_MACRO}"
"${LEDGER_ENTRIES_MACRO}"
"${SFIELDS_MACRO}"
"${GENERATE_TX_SCRIPT}"
"${GENERATE_LEDGER_SCRIPT}"
"${SCRIPTS_DIR}/macro_parser_common.py"
"${SCRIPTS_DIR}/templates/Transaction.h.mako"
"${SCRIPTS_DIR}/templates/LedgerEntry.h.mako"
"${REQUIREMENTS_FILE}")
endfunction ()

View File

@@ -10,10 +10,13 @@
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
"openssl/3.5.5#05a4ac5b7323f7a329b2db1391d9941f%1769599205.414",
"opentelemetry-cpp/1.18.0#efd9851e173f8a13b9c7d35232de8cf1%1750409186.472",
"openssl/3.5.5#05a4ac5b7323f7a329b2db1391d9941f%1770229825.601",
"nudb/2.0.9#0432758a24204da08fee953ec9ea03cb%1769436073.32",
"nlohmann_json/3.11.3#45828be26eb619a2e04ca517bb7b828d%1701220705.259",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libcurl/8.18.0#364bc3755cb9ef84ed9a7ae9c7efc1c1%1770984390.024",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1765850144.736",
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
@@ -30,9 +33,15 @@
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
"strawberryperl/5.32.1.1#707032463aa0620fa17ec0d887f5fe41%1765850165.196",
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
"pkgconf/2.5.1#93c2051284cba1279494a43a4fcfeae2%1757684701.089",
"opentelemetry-proto/1.4.0#4096a3b05916675ef9628f3ffd571f51%1732731336.11",
"ninja/1.13.2#c8c5dc2a52ed6e4e42a66d75b4717ceb%1764096931.974",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
"msys2/cci.latest#eea83308ad7e9023f7318c60d5a9e6cb%1770199879.083",
"meson/1.10.0#60786758ea978964c24525de19603cf4%1768294926.103",
"m4/1.4.19#70dc8bbb33e981d119d2acc0175cf381%1763158052.846",
"libtool/2.4.7#14e7739cc128bc1623d2ed318008e47e%1755679003.847",
"gnu-config/cci.20210814#466e9d4d7779e1c142443f7ea44b4284%1762363589.329",
"cmake/4.2.0#ae0a44f44a1ef9ab68fd4b3e9a1f8671%1765850153.937",
"cmake/3.31.10#313d16a1aa16bbdb2ca0792467214b76%1765850153.479",
"b2/5.3.3#107c15377719889654eb9a162a673975%1765850144.355",

View File

@@ -22,6 +22,7 @@ class Xrpl(ConanFile):
"rocksdb": [True, False],
"shared": [True, False],
"static": [True, False],
"telemetry": [True, False],
"tests": [True, False],
"unity": [True, False],
"xrpld": [True, False],
@@ -54,6 +55,7 @@ class Xrpl(ConanFile):
"rocksdb": True,
"shared": False,
"static": True,
"telemetry": True,
"tests": False,
"unity": False,
"xrpld": False,
@@ -140,6 +142,10 @@ class Xrpl(ConanFile):
self.requires("jemalloc/5.3.0")
if self.options.rocksdb:
self.requires("rocksdb/10.5.1")
# OpenTelemetry C++ SDK for distributed tracing (optional).
# Provides OTLP/HTTP exporter, batch span processor, and trace API.
if self.options.telemetry:
self.requires("opentelemetry-cpp/1.18.0")
self.requires("xxhash/0.8.3", **transitive_headers_opt)
exports_sources = (
@@ -168,6 +174,7 @@ class Xrpl(ConanFile):
tc.variables["rocksdb"] = self.options.rocksdb
tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
tc.variables["static"] = self.options.static
tc.variables["telemetry"] = self.options.telemetry
tc.variables["unity"] = self.options.unity
tc.variables["xrpld"] = self.options.xrpld
tc.generate()
@@ -220,3 +227,5 @@ class Xrpl(ConanFile):
]
if self.options.rocksdb:
libxrpl.requires.append("rocksdb::librocksdb")
if self.options.telemetry:
libxrpl.requires.append("opentelemetry-cpp::opentelemetry-cpp")

View File

@@ -87,6 +87,8 @@ words:
- daria
- dcmake
- dearmor
- Dedup
- dedup
- deleteme
- demultiplexer
- deserializaton
@@ -97,6 +99,7 @@ words:
- doxyfile
- dxrpl
- endmacro
- EOCFG
- exceptioned
- Falco
- finalizers
@@ -176,10 +179,9 @@ words:
- nixfmt
- nixos
- nixpkgs
- NOLINT
- NOLINTNEXTLINE
- nonxrp
- noripple
- nostd
- nudb
- nullptr
- nunl
@@ -192,8 +194,11 @@ words:
- permdex
- perminute
- permissioned
- pgrep
- pkill
- pointee
- populator
- pratik
- preauth
- preauthorization
- preauthorize
@@ -208,6 +213,7 @@ words:
- queuable
- Raphson
- replayer
- reqps
- rerere
- retriable
- RIPD
@@ -302,6 +308,10 @@ words:
- xchain
- ximinez
- EXPECT_STREQ
- Gantt
- gantt
- otelc
- traceql
- XMACRO
- xrpkuwait
- xrpl
@@ -309,3 +319,5 @@ words:
- xrplf
- xxhash
- xxhasher
- xychart
- zpages

507
docker/telemetry/TESTING.md Normal file
View File

@@ -0,0 +1,507 @@
# OpenTelemetry Integration Testing Guide
This document describes how to verify the rippled OpenTelemetry telemetry
pipeline end-to-end, from span generation through the observability stack
(otel-collector, Jaeger, Prometheus, Grafana).
---
## Prerequisites
### Build xrpld with telemetry
```bash
conan install . --build=missing -o telemetry=True
cmake --preset default -Dtelemetry=ON
cmake --build --preset default --target xrpld
```
The binary is at `.build/xrpld`.
### Required tools
- **Docker** with `docker compose` (v2)
- **curl**
- **jq** (JSON processor)
### Verify binary
```bash
.build/xrpld --version
```
---
## Test 1: Single-Node Standalone (Quick Verification)
This test verifies RPC and transaction spans in standalone mode. Consensus
spans will not fire because standalone mode does not run consensus.
### Step 1: Start the observability stack
```bash
docker compose -f docker/telemetry/docker-compose.yml up -d
```
Wait for services to be ready:
```bash
# otel-collector health
curl -sf http://localhost:13133/ && echo "collector ready"
# Jaeger UI
curl -sf http://localhost:16686/ > /dev/null && echo "jaeger ready"
```
### Step 2: Start xrpld in standalone mode
```bash
.build/xrpld --conf docker/telemetry/xrpld-telemetry.cfg -a --start
```
Wait a few seconds for the node to initialize.
### Step 3: Exercise RPC spans
```bash
# server_info
curl -s http://localhost:5005 \
-d '{"method":"server_info"}' | jq .result.info.server_state
# server_state
curl -s http://localhost:5005 \
-d '{"method":"server_state"}' | jq .result.state.server_state
# ledger
curl -s http://localhost:5005 \
-d '{"method":"ledger","params":[{"ledger_index":"current"}]}' \
| jq .result.ledger_current_index
```
### Step 4: Submit a transaction
Close the ledger first (required in standalone mode):
```bash
curl -s http://localhost:5005 -d '{"method":"ledger_accept"}'
```
Submit a Payment from the genesis account:
```bash
curl -s http://localhost:5005 -d '{
"method": "submit",
"params": [{
"secret": "snoPBrXtMeMyMHUVTgbuqAfg1SUTb",
"tx_json": {
"TransactionType": "Payment",
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Destination": "rPMh7Pi9ct699iZUTWzJaUMR1o42VEfGqF",
"Amount": "10000000"
}
}]
}' | jq .result.engine_result
```
Expected result: `"tesSUCCESS"`.
Close the ledger again to finalize:
```bash
curl -s http://localhost:5005 -d '{"method":"ledger_accept"}'
```
### Step 5: Verify traces in Jaeger
Wait 5 seconds for the batch export, then:
```bash
JAEGER="http://localhost:16686"
# Check rippled service is registered
curl -s "$JAEGER/api/services" | jq '.data'
# Check RPC spans
curl -s "$JAEGER/api/traces?service=rippled&operation=rpc.request&limit=5&lookback=1h" \
| jq '.data | length'
curl -s "$JAEGER/api/traces?service=rippled&operation=rpc.process&limit=5&lookback=1h" \
| jq '.data | length'
curl -s "$JAEGER/api/traces?service=rippled&operation=rpc.command.server_info&limit=5&lookback=1h" \
| jq '.data | length'
# Check transaction spans
curl -s "$JAEGER/api/traces?service=rippled&operation=tx.process&limit=5&lookback=1h" \
| jq '.data | length'
```
Or open the Jaeger UI: http://localhost:16686
### Step 6: Teardown
```bash
# Kill xrpld (Ctrl+C or)
kill $(pgrep -f 'xrpld.*xrpld-telemetry')
# Stop observability stack
docker compose -f docker/telemetry/docker-compose.yml down
# Clean xrpld data
rm -rf data/
```
### Expected spans (standalone mode)
| Span Name | Expected | Notes |
| --------------------------- | -------- | ----------------------------- |
| `rpc.request` | Yes | Every HTTP RPC call |
| `rpc.process` | Yes | Every RPC processing |
| `rpc.command.server_info` | Yes | server_info RPC |
| `rpc.command.server_state` | Yes | server_state RPC |
| `rpc.command.ledger` | Yes | ledger RPC |
| `rpc.command.submit` | Yes | submit RPC |
| `rpc.command.ledger_accept` | Yes | ledger_accept RPC |
| `tx.process` | Yes | Transaction submission |
| `tx.receive` | No | No peers in standalone |
| `consensus.*` | No | Consensus disabled standalone |
---
## Test 2: 6-Node Consensus Network (Full Verification)
This test verifies ALL span categories including consensus and peer
transaction relay, using a 6-node validator network.
### Automated
Run the integration test script:
```bash
bash docker/telemetry/integration-test.sh
```
The script will:
1. Start the observability stack
2. Generate 6 validator key pairs
3. Create config files for each node
4. Start all 6 nodes
5. Wait for consensus ("proposing" state)
6. Exercise RPC, submit transactions
7. Verify all span categories in Jaeger
8. Verify spanmetrics in Prometheus
9. Print results and leave the stack running
### Manual
If you prefer to run the steps manually:
#### Step 1: Start observability stack
```bash
docker compose -f docker/telemetry/docker-compose.yml up -d
```
#### Step 2: Generate validator keys
Start a temporary standalone xrpld:
```bash
.build/xrpld --conf docker/telemetry/xrpld-telemetry.cfg -a --start &
TEMP_PID=$!
sleep 5
```
Generate 6 key pairs:
```bash
for i in $(seq 1 6); do
curl -s http://localhost:5005 \
-d '{"method":"validation_create"}' | jq '.result'
done
```
Record the `validation_seed` and `validation_public_key` for each.
Kill the temporary node:
```bash
kill $TEMP_PID
rm -rf data/
```
#### Step 3: Create node configs
For each node (1-6), create a config file. Template:
```ini
[server]
port_rpc
port_peer
[port_rpc]
port = {5004 + node_number}
ip = 127.0.0.1
admin = 127.0.0.1
protocol = http
[port_peer]
port = {51234 + node_number}
ip = 0.0.0.0
protocol = peer
[node_db]
type=NuDB
path=/tmp/xrpld-integration/node{N}/nudb
online_delete=256
[database_path]
/tmp/xrpld-integration/node{N}/db
[debug_logfile]
/tmp/xrpld-integration/node{N}/debug.log
[validation_seed]
{seed from step 2}
[validators_file]
/tmp/xrpld-integration/validators.txt
[ips_fixed]
127.0.0.1 51235
127.0.0.1 51236
127.0.0.1 51237
127.0.0.1 51238
127.0.0.1 51239
127.0.0.1 51240
[peer_private]
1
[telemetry]
enabled=1
endpoint=http://localhost:4318/v1/traces
exporter=otlp_http
sampling_ratio=1.0
batch_size=512
batch_delay_ms=2000
max_queue_size=2048
trace_rpc=1
trace_transactions=1
trace_consensus=1
trace_peer=0
trace_ledger=1
[rpc_startup]
{ "command": "log_level", "severity": "warning" }
[ssl_verify]
0
```
#### Step 4: Create validators.txt
```ini
[validators]
{public_key_1}
{public_key_2}
{public_key_3}
{public_key_4}
{public_key_5}
{public_key_6}
```
#### Step 5: Start all 6 nodes
```bash
for i in $(seq 1 6); do
.build/xrpld --conf /tmp/xrpld-integration/node$i/xrpld.cfg --start &
echo $! > /tmp/xrpld-integration/node$i/xrpld.pid
done
```
#### Step 6: Wait for consensus
Poll each node until `server_state` = `"proposing"`:
```bash
for port in 5005 5006 5007 5008 5009 5010; do
while true; do
state=$(curl -s http://localhost:$port \
-d '{"method":"server_info"}' \
| jq -r '.result.info.server_state')
echo "Port $port: $state"
[ "$state" = "proposing" ] && break
sleep 5
done
done
```
#### Step 7: Exercise RPC and submit transaction
```bash
# RPC calls
curl -s http://localhost:5005 -d '{"method":"server_info"}'
curl -s http://localhost:5005 -d '{"method":"server_state"}'
curl -s http://localhost:5005 -d '{"method":"ledger","params":[{"ledger_index":"current"}]}'
# Submit transaction
curl -s http://localhost:5005 -d '{
"method": "submit",
"params": [{
"secret": "snoPBrXtMeMyMHUVTgbuqAfg1SUTb",
"tx_json": {
"TransactionType": "Payment",
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Destination": "rPMh7Pi9ct699iZUTWzJaUMR1o42VEfGqF",
"Amount": "10000000"
}
}]
}'
```
Wait 15 seconds for consensus and batch export.
#### Step 8: Verify in Jaeger
See the "Verification Queries" section below.
---
## Expected Span Catalog
All 12 production span names instrumented across Phases 2-4:
| Span Name | Source File | Phase | Key Attributes | How to Trigger |
| --------------------------- | --------------------- | ----- | ---------------------------------------------------------- | ------------------------- |
| `rpc.request` | ServerHandler.cpp:271 | 2 | -- | Any HTTP RPC call |
| `rpc.process` | ServerHandler.cpp:573 | 2 | -- | Any HTTP RPC call |
| `rpc.ws_message` | ServerHandler.cpp:384 | 2 | -- | WebSocket RPC message |
| `rpc.command.<name>` | RPCHandler.cpp:161 | 2 | `xrpl.rpc.command`, `xrpl.rpc.version`, `xrpl.rpc.role` | Any RPC command |
| `tx.process` | NetworkOPs.cpp:1227 | 3 | `xrpl.tx.hash`, `xrpl.tx.local`, `xrpl.tx.path` | Submit transaction |
| `tx.receive` | PeerImp.cpp:1273 | 3 | `xrpl.peer.id` | Peer relays transaction |
| `consensus.proposal.send` | RCLConsensus.cpp:177 | 4 | `xrpl.consensus.round` | Consensus proposing phase |
| `consensus.ledger_close` | RCLConsensus.cpp:282 | 4 | `xrpl.consensus.ledger.seq`, `xrpl.consensus.mode` | Ledger close event |
| `consensus.accept` | RCLConsensus.cpp:395 | 4 | `xrpl.consensus.proposers`, `xrpl.consensus.round_time_ms` | Ledger accepted |
| `consensus.validation.send` | RCLConsensus.cpp:753 | 4 | `xrpl.consensus.ledger.seq`, `xrpl.consensus.proposing` | Validation sent |
---
## Verification Queries
### Jaeger API
Base URL: `http://localhost:16686`
```bash
JAEGER="http://localhost:16686"
# List all services
curl -s "$JAEGER/api/services" | jq '.data'
# List operations for rippled
curl -s "$JAEGER/api/services/rippled/operations" | jq '.data'
# Query traces by operation
for op in "rpc.request" "rpc.process" \
"rpc.command.server_info" "rpc.command.server_state" "rpc.command.ledger" \
"tx.process" "tx.receive" \
"consensus.proposal.send" "consensus.ledger_close" \
"consensus.accept" "consensus.validation.send"; do
count=$(curl -s "$JAEGER/api/traces?service=rippled&operation=$op&limit=5&lookback=1h" \
| jq '.data | length')
printf "%-35s %s traces\n" "$op" "$count"
done
```
### Prometheus API
Base URL: `http://localhost:9090`
```bash
PROM="http://localhost:9090"
# Span call counts (from spanmetrics connector)
curl -s "$PROM/api/v1/query?query=traces_span_metrics_calls_total" \
| jq '.data.result[] | {span: .metric.span_name, count: .value[1]}'
# Latency histogram
curl -s "$PROM/api/v1/query?query=traces_span_metrics_duration_milliseconds_count" \
| jq '.data.result[] | {span: .metric.span_name, count: .value[1]}'
# RPC calls by command
curl -s "$PROM/api/v1/query?query=traces_span_metrics_calls_total{span_name=~\"rpc.command.*\"}" \
| jq '.data.result[] | {command: .metric["xrpl.rpc.command"], count: .value[1]}'
```
### Grafana
Open http://localhost:3000 (anonymous admin access enabled).
Pre-configured dashboards:
- **RPC Performance**: Request rates, latency percentiles by command
- **Transaction Overview**: Transaction processing rates and paths
- **Consensus Health**: Consensus round duration and proposer counts
Pre-configured datasources:
- **Jaeger**: Trace data at `http://jaeger:16686`
- **Prometheus**: Metrics at `http://prometheus:9090`
---
## Troubleshooting
### No traces in Jaeger
1. Check otel-collector logs:
```bash
docker compose -f docker/telemetry/docker-compose.yml logs otel-collector
```
2. Verify xrpld telemetry config has `enabled=1` and correct endpoint
3. Check that otel-collector port 4318 is accessible:
```bash
curl -sf http://localhost:4318 && echo "reachable"
```
4. Increase `batch_delay_ms` or decrease `batch_size` in xrpld config
### Nodes not reaching "proposing" state
1. Check that all peer ports (51235-51240) are not in use:
```bash
for p in 51235 51236 51237 51238 51239 51240; do
ss -tlnp | grep ":$p " && echo "port $p in use"
done
```
2. Verify `[ips_fixed]` lists all 6 peer ports
3. Verify `validators.txt` has all 6 public keys
4. Check node debug logs: `tail -50 /tmp/xrpld-integration/node1/debug.log`
5. Ensure `[peer_private]` is set to `1` (prevents reaching out to public network)
### Transaction not processing
1. Verify genesis account exists:
```bash
curl -s http://localhost:5005 \
-d '{"method":"account_info","params":[{"account":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"}]}' \
| jq .result.account_data.Balance
```
2. Check submit response for error codes
3. In standalone mode, remember to call `ledger_accept` after submitting
### Spanmetrics not appearing in Prometheus
1. Verify otel-collector config has `spanmetrics` connector
2. Check that the metrics pipeline is configured:
```yaml
service:
pipelines:
metrics:
receivers: [spanmetrics]
exporters: [prometheus]
```
3. Verify Prometheus can reach collector:
```bash
curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets'
```

View File

@@ -0,0 +1,74 @@
# Docker Compose stack for rippled OpenTelemetry observability.
#
# Provides three services for local development:
# - otel-collector: receives OTLP traces from rippled, batches and
# forwards them to Jaeger. Listens on ports 4317 (gRPC) and 4318 (HTTP).
# - jaeger: all-in-one tracing backend with UI on port 16686.
# - grafana: dashboards on port 3000, pre-configured with Jaeger datasource.
#
# Usage:
# docker compose -f docker/telemetry/docker-compose.yml up -d
#
# Configure rippled to export traces by adding to xrpld.cfg:
# [telemetry]
# enabled=1
# endpoint=http://localhost:4318/v1/traces
version: "3.8"
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yaml"]
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "8889:8889" # Prometheus metrics (spanmetrics)
- "13133:13133" # Health check
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro
depends_on:
- jaeger
networks:
- rippled-telemetry
jaeger:
image: jaegertracing/all-in-one:latest
environment:
- COLLECTOR_OTLP_ENABLED=true
ports:
- "16686:16686" # Jaeger UI
- "14250:14250" # gRPC
networks:
- rippled-telemetry
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
depends_on:
- otel-collector
networks:
- rippled-telemetry
grafana:
image: grafana/grafana:latest
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
ports:
- "3000:3000"
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
depends_on:
- jaeger
- prometheus
networks:
- rippled-telemetry
networks:
rippled-telemetry:
driver: bridge

View File

@@ -0,0 +1,93 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": null,
"links": [],
"panels": [
{
"title": "Consensus Round Duration",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.95, sum by (le) (rate(traces_span_metrics_duration_milliseconds_bucket{span_name=\"consensus.accept\"}[5m])))",
"legendFormat": "p95 round duration"
},
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.50, sum by (le) (rate(traces_span_metrics_duration_milliseconds_bucket{span_name=\"consensus.accept\"}[5m])))",
"legendFormat": "p50 round duration"
}
],
"fieldConfig": {
"defaults": {
"unit": "ms"
},
"overrides": []
}
},
{
"title": "Consensus Proposals Sent Rate",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum(rate(traces_span_metrics_calls_total{span_name=\"consensus.proposal.send\"}[5m]))",
"legendFormat": "proposals/sec"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops"
},
"overrides": []
}
},
{
"title": "Ledger Close Duration",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.95, sum by (le) (rate(traces_span_metrics_duration_milliseconds_bucket{span_name=\"consensus.ledger_close\"}[5m])))",
"legendFormat": "p95 close duration"
}
],
"fieldConfig": {
"defaults": {
"unit": "ms"
},
"overrides": []
}
},
{
"title": "Validation Send Rate",
"type": "stat",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum(rate(traces_span_metrics_calls_total{span_name=\"consensus.validation.send\"}[5m]))",
"legendFormat": "validations/sec"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops"
},
"overrides": []
}
}
],
"schemaVersion": 39,
"tags": ["rippled", "consensus", "telemetry"],
"templating": { "list": [] },
"time": { "from": "now-1h", "to": "now" },
"title": "rippled Consensus Health",
"uid": "rippled-consensus"
}

View File

@@ -0,0 +1,90 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": null,
"links": [],
"panels": [
{
"title": "RPC Request Rate by Command",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum by (xrpl_rpc_command) (rate(traces_span_metrics_calls_total{span_name=~\"rpc.command.*\"}[5m]))",
"legendFormat": "{{xrpl_rpc_command}}"
}
],
"fieldConfig": {
"defaults": {
"unit": "reqps"
},
"overrides": []
}
},
{
"title": "RPC Latency p95 by Command",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.95, sum by (le, xrpl_rpc_command) (rate(traces_span_metrics_duration_milliseconds_bucket{span_name=~\"rpc.command.*\"}[5m])))",
"legendFormat": "p95 {{xrpl_rpc_command}}"
}
],
"fieldConfig": {
"defaults": {
"unit": "ms"
},
"overrides": []
}
},
{
"title": "RPC Error Rate",
"type": "bargauge",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum by (xrpl_rpc_command) (rate(traces_span_metrics_calls_total{span_name=~\"rpc.command.*\", status_code=\"STATUS_CODE_ERROR\"}[5m])) / sum by (xrpl_rpc_command) (rate(traces_span_metrics_calls_total{span_name=~\"rpc.command.*\"}[5m])) * 100",
"legendFormat": "{{xrpl_rpc_command}}"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"thresholds": {
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 1 },
{ "color": "red", "value": 5 }
]
}
},
"overrides": []
}
},
{
"title": "RPC Latency Heatmap",
"type": "heatmap",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum(increase(traces_span_metrics_duration_milliseconds_bucket{span_name=~\"rpc.command.*\"}[5m])) by (le)",
"legendFormat": "{{le}}",
"format": "heatmap"
}
]
}
],
"schemaVersion": 39,
"tags": ["rippled", "rpc", "telemetry"],
"templating": { "list": [] },
"time": { "from": "now-1h", "to": "now" },
"title": "rippled RPC Performance",
"uid": "rippled-rpc-perf"
}

View File

@@ -0,0 +1,92 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": null,
"links": [],
"panels": [
{
"title": "Transaction Processing Rate",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum(rate(traces_span_metrics_calls_total{span_name=\"tx.process\"}[5m]))",
"legendFormat": "tx.process/sec"
},
{
"datasource": { "type": "prometheus" },
"expr": "sum(rate(traces_span_metrics_calls_total{span_name=\"tx.receive\"}[5m]))",
"legendFormat": "tx.receive/sec"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops"
},
"overrides": []
}
},
{
"title": "Transaction Processing Latency",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.95, sum by (le) (rate(traces_span_metrics_duration_milliseconds_bucket{span_name=\"tx.process\"}[5m])))",
"legendFormat": "p95"
},
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.50, sum by (le) (rate(traces_span_metrics_duration_milliseconds_bucket{span_name=\"tx.process\"}[5m])))",
"legendFormat": "p50"
}
],
"fieldConfig": {
"defaults": {
"unit": "ms"
},
"overrides": []
}
},
{
"title": "Transaction Path Distribution",
"type": "piechart",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum by (xrpl_tx_local) (rate(traces_span_metrics_calls_total{span_name=\"tx.process\"}[5m]))",
"legendFormat": "local={{xrpl_tx_local}}"
}
]
},
{
"title": "Transaction Receive vs Suppressed",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum(rate(traces_span_metrics_calls_total{span_name=\"tx.receive\"}[5m]))",
"legendFormat": "total received"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops"
},
"overrides": []
}
}
],
"schemaVersion": 39,
"tags": ["rippled", "transactions", "telemetry"],
"templating": { "list": [] },
"time": { "from": "now-1h", "to": "now" },
"title": "rippled Transaction Overview",
"uid": "rippled-transactions"
}

View File

@@ -0,0 +1,12 @@
apiVersion: 1
providers:
- name: rippled-telemetry
orgId: 1
folder: rippled
type: file
disableDeletion: false
editable: true
options:
path: /var/lib/grafana/dashboards
foldersFromFilesStructure: false

View File

@@ -0,0 +1,12 @@
# Grafana datasource provisioning for the rippled telemetry stack.
# Auto-configures Jaeger as a trace data source on Grafana startup.
# Access Grafana at http://localhost:3000, then use Explore -> Jaeger
# to browse rippled traces.
apiVersion: 1
datasources:
- name: Jaeger
type: jaeger
access: proxy
url: http://jaeger:16686

View File

@@ -0,0 +1,9 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: false
editable: true

View File

@@ -0,0 +1,554 @@
#!/usr/bin/env bash
# Integration test for rippled OpenTelemetry instrumentation.
#
# Launches a 6-node xrpld consensus network with telemetry enabled,
# exercises RPC / transaction / consensus code paths, then verifies
# that the expected spans and metrics appear in Jaeger and Prometheus.
#
# Usage:
# bash docker/telemetry/integration-test.sh
#
# Prerequisites:
# - .build/xrpld built with telemetry=ON
# - docker compose (v2)
# - curl, jq
#
# The script leaves the observability stack and xrpld nodes running
# so you can manually inspect Jaeger (localhost:16686) and Grafana
# (localhost:3000). Run with --cleanup to tear down instead.
set -euo pipefail
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
XRPLD="$REPO_ROOT/.build/xrpld"
COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yml"
STANDALONE_CFG="$SCRIPT_DIR/xrpld-telemetry.cfg"
WORKDIR="/tmp/xrpld-integration"
NUM_NODES=6
PEER_PORT_BASE=51235
RPC_PORT_BASE=5005
CONSENSUS_TIMEOUT=120
GENESIS_ACCOUNT="rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"
GENESIS_SEED="snoPBrXtMeMyMHUVTgbuqAfg1SUTb"
DEST_ACCOUNT="" # Generated dynamically via wallet_propose
JAEGER="http://localhost:16686"
PROM="http://localhost:9090"
# Counters for pass/fail
PASS=0
FAIL=0
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
log() { printf "\033[1;34m[INFO]\033[0m %s\n" "$*"; }
ok() { printf "\033[1;32m[PASS]\033[0m %s\n" "$*"; PASS=$((PASS + 1)); }
fail() { printf "\033[1;31m[FAIL]\033[0m %s\n" "$*"; FAIL=$((FAIL + 1)); }
die() { printf "\033[1;31m[ERROR]\033[0m %s\n" "$*" >&2; exit 1; }
check_span() {
local op="$1"
local count
count=$(curl -sf "$JAEGER/api/traces?service=rippled&operation=$op&limit=5&lookback=1h" \
| jq '.data | length' 2>/dev/null || echo 0)
if [ "$count" -gt 0 ]; then
ok "$op ($count traces)"
else
fail "$op (0 traces)"
fi
}
cleanup() {
log "Cleaning up..."
# Kill xrpld nodes
for i in $(seq 1 "$NUM_NODES"); do
local pidfile="$WORKDIR/node$i/xrpld.pid"
if [ -f "$pidfile" ]; then
kill "$(cat "$pidfile")" 2>/dev/null || true
rm -f "$pidfile"
fi
done
# Also kill any straggling xrpld processes from our workdir
pkill -f "$WORKDIR" 2>/dev/null || true
# Stop docker stack
docker compose -f "$COMPOSE_FILE" down 2>/dev/null || true
# Remove workdir
rm -rf "$WORKDIR"
log "Cleanup complete."
}
# Handle --cleanup flag
if [ "${1:-}" = "--cleanup" ]; then
cleanup
exit 0
fi
# ---------------------------------------------------------------------------
# Step 0: Prerequisites
# ---------------------------------------------------------------------------
log "Checking prerequisites..."
command -v docker >/dev/null 2>&1 || die "docker not found"
docker compose version >/dev/null 2>&1 || die "docker compose (v2) not found"
command -v curl >/dev/null 2>&1 || die "curl not found"
command -v jq >/dev/null 2>&1 || die "jq not found"
[ -x "$XRPLD" ] || die "xrpld binary not found at $XRPLD (build with telemetry=ON)"
[ -f "$COMPOSE_FILE" ] || die "docker-compose.yml not found at $COMPOSE_FILE"
[ -f "$STANDALONE_CFG" ] || die "xrpld-telemetry.cfg not found at $STANDALONE_CFG"
log "All prerequisites met."
# ---------------------------------------------------------------------------
# Step 1: Clean previous run
# ---------------------------------------------------------------------------
log "Cleaning previous run data..."
for i in $(seq 1 "$NUM_NODES"); do
pidfile="$WORKDIR/node$i/xrpld.pid"
if [ -f "$pidfile" ]; then
kill "$(cat "$pidfile")" 2>/dev/null || true
fi
done
pkill -f "$WORKDIR" 2>/dev/null || true
# Kill any xrpld using the standalone config (from key generation)
pkill -f "xrpld-telemetry.cfg" 2>/dev/null || true
sleep 2
rm -rf "$WORKDIR"
mkdir -p "$WORKDIR"
# ---------------------------------------------------------------------------
# Step 2: Start observability stack
# ---------------------------------------------------------------------------
log "Starting observability stack..."
docker compose -f "$COMPOSE_FILE" up -d
log "Waiting for otel-collector to be ready..."
for attempt in $(seq 1 30); do
# The OTLP HTTP endpoint returns 405 for GET (expects POST), which
# means it is listening. curl -sf would fail on 405, so we check
# the HTTP status code explicitly.
status=$(curl -so /dev/null -w '%{http_code}' http://localhost:4318/ 2>/dev/null || echo 000)
if [ "$status" != "000" ]; then
log "otel-collector ready (attempt $attempt, HTTP $status)."
break
fi
if [ "$attempt" -eq 30 ]; then
die "otel-collector not ready after 30s"
fi
sleep 1
done
log "Waiting for Jaeger to be ready..."
for attempt in $(seq 1 30); do
if curl -sf "$JAEGER/" >/dev/null 2>&1; then
log "Jaeger ready (attempt $attempt)."
break
fi
if [ "$attempt" -eq 30 ]; then
die "Jaeger not ready after 30s"
fi
sleep 1
done
# ---------------------------------------------------------------------------
# Step 3: Generate validator keys
# ---------------------------------------------------------------------------
log "Generating $NUM_NODES validator key pairs..."
# Start a temporary standalone xrpld for key generation
TEMP_DATA="$WORKDIR/temp-keygen"
mkdir -p "$TEMP_DATA"
# Create a minimal temp config for key generation
TEMP_CFG="$TEMP_DATA/xrpld.cfg"
cat > "$TEMP_CFG" <<EOCFG
[server]
port_rpc_temp
[port_rpc_temp]
port = 5099
ip = 127.0.0.1
admin = 127.0.0.1
protocol = http
[node_db]
type=NuDB
path=$TEMP_DATA/nudb
online_delete=256
[database_path]
$TEMP_DATA/db
[debug_logfile]
$TEMP_DATA/debug.log
[ssl_verify]
0
EOCFG
"$XRPLD" --conf "$TEMP_CFG" -a --start > "$TEMP_DATA/stdout.log" 2>&1 &
TEMP_PID=$!
log "Temporary xrpld started (PID $TEMP_PID), waiting for RPC..."
for attempt in $(seq 1 30); do
if curl -sf http://localhost:5099 -d '{"method":"server_info"}' >/dev/null 2>&1; then
log "Temporary xrpld RPC ready (attempt $attempt)."
break
fi
if [ "$attempt" -eq 30 ]; then
kill "$TEMP_PID" 2>/dev/null || true
die "Temporary xrpld RPC not ready after 30s"
fi
sleep 1
done
declare -a SEEDS
declare -a PUBKEYS
for i in $(seq 1 "$NUM_NODES"); do
result=$(curl -sf http://localhost:5099 -d '{"method":"validation_create"}')
seed=$(echo "$result" | jq -r '.result.validation_seed')
pubkey=$(echo "$result" | jq -r '.result.validation_public_key')
if [ -z "$seed" ] || [ "$seed" = "null" ]; then
kill "$TEMP_PID" 2>/dev/null || true
die "Failed to generate key pair $i"
fi
SEEDS+=("$seed")
PUBKEYS+=("$pubkey")
log " Node $i: $pubkey"
done
kill "$TEMP_PID" 2>/dev/null || true
wait "$TEMP_PID" 2>/dev/null || true
rm -rf "$TEMP_DATA"
log "Key generation complete."
# ---------------------------------------------------------------------------
# Step 4: Generate node configs and validators.txt
# ---------------------------------------------------------------------------
log "Generating node configs..."
# Create shared validators.txt
VALIDATORS_FILE="$WORKDIR/validators.txt"
{
echo "[validators]"
for i in $(seq 0 $((NUM_NODES - 1))); do
echo "${PUBKEYS[$i]}"
done
} > "$VALIDATORS_FILE"
# Create per-node configs
for i in $(seq 1 "$NUM_NODES"); do
NODE_DIR="$WORKDIR/node$i"
mkdir -p "$NODE_DIR/nudb" "$NODE_DIR/db"
RPC_PORT=$((RPC_PORT_BASE + i - 1))
PEER_PORT=$((PEER_PORT_BASE + i - 1))
SEED="${SEEDS[$((i - 1))]}"
# Build ips_fixed list (all peers except self)
IPS_FIXED=""
for j in $(seq 1 "$NUM_NODES"); do
if [ "$j" -ne "$i" ]; then
IPS_FIXED="${IPS_FIXED}127.0.0.1 $((PEER_PORT_BASE + j - 1))
"
fi
done
cat > "$NODE_DIR/xrpld.cfg" <<EOCFG
[server]
port_rpc
port_peer
[port_rpc]
port = $RPC_PORT
ip = 127.0.0.1
admin = 127.0.0.1
protocol = http
[port_peer]
port = $PEER_PORT
ip = 0.0.0.0
protocol = peer
[node_db]
type=NuDB
path=$NODE_DIR/nudb
online_delete=256
[database_path]
$NODE_DIR/db
[debug_logfile]
$NODE_DIR/debug.log
[validation_seed]
$SEED
[validators_file]
$VALIDATORS_FILE
[ips_fixed]
${IPS_FIXED}
[peer_private]
1
[telemetry]
enabled=1
endpoint=http://localhost:4318/v1/traces
exporter=otlp_http
sampling_ratio=1.0
batch_size=512
batch_delay_ms=2000
max_queue_size=2048
trace_rpc=1
trace_transactions=1
trace_consensus=1
trace_peer=0
trace_ledger=1
[rpc_startup]
{ "command": "log_level", "severity": "warning" }
[ssl_verify]
0
EOCFG
log " Node $i config: RPC=$RPC_PORT, Peer=$PEER_PORT"
done
# ---------------------------------------------------------------------------
# Step 5: Start all 6 nodes
# ---------------------------------------------------------------------------
log "Starting $NUM_NODES xrpld nodes..."
for i in $(seq 1 "$NUM_NODES"); do
NODE_DIR="$WORKDIR/node$i"
"$XRPLD" --conf "$NODE_DIR/xrpld.cfg" --start > "$NODE_DIR/stdout.log" 2>&1 &
echo $! > "$NODE_DIR/xrpld.pid"
log " Node $i started (PID $(cat "$NODE_DIR/xrpld.pid"))"
done
# Give nodes a moment to initialize
sleep 5
# ---------------------------------------------------------------------------
# Step 6: Wait for consensus
# ---------------------------------------------------------------------------
log "Waiting for nodes to reach 'proposing' state (timeout: ${CONSENSUS_TIMEOUT}s)..."
start_time=$(date +%s)
nodes_ready=0
while [ "$nodes_ready" -lt "$NUM_NODES" ]; do
elapsed=$(( $(date +%s) - start_time ))
if [ "$elapsed" -ge "$CONSENSUS_TIMEOUT" ]; then
fail "Consensus timeout after ${CONSENSUS_TIMEOUT}s ($nodes_ready/$NUM_NODES nodes ready)"
log "Continuing with partial consensus..."
break
fi
nodes_ready=0
for i in $(seq 1 "$NUM_NODES"); do
RPC_PORT=$((RPC_PORT_BASE + i - 1))
state=$(curl -sf "http://localhost:$RPC_PORT" \
-d '{"method":"server_info"}' 2>/dev/null \
| jq -r '.result.info.server_state' 2>/dev/null || echo "unreachable")
if [ "$state" = "proposing" ]; then
nodes_ready=$((nodes_ready + 1))
fi
done
printf "\r %d/%d nodes proposing (%ds elapsed)..." "$nodes_ready" "$NUM_NODES" "$elapsed"
if [ "$nodes_ready" -lt "$NUM_NODES" ]; then
sleep 3
fi
done
echo ""
if [ "$nodes_ready" -eq "$NUM_NODES" ]; then
ok "All $NUM_NODES nodes reached 'proposing' state"
else
fail "Only $nodes_ready/$NUM_NODES nodes reached 'proposing' state"
fi
# ---------------------------------------------------------------------------
# Step 6b: Wait for validated ledger
# ---------------------------------------------------------------------------
log "Waiting for first validated ledger..."
for attempt in $(seq 1 60); do
val_seq=$(curl -sf "http://localhost:$RPC_PORT_BASE" \
-d '{"method":"server_info"}' 2>/dev/null \
| jq -r '.result.info.validated_ledger.seq // 0' 2>/dev/null || echo 0)
if [ "$val_seq" -gt 2 ] 2>/dev/null; then
ok "First validated ledger: seq $val_seq"
break
fi
if [ "$attempt" -eq 60 ]; then
fail "No validated ledger after 60s"
fi
sleep 1
done
# ---------------------------------------------------------------------------
# Step 7: Exercise RPC spans (Phase 2)
# ---------------------------------------------------------------------------
log "Exercising RPC spans..."
curl -sf "http://localhost:$RPC_PORT_BASE" \
-d '{"method":"server_info"}' > /dev/null
curl -sf "http://localhost:$RPC_PORT_BASE" \
-d '{"method":"server_state"}' > /dev/null
curl -sf "http://localhost:$RPC_PORT_BASE" \
-d '{"method":"ledger","params":[{"ledger_index":"current"}]}' > /dev/null
log "RPC commands sent. Waiting 5s for batch export..."
sleep 5
# ---------------------------------------------------------------------------
# Step 8: Submit transaction (Phase 3)
# ---------------------------------------------------------------------------
log "Submitting Payment transaction..."
# Generate a destination wallet
log " Generating destination wallet..."
wallet_result=$(curl -sf "http://localhost:$RPC_PORT_BASE" \
-d '{"method":"wallet_propose"}')
DEST_ACCOUNT=$(echo "$wallet_result" | jq -r '.result.account_id' 2>/dev/null)
if [ -z "$DEST_ACCOUNT" ] || [ "$DEST_ACCOUNT" = "null" ]; then
fail "Could not generate destination wallet"
DEST_ACCOUNT="rrrrrrrrrrrrrrrrrrrrrhoLvTp" # ACCOUNT_ZERO fallback
fi
log " Destination: $DEST_ACCOUNT"
# Get genesis account info
acct_result=$(curl -sf "http://localhost:$RPC_PORT_BASE" \
-d "{\"method\":\"account_info\",\"params\":[{\"account\":\"$GENESIS_ACCOUNT\"}]}")
seq_num=$(echo "$acct_result" | jq -r '.result.account_data.Sequence' 2>/dev/null || echo "unknown")
log " Genesis account sequence: $seq_num"
# Submit payment
submit_result=$(curl -sf "http://localhost:$RPC_PORT_BASE" -d "{
\"method\": \"submit\",
\"params\": [{
\"secret\": \"$GENESIS_SEED\",
\"tx_json\": {
\"TransactionType\": \"Payment\",
\"Account\": \"$GENESIS_ACCOUNT\",
\"Destination\": \"$DEST_ACCOUNT\",
\"Amount\": \"10000000\"
}
}]
}")
engine_result=$(echo "$submit_result" | jq -r '.result.engine_result' 2>/dev/null || echo "unknown")
tx_hash=$(echo "$submit_result" | jq -r '.result.tx_json.hash' 2>/dev/null || echo "unknown")
if [ "$engine_result" = "tesSUCCESS" ] || [ "$engine_result" = "terQUEUED" ]; then
ok "Transaction submitted: $engine_result (hash: ${tx_hash:0:16}...)"
else
fail "Transaction submission: $engine_result"
log " Full response: $(echo "$submit_result" | jq -c .result 2>/dev/null)"
fi
log "Waiting 15s for consensus round + batch export..."
sleep 15
# ---------------------------------------------------------------------------
# Step 9: Verify Jaeger traces
# ---------------------------------------------------------------------------
log "Verifying spans in Jaeger..."
# Check service registration
services=$(curl -sf "$JAEGER/api/services" | jq -r '.data[]' 2>/dev/null || echo "")
if echo "$services" | grep -q "rippled"; then
ok "Service 'rippled' registered in Jaeger"
else
fail "Service 'rippled' NOT found in Jaeger (found: $services)"
fi
log ""
log "--- Phase 2: RPC Spans ---"
check_span "rpc.request"
check_span "rpc.process"
check_span "rpc.command.server_info"
check_span "rpc.command.server_state"
check_span "rpc.command.ledger"
log ""
log "--- Phase 3: Transaction Spans ---"
check_span "tx.process"
check_span "tx.receive"
log ""
log "--- Phase 4: Consensus Spans ---"
check_span "consensus.proposal.send"
check_span "consensus.ledger_close"
check_span "consensus.accept"
check_span "consensus.validation.send"
# ---------------------------------------------------------------------------
# Step 10: Verify Prometheus spanmetrics
# ---------------------------------------------------------------------------
log ""
log "--- Phase 5: Spanmetrics ---"
log "Waiting 20s for Prometheus scrape cycle..."
sleep 20
calls_count=$(curl -sf "$PROM/api/v1/query?query=traces_span_metrics_calls_total" \
| jq '.data.result | length' 2>/dev/null || echo 0)
if [ "$calls_count" -gt 0 ]; then
ok "Prometheus: traces_span_metrics_calls_total ($calls_count series)"
else
fail "Prometheus: traces_span_metrics_calls_total (0 series)"
fi
duration_count=$(curl -sf "$PROM/api/v1/query?query=traces_span_metrics_duration_milliseconds_count" \
| jq '.data.result | length' 2>/dev/null || echo 0)
if [ "$duration_count" -gt 0 ]; then
ok "Prometheus: duration histogram ($duration_count series)"
else
fail "Prometheus: duration histogram (0 series)"
fi
# Check Grafana
if curl -sf http://localhost:3000/api/health > /dev/null 2>&1; then
ok "Grafana: healthy at localhost:3000"
else
fail "Grafana: not reachable at localhost:3000"
fi
# ---------------------------------------------------------------------------
# Step 11: Summary
# ---------------------------------------------------------------------------
echo ""
echo "==========================================================="
echo " INTEGRATION TEST RESULTS"
echo "==========================================================="
printf " \033[1;32mPASSED: %d\033[0m\n" "$PASS"
printf " \033[1;31mFAILED: %d\033[0m\n" "$FAIL"
echo "==========================================================="
echo ""
echo " Observability stack is running:"
echo ""
echo " Jaeger UI: http://localhost:16686"
echo " Grafana: http://localhost:3000"
echo " Prometheus: http://localhost:9090"
echo ""
echo " xrpld nodes (6) are running:"
for i in $(seq 1 "$NUM_NODES"); do
RPC_PORT=$((RPC_PORT_BASE + i - 1))
PEER_PORT=$((PEER_PORT_BASE + i - 1))
echo " Node $i: RPC=localhost:$RPC_PORT Peer=:$PEER_PORT PID=$(cat "$WORKDIR/node$i/xrpld.pid" 2>/dev/null || echo 'unknown')"
done
echo ""
echo " To tear down:"
echo " bash docker/telemetry/integration-test.sh --cleanup"
echo ""
echo "==========================================================="
if [ "$FAIL" -gt 0 ]; then
exit 1
fi

View File

@@ -0,0 +1,53 @@
# OpenTelemetry Collector configuration for rippled development.
#
# Pipelines:
# traces: OTLP receiver -> batch processor -> debug + Jaeger + spanmetrics
# metrics: spanmetrics connector -> Prometheus exporter
#
# rippled sends traces via OTLP/HTTP to port 4318. The collector batches
# them, forwards to Jaeger, and derives RED metrics via the spanmetrics
# connector, which Prometheus scrapes on port 8889.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 100
connectors:
spanmetrics:
histogram:
explicit:
buckets: [1ms, 5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 5s]
dimensions:
- name: xrpl.rpc.command
- name: xrpl.rpc.status
- name: xrpl.consensus.mode
- name: xrpl.tx.local
exporters:
debug:
verbosity: detailed
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug, otlp/jaeger, spanmetrics]
metrics:
receivers: [spanmetrics]
exporters: [prometheus]

View File

@@ -0,0 +1,9 @@
# Prometheus configuration for scraping spanmetrics from OTel Collector.
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: otel-collector
static_configs:
- targets: ["otel-collector:8889"]

View File

@@ -0,0 +1,59 @@
# Standalone xrpld configuration with OpenTelemetry enabled.
#
# Usage:
# 1. Start the observability stack:
# docker compose -f docker/telemetry/docker-compose.yml up -d
# 2. Run xrpld in standalone mode:
# ./xrpld --conf docker/telemetry/xrpld-telemetry.cfg -a --start
# 3. Send RPC commands to exercise tracing:
# curl -s http://localhost:5005 -d '{"method":"server_info"}'
# 4. View traces in Jaeger UI: http://localhost:16686
[server]
port_rpc_admin_local
port_ws_admin_local
[port_rpc_admin_local]
port = 5005
ip = 127.0.0.1
admin = 127.0.0.1
protocol = http
[port_ws_admin_local]
port = 6006
ip = 127.0.0.1
admin = 127.0.0.1
protocol = ws
[node_db]
type=NuDB
path=./data/nudb
online_delete=256
advisory_delete=0
[database_path]
./data
[debug_logfile]
./data/debug.log
[rpc_startup]
{ "command": "log_level", "severity": "debug" }
[ssl_verify]
0
# --- OpenTelemetry tracing ---
[telemetry]
enabled=1
endpoint=http://localhost:4318/v1/traces
exporter=otlp_http
sampling_ratio=1.0
batch_size=512
batch_delay_ms=5000
max_queue_size=2048
trace_rpc=1
trace_transactions=1
trace_consensus=1
trace_peer=0
trace_ledger=1

278
docs/build/telemetry.md vendored Normal file
View File

@@ -0,0 +1,278 @@
# OpenTelemetry Tracing for Rippled
This document explains how to build rippled with OpenTelemetry distributed tracing support, configure the runtime telemetry options, and set up the observability backend to view traces.
- [OpenTelemetry Tracing for Rippled](#opentelemetry-tracing-for-rippled)
- [Overview](#overview)
- [Building with Telemetry](#building-with-telemetry)
- [Summary](#summary)
- [Build steps](#build-steps)
- [Install dependencies](#install-dependencies)
- [Call CMake](#call-cmake)
- [Build](#build)
- [Building without telemetry](#building-without-telemetry)
- [Runtime Configuration](#runtime-configuration)
- [Configuration options](#configuration-options)
- [Observability Stack](#observability-stack)
- [Start the stack](#start-the-stack)
- [Verify the stack](#verify-the-stack)
- [View traces in Jaeger](#view-traces-in-jaeger)
- [Running Tests](#running-tests)
- [Troubleshooting](#troubleshooting)
- [No traces appear in Jaeger](#no-traces-appear-in-jaeger)
- [Conan lockfile error](#conan-lockfile-error)
- [CMake target not found](#cmake-target-not-found)
- [Architecture](#architecture)
- [Key files](#key-files)
- [Conditional compilation](#conditional-compilation)
## Overview
Rippled supports optional [OpenTelemetry](https://opentelemetry.io/) distributed tracing.
When enabled, it instruments RPC requests with trace spans that are exported via
OTLP/HTTP to an OpenTelemetry Collector, which forwards them to a tracing backend
such as Jaeger.
Telemetry is **off by default** at both compile time and runtime:
- **Compile time**: The Conan option `telemetry` and CMake option `telemetry` must be set to `True`/`ON`.
When disabled, all tracing macros compile to `((void)0)` with zero overhead.
- **Runtime**: The `[telemetry]` config section must set `enabled=1`.
When disabled at runtime, a no-op implementation is used.
## Building with Telemetry
### Summary
Follow the same instructions as mentioned in [BUILD.md](../../BUILD.md) but with the following changes:
1. Pass `-o telemetry=True` to `conan install` to pull the `opentelemetry-cpp` dependency.
2. CMake will automatically pick up `telemetry=ON` from the Conan-generated toolchain.
3. Build as usual.
---
### Build steps
```bash
cd /path/to/rippled
rm -rf .build
mkdir .build
cd .build
```
#### Install dependencies
The `telemetry` option adds `opentelemetry-cpp/1.18.0` as a dependency.
If the Conan lockfile does not yet include this package, bypass it with `--lockfile=""`.
```bash
conan install .. \
--output-folder . \
--build missing \
--settings build_type=Debug \
-o telemetry=True \
-o tests=True \
-o xrpld=True \
--lockfile=""
```
> **Note**: The first build with telemetry may take longer as `opentelemetry-cpp`
> and its transitive dependencies are compiled from source.
#### Call CMake
The Conan-generated toolchain file sets `telemetry=ON` automatically.
No additional CMake flags are needed beyond the standard ones.
```bash
cmake .. -G Ninja \
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=Debug \
-Dtests=ON -Dxrpld=ON
```
You should see in the CMake output:
```
-- OpenTelemetry tracing enabled
```
#### Build
```bash
cmake --build . --parallel $(nproc)
```
### Building without telemetry
Omit the `-o telemetry=True` option (or pass `-o telemetry=False`).
The `opentelemetry-cpp` dependency will not be downloaded,
the `XRPL_ENABLE_TELEMETRY` preprocessor define will not be set,
and all tracing macros will compile to no-ops.
The resulting binary is identical to one built before telemetry support was added.
## Runtime Configuration
Add a `[telemetry]` section to your `xrpld.cfg` file:
```ini
[telemetry]
enabled=1
service_name=rippled
endpoint=http://localhost:4318/v1/traces
sampling_ratio=1.0
trace_rpc=1
trace_transactions=1
trace_consensus=1
trace_peer=0
```
### Configuration options
| Option | Type | Default | Description |
| --------------------- | ------ | --------------------------------- | -------------------------------------------------- |
| `enabled` | int | `0` | Enable (`1`) or disable (`0`) telemetry at runtime |
| `service_name` | string | `rippled` | Service name reported in traces |
| `service_instance_id` | string | node public key | Unique instance identifier |
| `exporter` | string | `otlp_http` | Exporter type |
| `endpoint` | string | `http://localhost:4318/v1/traces` | OTLP/HTTP collector endpoint |
| `use_tls` | int | `0` | Enable TLS for the exporter connection |
| `tls_ca_cert` | string | (empty) | Path to CA certificate for TLS |
| `sampling_ratio` | double | `1.0` | Fraction of traces to sample (`0.0` to `1.0`) |
| `batch_size` | uint32 | `512` | Maximum spans per export batch |
| `batch_delay_ms` | uint32 | `5000` | Maximum delay (ms) before flushing a batch |
| `max_queue_size` | uint32 | `2048` | Maximum spans queued in memory |
| `trace_rpc` | int | `1` | Enable RPC request tracing |
| `trace_transactions` | int | `1` | Enable transaction lifecycle tracing |
| `trace_consensus` | int | `1` | Enable consensus round tracing |
| `trace_peer` | int | `0` | Enable peer message tracing (high volume) |
| `trace_ledger` | int | `1` | Enable ledger close tracing |
## Observability Stack
A Docker Compose stack is provided in `docker/telemetry/` with three services:
| Service | Port | Purpose |
| ------------------ | ---------------------------------------------- | ---------------------------------------------------- |
| **OTel Collector** | `4317` (gRPC), `4318` (HTTP), `13133` (health) | Receives OTLP spans, batches, and forwards to Jaeger |
| **Jaeger** | `16686` (UI) | Trace storage and visualization |
| **Grafana** | `3000` | Dashboards (Jaeger pre-configured as datasource) |
### Start the stack
```bash
docker compose -f docker/telemetry/docker-compose.yml up -d
```
### Verify the stack
```bash
# Collector health
curl http://localhost:13133
# Jaeger UI
open http://localhost:16686
# Grafana
open http://localhost:3000
```
### View traces in Jaeger
1. Open `http://localhost:16686` in a browser.
2. Select the service name (e.g. `rippled`) from the **Service** dropdown.
3. Click **Find Traces**.
4. Click into any trace to see the span tree and attributes.
Traced RPC operations produce a span hierarchy like:
```
rpc.request
└── rpc.command.server_info (xrpl.rpc.command=server_info, xrpl.rpc.status=success)
```
Each span includes attributes:
- `xrpl.rpc.command` — the RPC method name
- `xrpl.rpc.version` — API version
- `xrpl.rpc.role``admin` or `user`
- `xrpl.rpc.status``success` or `error`
## Running Tests
Unit tests run with the telemetry-enabled build regardless of whether the
observability stack is running. When no collector is available, the exporter
silently drops spans with no impact on test results.
```bash
# Run all RPC tests
./xrpld --unittest=RPCCall,ServerInfo,AccountTx,LedgerRPC,Transaction --unittest-jobs $(nproc)
# Run the full test suite
./xrpld --unittest --unittest-jobs $(nproc)
```
To generate traces during manual testing, start rippled in standalone mode:
```bash
./xrpld --conf /path/to/xrpld.cfg --standalone --start
```
Then send RPC requests:
```bash
curl -s -X POST http://127.0.0.1:5005/ \
-H "Content-Type: application/json" \
-d '{"method":"server_info","params":[{}]}'
```
## Troubleshooting
### No traces appear in Jaeger
1. Confirm the OTel Collector is running: `docker compose -f docker/telemetry/docker-compose.yml ps`
2. Check collector logs for errors: `docker compose -f docker/telemetry/docker-compose.yml logs otel-collector`
3. Confirm `[telemetry] enabled=1` is set in the rippled config.
4. Confirm `endpoint` points to the correct collector address (`http://localhost:4318/v1/traces`).
5. Wait for the batch delay to elapse (default `5000` ms) before checking Jaeger.
### Conan lockfile error
If you see `ERROR: Requirement 'opentelemetry-cpp/1.18.0' not in lockfile 'requires'`,
the lockfile was generated without the telemetry dependency.
Pass `--lockfile=""` to bypass the lockfile, or regenerate it with telemetry enabled.
### CMake target not found
If CMake reports that `opentelemetry-cpp` targets are not found,
ensure you ran `conan install` with `-o telemetry=True` and that the
Conan-generated toolchain file is being used.
The Conan package provides a single umbrella target
`opentelemetry-cpp::opentelemetry-cpp` (not individual component targets).
## Architecture
### Key files
| File | Purpose |
| ---------------------------------------------- | ----------------------------------------------------------- |
| `include/xrpl/telemetry/Telemetry.h` | Abstract telemetry interface and `Setup` struct |
| `include/xrpl/telemetry/SpanGuard.h` | RAII span guard (activates scope, ends span on destruction) |
| `src/libxrpl/telemetry/Telemetry.cpp` | OTel-backed implementation (`TelemetryImpl`) |
| `src/libxrpl/telemetry/TelemetryConfig.cpp` | Config parser (`setup_Telemetry()`) |
| `src/libxrpl/telemetry/NullTelemetry.cpp` | No-op implementation (used when disabled) |
| `src/xrpld/telemetry/TracingInstrumentation.h` | Convenience macros (`XRPL_TRACE_RPC`, etc.) |
| `src/xrpld/rpc/detail/ServerHandler.cpp` | RPC entry point instrumentation |
| `src/xrpld/rpc/detail/RPCHandler.cpp` | Per-command instrumentation |
| `docker/telemetry/docker-compose.yml` | Observability stack (Collector + Jaeger + Grafana) |
| `docker/telemetry/otel-collector-config.yaml` | OTel Collector pipeline configuration |
### Conditional compilation
All OpenTelemetry SDK headers are guarded behind `#ifdef XRPL_ENABLE_TELEMETRY`.
The instrumentation macros in `TracingInstrumentation.h` compile to `((void)0)` when
the define is absent.
At runtime, if `enabled=0` is set in config (or the section is omitted), a
`NullTelemetry` implementation is used that returns no-op spans.
This two-layer approach ensures zero overhead when telemetry is not wanted.

212
docs/telemetry-runbook.md Normal file
View File

@@ -0,0 +1,212 @@
# rippled Telemetry Operator Runbook
## Overview
rippled supports OpenTelemetry distributed tracing to provide visibility into RPC requests, transaction processing, and consensus rounds.
## Quick Start
### 1. Start the observability stack
```bash
docker compose -f docker/telemetry/docker-compose.yml up -d
```
This starts:
- **OTel Collector** on ports 4317 (gRPC) and 4318 (HTTP)
- **Jaeger** UI on http://localhost:16686
- **Prometheus** on http://localhost:9090
- **Grafana** on http://localhost:3000
### 2. Enable telemetry in rippled
Add to your `xrpld.cfg`:
```ini
[telemetry]
enabled=1
endpoint=http://localhost:4318/v1/traces
```
### 3. Build with telemetry support
```bash
conan install . --build=missing -o telemetry=True
cmake --preset default -Dtelemetry=ON
cmake --build --preset default
```
## Configuration Reference
| Option | Default | Description |
| -------------------- | --------------------------------- | ----------------------------------------- |
| `enabled` | `0` | Master switch for telemetry |
| `endpoint` | `http://localhost:4318/v1/traces` | OTLP/HTTP endpoint |
| `exporter` | `otlp_http` | Exporter type |
| `sampling_ratio` | `1.0` | Head-based sampling ratio (0.01.0) |
| `trace_rpc` | `1` | Enable RPC request tracing |
| `trace_transactions` | `1` | Enable transaction tracing |
| `trace_consensus` | `1` | Enable consensus tracing |
| `trace_peer` | `0` | Enable peer message tracing (high volume) |
| `trace_ledger` | `1` | Enable ledger tracing |
| `batch_size` | `512` | Max spans per batch export |
| `batch_delay_ms` | `5000` | Delay between batch exports |
| `max_queue_size` | `2048` | Max spans queued before dropping |
| `use_tls` | `0` | Use TLS for exporter connection |
| `tls_ca_cert` | (empty) | Path to CA certificate bundle |
## Span Reference
All spans instrumented in rippled, grouped by subsystem:
### RPC Spans (Phase 2)
| Span Name | Source File | Attributes | Description |
| -------------------- | --------------------- | ------------------------------------------------------- | -------------------------------------------------- |
| `rpc.request` | ServerHandler.cpp:271 | — | Top-level HTTP RPC request |
| `rpc.process` | ServerHandler.cpp:573 | — | RPC processing (child of rpc.request) |
| `rpc.ws_message` | ServerHandler.cpp:384 | — | WebSocket RPC message |
| `rpc.command.<name>` | RPCHandler.cpp:161 | `xrpl.rpc.command`, `xrpl.rpc.version`, `xrpl.rpc.role` | Per-command span (e.g., `rpc.command.server_info`) |
### Transaction Spans (Phase 3)
| Span Name | Source File | Attributes | Description |
| ------------ | ------------------- | ----------------------------------------------- | ------------------------------------- |
| `tx.process` | NetworkOPs.cpp:1227 | `xrpl.tx.hash`, `xrpl.tx.local`, `xrpl.tx.path` | Transaction submission and processing |
| `tx.receive` | PeerImp.cpp:1273 | `xrpl.peer.id` | Transaction received from peer relay |
### Consensus Spans (Phase 4)
| Span Name | Source File | Attributes | Description |
| --------------------------- | -------------------- | ---------------------------------------------------------- | ---------------------------- |
| `consensus.proposal.send` | RCLConsensus.cpp:177 | `xrpl.consensus.round` | Consensus proposal broadcast |
| `consensus.ledger_close` | RCLConsensus.cpp:282 | `xrpl.consensus.ledger.seq`, `xrpl.consensus.mode` | Ledger close event |
| `consensus.accept` | RCLConsensus.cpp:395 | `xrpl.consensus.proposers`, `xrpl.consensus.round_time_ms` | Ledger accepted by consensus |
| `consensus.validation.send` | RCLConsensus.cpp:753 | `xrpl.consensus.ledger.seq`, `xrpl.consensus.proposing` | Validation sent after accept |
## Prometheus Metrics (Spanmetrics)
The OTel Collector's spanmetrics connector automatically derives RED (Rate, Errors, Duration) metrics from every span. No custom metrics code is needed in rippled.
### Generated Metric Names
| Prometheus Metric | Type | Description |
| -------------------------------------------------- | --------- | ---------------------------- |
| `traces_span_metrics_calls_total` | Counter | Total span invocations |
| `traces_span_metrics_duration_milliseconds_bucket` | Histogram | Latency distribution buckets |
| `traces_span_metrics_duration_milliseconds_count` | Histogram | Latency observation count |
| `traces_span_metrics_duration_milliseconds_sum` | Histogram | Cumulative latency |
### Metric Labels
Every metric carries these standard labels:
| Label | Source | Example |
| -------------- | ------------------ | ---------------------------------------- |
| `span_name` | Span name | `rpc.command.server_info` |
| `status_code` | Span status | `STATUS_CODE_UNSET`, `STATUS_CODE_ERROR` |
| `service_name` | Resource attribute | `rippled` |
| `span_kind` | Span kind | `SPAN_KIND_INTERNAL` |
Additionally, span attributes configured as dimensions in the collector become metric labels (dots → underscores):
| Span Attribute | Metric Label | Applies To |
| --------------------- | --------------------- | ------------------------------ |
| `xrpl.rpc.command` | `xrpl_rpc_command` | `rpc.command.*` spans |
| `xrpl.rpc.status` | `xrpl_rpc_status` | `rpc.command.*` spans |
| `xrpl.consensus.mode` | `xrpl_consensus_mode` | `consensus.ledger_close` spans |
| `xrpl.tx.local` | `xrpl_tx_local` | `tx.process` spans |
### Histogram Buckets
Configured in `otel-collector-config.yaml`:
```
1ms, 5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 5s
```
## Grafana Dashboards
Three dashboards are pre-provisioned in `docker/telemetry/grafana/dashboards/`:
### RPC Performance (`rippled-rpc-perf`)
| Panel | Type | PromQL | Labels Used |
| --------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| RPC Request Rate by Command | timeseries | `sum by (xrpl_rpc_command) (rate(traces_span_metrics_calls_total{span_name=~"rpc.command.*"}[5m]))` | `xrpl_rpc_command` |
| RPC Latency p95 by Command | timeseries | `histogram_quantile(0.95, sum by (le, xrpl_rpc_command) (rate(traces_span_metrics_duration_milliseconds_bucket{span_name=~"rpc.command.*"}[5m])))` | `xrpl_rpc_command` |
| RPC Error Rate | bargauge | Error spans / total spans × 100, grouped by `xrpl_rpc_command` | `xrpl_rpc_command`, `status_code` |
| RPC Latency Heatmap | heatmap | `sum(increase(traces_span_metrics_duration_milliseconds_bucket{span_name=~"rpc.command.*"}[5m])) by (le)` | `le` (bucket boundaries) |
### Transaction Overview (`rippled-transactions`)
| Panel | Type | PromQL | Labels Used |
| --------------------------------- | ---------- | -------------------------------------------------------------------------------------------- | --------------- |
| Transaction Processing Rate | timeseries | `rate(traces_span_metrics_calls_total{span_name="tx.process"}[5m])` and `tx.receive` | `span_name` |
| Transaction Processing Latency | timeseries | `histogram_quantile(0.95 / 0.50, ... {span_name="tx.process"})` | — |
| Transaction Path Distribution | piechart | `sum by (xrpl_tx_local) (rate(traces_span_metrics_calls_total{span_name="tx.process"}[5m]))` | `xrpl_tx_local` |
| Transaction Receive vs Suppressed | timeseries | `rate(traces_span_metrics_calls_total{span_name="tx.receive"}[5m])` | — |
### Consensus Health (`rippled-consensus`)
| Panel | Type | PromQL | Labels Used |
| ----------------------------- | ---------- | ---------------------------------------------------------------------------------- | ----------- |
| Consensus Round Duration | timeseries | `histogram_quantile(0.95 / 0.50, ... {span_name="consensus.accept"})` | — |
| Consensus Proposals Sent Rate | timeseries | `rate(traces_span_metrics_calls_total{span_name="consensus.proposal.send"}[5m])` | — |
| Ledger Close Duration | timeseries | `histogram_quantile(0.95, ... {span_name="consensus.ledger_close"})` | — |
| Validation Send Rate | stat | `rate(traces_span_metrics_calls_total{span_name="consensus.validation.send"}[5m])` | — |
### Span → Metric → Dashboard Summary
| Span Name | Prometheus Metric Filter | Grafana Dashboard |
| --------------------------- | ----------------------------------------- | ---------------------------------- |
| `rpc.request` | `{span_name="rpc.request"}` | — (available but not paneled) |
| `rpc.process` | `{span_name="rpc.process"}` | — (available but not paneled) |
| `rpc.command.*` | `{span_name=~"rpc.command.*"}` | RPC Performance (all 4 panels) |
| `tx.process` | `{span_name="tx.process"}` | Transaction Overview (3 panels) |
| `tx.receive` | `{span_name="tx.receive"}` | Transaction Overview (2 panels) |
| `consensus.accept` | `{span_name="consensus.accept"}` | Consensus Health (Round Duration) |
| `consensus.proposal.send` | `{span_name="consensus.proposal.send"}` | Consensus Health (Proposals Rate) |
| `consensus.ledger_close` | `{span_name="consensus.ledger_close"}` | Consensus Health (Close Duration) |
| `consensus.validation.send` | `{span_name="consensus.validation.send"}` | Consensus Health (Validation Rate) |
## Troubleshooting
### No traces appearing in Jaeger
1. Check rippled logs for `Telemetry starting` message
2. Verify `enabled=1` in the `[telemetry]` config section
3. Test collector connectivity: `curl -v http://localhost:4318/v1/traces`
4. Check collector logs: `docker compose logs otel-collector`
### High memory usage
- Reduce `sampling_ratio` (e.g., `0.1` for 10% sampling)
- Reduce `max_queue_size` and `batch_size`
- Disable high-volume trace categories: `trace_peer=0`
### Collector connection failures
- Verify endpoint URL matches collector address
- Check firewall rules for ports 4317/4318
- If using TLS, verify certificate path with `tls_ca_cert`
## Performance Tuning
| Scenario | Recommendation |
| ------------------------ | ------------------------------------------------- |
| Production mainnet | `sampling_ratio=0.01`, `trace_peer=0` |
| Testnet/devnet | `sampling_ratio=1.0` (full tracing) |
| Debugging specific issue | `sampling_ratio=1.0` temporarily |
| High-throughput node | Increase `batch_size=1024`, `max_queue_size=4096` |
## Disabling Telemetry
Set `enabled=0` in config (runtime disable) or build without the flag:
```bash
cmake --preset default -Dtelemetry=OFF
```
When telemetry is compiled out, all trace macros expand to no-ops with zero overhead.

View File

@@ -1,73 +0,0 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <chrono>
#include <cstdint>
#include <string_view>
namespace xrpl {
// cSpell:ignore ptmalloc
// -----------------------------------------------------------------------------
// Allocator interaction note:
// - This facility invokes glibc's malloc_trim(0) on Linux/glibc to request that
// ptmalloc return free heap pages to the OS.
// - If an alternative allocator (e.g. jemalloc or tcmalloc) is linked or
// preloaded (LD_PRELOAD), calling glibc's malloc_trim typically has no effect
// on the *active* heap. The call is harmless but may not reclaim memory
// because those allocators manage their own arenas.
// - Only glibc sbrk/arena space is eligible for trimming; large mmap-backed
// allocations are usually returned to the OS on free regardless of trimming.
// - Call at known reclamation points (e.g., after cache sweeps / online delete)
// and consider rate limiting to avoid churn.
// -----------------------------------------------------------------------------
struct MallocTrimReport
{
bool supported{false};
int trimResult{-1};
std::int64_t rssBeforeKB{-1};
std::int64_t rssAfterKB{-1};
std::chrono::microseconds durationUs{-1};
std::int64_t minfltDelta{-1};
std::int64_t majfltDelta{-1};
[[nodiscard]] std::int64_t
deltaKB() const noexcept
{
if (rssBeforeKB < 0 || rssAfterKB < 0)
return 0;
return rssAfterKB - rssBeforeKB;
}
};
/**
* @brief Attempt to return freed memory to the operating system.
*
* On Linux with glibc malloc, this issues ::malloc_trim(0), which may release
* free space from ptmalloc arenas back to the kernel. On other platforms, or if
* a different allocator is in use, this function is a no-op and the report will
* indicate that trimming is unsupported or had no effect.
*
* @param tag Identifier for logging/debugging purposes.
* @param journal Journal for diagnostic logging.
* @return Report containing before/after metrics and the trim result.
*
* @note If an alternative allocator (jemalloc/tcmalloc) is linked or preloaded,
* calling glibc's malloc_trim may have no effect on the active heap. The
* call is harmless but typically does not reclaim memory under those
* allocators.
*
* @note Only memory served from glibc's sbrk/arena heaps is eligible for trim.
* Large allocations satisfied via mmap are usually returned on free
* independently of trimming.
*
* @note Intended for use after operations that free significant memory (e.g.,
* cache sweeps, ledger cleanup, online delete). Consider rate limiting.
*/
MallocTrimReport
mallocTrim(std::string_view tag, beast::Journal journal);
} // namespace xrpl

View File

@@ -1,7 +1,6 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
namespace beast {
@@ -27,14 +26,14 @@ public:
SemanticVersion();
SemanticVersion(std::string_view version);
SemanticVersion(std::string const& version);
/** Parse a semantic version string.
The parsing is as strict as possible.
@return `true` if the string was parsed.
*/
bool
parse(std::string_view input);
parse(std::string const& input);
/** Produce a string from semantic version components. */
std::string

View File

@@ -19,6 +19,9 @@ class Manager;
namespace perf {
class PerfLog;
}
namespace telemetry {
class Telemetry;
}
// This is temporary until we migrate all code to use ServiceRegistry.
class Application;
@@ -205,6 +208,9 @@ public:
virtual perf::PerfLog&
getPerfLog() = 0;
virtual telemetry::Telemetry&
getTelemetry() = 0;
// Configuration and state
virtual bool
isStopping() const = 0;

View File

@@ -1,13 +0,0 @@
#pragma once
#include <string>
namespace xrpl::git {
std::string const&
getCommitHash();
std::string const&
getBuildBranch();
} // namespace xrpl::git

View File

@@ -23,13 +23,13 @@ public:
static constexpr size_t initialBufferSize = kilobytes(256);
RawStateTable()
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
initialBufferSize)}
, items_{monotonic_resource_.get()} {};
RawStateTable(RawStateTable const& rhs)
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
initialBufferSize)}
, items_{rhs.items_, monotonic_resource_.get()}
, dropsDestroyed_{rhs.dropsDestroyed_} {};

View File

@@ -77,16 +77,16 @@ public:
If the object is not found or an error is encountered, the
result will indicate the condition.
@note This will be called concurrently.
@param hash The hash of the object.
@param key A pointer to the key data.
@param pObject [out] The created object if successful.
@return The result of the operation.
*/
virtual Status
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) = 0;
fetch(void const* key, std::shared_ptr<NodeObject>* pObject) = 0;
/** Fetch a batch synchronously. */
virtual std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
fetchBatch(std::vector<uint256> const& hashes) = 0;
fetchBatch(std::vector<uint256 const*> const& hashes) = 0;
/** Store a single object.
Depending on the implementation this may happen immediately

View File

@@ -85,6 +85,19 @@ message TMPublicKey {
// If you want to send an amount that is greater than any single address of yours
// you must first combine coins from one address to another.
// Trace context for OpenTelemetry distributed tracing across nodes.
// Uses W3C Trace Context format internally.
message TraceContext {
optional bytes trace_id = 1; // 16-byte trace identifier
optional bytes span_id = 2; // 8-byte parent span identifier
optional uint32 trace_flags = 3; // bit 0 = sampled
// TODO: trace_state is reserved for W3C tracestate vendor-specific
// key-value pairs but is not yet read or written by
// TraceContextPropagator. Wire it when cross-vendor trace
// propagation is needed.
optional string trace_state = 4; // W3C tracestate header value
}
enum TransactionStatus {
tsNEW = 1; // origin node did/could not validate
tsCURRENT = 2; // scheduled to go in this ledger
@@ -101,6 +114,9 @@ message TMTransaction {
required TransactionStatus status = 2;
optional uint64 receiveTimestamp = 3;
optional bool deferred = 4; // not applied to open ledger
// Optional trace context for OpenTelemetry distributed tracing
optional TraceContext trace_context = 1001;
}
message TMTransactions {
@@ -149,6 +165,9 @@ message TMProposeSet {
// Number of hops traveled
optional uint32 hops = 12 [deprecated = true];
// Optional trace context for OpenTelemetry distributed tracing
optional TraceContext trace_context = 1001;
}
enum TxSetStatus {
@@ -194,6 +213,9 @@ message TMValidation {
// Number of hops traveled
optional uint32 hops = 3 [deprecated = true];
// Optional trace context for OpenTelemetry distributed tracing
optional TraceContext trace_context = 1001;
}
// An array of Endpoint messages

View File

@@ -49,7 +49,7 @@ getFullVersionString();
@return the encoded version in a 64-bit integer
*/
std::uint64_t
encodeSoftwareVersion(std::string_view versionStr);
encodeSoftwareVersion(char const* const versionStr);
/** Returns this server's version packed in a 64-bit integer. */
std::uint64_t

View File

@@ -30,11 +30,9 @@ public:
Item(
char const* name,
KeyType type,
std::vector<SOElement> uniqueFields,
std::vector<SOElement> commonFields)
: soTemplate_(std::move(uniqueFields), std::move(commonFields))
, name_(name)
, type_(type)
std::initializer_list<SOElement> uniqueFields,
std::initializer_list<SOElement> commonFields)
: soTemplate_(uniqueFields, commonFields), name_(name), type_(type)
{
// Verify that KeyType is appropriate.
static_assert(
@@ -144,16 +142,16 @@ protected:
@param name The name of this format.
@param type The type of this format.
@param uniqueFields A std::vector of unique fields
@param commonFields A std::vector of common fields
@param uniqueFields An std::initializer_list of unique fields
@param commonFields An std::initializer_list of common fields
@return The created format.
*/
Item const&
add(char const* name,
KeyType type,
std::vector<SOElement> uniqueFields,
std::vector<SOElement> commonFields = {})
std::initializer_list<SOElement> uniqueFields,
std::initializer_list<SOElement> commonFields = {})
{
if (auto const item = findByType(type))
{
@@ -162,7 +160,7 @@ protected:
item->getName());
}
formats_.emplace_front(name, type, std::move(uniqueFields), std::move(commonFields));
formats_.emplace_front(name, type, uniqueFields, commonFields);
Item const& item{formats_.front()};
names_[name] = &item;

View File

@@ -2,34 +2,36 @@
#include <xrpl/protocol/KnownFormats.h>
#include <map>
#include <string>
#include <vector>
namespace xrpl {
/** Identifiers for on-ledger objects.
Each ledger object requires a unique type identifier, which is stored within the object itself;
this makes it possible to iterate the entire ledger and determine each object's type and verify
that the object you retrieved from a given hash matches the expected type.
Each ledger object requires a unique type identifier, which is stored
within the object itself; this makes it possible to iterate the entire
ledger and determine each object's type and verify that the object you
retrieved from a given hash matches the expected type.
@warning Since these values are stored inside objects stored on the ledger they are part of the
protocol.
**Changing them should be avoided because without special handling, this will result in a hard
@warning Since these values are stored inside objects stored on the ledger
they are part of the protocol. **Changing them should be avoided
because without special handling, this will result in a hard
fork.**
@note Values outside this range may be used internally by the code for various purposes, but
attempting to use such values to identify on-ledger objects will result in an invariant failure.
@note Values outside this range may be used internally by the code for
various purposes, but attempting to use such values to identify
on-ledger objects will results in an invariant failure.
@note When retiring types, the specific values should not be removed but should be marked as
[[deprecated]]. This is to avoid accidental reuse of identifiers.
@note When retiring types, the specific values should not be removed but
should be marked as [[deprecated]]. This is to avoid accidental
reuse of identifiers.
@todo The C++ language does not enable checking for duplicate values here.
If it becomes possible then we should do this.
@todo The C++ language does not enable checking for duplicate values
here. If it becomes possible then we should do this.
@ingroup protocol
*/
enum LedgerEntryType : std::uint16_t {
// clang-format off
enum LedgerEntryType : std::uint16_t
{
#pragma push_macro("LEDGER_ENTRY")
#undef LEDGER_ENTRY
@@ -44,10 +46,12 @@ enum LedgerEntryType : std::uint16_t {
//---------------------------------------------------------------------------
/** A special type, matching any ledger entry type.
The value does not represent a concrete type, but rather is used in contexts where the
specific type of a ledger object is unimportant, unknown or unavailable.
The value does not represent a concrete type, but rather is used in
contexts where the specific type of a ledger object is unimportant,
unknown or unavailable.
Objects with this special type cannot be created or stored on the ledger.
Objects with this special type cannot be created or stored on the
ledger.
\sa keylet::unchecked
*/
@@ -55,11 +59,12 @@ enum LedgerEntryType : std::uint16_t {
/** A special type, matching any ledger type except directory nodes.
The value does not represent a concrete type, but rather is used in contexts where the
ledger object must not be a directory node but its specific type is otherwise unimportant,
unknown or unavailable.
The value does not represent a concrete type, but rather is used in
contexts where the ledger object must not be a directory node but
its specific type is otherwise unimportant, unknown or unavailable.
Objects with this special type cannot be created or stored on the ledger.
Objects with this special type cannot be created or stored on the
ledger.
\sa keylet::child
*/
@@ -88,188 +93,104 @@ enum LedgerEntryType : std::uint16_t {
Support for this type of object was never implemented.
No objects of this type were ever created.
*/
ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] =
0x0067,
ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = 0x0067,
};
/** Ledger object flags.
These flags are specified in ledger objects and modify their behavior.
@warning Ledger object flags form part of the protocol.
**Changing them should be avoided because without special handling, this will result in a hard
fork.**
@ingroup protocol
*/
#pragma push_macro("XMACRO")
#pragma push_macro("TO_VALUE")
#pragma push_macro("VALUE_TO_MAP")
#pragma push_macro("NULL_NAME")
#pragma push_macro("TO_MAP")
#pragma push_macro("ALL_LEDGER_FLAGS")
#undef XMACRO
#undef TO_VALUE
#undef VALUE_TO_MAP
#undef NULL_NAME
#undef TO_MAP
#undef ALL_LEDGER_FLAGS
// clang-format off
#define XMACRO(LEDGER_OBJECT, LSF_FLAG, LSF_FLAG2) \
LEDGER_OBJECT(AccountRoot, \
LSF_FLAG(lsfPasswordSpent, 0x00010000) /* True, if password set fee is spent. */ \
LSF_FLAG(lsfRequireDestTag, 0x00020000) /* True, to require a DestinationTag for payments. */ \
LSF_FLAG(lsfRequireAuth, 0x00040000) /* True, to require a authorization to hold IOUs. */ \
LSF_FLAG(lsfDisallowXRP, 0x00080000) /* True, to disallow sending XRP. */ \
LSF_FLAG(lsfDisableMaster, 0x00100000) /* True, force regular key */ \
LSF_FLAG(lsfNoFreeze, 0x00200000) /* True, cannot freeze ripple states */ \
LSF_FLAG(lsfGlobalFreeze, 0x00400000) /* True, all assets frozen */ \
LSF_FLAG(lsfDefaultRipple, 0x00800000) /* True, incoming trust lines allow rippling by default */ \
LSF_FLAG(lsfDepositAuth, 0x01000000) /* True, all deposits require authorization */ \
LSF_FLAG(lsfDisallowIncomingNFTokenOffer, 0x04000000) /* True, reject new incoming NFT offers */ \
LSF_FLAG(lsfDisallowIncomingCheck, 0x08000000) /* True, reject new checks */ \
LSF_FLAG(lsfDisallowIncomingPayChan, 0x10000000) /* True, reject new paychans */ \
LSF_FLAG(lsfDisallowIncomingTrustline, 0x20000000) /* True, reject new trustlines (only if no issued assets) */ \
LSF_FLAG(lsfAllowTrustLineLocking, 0x40000000) /* True, enable trustline locking */ \
LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000)) /* True, enable clawback */ \
\
LEDGER_OBJECT(Offer, \
LSF_FLAG(lsfPassive, 0x00010000) \
LSF_FLAG(lsfSell, 0x00020000) /* True, offer was placed as a sell. */ \
LSF_FLAG(lsfHybrid, 0x00040000)) /* True, offer is hybrid. */ \
\
LEDGER_OBJECT(RippleState, \
LSF_FLAG(lsfLowReserve, 0x00010000) /* True, if entry counts toward reserve. */ \
LSF_FLAG(lsfHighReserve, 0x00020000) \
LSF_FLAG(lsfLowAuth, 0x00040000) \
LSF_FLAG(lsfHighAuth, 0x00080000) \
LSF_FLAG(lsfLowNoRipple, 0x00100000) \
LSF_FLAG(lsfHighNoRipple, 0x00200000) \
LSF_FLAG(lsfLowFreeze, 0x00400000) /* True, low side has set freeze flag */ \
LSF_FLAG(lsfHighFreeze, 0x00800000) /* True, high side has set freeze flag */ \
LSF_FLAG(lsfAMMNode, 0x01000000) /* True, trust line to AMM. */ \
/* Used by client apps to identify payments via AMM. */ \
LSF_FLAG(lsfLowDeepFreeze, 0x02000000) /* True, low side has set deep freeze flag */ \
LSF_FLAG(lsfHighDeepFreeze, 0x04000000)) /* True, high side has set deep freeze flag */ \
\
LEDGER_OBJECT(SignerList, \
LSF_FLAG(lsfOneOwnerCount, 0x00010000)) /* True, uses only one OwnerCount */ \
\
LEDGER_OBJECT(DirNode, \
LSF_FLAG(lsfNFTokenBuyOffers, 0x00000001) \
LSF_FLAG(lsfNFTokenSellOffers, 0x00000002)) \
\
LEDGER_OBJECT(NFTokenOffer, \
LSF_FLAG(lsfSellNFToken, 0x00000001)) \
\
LEDGER_OBJECT(MPTokenIssuance, \
LSF_FLAG(lsfMPTLocked, 0x00000001) /* Also used in ltMPTOKEN */ \
LSF_FLAG(lsfMPTCanLock, 0x00000002) \
LSF_FLAG(lsfMPTRequireAuth, 0x00000004) \
LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \
\
LEDGER_OBJECT(MPTokenIssuanceMutable, \
LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \
LSF_FLAG(lsmfMPTCanMutateRequireAuth, 0x00000004) \
LSF_FLAG(lsmfMPTCanMutateCanEscrow, 0x00000008) \
LSF_FLAG(lsmfMPTCanMutateCanTrade, 0x00000010) \
LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \
LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \
LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \
\
LEDGER_OBJECT(MPToken, \
LSF_FLAG2(lsfMPTLocked, 0x00000001) \
LSF_FLAG(lsfMPTAuthorized, 0x00000002)) \
\
LEDGER_OBJECT(Credential, \
LSF_FLAG(lsfAccepted, 0x00010000)) \
\
LEDGER_OBJECT(Vault, \
LSF_FLAG(lsfVaultPrivate, 0x00010000)) \
\
LEDGER_OBJECT(Loan, \
LSF_FLAG(lsfLoanDefault, 0x00010000) \
LSF_FLAG(lsfLoanImpaired, 0x00020000) \
LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */
/**
@ingroup protocol
*/
enum LedgerSpecificFlags {
// ltACCOUNT_ROOT
lsfPasswordSpent = 0x00010000, // True, if password set fee is spent.
lsfRequireDestTag =
0x00020000, // True, to require a DestinationTag for payments.
lsfRequireAuth =
0x00040000, // True, to require a authorization to hold IOUs.
lsfDisallowXRP = 0x00080000, // True, to disallow sending XRP.
lsfDisableMaster = 0x00100000, // True, force regular key
lsfNoFreeze = 0x00200000, // True, cannot freeze ripple states
lsfGlobalFreeze = 0x00400000, // True, all assets frozen
lsfDefaultRipple =
0x00800000, // True, incoming trust lines allow rippling by default
lsfDepositAuth = 0x01000000, // True, all deposits require authorization
/* // reserved for Hooks amendment
lsfTshCollect = 0x02000000, // True, allow TSH collect-calls to acc hooks
*/
lsfDisallowIncomingNFTokenOffer =
0x04000000, // True, reject new incoming NFT offers
lsfDisallowIncomingCheck =
0x08000000, // True, reject new checks
lsfDisallowIncomingPayChan =
0x10000000, // True, reject new paychans
lsfDisallowIncomingTrustline =
0x20000000, // True, reject new trustlines (only if no issued assets)
lsfAllowTrustLineLocking =
0x40000000, // True, enable trustline locking
lsfAllowTrustLineClawback =
0x80000000, // True, enable clawback
// clang-format on
// ltOFFER
lsfPassive = 0x00010000,
lsfSell = 0x00020000, // True, offer was placed as a sell.
lsfHybrid = 0x00040000, // True, offer is hybrid.
// Create all the flag values as an enum.
//
// example:
// enum LedgerSpecificFlags {
// lsfPasswordSpent = 0x00010000,
// lsfRequireDestTag = 0x00020000,
// ...
// };
#define TO_VALUE(name, value) name = value,
#define NULL_NAME(name, values) values
#define NULL_OUTPUT(name, value)
enum LedgerSpecificFlags : std::uint32_t { XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT) };
// ltRIPPLE_STATE
lsfLowReserve = 0x00010000, // True, if entry counts toward reserve.
lsfHighReserve = 0x00020000,
lsfLowAuth = 0x00040000,
lsfHighAuth = 0x00080000,
lsfLowNoRipple = 0x00100000,
lsfHighNoRipple = 0x00200000,
lsfLowFreeze = 0x00400000, // True, low side has set freeze flag
lsfHighFreeze = 0x00800000, // True, high side has set freeze flag
lsfLowDeepFreeze = 0x02000000, // True, low side has set deep freeze flag
lsfHighDeepFreeze = 0x04000000, // True, high side has set deep freeze flag
lsfAMMNode = 0x01000000, // True, trust line to AMM. Used by client
// apps to identify payments via AMM.
// Create getter functions for each set of flags using Meyer's singleton pattern.
// This avoids static initialization order fiasco while still providing efficient access.
// This is used below in `getAllLedgerFlags()` to generate the server_definitions RPC output.
//
// example:
// inline LedgerFlagMap const& getAccountRootFlags() {
// static LedgerFlagMap const flags = {
// {"lsfPasswordSpent", 0x00010000},
// {"lsfRequireDestTag", 0x00020000},
// ...};
// return flags;
// }
using LedgerFlagMap = std::map<std::string, std::uint32_t>;
#define VALUE_TO_MAP(name, value) {#name, value},
#define TO_MAP(name, values) \
inline LedgerFlagMap const& get##name##Flags() \
{ \
static LedgerFlagMap const flags = {values}; \
return flags; \
}
XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP)
// ltSIGNER_LIST
lsfOneOwnerCount = 0x00010000, // True, uses only one OwnerCount
// Create a getter function for all ledger flag maps using Meyer's singleton pattern.
// This is used to generate the server_definitions RPC output.
//
// example:
// inline std::vector<std::pair<std::string, LedgerFlagMap>> const& getAllLedgerFlags() {
// static std::vector<std::pair<std::string, LedgerFlagMap>> const flags = {
// {"AccountRoot", getAccountRootFlags()},
// ...};
// return flags;
// }
#define ALL_LEDGER_FLAGS(name, values) {#name, get##name##Flags()},
inline std::vector<std::pair<std::string, LedgerFlagMap>> const&
getAllLedgerFlags()
{
static std::vector<std::pair<std::string, LedgerFlagMap>> const flags = {
XMACRO(ALL_LEDGER_FLAGS, NULL_OUTPUT, NULL_OUTPUT)};
return flags;
}
// ltDIR_NODE
lsfNFTokenBuyOffers = 0x00000001,
lsfNFTokenSellOffers = 0x00000002,
#undef XMACRO
#undef TO_VALUE
#undef VALUE_TO_MAP
#undef NULL_NAME
#undef NULL_OUTPUT
#undef TO_MAP
#undef ALL_LEDGER_FLAGS
// ltNFTOKEN_OFFER
lsfSellNFToken = 0x00000001,
#pragma pop_macro("XMACRO")
#pragma pop_macro("TO_VALUE")
#pragma pop_macro("VALUE_TO_MAP")
#pragma pop_macro("NULL_NAME")
#pragma pop_macro("TO_MAP")
#pragma pop_macro("ALL_LEDGER_FLAGS")
// ltMPTOKEN_ISSUANCE
lsfMPTLocked = 0x00000001, // Also used in ltMPTOKEN
lsfMPTCanLock = 0x00000002,
lsfMPTRequireAuth = 0x00000004,
lsfMPTCanEscrow = 0x00000008,
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
lsmfMPTCanMutateCanLock = 0x00000002,
lsmfMPTCanMutateRequireAuth = 0x00000004,
lsmfMPTCanMutateCanEscrow = 0x00000008,
lsmfMPTCanMutateCanTrade = 0x00000010,
lsmfMPTCanMutateCanTransfer = 0x00000020,
lsmfMPTCanMutateCanClawback = 0x00000040,
lsmfMPTCanMutateMetadata = 0x00010000,
lsmfMPTCanMutateTransferFee = 0x00020000,
// ltMPTOKEN
lsfMPTAuthorized = 0x00000002,
// ltCREDENTIAL
lsfAccepted = 0x00010000,
// ltVAULT
lsfVaultPrivate = 0x00010000,
// ltLOAN
lsfLoanDefault = 0x00010000,
lsfLoanImpaired = 0x00020000,
lsfLoanOverpayment = 0x00040000, // True, loan allows overpayments
};
//------------------------------------------------------------------------------
@@ -286,10 +207,6 @@ private:
public:
static LedgerFormats const&
getInstance();
// Fields shared by all ledger entry formats:
static std::vector<SOElement> const&
getCommonFields();
};
} // namespace xrpl

View File

@@ -6,7 +6,6 @@
#include <functional>
#include <initializer_list>
#include <stdexcept>
#include <vector>
namespace xrpl {
@@ -98,12 +97,8 @@ public:
operator=(SOTemplate&& other) = default;
/** Create a template populated with all fields.
After creating the template fields cannot be added, modified, or removed.
*/
SOTemplate(std::vector<SOElement> uniqueFields, std::vector<SOElement> commonFields = {});
/** Create a template populated with all fields.
Note: Defers to the vector constructor above.
After creating the template fields cannot be
added, modified, or removed.
*/
SOTemplate(
std::initializer_list<SOElement> uniqueFields,

View File

@@ -3,444 +3,294 @@
#include <xrpl/protocol/LedgerFormats.h>
#include <cstdint>
#include <map>
#include <string>
#include <utility>
#include <vector>
namespace xrpl {
/** Transaction flags.
These flags are specified in a transaction's 'Flags' field and modify
the behavior of that transaction.
These flags are specified in a transaction's 'Flags' field and modify the
behavior of that transaction.
There are two types of flags:
(1) Universal flags: these are flags which apply to, and are interpreted the same way by,
all transactions, except, perhaps, to special pseudo-transactions.
(1) Universal flags: these are flags which apply to, and are interpreted
the same way by, all transactions, except, perhaps,
to special pseudo-transactions.
(2) Tx-Specific flags: these are flags which are interpreted according to the type of the
transaction being executed. That is, the same numerical flag value may have different
effects, depending on the transaction being executed.
(2) Tx-Specific flags: these are flags which are interpreted according
to the type of the transaction being executed.
That is, the same numerical flag value may have
different effects, depending on the transaction
being executed.
@note The universal transaction flags occupy the high-order 8 bits.
The tx-specific flags occupy the remaining 24 bits.
@note The universal transaction flags occupy the high-order 8 bits. The
tx-specific flags occupy the remaining 24 bits.
@warning Transaction flags form part of the protocol.
**Changing them should be avoided because without special handling, this will result in
a hard fork.**
@warning Transaction flags form part of the protocol. **Changing them
should be avoided because without special handling, this will
result in a hard fork.**
@ingroup protocol
*/
using FlagValue = std::uint32_t;
// Universal Transaction flags:
inline constexpr FlagValue tfFullyCanonicalSig = 0x80000000;
inline constexpr FlagValue tfInnerBatchTxn = 0x40000000;
inline constexpr FlagValue tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn;
inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
#pragma push_macro("XMACRO")
#pragma push_macro("TO_VALUE")
#pragma push_macro("VALUE_TO_MAP")
#pragma push_macro("NULL_NAME")
#pragma push_macro("NULL_OUTPUT")
#pragma push_macro("TO_MAP")
#pragma push_macro("TO_MASK")
#pragma push_macro("VALUE_TO_MASK")
#pragma push_macro("ALL_TX_FLAGS")
#pragma push_macro("NULL_MASK_ADJ")
#pragma push_macro("MASK_ADJ_TO_MASK")
#undef XMACRO
#undef TO_VALUE
#undef VALUE_TO_MAP
#undef NULL_NAME
#undef NULL_OUTPUT
#undef TO_MAP
#undef TO_MASK
#undef VALUE_TO_MASK
#undef NULL_MASK_ADJ
#undef MASK_ADJ_TO_MASK
// Formatting equals sign aligned 4 spaces after longest prefix, except for
// wrapped lines
// clang-format off
#undef ALL_TX_FLAGS
// Universal Transaction flags:
constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000;
constexpr std::uint32_t tfInnerBatchTxn = 0x40000000;
constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn;
constexpr std::uint32_t tfUniversalMask = ~tfUniversal;
// XMACRO parameters:
// - TRANSACTION: handles the transaction name, its flags, and mask adjustment
// - TF_FLAG: defines a new flag constant
// - TF_FLAG2: references an existing flag constant (no new definition)
// - MASK_ADJ: specifies flags to add back to the mask (making them invalid for this tx type)
//
// Note: MASK_ADJ is used when a universal flag should be invalid for a specific transaction.
// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) because the outer Batch transaction
// must not have tfInnerBatchTxn set (only inner transactions should have it).
//
// TODO: Consider rewriting this using reflection in C++26 or later. Alternatively this could be a DSL processed by a script at build time.
#define XMACRO(TRANSACTION, TF_FLAG, TF_FLAG2, MASK_ADJ) \
TRANSACTION(AccountSet, \
TF_FLAG(tfRequireDestTag, 0x00010000) \
TF_FLAG(tfOptionalDestTag, 0x00020000) \
TF_FLAG(tfRequireAuth, 0x00040000) \
TF_FLAG(tfOptionalAuth, 0x00080000) \
TF_FLAG(tfDisallowXRP, 0x00100000) \
TF_FLAG(tfAllowXRP, 0x00200000), \
MASK_ADJ(0)) \
\
TRANSACTION(OfferCreate, \
TF_FLAG(tfPassive, 0x00010000) \
TF_FLAG(tfImmediateOrCancel, 0x00020000) \
TF_FLAG(tfFillOrKill, 0x00040000) \
TF_FLAG(tfSell, 0x00080000) \
TF_FLAG(tfHybrid, 0x00100000), \
MASK_ADJ(0)) \
\
TRANSACTION(Payment, \
TF_FLAG(tfNoRippleDirect, 0x00010000) \
TF_FLAG(tfPartialPayment, 0x00020000) \
TF_FLAG(tfLimitQuality, 0x00040000), \
MASK_ADJ(0)) \
\
TRANSACTION(TrustSet, \
TF_FLAG(tfSetfAuth, 0x00010000) \
TF_FLAG(tfSetNoRipple, 0x00020000) \
TF_FLAG(tfClearNoRipple, 0x00040000) \
TF_FLAG(tfSetFreeze, 0x00100000) \
TF_FLAG(tfClearFreeze, 0x00200000) \
TF_FLAG(tfSetDeepFreeze, 0x00400000) \
TF_FLAG(tfClearDeepFreeze, 0x00800000), \
MASK_ADJ(0)) \
\
TRANSACTION(EnableAmendment, \
TF_FLAG(tfGotMajority, 0x00010000) \
TF_FLAG(tfLostMajority, 0x00020000), \
MASK_ADJ(0)) \
\
TRANSACTION(PaymentChannelClaim, \
TF_FLAG(tfRenew, 0x00010000) \
TF_FLAG(tfClose, 0x00020000), \
MASK_ADJ(0)) \
\
TRANSACTION(NFTokenMint, \
TF_FLAG(tfBurnable, 0x00000001) \
TF_FLAG(tfOnlyXRP, 0x00000002) \
/* deprecated TF_FLAG(tfTrustLine, 0x00000004) */ \
TF_FLAG(tfTransferable, 0x00000008) \
TF_FLAG(tfMutable, 0x00000010), \
MASK_ADJ(0)) \
\
TRANSACTION(MPTokenIssuanceCreate, \
/* Note: tf/lsfMPTLocked is intentionally omitted since this transaction is not allowed to modify it. */ \
TF_FLAG(tfMPTCanLock, lsfMPTCanLock) \
TF_FLAG(tfMPTRequireAuth, lsfMPTRequireAuth) \
TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \
TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \
TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \
MASK_ADJ(0)) \
\
TRANSACTION(MPTokenAuthorize, \
TF_FLAG(tfMPTUnauthorize, 0x00000001), \
MASK_ADJ(0)) \
\
TRANSACTION(MPTokenIssuanceSet, \
TF_FLAG(tfMPTLock, 0x00000001) \
TF_FLAG(tfMPTUnlock, 0x00000002), \
MASK_ADJ(0)) \
\
TRANSACTION(NFTokenCreateOffer, \
TF_FLAG(tfSellNFToken, 0x00000001), \
MASK_ADJ(0)) \
\
TRANSACTION(AMMDeposit, \
TF_FLAG(tfLPToken, 0x00010000) \
TF_FLAG(tfSingleAsset, 0x00080000) \
TF_FLAG(tfTwoAsset, 0x00100000) \
TF_FLAG(tfOneAssetLPToken, 0x00200000) \
TF_FLAG(tfLimitLPToken, 0x00400000) \
TF_FLAG(tfTwoAssetIfEmpty, 0x00800000), \
MASK_ADJ(0)) \
\
TRANSACTION(AMMWithdraw, \
TF_FLAG2(tfLPToken, 0x00010000) \
TF_FLAG(tfWithdrawAll, 0x00020000) \
TF_FLAG(tfOneAssetWithdrawAll, 0x00040000) \
TF_FLAG2(tfSingleAsset, 0x00080000) \
TF_FLAG2(tfTwoAsset, 0x00100000) \
TF_FLAG2(tfOneAssetLPToken, 0x00200000) \
TF_FLAG2(tfLimitLPToken, 0x00400000), \
MASK_ADJ(0)) \
\
TRANSACTION(AMMClawback, \
TF_FLAG(tfClawTwoAssets, 0x00000001), \
MASK_ADJ(0)) \
\
TRANSACTION(XChainModifyBridge, \
TF_FLAG(tfClearAccountCreateAmount, 0x00010000), \
MASK_ADJ(0)) \
\
TRANSACTION(VaultCreate, \
TF_FLAG(tfVaultPrivate, lsfVaultPrivate) \
TF_FLAG(tfVaultShareNonTransferable, 0x00020000), \
MASK_ADJ(0)) \
\
TRANSACTION(Batch, \
TF_FLAG(tfAllOrNothing, 0x00010000) \
TF_FLAG(tfOnlyOne, 0x00020000) \
TF_FLAG(tfUntilFailure, 0x00040000) \
TF_FLAG(tfIndependent, 0x00080000), \
MASK_ADJ(tfInnerBatchTxn)) /* Batch must reject tfInnerBatchTxn - only inner transactions should have this flag */ \
\
TRANSACTION(LoanSet, /* True indicates the loan supports overpayments */ \
TF_FLAG(tfLoanOverpayment, 0x00010000), \
MASK_ADJ(0)) \
\
TRANSACTION(LoanPay, /* True indicates any excess in this payment can be used as an overpayment. */ \
/* False: no overpayments will be taken. */ \
TF_FLAG2(tfLoanOverpayment, 0x00010000) \
TF_FLAG(tfLoanFullPayment, 0x00020000) /* True indicates that the payment is an early full payment. */ \
/* It must pay the entire loan including close interest and fees, or it will fail. */ \
/* False: Not a full payment. */ \
TF_FLAG(tfLoanLatePayment, 0x00040000), /* True indicates that the payment is late, and includes late interest and fees. */ \
/* If the loan is not late, it will fail. */ \
/* False: not a late payment. If the current payment is overdue, the transaction will fail.*/ \
MASK_ADJ(0)) \
\
TRANSACTION(LoanManage, \
TF_FLAG(tfLoanDefault, 0x00010000) \
TF_FLAG(tfLoanImpair, 0x00020000) \
TF_FLAG(tfLoanUnimpair, 0x00040000), \
MASK_ADJ(0))
// AccountSet flags:
constexpr std::uint32_t tfRequireDestTag = 0x00010000;
constexpr std::uint32_t tfOptionalDestTag = 0x00020000;
constexpr std::uint32_t tfRequireAuth = 0x00040000;
constexpr std::uint32_t tfOptionalAuth = 0x00080000;
constexpr std::uint32_t tfDisallowXRP = 0x00100000;
constexpr std::uint32_t tfAllowXRP = 0x00200000;
constexpr std::uint32_t tfAccountSetMask =
~(tfUniversal | tfRequireDestTag | tfOptionalDestTag | tfRequireAuth |
tfOptionalAuth | tfDisallowXRP | tfAllowXRP);
// clang-format on
// AccountSet SetFlag/ClearFlag values
constexpr std::uint32_t asfRequireDest = 1;
constexpr std::uint32_t asfRequireAuth = 2;
constexpr std::uint32_t asfDisallowXRP = 3;
constexpr std::uint32_t asfDisableMaster = 4;
constexpr std::uint32_t asfAccountTxnID = 5;
constexpr std::uint32_t asfNoFreeze = 6;
constexpr std::uint32_t asfGlobalFreeze = 7;
constexpr std::uint32_t asfDefaultRipple = 8;
constexpr std::uint32_t asfDepositAuth = 9;
constexpr std::uint32_t asfAuthorizedNFTokenMinter = 10;
/* // reserved for Hooks amendment
constexpr std::uint32_t asfTshCollect = 11;
*/
constexpr std::uint32_t asfDisallowIncomingNFTokenOffer = 12;
constexpr std::uint32_t asfDisallowIncomingCheck = 13;
constexpr std::uint32_t asfDisallowIncomingPayChan = 14;
constexpr std::uint32_t asfDisallowIncomingTrustline = 15;
constexpr std::uint32_t asfAllowTrustLineClawback = 16;
constexpr std::uint32_t asfAllowTrustLineLocking = 17;
// Create all the flag values.
//
// example:
// inline constexpr FlagValue tfAccountSetRequireDestTag = 0x00010000;
#define TO_VALUE(name, value) inline constexpr FlagValue name = value;
#define NULL_NAME(name, values, maskAdj) values
#define NULL_OUTPUT(name, value)
#define NULL_MASK_ADJ(value)
XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT, NULL_MASK_ADJ)
// OfferCreate flags:
constexpr std::uint32_t tfPassive = 0x00010000;
constexpr std::uint32_t tfImmediateOrCancel = 0x00020000;
constexpr std::uint32_t tfFillOrKill = 0x00040000;
constexpr std::uint32_t tfSell = 0x00080000;
constexpr std::uint32_t tfHybrid = 0x00100000;
constexpr std::uint32_t tfOfferCreateMask =
~(tfUniversal | tfPassive | tfImmediateOrCancel | tfFillOrKill | tfSell | tfHybrid);
// Create masks for each transaction type that has flags.
//
// example:
// inline constexpr FlagValue tfAccountSetMask = ~(tfUniversal | tfRequireDestTag |
// tfOptionalDestTag | tfRequireAuth | tfOptionalAuth | tfDisallowXRP | tfAllowXRP);
//
// The mask adjustment (maskAdj) allows adding flags back to the mask, making them invalid.
// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) to reject tfInnerBatchTxn on outer Batch.
#define TO_MASK(name, values, maskAdj) \
inline constexpr FlagValue tf##name##Mask = ~(tfUniversal values) | maskAdj;
#define VALUE_TO_MASK(name, value) | name
#define MASK_ADJ_TO_MASK(value) value
XMACRO(TO_MASK, VALUE_TO_MASK, VALUE_TO_MASK, MASK_ADJ_TO_MASK)
// Payment flags:
constexpr std::uint32_t tfNoRippleDirect = 0x00010000;
constexpr std::uint32_t tfPartialPayment = 0x00020000;
constexpr std::uint32_t tfLimitQuality = 0x00040000;
constexpr std::uint32_t tfPaymentMask =
~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect);
constexpr std::uint32_t tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment);
// Verify that tfBatchMask correctly rejects tfInnerBatchTxn.
// The outer Batch transaction must NOT have tfInnerBatchTxn set; only inner transactions should
// have it.
static_assert(
(tfBatchMask & tfInnerBatchTxn) == tfInnerBatchTxn,
"tfBatchMask must include tfInnerBatchTxn to reject it on outer Batch");
// TrustSet flags:
constexpr std::uint32_t tfSetfAuth = 0x00010000;
constexpr std::uint32_t tfSetNoRipple = 0x00020000;
constexpr std::uint32_t tfClearNoRipple = 0x00040000;
constexpr std::uint32_t tfSetFreeze = 0x00100000;
constexpr std::uint32_t tfClearFreeze = 0x00200000;
constexpr std::uint32_t tfSetDeepFreeze = 0x00400000;
constexpr std::uint32_t tfClearDeepFreeze = 0x00800000;
constexpr std::uint32_t tfTrustSetMask =
~(tfUniversal | tfSetfAuth | tfSetNoRipple | tfClearNoRipple | tfSetFreeze |
tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze);
constexpr std::uint32_t tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze);
// Verify that other transaction masks correctly allow tfInnerBatchTxn.
// Inner transactions need tfInnerBatchTxn to be valid, so these masks must not reject it.
static_assert(
(tfPaymentMask & tfInnerBatchTxn) == 0,
"tfPaymentMask must not reject tfInnerBatchTxn");
static_assert(
(tfAccountSetMask & tfInnerBatchTxn) == 0,
"tfAccountSetMask must not reject tfInnerBatchTxn");
// EnableAmendment flags:
constexpr std::uint32_t tfGotMajority = 0x00010000;
constexpr std::uint32_t tfLostMajority = 0x00020000;
constexpr std::uint32_t tfChangeMask =
~( tfUniversal | tfGotMajority | tfLostMajority);
// Create getter functions for each set of flags using Meyer's singleton pattern.
// This avoids static initialization order fiasco while still providing efficient access.
// This is used below in `getAllTxFlags()` to generate the server_definitions RPC
// output.
//
// example:
// inline FlagMap const& getAccountSetFlags() {
// static FlagMap const flags = {
// {"tfRequireDestTag", 0x00010000},
// {"tfOptionalDestTag", 0x00020000},
// ...};
// return flags;
// }
using FlagMap = std::map<std::string, FlagValue>;
#define VALUE_TO_MAP(name, value) {#name, value},
#define TO_MAP(name, values, maskAdj) \
inline FlagMap const& get##name##Flags() \
{ \
static FlagMap const flags = {values}; \
return flags; \
}
XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP, NULL_MASK_ADJ)
// PaymentChannelClaim flags:
constexpr std::uint32_t tfRenew = 0x00010000;
constexpr std::uint32_t tfClose = 0x00020000;
constexpr std::uint32_t tfPayChanClaimMask = ~(tfUniversal | tfRenew | tfClose);
inline FlagMap const&
getUniversalFlags()
{
static FlagMap const flags = {
{"tfFullyCanonicalSig", tfFullyCanonicalSig}, {"tfInnerBatchTxn", tfInnerBatchTxn}};
return flags;
}
// NFTokenMint flags:
constexpr std::uint32_t const tfBurnable = 0x00000001;
constexpr std::uint32_t const tfOnlyXRP = 0x00000002;
constexpr std::uint32_t const tfTrustLine = 0x00000004;
constexpr std::uint32_t const tfTransferable = 0x00000008;
constexpr std::uint32_t const tfMutable = 0x00000010;
// Create a getter function for all transaction flag maps using Meyer's singleton pattern.
// This is used to generate the server_definitions RPC output.
//
// example:
// inline FlagMapPairList const& getAllTxFlags() {
// static FlagMapPairList const flags = {
// {"AccountSet", getAccountSetFlags()},
// ...};
// return flags;
// }
using FlagMapPairList = std::vector<std::pair<std::string, FlagMap>>;
#define ALL_TX_FLAGS(name, values, maskAdj) {#name, get##name##Flags()},
inline FlagMapPairList const&
getAllTxFlags()
{
static FlagMapPairList const flags = {
{"universal", getUniversalFlags()},
XMACRO(ALL_TX_FLAGS, NULL_OUTPUT, NULL_OUTPUT, NULL_MASK_ADJ)};
return flags;
}
#undef XMACRO
#undef TO_VALUE
#undef VALUE_TO_MAP
#undef NULL_NAME
#undef NULL_OUTPUT
#undef TO_MAP
#undef TO_MASK
#undef VALUE_TO_MASK
#undef ALL_TX_FLAGS
#undef NULL_MASK_ADJ
#undef MASK_ADJ_TO_MASK
#pragma pop_macro("XMACRO")
#pragma pop_macro("TO_VALUE")
#pragma pop_macro("VALUE_TO_MAP")
#pragma pop_macro("NULL_NAME")
#pragma pop_macro("NULL_OUTPUT")
#pragma pop_macro("TO_MAP")
#pragma pop_macro("TO_MASK")
#pragma pop_macro("VALUE_TO_MASK")
#pragma pop_macro("ALL_TX_FLAGS")
#pragma pop_macro("NULL_MASK_ADJ")
#pragma pop_macro("MASK_ADJ_TO_MASK")
// Additional transaction masks and combos
inline constexpr FlagValue tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment);
inline constexpr FlagValue tfTrustSetPermissionMask =
~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze);
// MPTokenIssuanceCreate flags:
// Note: tf/lsfMPTLocked is intentionally omitted, since this transaction
// is not allowed to modify it.
constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock;
constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth;
constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow;
constexpr std::uint32_t const tfMPTCanTrade = lsfMPTCanTrade;
constexpr std::uint32_t const tfMPTCanTransfer = lsfMPTCanTransfer;
constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback;
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback);
// MPTokenIssuanceCreate MutableFlags:
// Indicating specific fields or flags may be changed after issuance.
inline constexpr FlagValue tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock;
inline constexpr FlagValue tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth;
inline constexpr FlagValue tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow;
inline constexpr FlagValue tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade;
inline constexpr FlagValue tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer;
inline constexpr FlagValue tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask =
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
constexpr std::uint32_t const tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock;
constexpr std::uint32_t const tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth;
constexpr std::uint32_t const tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow;
constexpr std::uint32_t const tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade;
constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer;
constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
constexpr std::uint32_t const tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask =
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
// MPTokenAuthorize flags:
constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001;
constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUnauthorize);
// MPTokenIssuanceSet flags:
constexpr std::uint32_t const tfMPTLock = 0x00000001;
constexpr std::uint32_t const tfMPTUnlock = 0x00000002;
constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
// MPTokenIssuanceSet MutableFlags:
// Set or Clear flags.
constexpr std::uint32_t const tmfMPTSetCanLock = 0x00000001;
constexpr std::uint32_t const tmfMPTClearCanLock = 0x00000002;
constexpr std::uint32_t const tmfMPTSetRequireAuth = 0x00000004;
constexpr std::uint32_t const tmfMPTClearRequireAuth = 0x00000008;
constexpr std::uint32_t const tmfMPTSetCanEscrow = 0x00000010;
constexpr std::uint32_t const tmfMPTClearCanEscrow = 0x00000020;
constexpr std::uint32_t const tmfMPTSetCanTrade = 0x00000040;
constexpr std::uint32_t const tmfMPTClearCanTrade = 0x00000080;
constexpr std::uint32_t const tmfMPTSetCanTransfer = 0x00000100;
constexpr std::uint32_t const tmfMPTClearCanTransfer = 0x00000200;
constexpr std::uint32_t const tmfMPTSetCanClawback = 0x00000400;
constexpr std::uint32_t const tmfMPTClearCanClawback = 0x00000800;
constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock |
tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow |
tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer |
tmfMPTSetCanClawback | tmfMPTClearCanClawback);
inline constexpr FlagValue tmfMPTSetCanLock = 0x00000001;
inline constexpr FlagValue tmfMPTClearCanLock = 0x00000002;
inline constexpr FlagValue tmfMPTSetRequireAuth = 0x00000004;
inline constexpr FlagValue tmfMPTClearRequireAuth = 0x00000008;
inline constexpr FlagValue tmfMPTSetCanEscrow = 0x00000010;
inline constexpr FlagValue tmfMPTClearCanEscrow = 0x00000020;
inline constexpr FlagValue tmfMPTSetCanTrade = 0x00000040;
inline constexpr FlagValue tmfMPTClearCanTrade = 0x00000080;
inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000100;
inline constexpr FlagValue tmfMPTClearCanTransfer = 0x00000200;
inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000400;
inline constexpr FlagValue tmfMPTClearCanClawback = 0x00000800;
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = ~(
tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback | tmfMPTClearCanClawback);
// MPTokenIssuanceDestroy flags:
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a
// TrustLine to be added to the issuer of that token without explicit permission from that issuer.
// This was enabled by minting the NFToken with the tfTrustLine flag set.
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between
// accounts allowed a TrustLine to be added to the issuer of that token
// without explicit permission from that issuer. This was enabled by
// minting the NFToken with the tfTrustLine flag set.
//
// That capability could be used to attack the NFToken issuer.
// It would be possible for two accounts to trade the NFToken back and forth building up any number
// of TrustLines on the issuer, increasing the issuer's reserve without bound.
// That capability could be used to attack the NFToken issuer. It
// would be possible for two accounts to trade the NFToken back and forth
// building up any number of TrustLines on the issuer, increasing the
// issuer's reserve without bound.
//
// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the tfTrustLine flag as a way
// to prevent the attack. But until the amendment passes we still need to keep the old behavior
// available.
inline constexpr FlagValue tfTrustLine = 0x00000004; // needed for backwards compatibility
inline constexpr FlagValue tfNFTokenMintMaskWithoutMutable =
// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the
// tfTrustLine flag as a way to prevent the attack. But until the
// amendment passes we still need to keep the old behavior available.
constexpr std::uint32_t const tfNFTokenMintMask =
~(tfUniversal | tfBurnable | tfOnlyXRP | tfTransferable);
inline constexpr FlagValue tfNFTokenMintOldMask = ~(~tfNFTokenMintMaskWithoutMutable | tfTrustLine);
constexpr std::uint32_t const tfNFTokenMintOldMask =
~( ~tfNFTokenMintMask | tfTrustLine);
// if featureDynamicNFT enabled then new flag allowing mutable URI available.
inline constexpr FlagValue tfNFTokenMintOldMaskWithMutable = ~(~tfNFTokenMintOldMask | tfMutable);
constexpr std::uint32_t const tfNFTokenMintOldMaskWithMutable =
~( ~tfNFTokenMintOldMask | tfMutable);
inline constexpr FlagValue tfWithdrawSubTx = tfLPToken | tfSingleAsset | tfTwoAsset |
tfOneAssetLPToken | tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll;
inline constexpr FlagValue tfDepositSubTx =
tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | tfLimitLPToken | tfTwoAssetIfEmpty;
constexpr std::uint32_t const tfNFTokenMintMaskWithMutable =
~( ~tfNFTokenMintMask | tfMutable);
#pragma push_macro("ACCOUNTSET_FLAGS")
#pragma push_macro("ACCOUNTSET_FLAG_TO_VALUE")
#pragma push_macro("ACCOUNTSET_FLAG_TO_MAP")
// NFTokenCreateOffer flags:
constexpr std::uint32_t const tfSellNFToken = 0x00000001;
constexpr std::uint32_t const tfNFTokenCreateOfferMask =
~(tfUniversal | tfSellNFToken);
// AccountSet SetFlag/ClearFlag values
#define ACCOUNTSET_FLAGS(ASF_FLAG) \
ASF_FLAG(asfRequireDest, 1) \
ASF_FLAG(asfRequireAuth, 2) \
ASF_FLAG(asfDisallowXRP, 3) \
ASF_FLAG(asfDisableMaster, 4) \
ASF_FLAG(asfAccountTxnID, 5) \
ASF_FLAG(asfNoFreeze, 6) \
ASF_FLAG(asfGlobalFreeze, 7) \
ASF_FLAG(asfDefaultRipple, 8) \
ASF_FLAG(asfDepositAuth, 9) \
ASF_FLAG(asfAuthorizedNFTokenMinter, 10) \
/* 11 is reserved for Hooks amendment */ \
/* ASF_FLAG(asfTshCollect, 11) */ \
ASF_FLAG(asfDisallowIncomingNFTokenOffer, 12) \
ASF_FLAG(asfDisallowIncomingCheck, 13) \
ASF_FLAG(asfDisallowIncomingPayChan, 14) \
ASF_FLAG(asfDisallowIncomingTrustline, 15) \
ASF_FLAG(asfAllowTrustLineClawback, 16) \
ASF_FLAG(asfAllowTrustLineLocking, 17)
// NFTokenCancelOffer flags:
constexpr std::uint32_t const tfNFTokenCancelOfferMask = ~tfUniversal;
#define ACCOUNTSET_FLAG_TO_VALUE(name, value) inline constexpr FlagValue name = value;
#define ACCOUNTSET_FLAG_TO_MAP(name, value) {#name, value},
// NFTokenAcceptOffer flags:
constexpr std::uint32_t const tfNFTokenAcceptOfferMask = ~tfUniversal;
ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_VALUE)
// Clawback flags:
constexpr std::uint32_t const tfClawbackMask = ~tfUniversal;
inline std::map<std::string, FlagValue> const&
getAsfFlagMap()
{
static std::map<std::string, FlagValue> const flags = {
ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_MAP)};
return flags;
}
// AMM Flags:
constexpr std::uint32_t tfLPToken = 0x00010000;
constexpr std::uint32_t tfWithdrawAll = 0x00020000;
constexpr std::uint32_t tfOneAssetWithdrawAll = 0x00040000;
constexpr std::uint32_t tfSingleAsset = 0x00080000;
constexpr std::uint32_t tfTwoAsset = 0x00100000;
constexpr std::uint32_t tfOneAssetLPToken = 0x00200000;
constexpr std::uint32_t tfLimitLPToken = 0x00400000;
constexpr std::uint32_t tfTwoAssetIfEmpty = 0x00800000;
constexpr std::uint32_t tfWithdrawSubTx =
tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken |
tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll;
constexpr std::uint32_t tfDepositSubTx =
tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken |
tfLimitLPToken | tfTwoAssetIfEmpty;
constexpr std::uint32_t tfWithdrawMask = ~(tfUniversal | tfWithdrawSubTx);
constexpr std::uint32_t tfDepositMask = ~(tfUniversal | tfDepositSubTx);
#undef ACCOUNTSET_FLAG_TO_VALUE
#undef ACCOUNTSET_FLAG_TO_MAP
#undef ACCOUNTSET_FLAGS
// AMMClawback flags:
constexpr std::uint32_t tfClawTwoAssets = 0x00000001;
constexpr std::uint32_t tfAMMClawbackMask = ~(tfUniversal | tfClawTwoAssets);
#pragma pop_macro("ACCOUNTSET_FLAG_TO_VALUE")
#pragma pop_macro("ACCOUNTSET_FLAG_TO_MAP")
#pragma pop_macro("ACCOUNTSET_FLAGS")
// BridgeModify flags:
constexpr std::uint32_t tfClearAccountCreateAmount = 0x00010000;
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount);
// VaultCreate flags:
constexpr std::uint32_t const tfVaultPrivate = 0x00010000;
static_assert(tfVaultPrivate == lsfVaultPrivate);
constexpr std::uint32_t const tfVaultShareNonTransferable = 0x00020000;
constexpr std::uint32_t const tfVaultCreateMask = ~(tfUniversal | tfVaultPrivate | tfVaultShareNonTransferable);
// Batch Flags:
constexpr std::uint32_t tfAllOrNothing = 0x00010000;
constexpr std::uint32_t tfOnlyOne = 0x00020000;
constexpr std::uint32_t tfUntilFailure = 0x00040000;
constexpr std::uint32_t tfIndependent = 0x00080000;
/**
* @note If nested Batch transactions are supported in the future, the tfInnerBatchTxn flag
* will need to be removed from this mask to allow Batch transaction to be inside
* the sfRawTransactions array.
*/
constexpr std::uint32_t const tfBatchMask =
~(tfUniversal | tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent) | tfInnerBatchTxn;
// LoanSet and LoanPay flags:
// LoanSet: True, indicates the loan supports overpayments
// LoanPay: True, indicates any excess in this payment can be used
// as an overpayment. False, no overpayments will be taken.
constexpr std::uint32_t const tfLoanOverpayment = 0x00010000;
// LoanPay exclusive flags:
// tfLoanFullPayment: True, indicates that the payment is an early
// full payment. It must pay the entire loan including close
// interest and fees, or it will fail. False: Not a full payment.
constexpr std::uint32_t const tfLoanFullPayment = 0x00020000;
// tfLoanLatePayment: True, indicates that the payment is late,
// and includes late interest and fees. If the loan is not late,
// it will fail. False: not a late payment. If the current payment
// is overdue, the transaction will fail.
constexpr std::uint32_t const tfLoanLatePayment = 0x00040000;
constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal |
tfLoanOverpayment);
constexpr std::uint32_t const tfLoanPayMask = ~(tfUniversal |
tfLoanOverpayment | tfLoanFullPayment | tfLoanLatePayment);
// LoanManage flags:
constexpr std::uint32_t const tfLoanDefault = 0x00010000;
constexpr std::uint32_t const tfLoanImpair = 0x00020000;
constexpr std::uint32_t const tfLoanUnimpair = 0x00040000;
constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair);
// clang-format on
} // namespace xrpl

View File

@@ -2,8 +2,6 @@
#include <xrpl/protocol/KnownFormats.h>
#include <vector>
namespace xrpl {
/** Transaction type identifiers.
@@ -75,9 +73,6 @@ private:
public:
static TxFormats const&
getInstance();
static std::vector<SOElement> const&
getCommonFields();
};
} // namespace xrpl

View File

@@ -15,10 +15,9 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo)
@@ -32,7 +31,7 @@ XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionedDEX, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Batch, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(SingleAssetVault, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo)
// Check flags in Credential transactions

View File

@@ -25,7 +25,6 @@ namespace jss {
JSS(AL_size); // out: GetCounts
JSS(AL_hit_rate); // out: GetCounts
JSS(AcceptedCredentials); // out: AccountObjects
JSS(ACCOUNT_SET_FLAGS); // out: RPC server_definitions
JSS(Account); // in: TransactionSign; field.
JSS(AMMID); // field
JSS(Amount); // in: TransactionSign; field.
@@ -188,7 +187,6 @@ JSS(closed_ledger); // out: NetworkOPs
JSS(cluster); // out: PeerImp
JSS(code); // out: errors
JSS(command); // in: RPCHandler
JSS(common); // out: RPC server_definitions
JSS(complete); // out: NetworkOPs, InboundLedger
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
JSS(consensus); // out: NetworkOPs, LedgerConsensus
@@ -358,8 +356,6 @@ JSS(ledger_min); // in, out: AccountTx*
JSS(ledger_time); // out: NetworkOPs
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
// matches definitions.json format
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
JSS(levels); // LogLevels
JSS(limit); // in/out: AccountTx*, AccountOffers,
// AccountLines, AccountObjects
@@ -461,7 +457,6 @@ JSS(open); // out: handlers/Ledger
JSS(open_ledger_cost); // out: SubmitTransaction
JSS(open_ledger_fee); // out: TxQ
JSS(open_ledger_level); // out: TxQ
JSS(optionality); // out: server_definitions
JSS(oracles); // in: get_aggregate_price
JSS(oracle_document_id); // in: get_aggregate_price
JSS(owner); // in: LedgerEntry, out: NetworkOPs
@@ -621,8 +616,6 @@ JSS(TRANSACTION_RESULTS); // out: RPC server_definitions
// matches definitions.json format
JSS(TRANSACTION_TYPES); // out: RPC server_definitions
// matches definitions.json format
JSS(TRANSACTION_FLAGS); // out: RPC server_definitions
JSS(TRANSACTION_FORMATS); // out: RPC server_definitions
JSS(TYPES); // out: RPC server_definitions
// matches definitions.json format
JSS(transfer_rate); // out: nft_info (clio)

View File

@@ -1,152 +0,0 @@
#pragma once
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol_autogen/STObjectValidation.h>
#include <xrpl/protocol_autogen/Utils.h>
#include <optional>
#include <string>
namespace xrpl::ledger_entries {
/**
* @brief Base class for type-safe ledger entry wrappers.
*
* This class provides common functionality for all ledger entry types,
* including access to common fields (sfLedgerIndex, sfLedgerEntryType, sfFlags).
*
* This is an immutable wrapper around SLE (Serialized Ledger Entry).
* Use the corresponding Builder classes to construct new ledger entries.
*/
class LedgerEntryBase
{
public:
/**
* @brief Construct a ledger entry wrapper from an existing SLE object.
* @param sle The underlying serialized ledger entry to wrap
*/
explicit LedgerEntryBase(SLE const& sle) : sle_(sle)
{
}
/**
* @brief Validate the ledger entry
* @return true if validation passes, false otherwise
*/
[[nodiscard]]
bool
validate()
{
if (!sle_.isFieldPresent(sfLedgerEntryType))
{
return false;
}
auto ledgerEntryType = static_cast<LedgerEntryType>(sle_.getFieldU16(sfLedgerEntryType));
return protocol_autogen::validateSTObject(
sle_, LedgerFormats::getInstance().findByType(ledgerEntryType)->getSOTemplate());
}
/**
* @brief Get the ledger entry type.
* @return The type of this ledger entry
*/
[[nodiscard]]
LedgerEntryType
getType() const
{
return sle_.getType();
}
/**
* @brief Get the key (index) of this ledger entry.
*
* The key uniquely identifies this ledger entry in the ledger state.
* @return A constant reference to the 256-bit key
*/
[[nodiscard]]
uint256 const&
getKey() const
{
return sle_.key();
}
// Common field getters (from LedgerFormats.cpp commonFields)
/**
* @brief Get the ledger index (sfLedgerIndex).
*
* This field is OPTIONAL and represents the index of the ledger entry.
* @return The ledger index if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint256>
getLedgerIndex() const
{
if (sle_.isFieldPresent(sfLedgerIndex))
{
return sle_.at(sfLedgerIndex);
}
return std::nullopt;
}
/**
* @brief Check if the ledger entry has a ledger index.
* @return true if sfLedgerIndex is present, false otherwise
*/
[[nodiscard]]
bool
hasLedgerIndex() const
{
return sle_.isFieldPresent(sfLedgerIndex);
}
/**
* @brief Get the ledger entry type field (sfLedgerEntryType).
*
* This field is REQUIRED for all ledger entries and indicates the type
* of the ledger entry (e.g., AccountRoot, RippleState, Offer, etc.).
* @return The ledger entry type as a 16-bit unsigned integer
*/
[[nodiscard]]
uint16_t
getLedgerEntryType() const
{
return sle_.at(sfLedgerEntryType);
}
/**
* @brief Get the flags field (sfFlags).
*
* This field is REQUIRED for all ledger entries and contains
* type-specific flags that modify the behavior of the ledger entry.
* @return The flags value as a 32-bit unsigned integer
*/
[[nodiscard]]
std::uint32_t
getFlags() const
{
return sle_.at(sfFlags);
}
/**
* @brief Get the underlying SLE object.
*
* Provides direct access to the wrapped serialized ledger entry object
* for cases where the type-safe accessors are insufficient.
* @return A constant reference to the underlying SLE object
*/
[[nodiscard]]
SLE const&
getSle() const
{
return sle_;
}
protected:
/** @brief The underlying serialized ledger entry being wrapped. */
SLE const& sle_;
};
} // namespace xrpl::ledger_entries

View File

@@ -1,78 +0,0 @@
#pragma once
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STBlob.h>
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol_autogen/STObjectValidation.h>
namespace xrpl::ledger_entries {
/**
* Base class for all ledger entry builders.
* Provides common field setters that are available for all ledger entry types.
*/
template <typename Derived>
class LedgerEntryBuilderBase
{
public:
LedgerEntryBuilderBase() = default;
LedgerEntryBuilderBase(
SF_UINT16::type::value_type ledgerEntryType,
SF_UINT32::type::value_type flags = 0)
{
object_[sfLedgerEntryType] = ledgerEntryType;
object_[sfFlags] = flags;
}
/**
* @brief Validate the ledger entry
* @return true if validation passes, false otherwise
*/
[[nodiscard]]
bool
validate()
{
if (!object_.isFieldPresent(sfLedgerEntryType))
{
return false;
}
auto ledgerEntryType = static_cast<LedgerEntryType>(object_.getFieldU16(sfLedgerEntryType));
return protocol_autogen::validateSTObject(
object_, LedgerFormats::getInstance().findByType(ledgerEntryType)->getSOTemplate());
}
/**
* Set the ledger index.
* @param value Ledger index
* @return Reference to the derived builder for method chaining.
*/
Derived&
setLedgerIndex(uint256 const& value)
{
object_[sfLedgerIndex] = value;
return static_cast<Derived&>(*this);
}
/**
* Set the flags.
* @param value Flags value
* @return Reference to the derived builder for method chaining.
*/
Derived&
setFlags(uint32_t value)
{
object_.setFieldU32(sfFlags, value);
return static_cast<Derived&>(*this);
}
protected:
STObject object_{sfLedgerEntry};
};
} // namespace xrpl::ledger_entries

View File

@@ -1,81 +0,0 @@
<!-- cspell:words pyparsing -->
# Protocol Autogen
This directory contains auto-generated C++ wrapper classes for XRP Ledger protocol types.
## Generated Files
The files in this directory are automatically generated at **CMake configure time** from macro definition files:
- **Transaction classes** (in `transactions/`): Generated from `include/xrpl/protocol/detail/transactions.macro` by `scripts/generate_tx_classes.py`
- **Ledger entry classes** (in `ledger_objects/`): Generated from `include/xrpl/protocol/detail/ledger_entries.macro` by `scripts/generate_ledger_classes.py`
## Generation Process
The generation happens automatically when you **configure** the project (not during build). When you run CMake, the system:
1. Creates a Python virtual environment in the build directory (`codegen_venv`)
2. Installs Python dependencies from `scripts/requirements.txt` into the venv (only if needed)
3. Runs the Python generation scripts using the venv Python interpreter
4. Parses the macro files to extract type definitions
5. Generates type-safe C++ wrapper classes using Mako templates
6. Places the generated headers in this directory
### When Regeneration Happens
The code is regenerated when:
- You run CMake configure for the first time
- The Python virtual environment doesn't exist
- `scripts/requirements.txt` has been modified
To force regeneration, delete the build directory and reconfigure.
### Python Dependencies
The code generation requires the following Python packages (automatically installed):
- `pcpp` - C preprocessor for Python
- `pyparsing` - Parser combinator library
- `Mako` - Template engine
These are isolated in a virtual environment and won't affect your system Python installation.
## Version Control
The generated `.h` files **are checked into version control**. This means:
- Developers without Python 3 can still build the project using the committed files
- CI/CD systems don't need to run code generation if files are up to date
- Changes to generated files are visible in code review
## Modifying Generated Code
**Do not manually edit generated files.** Any changes will be overwritten the next time CMake configure runs.
To modify the generated classes:
- Edit the macro files in `include/xrpl/protocol/detail/`
- Edit the Mako templates in `scripts/templates/`
- Edit the generation scripts in `scripts/`
- Update Python dependencies in `scripts/requirements.txt`
- Run CMake configure to regenerate
## Adding Common Fields
If you add a new common field to `TxFormats.cpp` or `LedgerFormats.cpp`, you should also update the corresponding base classes and templates manually:
Base classes:
- `TransactionBase.h` - Add getters for new common transaction fields
- `TransactionBuilderBase.h` - Add setters, and if the field is required, add it to the constructor parameters
- `LedgerEntryBase.h` - Add getters for new common ledger entry fields
- `LedgerEntryBuilderBase.h` - Add setters, and if the field is required, add it to the constructor parameters
Templates (update to pass required common fields to base class constructors):
- `scripts/templates/Transaction.h.mako`
- `scripts/templates/LedgerEntry.h.mako`
These files are **not auto-generated** and must be updated by hand.

View File

@@ -1,32 +0,0 @@
#pragma once
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/STObject.h>
namespace xrpl::protocol_autogen {
[[nodiscard]]
bool
validateSTObject(STObject const& obj, SOTemplate const& format)
{
for (auto const& field : format)
{
if (obj.isFieldPresent(field.sField()) && field.style() == soeREQUIRED)
{
return false;
}
if (!field.supportMPT() && obj.isFieldPresent(field.sField()) &&
field.sField().fieldType == STI_ISSUE)
{
auto const& amount = obj.getFieldAmount(field.sField());
if (amount.asset().holds<MPTIssue>())
return false;
}
}
return true;
}
} // namespace xrpl::protocol_autogen

View File

@@ -1,444 +0,0 @@
#pragma once
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol_autogen/STObjectValidation.h>
#include <xrpl/protocol_autogen/Utils.h>
#include <optional>
#include <string>
namespace xrpl::transactions {
/**
* @brief Base class for all transaction wrapper types.
*
* Provides type-safe read-only accessors for common transaction fields.
* This is an immutable wrapper around STTx. Use the corresponding Builder classes
* to construct new transactions.
*/
class TransactionBase
{
public:
/**
* @brief Construct a transaction wrapper from an existing STTx object.
* @param tx The underlying transaction object to wrap
*/
explicit TransactionBase(STTx const& tx) : tx_(tx)
{
}
/**
* @brief Validate the transaction
* @return true if validation passes, false otherwise
*/
[[nodiscard]]
bool
validate() const
{
return protocol_autogen::validateSTObject(
tx_, TxFormats::getInstance().findByType(tx_.getTxnType())->getSOTemplate());
}
/**
* @brief Get the transaction type.
* @return The type of this transaction
*/
[[nodiscard]]
xrpl::TxType
getTransactionType() const
{
return tx_.getTxnType();
}
/**
* @brief Get the account initiating the transaction (sfAccount).
*
* This field is REQUIRED for all transactions.
* @return The account ID of the transaction sender
*/
[[nodiscard]]
AccountID
getAccount() const
{
return tx_.at(sfAccount);
}
/**
* @brief Get the sequence number of the transaction (sfSequence).
*
* This field is REQUIRED for all transactions.
* @return The sequence number
*/
[[nodiscard]]
std::uint32_t
getSequence() const
{
return tx_.at(sfSequence);
}
/**
* @brief Get the transaction fee (sfFee).
*
* This field is REQUIRED for all transactions.
* @return The fee amount
*/
[[nodiscard]]
STAmount
getFee() const
{
return tx_.at(sfFee);
}
/**
* @brief Get the signing public key (sfSigningPubKey).
*
* This field is REQUIRED for all transactions.
* @return The public key used for signing as a blob
*/
[[nodiscard]]
Blob
getSigningPubKey() const
{
return tx_.getFieldVL(sfSigningPubKey);
}
/**
* @brief Get the transaction flags (sfFlags).
*
* This field is OPTIONAL.
* @return The flags value if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint32_t>
getFlags() const
{
if (tx_.isFieldPresent(sfFlags))
return tx_.at(sfFlags);
return std::nullopt;
}
/**
* @brief Check if the transaction has flags set.
* @return true if sfFlags is present, false otherwise
*/
[[nodiscard]]
bool
hasFlags() const
{
return tx_.isFieldPresent(sfFlags);
}
/**
* @brief Get the source tag (sfSourceTag).
*
* This field is OPTIONAL and used to identify the source of a payment.
* @return The source tag value if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint32_t>
getSourceTag() const
{
if (tx_.isFieldPresent(sfSourceTag))
return tx_.at(sfSourceTag);
return std::nullopt;
}
/**
* @brief Check if the transaction has a source tag.
* @return true if sfSourceTag is present, false otherwise
*/
[[nodiscard]]
bool
hasSourceTag() const
{
return tx_.isFieldPresent(sfSourceTag);
}
/**
* @brief Get the previous transaction ID (sfPreviousTxnID).
*
* This field is OPTIONAL and used for transaction chaining.
* @return The previous transaction ID if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint256>
getPreviousTxnID() const
{
if (tx_.isFieldPresent(sfPreviousTxnID))
return tx_.at(sfPreviousTxnID);
return std::nullopt;
}
/**
* @brief Check if the transaction has a previous transaction ID.
* @return true if sfPreviousTxnID is present, false otherwise
*/
[[nodiscard]]
bool
hasPreviousTxnID() const
{
return tx_.isFieldPresent(sfPreviousTxnID);
}
/**
* @brief Get the last ledger sequence (sfLastLedgerSequence).
*
* This field is OPTIONAL and specifies the latest ledger sequence
* in which this transaction can be included.
* @return The last ledger sequence if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint32_t>
getLastLedgerSequence() const
{
if (tx_.isFieldPresent(sfLastLedgerSequence))
return tx_.at(sfLastLedgerSequence);
return std::nullopt;
}
/**
* @brief Check if the transaction has a last ledger sequence.
* @return true if sfLastLedgerSequence is present, false otherwise
*/
[[nodiscard]]
bool
hasLastLedgerSequence() const
{
return tx_.isFieldPresent(sfLastLedgerSequence);
}
/**
* @brief Get the account transaction ID (sfAccountTxnID).
*
* This field is OPTIONAL and used to track transaction sequences.
* @return The account transaction ID if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint256>
getAccountTxnID() const
{
if (tx_.isFieldPresent(sfAccountTxnID))
return tx_.at(sfAccountTxnID);
return std::nullopt;
}
/**
* @brief Check if the transaction has an account transaction ID.
* @return true if sfAccountTxnID is present, false otherwise
*/
[[nodiscard]]
bool
hasAccountTxnID() const
{
return tx_.isFieldPresent(sfAccountTxnID);
}
/**
* @brief Get the operation limit (sfOperationLimit).
*
* This field is OPTIONAL and limits the number of operations in a transaction.
* @return The operation limit if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint32_t>
getOperationLimit() const
{
if (tx_.isFieldPresent(sfOperationLimit))
return tx_.at(sfOperationLimit);
return std::nullopt;
}
/**
* @brief Check if the transaction has an operation limit.
* @return true if sfOperationLimit is present, false otherwise
*/
[[nodiscard]]
bool
hasOperationLimit() const
{
return tx_.isFieldPresent(sfOperationLimit);
}
/**
* @brief Get the memos array (sfMemos).
*
* This field is OPTIONAL and contains arbitrary data attached to the transaction.
* @note This is an untyped field (STArray).
* @return A reference wrapper to the memos array if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<std::reference_wrapper<STArray const>>
getMemos() const
{
if (tx_.isFieldPresent(sfMemos))
return tx_.getFieldArray(sfMemos);
return std::nullopt;
}
/**
* @brief Check if the transaction has memos.
* @return true if sfMemos is present, false otherwise
*/
[[nodiscard]]
bool
hasMemos() const
{
return tx_.isFieldPresent(sfMemos);
}
/**
* @brief Get the ticket sequence (sfTicketSequence).
*
* This field is OPTIONAL and used when consuming a ticket instead of a sequence number.
* @return The ticket sequence if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint32_t>
getTicketSequence() const
{
if (tx_.isFieldPresent(sfTicketSequence))
return tx_.at(sfTicketSequence);
return std::nullopt;
}
/**
* @brief Check if the transaction has a ticket sequence.
* @return true if sfTicketSequence is present, false otherwise
*/
[[nodiscard]]
bool
hasTicketSequence() const
{
return tx_.isFieldPresent(sfTicketSequence);
}
/**
* @brief Get the transaction signature (sfTxnSignature).
*
* This field is OPTIONAL and contains the signature for single-signed transactions.
* @return The transaction signature as a blob if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<Blob>
getTxnSignature() const
{
if (tx_.isFieldPresent(sfTxnSignature))
return tx_.getFieldVL(sfTxnSignature);
return std::nullopt;
}
/**
* @brief Check if the transaction has a transaction signature.
* @return true if sfTxnSignature is present, false otherwise
*/
[[nodiscard]]
bool
hasTxnSignature() const
{
return tx_.isFieldPresent(sfTxnSignature);
}
/**
* @brief Get the signers array (sfSigners).
*
* This field is OPTIONAL and contains the list of signers for multi-signed transactions.
* @note This is an untyped field (STArray).
* @return A reference wrapper to the signers array if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<std::reference_wrapper<STArray const>>
getSigners() const
{
if (tx_.isFieldPresent(sfSigners))
return tx_.getFieldArray(sfSigners);
return std::nullopt;
}
/**
* @brief Check if the transaction has signers.
* @return true if sfSigners is present, false otherwise
*/
[[nodiscard]]
bool
hasSigners() const
{
return tx_.isFieldPresent(sfSigners);
}
/**
* @brief Get the network ID (sfNetworkID).
*
* This field is OPTIONAL and identifies the network this transaction is intended for.
* @return The network ID if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<uint32_t>
getNetworkID() const
{
if (tx_.isFieldPresent(sfNetworkID))
return tx_.at(sfNetworkID);
return std::nullopt;
}
/**
* @brief Check if the transaction has a network ID.
* @return true if sfNetworkID is present, false otherwise
*/
[[nodiscard]]
bool
hasNetworkID() const
{
return tx_.isFieldPresent(sfNetworkID);
}
/**
* @brief Get the delegate account (sfDelegate).
*
* This field is OPTIONAL and specifies a delegate account for the transaction.
* @return The delegate account ID if present, std::nullopt otherwise
*/
[[nodiscard]]
std::optional<AccountID>
getDelegate() const
{
if (tx_.isFieldPresent(sfDelegate))
return tx_.at(sfDelegate);
return std::nullopt;
}
/**
* @brief Check if the transaction has a delegate account.
* @return true if sfDelegate is present, false otherwise
*/
[[nodiscard]]
bool
hasDelegate() const
{
return tx_.isFieldPresent(sfDelegate);
}
/**
* @brief Get the underlying STTx object.
*
* Provides direct access to the wrapped transaction object for cases
* where the type-safe accessors are insufficient.
* @return A constant reference to the underlying STTx object
*/
[[nodiscard]]
STTx const&
getSTTx() const
{
return tx_;
}
protected:
/** @brief The underlying transaction object being wrapped. */
STTx const& tx_;
};
} // namespace xrpl::transactions

View File

@@ -1,182 +0,0 @@
#pragma once
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STBlob.h>
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/STObjectValidation.h>
namespace xrpl::transactions {
/**
* Base class for all transaction builders.
* Provides common field setters that are available for all transaction types.
*/
template <typename Derived>
class TransactionBuilderBase
{
public:
TransactionBuilderBase() = default;
TransactionBuilderBase(
SF_UINT16::type::value_type transactionType,
SF_ACCOUNT::type::value_type account,
std::optional<SF_UINT32::type::value_type> sequence,
std::optional<SF_AMOUNT::type::value_type> fee)
{
object_[sfTransactionType] = transactionType;
setAccount(account);
if (sequence)
{
setSequence(*sequence);
}
if (fee)
{
setFee(*fee);
}
}
/**
* @brief Validate the transaction
* @return true if validation passes, false otherwise
*/
[[nodiscard]]
bool
validate()
{
if (!object_.isFieldPresent(sfTransactionType))
{
return false;
}
auto transactionType = static_cast<TxType>(object_.getFieldU16(sfTransactionType));
return protocol_autogen::validateSTObject(
object_, TxFormats::getInstance().findByType(transactionType)->getSOTemplate());
}
/**
* Set the account that is sending the transaction.
* @param value Account address (typically as a string)
* @return Reference to the derived builder for method chaining.
*/
Derived&
setAccount(AccountID const& value)
{
object_[sfAccount] = value;
return static_cast<Derived&>(*this);
}
/**
* Set the transaction fee.
* @param value Fee in drops (typically as a string or number)
* @return Reference to the derived builder for method chaining.
*/
Derived&
setFee(STAmount const& value)
{
object_[sfFee] = value;
return static_cast<Derived&>(*this);
}
/**
* Set the sequence number.
* @param value Sequence number
* @return Reference to the derived builder for method chaining.
*/
Derived&
setSequence(std::uint32_t const& value)
{
object_[sfSequence] = value;
return static_cast<Derived&>(*this);
}
/**
* Set transaction flags.
* @param value Flags value
* @return Reference to the derived builder for method chaining.
*/
Derived&
setFlags(std::uint32_t const& value)
{
object_[sfFlags] = value;
return static_cast<Derived&>(*this);
}
/**
* Set the source tag.
* @param value Source tag
* @return Reference to the derived builder for method chaining.
*/
Derived&
setSourceTag(std::uint32_t const& value)
{
object_[sfSourceTag] = value;
return static_cast<Derived&>(*this);
}
/**
* Set the last ledger sequence.
* @param value Last ledger sequence number
* @return Reference to the derived builder for method chaining.
*/
Derived&
setLastLedgerSequence(std::uint32_t const& value)
{
object_[sfLastLedgerSequence] = value;
return static_cast<Derived&>(*this);
}
/**
* Set the account transaction ID.
* @param value Account transaction ID (typically as a hex string)
* @return Reference to the derived builder for method chaining.
*/
Derived&
setAccountTxnID(uint256 const& value)
{
object_[sfAccountTxnID] = value;
return static_cast<Derived&>(*this);
}
/**
* Sign the transaction with the given keys.
*
* This sets the SigningPubKey field and computes the TxnSignature.
*
* @param publicKey The public key for signing
* @param secretKey The secret key for signing
* @return Reference to the derived builder for method chaining.
*/
Derived&
sign(PublicKey const& publicKey, SecretKey const& secretKey)
{
// Set the signing public key
object_.setFieldVL(sfSigningPubKey, publicKey.slice());
// Build the signing data: HashPrefix::txSign + serialized object
// (without signing fields)
Serializer s;
s.add32(HashPrefix::txSign);
object_.addWithoutSigningFields(s);
// Sign and set the signature
auto const sig = xrpl::sign(publicKey, secretKey, s.slice());
object_.setFieldVL(sfTxnSignature, sig);
return static_cast<Derived&>(*this);
}
protected:
STObject object_{sfTransaction};
};
} // namespace xrpl::transactions

View File

@@ -1,14 +0,0 @@
#pragma once
#include <optional>
#include <type_traits>
namespace xrpl::protocol_autogen {
template <typename ValueType>
using Optional = std::conditional_t<
std::is_reference_v<ValueType>,
std::optional<std::reference_wrapper<std::remove_reference_t<ValueType>>>,
std::optional<ValueType>>;
}

View File

@@ -1,347 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class AMMBuilder;
/**
* Ledger Entry: AMM
* Type: ltAMM (0x0079)
* RPC Name: amm
*
* Immutable wrapper around SLE providing type-safe field access.
* Use AMMBuilder to construct new ledger entries.
*/
class AMM : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltAMM;
/**
* Construct a AMM ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit AMM(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for AMM");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfTradingFee (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT16::type::value_type>
getTradingFee() const
{
if (hasTradingFee())
return this->sle_.at(sfTradingFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasTradingFee() const
{
return this->sle_.isFieldPresent(sfTradingFee);
}
/**
* Get sfVoteSlots (soeOPTIONAL)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
std::optional<std::reference_wrapper<STArray const>>
getVoteSlots() const
{
if (this->sle_.isFieldPresent(sfVoteSlots))
return this->sle_.getFieldArray(sfVoteSlots);
return std::nullopt;
}
[[nodiscard]]
bool
hasVoteSlots() const
{
return this->sle_.isFieldPresent(sfVoteSlots);
}
/**
* Get sfAuctionSlot (soeOPTIONAL)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
std::optional<STObject>
getAuctionSlot() const
{
if (this->sle_.isFieldPresent(sfAuctionSlot))
return this->sle_.getFieldObject(sfAuctionSlot);
return std::nullopt;
}
[[nodiscard]]
bool
hasAuctionSlot() const
{
return this->sle_.isFieldPresent(sfAuctionSlot);
}
/**
* Get sfLPTokenBalance (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getLPTokenBalance() const
{
return this->sle_.at(sfLPTokenBalance);
}
/**
* Get sfAsset (soeREQUIRED)
*/
[[nodiscard]]
SF_ISSUE::type::value_type
getAsset() const
{
return this->sle_.at(sfAsset);
}
/**
* Get sfAsset2 (soeREQUIRED)
*/
[[nodiscard]]
SF_ISSUE::type::value_type
getAsset2() const
{
return this->sle_.at(sfAsset2);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getPreviousTxnID() const
{
if (hasPreviousTxnID())
return this->sle_.at(sfPreviousTxnID);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnID() const
{
return this->sle_.isFieldPresent(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getPreviousTxnLgrSeq() const
{
if (hasPreviousTxnLgrSeq())
return this->sle_.at(sfPreviousTxnLgrSeq);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnLgrSeq() const
{
return this->sle_.isFieldPresent(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for AMM ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class AMMBuilder : public LedgerEntryBuilderBase<AMMBuilder>
{
public:
AMMBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_AMOUNT::type::value_type> const& lPTokenBalance,std::decay_t<typename SF_ISSUE::type::value_type> const& asset,std::decay_t<typename SF_ISSUE::type::value_type> const& asset2,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode)
: LedgerEntryBuilderBase<AMMBuilder>(ltAMM)
{
setAccount(account);
setLPTokenBalance(lPTokenBalance);
setAsset(asset);
setAsset2(asset2);
setOwnerNode(ownerNode);
}
AMMBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltAMM)
{
throw std::runtime_error("Invalid ledger entry type for AMM");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfTradingFee (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setTradingFee(std::decay_t<typename SF_UINT16::type::value_type> const& value)
{
object_[sfTradingFee] = value;
return *this;
}
/**
* Set sfVoteSlots (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setVoteSlots(STArray const& value)
{
object_.setFieldArray(sfVoteSlots, value);
return *this;
}
/**
* Set sfAuctionSlot (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setAuctionSlot(STObject const& value)
{
object_.setFieldObject(sfAuctionSlot, value);
return *this;
}
/**
* Set sfLPTokenBalance (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setLPTokenBalance(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfLPTokenBalance] = value;
return *this;
}
/**
* Set sfAsset (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setAsset(std::decay_t<typename SF_ISSUE::type::value_type> const& value)
{
object_[sfAsset] = STIssue(sfAsset, value);
return *this;
}
/**
* Set sfAsset2 (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setAsset2(std::decay_t<typename SF_ISSUE::type::value_type> const& value)
{
object_[sfAsset2] = STIssue(sfAsset2, value);
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AMMBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed AMM wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
AMM
build(uint256 const& index)
{
return AMM{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,727 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class AccountRootBuilder;
/**
* Ledger Entry: AccountRoot
* Type: ltACCOUNT_ROOT (0x0061)
* RPC Name: account
*
* Immutable wrapper around SLE providing type-safe field access.
* Use AccountRootBuilder to construct new ledger entries.
*/
class AccountRoot : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltACCOUNT_ROOT;
/**
* Construct a AccountRoot ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit AccountRoot(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for AccountRoot");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSequence() const
{
return this->sle_.at(sfSequence);
}
/**
* Get sfBalance (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getBalance() const
{
return this->sle_.at(sfBalance);
}
/**
* Get sfOwnerCount (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getOwnerCount() const
{
return this->sle_.at(sfOwnerCount);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfAccountTxnID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getAccountTxnID() const
{
if (hasAccountTxnID())
return this->sle_.at(sfAccountTxnID);
return std::nullopt;
}
[[nodiscard]]
bool
hasAccountTxnID() const
{
return this->sle_.isFieldPresent(sfAccountTxnID);
}
/**
* Get sfRegularKey (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getRegularKey() const
{
if (hasRegularKey())
return this->sle_.at(sfRegularKey);
return std::nullopt;
}
[[nodiscard]]
bool
hasRegularKey() const
{
return this->sle_.isFieldPresent(sfRegularKey);
}
/**
* Get sfEmailHash (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT128::type::value_type>
getEmailHash() const
{
if (hasEmailHash())
return this->sle_.at(sfEmailHash);
return std::nullopt;
}
[[nodiscard]]
bool
hasEmailHash() const
{
return this->sle_.isFieldPresent(sfEmailHash);
}
/**
* Get sfWalletLocator (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getWalletLocator() const
{
if (hasWalletLocator())
return this->sle_.at(sfWalletLocator);
return std::nullopt;
}
[[nodiscard]]
bool
hasWalletLocator() const
{
return this->sle_.isFieldPresent(sfWalletLocator);
}
/**
* Get sfWalletSize (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getWalletSize() const
{
if (hasWalletSize())
return this->sle_.at(sfWalletSize);
return std::nullopt;
}
[[nodiscard]]
bool
hasWalletSize() const
{
return this->sle_.isFieldPresent(sfWalletSize);
}
/**
* Get sfMessageKey (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getMessageKey() const
{
if (hasMessageKey())
return this->sle_.at(sfMessageKey);
return std::nullopt;
}
[[nodiscard]]
bool
hasMessageKey() const
{
return this->sle_.isFieldPresent(sfMessageKey);
}
/**
* Get sfTransferRate (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getTransferRate() const
{
if (hasTransferRate())
return this->sle_.at(sfTransferRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasTransferRate() const
{
return this->sle_.isFieldPresent(sfTransferRate);
}
/**
* Get sfDomain (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getDomain() const
{
if (hasDomain())
return this->sle_.at(sfDomain);
return std::nullopt;
}
[[nodiscard]]
bool
hasDomain() const
{
return this->sle_.isFieldPresent(sfDomain);
}
/**
* Get sfTickSize (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT8::type::value_type>
getTickSize() const
{
if (hasTickSize())
return this->sle_.at(sfTickSize);
return std::nullopt;
}
[[nodiscard]]
bool
hasTickSize() const
{
return this->sle_.isFieldPresent(sfTickSize);
}
/**
* Get sfTicketCount (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getTicketCount() const
{
if (hasTicketCount())
return this->sle_.at(sfTicketCount);
return std::nullopt;
}
[[nodiscard]]
bool
hasTicketCount() const
{
return this->sle_.isFieldPresent(sfTicketCount);
}
/**
* Get sfNFTokenMinter (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getNFTokenMinter() const
{
if (hasNFTokenMinter())
return this->sle_.at(sfNFTokenMinter);
return std::nullopt;
}
[[nodiscard]]
bool
hasNFTokenMinter() const
{
return this->sle_.isFieldPresent(sfNFTokenMinter);
}
/**
* Get sfMintedNFTokens (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getMintedNFTokens() const
{
if (hasMintedNFTokens())
return this->sle_.at(sfMintedNFTokens);
return std::nullopt;
}
[[nodiscard]]
bool
hasMintedNFTokens() const
{
return this->sle_.isFieldPresent(sfMintedNFTokens);
}
/**
* Get sfBurnedNFTokens (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getBurnedNFTokens() const
{
if (hasBurnedNFTokens())
return this->sle_.at(sfBurnedNFTokens);
return std::nullopt;
}
[[nodiscard]]
bool
hasBurnedNFTokens() const
{
return this->sle_.isFieldPresent(sfBurnedNFTokens);
}
/**
* Get sfFirstNFTokenSequence (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getFirstNFTokenSequence() const
{
if (hasFirstNFTokenSequence())
return this->sle_.at(sfFirstNFTokenSequence);
return std::nullopt;
}
[[nodiscard]]
bool
hasFirstNFTokenSequence() const
{
return this->sle_.isFieldPresent(sfFirstNFTokenSequence);
}
/**
* Get sfAMMID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getAMMID() const
{
if (hasAMMID())
return this->sle_.at(sfAMMID);
return std::nullopt;
}
[[nodiscard]]
bool
hasAMMID() const
{
return this->sle_.isFieldPresent(sfAMMID);
}
/**
* Get sfVaultID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getVaultID() const
{
if (hasVaultID())
return this->sle_.at(sfVaultID);
return std::nullopt;
}
[[nodiscard]]
bool
hasVaultID() const
{
return this->sle_.isFieldPresent(sfVaultID);
}
/**
* Get sfLoanBrokerID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getLoanBrokerID() const
{
if (hasLoanBrokerID())
return this->sle_.at(sfLoanBrokerID);
return std::nullopt;
}
[[nodiscard]]
bool
hasLoanBrokerID() const
{
return this->sle_.isFieldPresent(sfLoanBrokerID);
}
};
/**
* Builder for AccountRoot ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class AccountRootBuilder : public LedgerEntryBuilderBase<AccountRootBuilder>
{
public:
AccountRootBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_UINT32::type::value_type> const& sequence,std::decay_t<typename SF_AMOUNT::type::value_type> const& balance,std::decay_t<typename SF_UINT32::type::value_type> const& ownerCount,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<AccountRootBuilder>(ltACCOUNT_ROOT)
{
setAccount(account);
setSequence(sequence);
setBalance(balance);
setOwnerCount(ownerCount);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
AccountRootBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltACCOUNT_ROOT)
{
throw std::runtime_error("Invalid ledger entry type for AccountRoot");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfBalance (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setBalance(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfBalance] = value;
return *this;
}
/**
* Set sfOwnerCount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setOwnerCount(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfOwnerCount] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfAccountTxnID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setAccountTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfAccountTxnID] = value;
return *this;
}
/**
* Set sfRegularKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setRegularKey(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfRegularKey] = value;
return *this;
}
/**
* Set sfEmailHash (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setEmailHash(std::decay_t<typename SF_UINT128::type::value_type> const& value)
{
object_[sfEmailHash] = value;
return *this;
}
/**
* Set sfWalletLocator (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setWalletLocator(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfWalletLocator] = value;
return *this;
}
/**
* Set sfWalletSize (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setWalletSize(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfWalletSize] = value;
return *this;
}
/**
* Set sfMessageKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setMessageKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfMessageKey] = value;
return *this;
}
/**
* Set sfTransferRate (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setTransferRate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfTransferRate] = value;
return *this;
}
/**
* Set sfDomain (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setDomain(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfDomain] = value;
return *this;
}
/**
* Set sfTickSize (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setTickSize(std::decay_t<typename SF_UINT8::type::value_type> const& value)
{
object_[sfTickSize] = value;
return *this;
}
/**
* Set sfTicketCount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setTicketCount(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfTicketCount] = value;
return *this;
}
/**
* Set sfNFTokenMinter (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setNFTokenMinter(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfNFTokenMinter] = value;
return *this;
}
/**
* Set sfMintedNFTokens (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setMintedNFTokens(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfMintedNFTokens] = value;
return *this;
}
/**
* Set sfBurnedNFTokens (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setBurnedNFTokens(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfBurnedNFTokens] = value;
return *this;
}
/**
* Set sfFirstNFTokenSequence (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setFirstNFTokenSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfFirstNFTokenSequence] = value;
return *this;
}
/**
* Set sfAMMID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setAMMID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfAMMID] = value;
return *this;
}
/**
* Set sfVaultID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setVaultID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfVaultID] = value;
return *this;
}
/**
* Set sfLoanBrokerID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AccountRootBuilder&
setLoanBrokerID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfLoanBrokerID] = value;
return *this;
}
/**
* Build and return the completed AccountRoot wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
AccountRoot
build(uint256 const& index)
{
return AccountRoot{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,206 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class AmendmentsBuilder;
/**
* Ledger Entry: Amendments
* Type: ltAMENDMENTS (0x0066)
* RPC Name: amendments
*
* Immutable wrapper around SLE providing type-safe field access.
* Use AmendmentsBuilder to construct new ledger entries.
*/
class Amendments : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltAMENDMENTS;
/**
* Construct a Amendments ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Amendments(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Amendments");
}
}
// Ledger entry-specific field getters
/**
* Get sfAmendments (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VECTOR256::type::value_type>
getAmendments() const
{
if (hasAmendments())
return this->sle_.at(sfAmendments);
return std::nullopt;
}
[[nodiscard]]
bool
hasAmendments() const
{
return this->sle_.isFieldPresent(sfAmendments);
}
/**
* Get sfMajorities (soeOPTIONAL)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
std::optional<std::reference_wrapper<STArray const>>
getMajorities() const
{
if (this->sle_.isFieldPresent(sfMajorities))
return this->sle_.getFieldArray(sfMajorities);
return std::nullopt;
}
[[nodiscard]]
bool
hasMajorities() const
{
return this->sle_.isFieldPresent(sfMajorities);
}
/**
* Get sfPreviousTxnID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getPreviousTxnID() const
{
if (hasPreviousTxnID())
return this->sle_.at(sfPreviousTxnID);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnID() const
{
return this->sle_.isFieldPresent(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getPreviousTxnLgrSeq() const
{
if (hasPreviousTxnLgrSeq())
return this->sle_.at(sfPreviousTxnLgrSeq);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnLgrSeq() const
{
return this->sle_.isFieldPresent(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for Amendments ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class AmendmentsBuilder : public LedgerEntryBuilderBase<AmendmentsBuilder>
{
public:
AmendmentsBuilder()
: LedgerEntryBuilderBase<AmendmentsBuilder>(ltAMENDMENTS)
{
}
AmendmentsBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltAMENDMENTS)
{
throw std::runtime_error("Invalid ledger entry type for Amendments");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAmendments (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AmendmentsBuilder&
setAmendments(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfAmendments] = value;
return *this;
}
/**
* Set sfMajorities (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AmendmentsBuilder&
setMajorities(STArray const& value)
{
object_.setFieldArray(sfMajorities, value);
return *this;
}
/**
* Set sfPreviousTxnID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AmendmentsBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
AmendmentsBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed Amendments wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Amendments
build(uint256 const& index)
{
return Amendments{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,313 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class BridgeBuilder;
/**
* Ledger Entry: Bridge
* Type: ltBRIDGE (0x0069)
* RPC Name: bridge
*
* Immutable wrapper around SLE providing type-safe field access.
* Use BridgeBuilder to construct new ledger entries.
*/
class Bridge : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltBRIDGE;
/**
* Construct a Bridge ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Bridge(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Bridge");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfSignatureReward (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSignatureReward() const
{
return this->sle_.at(sfSignatureReward);
}
/**
* Get sfMinAccountCreateAmount (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getMinAccountCreateAmount() const
{
if (hasMinAccountCreateAmount())
return this->sle_.at(sfMinAccountCreateAmount);
return std::nullopt;
}
[[nodiscard]]
bool
hasMinAccountCreateAmount() const
{
return this->sle_.isFieldPresent(sfMinAccountCreateAmount);
}
/**
* Get sfXChainBridge (soeREQUIRED)
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->sle_.at(sfXChainBridge);
}
/**
* Get sfXChainClaimID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainClaimID() const
{
return this->sle_.at(sfXChainClaimID);
}
/**
* Get sfXChainAccountCreateCount (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainAccountCreateCount() const
{
return this->sle_.at(sfXChainAccountCreateCount);
}
/**
* Get sfXChainAccountClaimCount (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainAccountClaimCount() const
{
return this->sle_.at(sfXChainAccountClaimCount);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for Bridge ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class BridgeBuilder : public LedgerEntryBuilderBase<BridgeBuilder>
{
public:
BridgeBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_AMOUNT::type::value_type> const& signatureReward,std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge,std::decay_t<typename SF_UINT64::type::value_type> const& xChainClaimID,std::decay_t<typename SF_UINT64::type::value_type> const& xChainAccountCreateCount,std::decay_t<typename SF_UINT64::type::value_type> const& xChainAccountClaimCount,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<BridgeBuilder>(ltBRIDGE)
{
setAccount(account);
setSignatureReward(signatureReward);
setXChainBridge(xChainBridge);
setXChainClaimID(xChainClaimID);
setXChainAccountCreateCount(xChainAccountCreateCount);
setXChainAccountClaimCount(xChainAccountClaimCount);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
BridgeBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltBRIDGE)
{
throw std::runtime_error("Invalid ledger entry type for Bridge");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfSignatureReward (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* Set sfMinAccountCreateAmount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setMinAccountCreateAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfMinAccountCreateAmount] = value;
return *this;
}
/**
* Set sfXChainBridge (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* Set sfXChainClaimID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainClaimID(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainClaimID] = value;
return *this;
}
/**
* Set sfXChainAccountCreateCount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainAccountCreateCount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainAccountCreateCount] = value;
return *this;
}
/**
* Set sfXChainAccountClaimCount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainAccountClaimCount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainAccountClaimCount] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed Bridge wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Bridge
build(uint256 const& index)
{
return Bridge{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,381 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class CheckBuilder;
/**
* Ledger Entry: Check
* Type: ltCHECK (0x0043)
* RPC Name: check
*
* Immutable wrapper around SLE providing type-safe field access.
* Use CheckBuilder to construct new ledger entries.
*/
class Check : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltCHECK;
/**
* Construct a Check ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Check(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Check");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfDestination (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->sle_.at(sfDestination);
}
/**
* Get sfSendMax (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSendMax() const
{
return this->sle_.at(sfSendMax);
}
/**
* Get sfSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSequence() const
{
return this->sle_.at(sfSequence);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfDestinationNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getDestinationNode() const
{
return this->sle_.at(sfDestinationNode);
}
/**
* Get sfExpiration (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getExpiration() const
{
if (hasExpiration())
return this->sle_.at(sfExpiration);
return std::nullopt;
}
[[nodiscard]]
bool
hasExpiration() const
{
return this->sle_.isFieldPresent(sfExpiration);
}
/**
* Get sfInvoiceID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getInvoiceID() const
{
if (hasInvoiceID())
return this->sle_.at(sfInvoiceID);
return std::nullopt;
}
[[nodiscard]]
bool
hasInvoiceID() const
{
return this->sle_.isFieldPresent(sfInvoiceID);
}
/**
* Get sfSourceTag (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getSourceTag() const
{
if (hasSourceTag())
return this->sle_.at(sfSourceTag);
return std::nullopt;
}
[[nodiscard]]
bool
hasSourceTag() const
{
return this->sle_.isFieldPresent(sfSourceTag);
}
/**
* Get sfDestinationTag (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
{
if (hasDestinationTag())
return this->sle_.at(sfDestinationTag);
return std::nullopt;
}
[[nodiscard]]
bool
hasDestinationTag() const
{
return this->sle_.isFieldPresent(sfDestinationTag);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for Check ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class CheckBuilder : public LedgerEntryBuilderBase<CheckBuilder>
{
public:
CheckBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination,std::decay_t<typename SF_AMOUNT::type::value_type> const& sendMax,std::decay_t<typename SF_UINT32::type::value_type> const& sequence,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT64::type::value_type> const& destinationNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<CheckBuilder>(ltCHECK)
{
setAccount(account);
setDestination(destination);
setSendMax(sendMax);
setSequence(sequence);
setOwnerNode(ownerNode);
setDestinationNode(destinationNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
CheckBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltCHECK)
{
throw std::runtime_error("Invalid ledger entry type for Check");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfDestination (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* Set sfSendMax (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setSendMax(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSendMax] = value;
return *this;
}
/**
* Set sfSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfDestinationNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setDestinationNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfDestinationNode] = value;
return *this;
}
/**
* Set sfExpiration (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setExpiration(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfExpiration] = value;
return *this;
}
/**
* Set sfInvoiceID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setInvoiceID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfInvoiceID] = value;
return *this;
}
/**
* Set sfSourceTag (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setSourceTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSourceTag] = value;
return *this;
}
/**
* Set sfDestinationTag (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CheckBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed Check wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Check
build(uint256 const& index)
{
return Check{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,307 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class CredentialBuilder;
/**
* Ledger Entry: Credential
* Type: ltCREDENTIAL (0x0081)
* RPC Name: credential
*
* Immutable wrapper around SLE providing type-safe field access.
* Use CredentialBuilder to construct new ledger entries.
*/
class Credential : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltCREDENTIAL;
/**
* Construct a Credential ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Credential(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Credential");
}
}
// Ledger entry-specific field getters
/**
* Get sfSubject (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getSubject() const
{
return this->sle_.at(sfSubject);
}
/**
* Get sfIssuer (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getIssuer() const
{
return this->sle_.at(sfIssuer);
}
/**
* Get sfCredentialType (soeREQUIRED)
*/
[[nodiscard]]
SF_VL::type::value_type
getCredentialType() const
{
return this->sle_.at(sfCredentialType);
}
/**
* Get sfExpiration (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getExpiration() const
{
if (hasExpiration())
return this->sle_.at(sfExpiration);
return std::nullopt;
}
[[nodiscard]]
bool
hasExpiration() const
{
return this->sle_.isFieldPresent(sfExpiration);
}
/**
* Get sfURI (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getURI() const
{
if (hasURI())
return this->sle_.at(sfURI);
return std::nullopt;
}
[[nodiscard]]
bool
hasURI() const
{
return this->sle_.isFieldPresent(sfURI);
}
/**
* Get sfIssuerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getIssuerNode() const
{
return this->sle_.at(sfIssuerNode);
}
/**
* Get sfSubjectNode (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getSubjectNode() const
{
if (hasSubjectNode())
return this->sle_.at(sfSubjectNode);
return std::nullopt;
}
[[nodiscard]]
bool
hasSubjectNode() const
{
return this->sle_.isFieldPresent(sfSubjectNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for Credential ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class CredentialBuilder : public LedgerEntryBuilderBase<CredentialBuilder>
{
public:
CredentialBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& subject,std::decay_t<typename SF_ACCOUNT::type::value_type> const& issuer,std::decay_t<typename SF_VL::type::value_type> const& credentialType,std::decay_t<typename SF_UINT64::type::value_type> const& issuerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<CredentialBuilder>(ltCREDENTIAL)
{
setSubject(subject);
setIssuer(issuer);
setCredentialType(credentialType);
setIssuerNode(issuerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
CredentialBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltCREDENTIAL)
{
throw std::runtime_error("Invalid ledger entry type for Credential");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfSubject (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setSubject(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfSubject] = value;
return *this;
}
/**
* Set sfIssuer (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setIssuer(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfIssuer] = value;
return *this;
}
/**
* Set sfCredentialType (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setCredentialType(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfCredentialType] = value;
return *this;
}
/**
* Set sfExpiration (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setExpiration(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfExpiration] = value;
return *this;
}
/**
* Set sfURI (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setURI(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfURI] = value;
return *this;
}
/**
* Set sfIssuerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setIssuerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfIssuerNode] = value;
return *this;
}
/**
* Set sfSubjectNode (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setSubjectNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfSubjectNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
CredentialBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed Credential wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Credential
build(uint256 const& index)
{
return Credential{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,263 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class DIDBuilder;
/**
* Ledger Entry: DID
* Type: ltDID (0x0049)
* RPC Name: did
*
* Immutable wrapper around SLE providing type-safe field access.
* Use DIDBuilder to construct new ledger entries.
*/
class DID : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltDID;
/**
* Construct a DID ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit DID(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for DID");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfDIDDocument (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getDIDDocument() const
{
if (hasDIDDocument())
return this->sle_.at(sfDIDDocument);
return std::nullopt;
}
[[nodiscard]]
bool
hasDIDDocument() const
{
return this->sle_.isFieldPresent(sfDIDDocument);
}
/**
* Get sfURI (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getURI() const
{
if (hasURI())
return this->sle_.at(sfURI);
return std::nullopt;
}
[[nodiscard]]
bool
hasURI() const
{
return this->sle_.isFieldPresent(sfURI);
}
/**
* Get sfData (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getData() const
{
if (hasData())
return this->sle_.at(sfData);
return std::nullopt;
}
[[nodiscard]]
bool
hasData() const
{
return this->sle_.isFieldPresent(sfData);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for DID ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class DIDBuilder : public LedgerEntryBuilderBase<DIDBuilder>
{
public:
DIDBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<DIDBuilder>(ltDID)
{
setAccount(account);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
DIDBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltDID)
{
throw std::runtime_error("Invalid ledger entry type for DID");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DIDBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfDIDDocument (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DIDBuilder&
setDIDDocument(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfDIDDocument] = value;
return *this;
}
/**
* Set sfURI (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DIDBuilder&
setURI(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfURI] = value;
return *this;
}
/**
* Set sfData (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DIDBuilder&
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfData] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DIDBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DIDBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DIDBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed DID wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
DID
build(uint256 const& index)
{
return DID{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,218 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class DelegateBuilder;
/**
* Ledger Entry: Delegate
* Type: ltDELEGATE (0x0083)
* RPC Name: delegate
*
* Immutable wrapper around SLE providing type-safe field access.
* Use DelegateBuilder to construct new ledger entries.
*/
class Delegate : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltDELEGATE;
/**
* Construct a Delegate ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Delegate(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Delegate");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfAuthorize (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAuthorize() const
{
return this->sle_.at(sfAuthorize);
}
/**
* Get sfPermissions (soeREQUIRED)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
STArray const&
getPermissions() const
{
return this->sle_.getFieldArray(sfPermissions);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for Delegate ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class DelegateBuilder : public LedgerEntryBuilderBase<DelegateBuilder>
{
public:
DelegateBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_ACCOUNT::type::value_type> const& authorize,STArray const& permissions,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<DelegateBuilder>(ltDELEGATE)
{
setAccount(account);
setAuthorize(authorize);
setPermissions(permissions);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
DelegateBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltDELEGATE)
{
throw std::runtime_error("Invalid ledger entry type for Delegate");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DelegateBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfAuthorize (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DelegateBuilder&
setAuthorize(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAuthorize] = value;
return *this;
}
/**
* Set sfPermissions (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DelegateBuilder&
setPermissions(STArray const& value)
{
object_.setFieldArray(sfPermissions, value);
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DelegateBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DelegateBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DelegateBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed Delegate wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Delegate
build(uint256 const& index)
{
return Delegate{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,234 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class DepositPreauthBuilder;
/**
* Ledger Entry: DepositPreauth
* Type: ltDEPOSIT_PREAUTH (0x0070)
* RPC Name: deposit_preauth
*
* Immutable wrapper around SLE providing type-safe field access.
* Use DepositPreauthBuilder to construct new ledger entries.
*/
class DepositPreauth : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltDEPOSIT_PREAUTH;
/**
* Construct a DepositPreauth ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit DepositPreauth(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for DepositPreauth");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfAuthorize (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getAuthorize() const
{
if (hasAuthorize())
return this->sle_.at(sfAuthorize);
return std::nullopt;
}
[[nodiscard]]
bool
hasAuthorize() const
{
return this->sle_.isFieldPresent(sfAuthorize);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfAuthorizeCredentials (soeOPTIONAL)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
std::optional<std::reference_wrapper<STArray const>>
getAuthorizeCredentials() const
{
if (this->sle_.isFieldPresent(sfAuthorizeCredentials))
return this->sle_.getFieldArray(sfAuthorizeCredentials);
return std::nullopt;
}
[[nodiscard]]
bool
hasAuthorizeCredentials() const
{
return this->sle_.isFieldPresent(sfAuthorizeCredentials);
}
};
/**
* Builder for DepositPreauth ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class DepositPreauthBuilder : public LedgerEntryBuilderBase<DepositPreauthBuilder>
{
public:
DepositPreauthBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<DepositPreauthBuilder>(ltDEPOSIT_PREAUTH)
{
setAccount(account);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
DepositPreauthBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltDEPOSIT_PREAUTH)
{
throw std::runtime_error("Invalid ledger entry type for DepositPreauth");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DepositPreauthBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfAuthorize (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DepositPreauthBuilder&
setAuthorize(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAuthorize] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DepositPreauthBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DepositPreauthBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DepositPreauthBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfAuthorizeCredentials (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DepositPreauthBuilder&
setAuthorizeCredentials(STArray const& value)
{
object_.setFieldArray(sfAuthorizeCredentials, value);
return *this;
}
/**
* Build and return the completed DepositPreauth wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
DepositPreauth
build(uint256 const& index)
{
return DepositPreauth{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,489 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class DirectoryNodeBuilder;
/**
* Ledger Entry: DirectoryNode
* Type: ltDIR_NODE (0x0064)
* RPC Name: directory
*
* Immutable wrapper around SLE providing type-safe field access.
* Use DirectoryNodeBuilder to construct new ledger entries.
*/
class DirectoryNode : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltDIR_NODE;
/**
* Construct a DirectoryNode ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit DirectoryNode(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for DirectoryNode");
}
}
// Ledger entry-specific field getters
/**
* Get sfOwner (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getOwner() const
{
if (hasOwner())
return this->sle_.at(sfOwner);
return std::nullopt;
}
[[nodiscard]]
bool
hasOwner() const
{
return this->sle_.isFieldPresent(sfOwner);
}
/**
* Get sfTakerPaysCurrency (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT160::type::value_type>
getTakerPaysCurrency() const
{
if (hasTakerPaysCurrency())
return this->sle_.at(sfTakerPaysCurrency);
return std::nullopt;
}
[[nodiscard]]
bool
hasTakerPaysCurrency() const
{
return this->sle_.isFieldPresent(sfTakerPaysCurrency);
}
/**
* Get sfTakerPaysIssuer (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT160::type::value_type>
getTakerPaysIssuer() const
{
if (hasTakerPaysIssuer())
return this->sle_.at(sfTakerPaysIssuer);
return std::nullopt;
}
[[nodiscard]]
bool
hasTakerPaysIssuer() const
{
return this->sle_.isFieldPresent(sfTakerPaysIssuer);
}
/**
* Get sfTakerGetsCurrency (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT160::type::value_type>
getTakerGetsCurrency() const
{
if (hasTakerGetsCurrency())
return this->sle_.at(sfTakerGetsCurrency);
return std::nullopt;
}
[[nodiscard]]
bool
hasTakerGetsCurrency() const
{
return this->sle_.isFieldPresent(sfTakerGetsCurrency);
}
/**
* Get sfTakerGetsIssuer (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT160::type::value_type>
getTakerGetsIssuer() const
{
if (hasTakerGetsIssuer())
return this->sle_.at(sfTakerGetsIssuer);
return std::nullopt;
}
[[nodiscard]]
bool
hasTakerGetsIssuer() const
{
return this->sle_.isFieldPresent(sfTakerGetsIssuer);
}
/**
* Get sfExchangeRate (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getExchangeRate() const
{
if (hasExchangeRate())
return this->sle_.at(sfExchangeRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasExchangeRate() const
{
return this->sle_.isFieldPresent(sfExchangeRate);
}
/**
* Get sfIndexes (soeREQUIRED)
*/
[[nodiscard]]
SF_VECTOR256::type::value_type
getIndexes() const
{
return this->sle_.at(sfIndexes);
}
/**
* Get sfRootIndex (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getRootIndex() const
{
return this->sle_.at(sfRootIndex);
}
/**
* Get sfIndexNext (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getIndexNext() const
{
if (hasIndexNext())
return this->sle_.at(sfIndexNext);
return std::nullopt;
}
[[nodiscard]]
bool
hasIndexNext() const
{
return this->sle_.isFieldPresent(sfIndexNext);
}
/**
* Get sfIndexPrevious (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getIndexPrevious() const
{
if (hasIndexPrevious())
return this->sle_.at(sfIndexPrevious);
return std::nullopt;
}
[[nodiscard]]
bool
hasIndexPrevious() const
{
return this->sle_.isFieldPresent(sfIndexPrevious);
}
/**
* Get sfNFTokenID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getNFTokenID() const
{
if (hasNFTokenID())
return this->sle_.at(sfNFTokenID);
return std::nullopt;
}
[[nodiscard]]
bool
hasNFTokenID() const
{
return this->sle_.isFieldPresent(sfNFTokenID);
}
/**
* Get sfPreviousTxnID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getPreviousTxnID() const
{
if (hasPreviousTxnID())
return this->sle_.at(sfPreviousTxnID);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnID() const
{
return this->sle_.isFieldPresent(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getPreviousTxnLgrSeq() const
{
if (hasPreviousTxnLgrSeq())
return this->sle_.at(sfPreviousTxnLgrSeq);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnLgrSeq() const
{
return this->sle_.isFieldPresent(sfPreviousTxnLgrSeq);
}
/**
* Get sfDomainID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getDomainID() const
{
if (hasDomainID())
return this->sle_.at(sfDomainID);
return std::nullopt;
}
[[nodiscard]]
bool
hasDomainID() const
{
return this->sle_.isFieldPresent(sfDomainID);
}
};
/**
* Builder for DirectoryNode ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class DirectoryNodeBuilder : public LedgerEntryBuilderBase<DirectoryNodeBuilder>
{
public:
DirectoryNodeBuilder(std::decay_t<typename SF_VECTOR256::type::value_type> const& indexes,std::decay_t<typename SF_UINT256::type::value_type> const& rootIndex)
: LedgerEntryBuilderBase<DirectoryNodeBuilder>(ltDIR_NODE)
{
setIndexes(indexes);
setRootIndex(rootIndex);
}
DirectoryNodeBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltDIR_NODE)
{
throw std::runtime_error("Invalid ledger entry type for DirectoryNode");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfOwner (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setOwner(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOwner] = value;
return *this;
}
/**
* Set sfTakerPaysCurrency (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setTakerPaysCurrency(std::decay_t<typename SF_UINT160::type::value_type> const& value)
{
object_[sfTakerPaysCurrency] = value;
return *this;
}
/**
* Set sfTakerPaysIssuer (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setTakerPaysIssuer(std::decay_t<typename SF_UINT160::type::value_type> const& value)
{
object_[sfTakerPaysIssuer] = value;
return *this;
}
/**
* Set sfTakerGetsCurrency (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setTakerGetsCurrency(std::decay_t<typename SF_UINT160::type::value_type> const& value)
{
object_[sfTakerGetsCurrency] = value;
return *this;
}
/**
* Set sfTakerGetsIssuer (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setTakerGetsIssuer(std::decay_t<typename SF_UINT160::type::value_type> const& value)
{
object_[sfTakerGetsIssuer] = value;
return *this;
}
/**
* Set sfExchangeRate (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setExchangeRate(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfExchangeRate] = value;
return *this;
}
/**
* Set sfIndexes (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setIndexes(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfIndexes] = value;
return *this;
}
/**
* Set sfRootIndex (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setRootIndex(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfRootIndex] = value;
return *this;
}
/**
* Set sfIndexNext (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setIndexNext(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfIndexNext] = value;
return *this;
}
/**
* Set sfIndexPrevious (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setIndexPrevious(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfIndexPrevious] = value;
return *this;
}
/**
* Set sfNFTokenID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setNFTokenID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfNFTokenID] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfDomainID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
DirectoryNodeBuilder&
setDomainID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfDomainID] = value;
return *this;
}
/**
* Build and return the completed DirectoryNode wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
DirectoryNode
build(uint256 const& index)
{
return DirectoryNode{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,487 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class EscrowBuilder;
/**
* Ledger Entry: Escrow
* Type: ltESCROW (0x0075)
* RPC Name: escrow
*
* Immutable wrapper around SLE providing type-safe field access.
* Use EscrowBuilder to construct new ledger entries.
*/
class Escrow : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltESCROW;
/**
* Construct a Escrow ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Escrow(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Escrow");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfSequence (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getSequence() const
{
if (hasSequence())
return this->sle_.at(sfSequence);
return std::nullopt;
}
[[nodiscard]]
bool
hasSequence() const
{
return this->sle_.isFieldPresent(sfSequence);
}
/**
* Get sfDestination (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->sle_.at(sfDestination);
}
/**
* Get sfAmount (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->sle_.at(sfAmount);
}
/**
* Get sfCondition (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getCondition() const
{
if (hasCondition())
return this->sle_.at(sfCondition);
return std::nullopt;
}
[[nodiscard]]
bool
hasCondition() const
{
return this->sle_.isFieldPresent(sfCondition);
}
/**
* Get sfCancelAfter (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getCancelAfter() const
{
if (hasCancelAfter())
return this->sle_.at(sfCancelAfter);
return std::nullopt;
}
[[nodiscard]]
bool
hasCancelAfter() const
{
return this->sle_.isFieldPresent(sfCancelAfter);
}
/**
* Get sfFinishAfter (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getFinishAfter() const
{
if (hasFinishAfter())
return this->sle_.at(sfFinishAfter);
return std::nullopt;
}
[[nodiscard]]
bool
hasFinishAfter() const
{
return this->sle_.isFieldPresent(sfFinishAfter);
}
/**
* Get sfSourceTag (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getSourceTag() const
{
if (hasSourceTag())
return this->sle_.at(sfSourceTag);
return std::nullopt;
}
[[nodiscard]]
bool
hasSourceTag() const
{
return this->sle_.isFieldPresent(sfSourceTag);
}
/**
* Get sfDestinationTag (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
{
if (hasDestinationTag())
return this->sle_.at(sfDestinationTag);
return std::nullopt;
}
[[nodiscard]]
bool
hasDestinationTag() const
{
return this->sle_.isFieldPresent(sfDestinationTag);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfDestinationNode (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getDestinationNode() const
{
if (hasDestinationNode())
return this->sle_.at(sfDestinationNode);
return std::nullopt;
}
[[nodiscard]]
bool
hasDestinationNode() const
{
return this->sle_.isFieldPresent(sfDestinationNode);
}
/**
* Get sfTransferRate (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getTransferRate() const
{
if (hasTransferRate())
return this->sle_.at(sfTransferRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasTransferRate() const
{
return this->sle_.isFieldPresent(sfTransferRate);
}
/**
* Get sfIssuerNode (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getIssuerNode() const
{
if (hasIssuerNode())
return this->sle_.at(sfIssuerNode);
return std::nullopt;
}
[[nodiscard]]
bool
hasIssuerNode() const
{
return this->sle_.isFieldPresent(sfIssuerNode);
}
};
/**
* Builder for Escrow ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class EscrowBuilder : public LedgerEntryBuilderBase<EscrowBuilder>
{
public:
EscrowBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination,std::decay_t<typename SF_AMOUNT::type::value_type> const& amount,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<EscrowBuilder>(ltESCROW)
{
setAccount(account);
setDestination(destination);
setAmount(amount);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
EscrowBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltESCROW)
{
throw std::runtime_error("Invalid ledger entry type for Escrow");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfSequence (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfDestination (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* Set sfAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* Set sfCondition (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setCondition(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfCondition] = value;
return *this;
}
/**
* Set sfCancelAfter (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setCancelAfter(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfCancelAfter] = value;
return *this;
}
/**
* Set sfFinishAfter (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setFinishAfter(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfFinishAfter] = value;
return *this;
}
/**
* Set sfSourceTag (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setSourceTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSourceTag] = value;
return *this;
}
/**
* Set sfDestinationTag (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfDestinationNode (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setDestinationNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfDestinationNode] = value;
return *this;
}
/**
* Set sfTransferRate (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setTransferRate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfTransferRate] = value;
return *this;
}
/**
* Set sfIssuerNode (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setIssuerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfIssuerNode] = value;
return *this;
}
/**
* Build and return the completed Escrow wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Escrow
build(uint256 const& index)
{
return Escrow{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,355 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class FeeSettingsBuilder;
/**
* Ledger Entry: FeeSettings
* Type: ltFEE_SETTINGS (0x0073)
* RPC Name: fee
*
* Immutable wrapper around SLE providing type-safe field access.
* Use FeeSettingsBuilder to construct new ledger entries.
*/
class FeeSettings : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltFEE_SETTINGS;
/**
* Construct a FeeSettings ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit FeeSettings(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for FeeSettings");
}
}
// Ledger entry-specific field getters
/**
* Get sfBaseFee (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getBaseFee() const
{
if (hasBaseFee())
return this->sle_.at(sfBaseFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasBaseFee() const
{
return this->sle_.isFieldPresent(sfBaseFee);
}
/**
* Get sfReferenceFeeUnits (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getReferenceFeeUnits() const
{
if (hasReferenceFeeUnits())
return this->sle_.at(sfReferenceFeeUnits);
return std::nullopt;
}
[[nodiscard]]
bool
hasReferenceFeeUnits() const
{
return this->sle_.isFieldPresent(sfReferenceFeeUnits);
}
/**
* Get sfReserveBase (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getReserveBase() const
{
if (hasReserveBase())
return this->sle_.at(sfReserveBase);
return std::nullopt;
}
[[nodiscard]]
bool
hasReserveBase() const
{
return this->sle_.isFieldPresent(sfReserveBase);
}
/**
* Get sfReserveIncrement (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getReserveIncrement() const
{
if (hasReserveIncrement())
return this->sle_.at(sfReserveIncrement);
return std::nullopt;
}
[[nodiscard]]
bool
hasReserveIncrement() const
{
return this->sle_.isFieldPresent(sfReserveIncrement);
}
/**
* Get sfBaseFeeDrops (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getBaseFeeDrops() const
{
if (hasBaseFeeDrops())
return this->sle_.at(sfBaseFeeDrops);
return std::nullopt;
}
[[nodiscard]]
bool
hasBaseFeeDrops() const
{
return this->sle_.isFieldPresent(sfBaseFeeDrops);
}
/**
* Get sfReserveBaseDrops (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getReserveBaseDrops() const
{
if (hasReserveBaseDrops())
return this->sle_.at(sfReserveBaseDrops);
return std::nullopt;
}
[[nodiscard]]
bool
hasReserveBaseDrops() const
{
return this->sle_.isFieldPresent(sfReserveBaseDrops);
}
/**
* Get sfReserveIncrementDrops (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getReserveIncrementDrops() const
{
if (hasReserveIncrementDrops())
return this->sle_.at(sfReserveIncrementDrops);
return std::nullopt;
}
[[nodiscard]]
bool
hasReserveIncrementDrops() const
{
return this->sle_.isFieldPresent(sfReserveIncrementDrops);
}
/**
* Get sfPreviousTxnID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getPreviousTxnID() const
{
if (hasPreviousTxnID())
return this->sle_.at(sfPreviousTxnID);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnID() const
{
return this->sle_.isFieldPresent(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getPreviousTxnLgrSeq() const
{
if (hasPreviousTxnLgrSeq())
return this->sle_.at(sfPreviousTxnLgrSeq);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnLgrSeq() const
{
return this->sle_.isFieldPresent(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for FeeSettings ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class FeeSettingsBuilder : public LedgerEntryBuilderBase<FeeSettingsBuilder>
{
public:
FeeSettingsBuilder()
: LedgerEntryBuilderBase<FeeSettingsBuilder>(ltFEE_SETTINGS)
{
}
FeeSettingsBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltFEE_SETTINGS)
{
throw std::runtime_error("Invalid ledger entry type for FeeSettings");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfBaseFee (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setBaseFee(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfBaseFee] = value;
return *this;
}
/**
* Set sfReferenceFeeUnits (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setReferenceFeeUnits(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfReferenceFeeUnits] = value;
return *this;
}
/**
* Set sfReserveBase (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setReserveBase(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfReserveBase] = value;
return *this;
}
/**
* Set sfReserveIncrement (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setReserveIncrement(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfReserveIncrement] = value;
return *this;
}
/**
* Set sfBaseFeeDrops (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setBaseFeeDrops(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfBaseFeeDrops] = value;
return *this;
}
/**
* Set sfReserveBaseDrops (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setReserveBaseDrops(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfReserveBaseDrops] = value;
return *this;
}
/**
* Set sfReserveIncrementDrops (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setReserveIncrementDrops(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfReserveIncrementDrops] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
FeeSettingsBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed FeeSettings wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
FeeSettings
build(uint256 const& index)
{
return FeeSettings{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,167 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class LedgerHashesBuilder;
/**
* Ledger Entry: LedgerHashes
* Type: ltLEDGER_HASHES (0x0068)
* RPC Name: hashes
*
* Immutable wrapper around SLE providing type-safe field access.
* Use LedgerHashesBuilder to construct new ledger entries.
*/
class LedgerHashes : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltLEDGER_HASHES;
/**
* Construct a LedgerHashes ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit LedgerHashes(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for LedgerHashes");
}
}
// Ledger entry-specific field getters
/**
* Get sfFirstLedgerSequence (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getFirstLedgerSequence() const
{
if (hasFirstLedgerSequence())
return this->sle_.at(sfFirstLedgerSequence);
return std::nullopt;
}
[[nodiscard]]
bool
hasFirstLedgerSequence() const
{
return this->sle_.isFieldPresent(sfFirstLedgerSequence);
}
/**
* Get sfLastLedgerSequence (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getLastLedgerSequence() const
{
if (hasLastLedgerSequence())
return this->sle_.at(sfLastLedgerSequence);
return std::nullopt;
}
[[nodiscard]]
bool
hasLastLedgerSequence() const
{
return this->sle_.isFieldPresent(sfLastLedgerSequence);
}
/**
* Get sfHashes (soeREQUIRED)
*/
[[nodiscard]]
SF_VECTOR256::type::value_type
getHashes() const
{
return this->sle_.at(sfHashes);
}
};
/**
* Builder for LedgerHashes ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class LedgerHashesBuilder : public LedgerEntryBuilderBase<LedgerHashesBuilder>
{
public:
LedgerHashesBuilder(std::decay_t<typename SF_VECTOR256::type::value_type> const& hashes)
: LedgerEntryBuilderBase<LedgerHashesBuilder>(ltLEDGER_HASHES)
{
setHashes(hashes);
}
LedgerHashesBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltLEDGER_HASHES)
{
throw std::runtime_error("Invalid ledger entry type for LedgerHashes");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfFirstLedgerSequence (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
LedgerHashesBuilder&
setFirstLedgerSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfFirstLedgerSequence] = value;
return *this;
}
/**
* Set sfLastLedgerSequence (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
LedgerHashesBuilder&
setLastLedgerSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfLastLedgerSequence] = value;
return *this;
}
/**
* Set sfHashes (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LedgerHashesBuilder&
setHashes(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfHashes] = value;
return *this;
}
/**
* Build and return the completed LedgerHashes wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
LedgerHashes
build(uint256 const& index)
{
return LedgerHashes{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,815 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class LoanBuilder;
/**
* Ledger Entry: Loan
* Type: ltLOAN (0x0089)
* RPC Name: loan
*
* Immutable wrapper around SLE providing type-safe field access.
* Use LoanBuilder to construct new ledger entries.
*/
class Loan : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltLOAN;
/**
* Construct a Loan ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Loan(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Loan");
}
}
// Ledger entry-specific field getters
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfLoanBrokerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getLoanBrokerNode() const
{
return this->sle_.at(sfLoanBrokerNode);
}
/**
* Get sfLoanBrokerID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getLoanBrokerID() const
{
return this->sle_.at(sfLoanBrokerID);
}
/**
* Get sfLoanSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getLoanSequence() const
{
return this->sle_.at(sfLoanSequence);
}
/**
* Get sfBorrower (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getBorrower() const
{
return this->sle_.at(sfBorrower);
}
/**
* Get sfLoanOriginationFee (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getLoanOriginationFee() const
{
if (hasLoanOriginationFee())
return this->sle_.at(sfLoanOriginationFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasLoanOriginationFee() const
{
return this->sle_.isFieldPresent(sfLoanOriginationFee);
}
/**
* Get sfLoanServiceFee (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getLoanServiceFee() const
{
if (hasLoanServiceFee())
return this->sle_.at(sfLoanServiceFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasLoanServiceFee() const
{
return this->sle_.isFieldPresent(sfLoanServiceFee);
}
/**
* Get sfLatePaymentFee (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getLatePaymentFee() const
{
if (hasLatePaymentFee())
return this->sle_.at(sfLatePaymentFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasLatePaymentFee() const
{
return this->sle_.isFieldPresent(sfLatePaymentFee);
}
/**
* Get sfClosePaymentFee (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getClosePaymentFee() const
{
if (hasClosePaymentFee())
return this->sle_.at(sfClosePaymentFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasClosePaymentFee() const
{
return this->sle_.isFieldPresent(sfClosePaymentFee);
}
/**
* Get sfOverpaymentFee (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getOverpaymentFee() const
{
if (hasOverpaymentFee())
return this->sle_.at(sfOverpaymentFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasOverpaymentFee() const
{
return this->sle_.isFieldPresent(sfOverpaymentFee);
}
/**
* Get sfInterestRate (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getInterestRate() const
{
if (hasInterestRate())
return this->sle_.at(sfInterestRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasInterestRate() const
{
return this->sle_.isFieldPresent(sfInterestRate);
}
/**
* Get sfLateInterestRate (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getLateInterestRate() const
{
if (hasLateInterestRate())
return this->sle_.at(sfLateInterestRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasLateInterestRate() const
{
return this->sle_.isFieldPresent(sfLateInterestRate);
}
/**
* Get sfCloseInterestRate (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getCloseInterestRate() const
{
if (hasCloseInterestRate())
return this->sle_.at(sfCloseInterestRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasCloseInterestRate() const
{
return this->sle_.isFieldPresent(sfCloseInterestRate);
}
/**
* Get sfOverpaymentInterestRate (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getOverpaymentInterestRate() const
{
if (hasOverpaymentInterestRate())
return this->sle_.at(sfOverpaymentInterestRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasOverpaymentInterestRate() const
{
return this->sle_.isFieldPresent(sfOverpaymentInterestRate);
}
/**
* Get sfStartDate (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getStartDate() const
{
return this->sle_.at(sfStartDate);
}
/**
* Get sfPaymentInterval (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPaymentInterval() const
{
return this->sle_.at(sfPaymentInterval);
}
/**
* Get sfGracePeriod (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getGracePeriod() const
{
if (hasGracePeriod())
return this->sle_.at(sfGracePeriod);
return std::nullopt;
}
[[nodiscard]]
bool
hasGracePeriod() const
{
return this->sle_.isFieldPresent(sfGracePeriod);
}
/**
* Get sfPreviousPaymentDueDate (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getPreviousPaymentDueDate() const
{
if (hasPreviousPaymentDueDate())
return this->sle_.at(sfPreviousPaymentDueDate);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousPaymentDueDate() const
{
return this->sle_.isFieldPresent(sfPreviousPaymentDueDate);
}
/**
* Get sfNextPaymentDueDate (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getNextPaymentDueDate() const
{
if (hasNextPaymentDueDate())
return this->sle_.at(sfNextPaymentDueDate);
return std::nullopt;
}
[[nodiscard]]
bool
hasNextPaymentDueDate() const
{
return this->sle_.isFieldPresent(sfNextPaymentDueDate);
}
/**
* Get sfPaymentRemaining (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getPaymentRemaining() const
{
if (hasPaymentRemaining())
return this->sle_.at(sfPaymentRemaining);
return std::nullopt;
}
[[nodiscard]]
bool
hasPaymentRemaining() const
{
return this->sle_.isFieldPresent(sfPaymentRemaining);
}
/**
* Get sfPeriodicPayment (soeREQUIRED)
*/
[[nodiscard]]
SF_NUMBER::type::value_type
getPeriodicPayment() const
{
return this->sle_.at(sfPeriodicPayment);
}
/**
* Get sfPrincipalOutstanding (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getPrincipalOutstanding() const
{
if (hasPrincipalOutstanding())
return this->sle_.at(sfPrincipalOutstanding);
return std::nullopt;
}
[[nodiscard]]
bool
hasPrincipalOutstanding() const
{
return this->sle_.isFieldPresent(sfPrincipalOutstanding);
}
/**
* Get sfTotalValueOutstanding (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getTotalValueOutstanding() const
{
if (hasTotalValueOutstanding())
return this->sle_.at(sfTotalValueOutstanding);
return std::nullopt;
}
[[nodiscard]]
bool
hasTotalValueOutstanding() const
{
return this->sle_.isFieldPresent(sfTotalValueOutstanding);
}
/**
* Get sfManagementFeeOutstanding (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getManagementFeeOutstanding() const
{
if (hasManagementFeeOutstanding())
return this->sle_.at(sfManagementFeeOutstanding);
return std::nullopt;
}
[[nodiscard]]
bool
hasManagementFeeOutstanding() const
{
return this->sle_.isFieldPresent(sfManagementFeeOutstanding);
}
/**
* Get sfLoanScale (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_INT32::type::value_type>
getLoanScale() const
{
if (hasLoanScale())
return this->sle_.at(sfLoanScale);
return std::nullopt;
}
[[nodiscard]]
bool
hasLoanScale() const
{
return this->sle_.isFieldPresent(sfLoanScale);
}
};
/**
* Builder for Loan ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class LoanBuilder : public LedgerEntryBuilderBase<LoanBuilder>
{
public:
LoanBuilder(std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT64::type::value_type> const& loanBrokerNode,std::decay_t<typename SF_UINT256::type::value_type> const& loanBrokerID,std::decay_t<typename SF_UINT32::type::value_type> const& loanSequence,std::decay_t<typename SF_ACCOUNT::type::value_type> const& borrower,std::decay_t<typename SF_UINT32::type::value_type> const& startDate,std::decay_t<typename SF_UINT32::type::value_type> const& paymentInterval,std::decay_t<typename SF_NUMBER::type::value_type> const& periodicPayment)
: LedgerEntryBuilderBase<LoanBuilder>(ltLOAN)
{
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
setOwnerNode(ownerNode);
setLoanBrokerNode(loanBrokerNode);
setLoanBrokerID(loanBrokerID);
setLoanSequence(loanSequence);
setBorrower(borrower);
setStartDate(startDate);
setPaymentInterval(paymentInterval);
setPeriodicPayment(periodicPayment);
}
LoanBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltLOAN)
{
throw std::runtime_error("Invalid ledger entry type for Loan");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfLoanBrokerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLoanBrokerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfLoanBrokerNode] = value;
return *this;
}
/**
* Set sfLoanBrokerID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLoanBrokerID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfLoanBrokerID] = value;
return *this;
}
/**
* Set sfLoanSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLoanSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfLoanSequence] = value;
return *this;
}
/**
* Set sfBorrower (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setBorrower(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfBorrower] = value;
return *this;
}
/**
* Set sfLoanOriginationFee (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLoanOriginationFee(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfLoanOriginationFee] = value;
return *this;
}
/**
* Set sfLoanServiceFee (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLoanServiceFee(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfLoanServiceFee] = value;
return *this;
}
/**
* Set sfLatePaymentFee (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLatePaymentFee(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfLatePaymentFee] = value;
return *this;
}
/**
* Set sfClosePaymentFee (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setClosePaymentFee(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfClosePaymentFee] = value;
return *this;
}
/**
* Set sfOverpaymentFee (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setOverpaymentFee(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfOverpaymentFee] = value;
return *this;
}
/**
* Set sfInterestRate (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setInterestRate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfInterestRate] = value;
return *this;
}
/**
* Set sfLateInterestRate (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLateInterestRate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfLateInterestRate] = value;
return *this;
}
/**
* Set sfCloseInterestRate (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setCloseInterestRate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfCloseInterestRate] = value;
return *this;
}
/**
* Set sfOverpaymentInterestRate (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setOverpaymentInterestRate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfOverpaymentInterestRate] = value;
return *this;
}
/**
* Set sfStartDate (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setStartDate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfStartDate] = value;
return *this;
}
/**
* Set sfPaymentInterval (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setPaymentInterval(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPaymentInterval] = value;
return *this;
}
/**
* Set sfGracePeriod (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setGracePeriod(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfGracePeriod] = value;
return *this;
}
/**
* Set sfPreviousPaymentDueDate (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setPreviousPaymentDueDate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousPaymentDueDate] = value;
return *this;
}
/**
* Set sfNextPaymentDueDate (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setNextPaymentDueDate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfNextPaymentDueDate] = value;
return *this;
}
/**
* Set sfPaymentRemaining (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setPaymentRemaining(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPaymentRemaining] = value;
return *this;
}
/**
* Set sfPeriodicPayment (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setPeriodicPayment(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfPeriodicPayment] = value;
return *this;
}
/**
* Set sfPrincipalOutstanding (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setPrincipalOutstanding(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfPrincipalOutstanding] = value;
return *this;
}
/**
* Set sfTotalValueOutstanding (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setTotalValueOutstanding(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfTotalValueOutstanding] = value;
return *this;
}
/**
* Set sfManagementFeeOutstanding (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setManagementFeeOutstanding(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfManagementFeeOutstanding] = value;
return *this;
}
/**
* Set sfLoanScale (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBuilder&
setLoanScale(std::decay_t<typename SF_INT32::type::value_type> const& value)
{
object_[sfLoanScale] = value;
return *this;
}
/**
* Build and return the completed Loan wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Loan
build(uint256 const& index)
{
return Loan{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,523 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class LoanBrokerBuilder;
/**
* Ledger Entry: LoanBroker
* Type: ltLOAN_BROKER (0x0088)
* RPC Name: loan_broker
*
* Immutable wrapper around SLE providing type-safe field access.
* Use LoanBrokerBuilder to construct new ledger entries.
*/
class LoanBroker : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltLOAN_BROKER;
/**
* Construct a LoanBroker ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit LoanBroker(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for LoanBroker");
}
}
// Ledger entry-specific field getters
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSequence() const
{
return this->sle_.at(sfSequence);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfVaultNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getVaultNode() const
{
return this->sle_.at(sfVaultNode);
}
/**
* Get sfVaultID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getVaultID() const
{
return this->sle_.at(sfVaultID);
}
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfOwner (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOwner() const
{
return this->sle_.at(sfOwner);
}
/**
* Get sfLoanSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getLoanSequence() const
{
return this->sle_.at(sfLoanSequence);
}
/**
* Get sfData (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getData() const
{
if (hasData())
return this->sle_.at(sfData);
return std::nullopt;
}
[[nodiscard]]
bool
hasData() const
{
return this->sle_.isFieldPresent(sfData);
}
/**
* Get sfManagementFeeRate (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT16::type::value_type>
getManagementFeeRate() const
{
if (hasManagementFeeRate())
return this->sle_.at(sfManagementFeeRate);
return std::nullopt;
}
[[nodiscard]]
bool
hasManagementFeeRate() const
{
return this->sle_.isFieldPresent(sfManagementFeeRate);
}
/**
* Get sfOwnerCount (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getOwnerCount() const
{
if (hasOwnerCount())
return this->sle_.at(sfOwnerCount);
return std::nullopt;
}
[[nodiscard]]
bool
hasOwnerCount() const
{
return this->sle_.isFieldPresent(sfOwnerCount);
}
/**
* Get sfDebtTotal (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getDebtTotal() const
{
if (hasDebtTotal())
return this->sle_.at(sfDebtTotal);
return std::nullopt;
}
[[nodiscard]]
bool
hasDebtTotal() const
{
return this->sle_.isFieldPresent(sfDebtTotal);
}
/**
* Get sfDebtMaximum (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getDebtMaximum() const
{
if (hasDebtMaximum())
return this->sle_.at(sfDebtMaximum);
return std::nullopt;
}
[[nodiscard]]
bool
hasDebtMaximum() const
{
return this->sle_.isFieldPresent(sfDebtMaximum);
}
/**
* Get sfCoverAvailable (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_NUMBER::type::value_type>
getCoverAvailable() const
{
if (hasCoverAvailable())
return this->sle_.at(sfCoverAvailable);
return std::nullopt;
}
[[nodiscard]]
bool
hasCoverAvailable() const
{
return this->sle_.isFieldPresent(sfCoverAvailable);
}
/**
* Get sfCoverRateMinimum (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getCoverRateMinimum() const
{
if (hasCoverRateMinimum())
return this->sle_.at(sfCoverRateMinimum);
return std::nullopt;
}
[[nodiscard]]
bool
hasCoverRateMinimum() const
{
return this->sle_.isFieldPresent(sfCoverRateMinimum);
}
/**
* Get sfCoverRateLiquidation (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getCoverRateLiquidation() const
{
if (hasCoverRateLiquidation())
return this->sle_.at(sfCoverRateLiquidation);
return std::nullopt;
}
[[nodiscard]]
bool
hasCoverRateLiquidation() const
{
return this->sle_.isFieldPresent(sfCoverRateLiquidation);
}
};
/**
* Builder for LoanBroker ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class LoanBrokerBuilder : public LedgerEntryBuilderBase<LoanBrokerBuilder>
{
public:
LoanBrokerBuilder(std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq,std::decay_t<typename SF_UINT32::type::value_type> const& sequence,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT64::type::value_type> const& vaultNode,std::decay_t<typename SF_UINT256::type::value_type> const& vaultID,std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_ACCOUNT::type::value_type> const& owner,std::decay_t<typename SF_UINT32::type::value_type> const& loanSequence)
: LedgerEntryBuilderBase<LoanBrokerBuilder>(ltLOAN_BROKER)
{
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
setSequence(sequence);
setOwnerNode(ownerNode);
setVaultNode(vaultNode);
setVaultID(vaultID);
setAccount(account);
setOwner(owner);
setLoanSequence(loanSequence);
}
LoanBrokerBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltLOAN_BROKER)
{
throw std::runtime_error("Invalid ledger entry type for LoanBroker");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfVaultNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setVaultNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfVaultNode] = value;
return *this;
}
/**
* Set sfVaultID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setVaultID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfVaultID] = value;
return *this;
}
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfOwner (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setOwner(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOwner] = value;
return *this;
}
/**
* Set sfLoanSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setLoanSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfLoanSequence] = value;
return *this;
}
/**
* Set sfData (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfData] = value;
return *this;
}
/**
* Set sfManagementFeeRate (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setManagementFeeRate(std::decay_t<typename SF_UINT16::type::value_type> const& value)
{
object_[sfManagementFeeRate] = value;
return *this;
}
/**
* Set sfOwnerCount (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setOwnerCount(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfOwnerCount] = value;
return *this;
}
/**
* Set sfDebtTotal (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setDebtTotal(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfDebtTotal] = value;
return *this;
}
/**
* Set sfDebtMaximum (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setDebtMaximum(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfDebtMaximum] = value;
return *this;
}
/**
* Set sfCoverAvailable (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setCoverAvailable(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
{
object_[sfCoverAvailable] = value;
return *this;
}
/**
* Set sfCoverRateMinimum (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setCoverRateMinimum(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfCoverRateMinimum] = value;
return *this;
}
/**
* Set sfCoverRateLiquidation (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
LoanBrokerBuilder&
setCoverRateLiquidation(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfCoverRateLiquidation] = value;
return *this;
}
/**
* Build and return the completed LoanBroker wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
LoanBroker
build(uint256 const& index)
{
return LoanBroker{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,255 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class MPTokenBuilder;
/**
* Ledger Entry: MPToken
* Type: ltMPTOKEN (0x007f)
* RPC Name: mptoken
*
* Immutable wrapper around SLE providing type-safe field access.
* Use MPTokenBuilder to construct new ledger entries.
*/
class MPToken : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltMPTOKEN;
/**
* Construct a MPToken ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit MPToken(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for MPToken");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfMPTokenIssuanceID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->sle_.at(sfMPTokenIssuanceID);
}
/**
* Get sfMPTAmount (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getMPTAmount() const
{
if (hasMPTAmount())
return this->sle_.at(sfMPTAmount);
return std::nullopt;
}
[[nodiscard]]
bool
hasMPTAmount() const
{
return this->sle_.isFieldPresent(sfMPTAmount);
}
/**
* Get sfLockedAmount (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getLockedAmount() const
{
if (hasLockedAmount())
return this->sle_.at(sfLockedAmount);
return std::nullopt;
}
[[nodiscard]]
bool
hasLockedAmount() const
{
return this->sle_.isFieldPresent(sfLockedAmount);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for MPToken ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class MPTokenBuilder : public LedgerEntryBuilderBase<MPTokenBuilder>
{
public:
MPTokenBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<MPTokenBuilder>(ltMPTOKEN)
{
setAccount(account);
setMPTokenIssuanceID(mPTokenIssuanceID);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
MPTokenBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltMPTOKEN)
{
throw std::runtime_error("Invalid ledger entry type for MPToken");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfMPTokenIssuanceID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* Set sfMPTAmount (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* Set sfLockedAmount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setLockedAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfLockedAmount] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed MPToken wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
MPToken
build(uint256 const& index)
{
return MPToken{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,427 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class MPTokenIssuanceBuilder;
/**
* Ledger Entry: MPTokenIssuance
* Type: ltMPTOKEN_ISSUANCE (0x007e)
* RPC Name: mpt_issuance
*
* Immutable wrapper around SLE providing type-safe field access.
* Use MPTokenIssuanceBuilder to construct new ledger entries.
*/
class MPTokenIssuance : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltMPTOKEN_ISSUANCE;
/**
* Construct a MPTokenIssuance ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit MPTokenIssuance(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for MPTokenIssuance");
}
}
// Ledger entry-specific field getters
/**
* Get sfIssuer (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getIssuer() const
{
return this->sle_.at(sfIssuer);
}
/**
* Get sfSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSequence() const
{
return this->sle_.at(sfSequence);
}
/**
* Get sfTransferFee (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT16::type::value_type>
getTransferFee() const
{
if (hasTransferFee())
return this->sle_.at(sfTransferFee);
return std::nullopt;
}
[[nodiscard]]
bool
hasTransferFee() const
{
return this->sle_.isFieldPresent(sfTransferFee);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfAssetScale (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT8::type::value_type>
getAssetScale() const
{
if (hasAssetScale())
return this->sle_.at(sfAssetScale);
return std::nullopt;
}
[[nodiscard]]
bool
hasAssetScale() const
{
return this->sle_.isFieldPresent(sfAssetScale);
}
/**
* Get sfMaximumAmount (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getMaximumAmount() const
{
if (hasMaximumAmount())
return this->sle_.at(sfMaximumAmount);
return std::nullopt;
}
[[nodiscard]]
bool
hasMaximumAmount() const
{
return this->sle_.isFieldPresent(sfMaximumAmount);
}
/**
* Get sfOutstandingAmount (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOutstandingAmount() const
{
return this->sle_.at(sfOutstandingAmount);
}
/**
* Get sfLockedAmount (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getLockedAmount() const
{
if (hasLockedAmount())
return this->sle_.at(sfLockedAmount);
return std::nullopt;
}
[[nodiscard]]
bool
hasLockedAmount() const
{
return this->sle_.isFieldPresent(sfLockedAmount);
}
/**
* Get sfMPTokenMetadata (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getMPTokenMetadata() const
{
if (hasMPTokenMetadata())
return this->sle_.at(sfMPTokenMetadata);
return std::nullopt;
}
[[nodiscard]]
bool
hasMPTokenMetadata() const
{
return this->sle_.isFieldPresent(sfMPTokenMetadata);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfDomainID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getDomainID() const
{
if (hasDomainID())
return this->sle_.at(sfDomainID);
return std::nullopt;
}
[[nodiscard]]
bool
hasDomainID() const
{
return this->sle_.isFieldPresent(sfDomainID);
}
/**
* Get sfMutableFlags (soeDEFAULT)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getMutableFlags() const
{
if (hasMutableFlags())
return this->sle_.at(sfMutableFlags);
return std::nullopt;
}
[[nodiscard]]
bool
hasMutableFlags() const
{
return this->sle_.isFieldPresent(sfMutableFlags);
}
};
/**
* Builder for MPTokenIssuance ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class MPTokenIssuanceBuilder : public LedgerEntryBuilderBase<MPTokenIssuanceBuilder>
{
public:
MPTokenIssuanceBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& issuer,std::decay_t<typename SF_UINT32::type::value_type> const& sequence,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT64::type::value_type> const& outstandingAmount,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<MPTokenIssuanceBuilder>(ltMPTOKEN_ISSUANCE)
{
setIssuer(issuer);
setSequence(sequence);
setOwnerNode(ownerNode);
setOutstandingAmount(outstandingAmount);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
MPTokenIssuanceBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltMPTOKEN_ISSUANCE)
{
throw std::runtime_error("Invalid ledger entry type for MPTokenIssuance");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfIssuer (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setIssuer(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfIssuer] = value;
return *this;
}
/**
* Set sfSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfTransferFee (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setTransferFee(std::decay_t<typename SF_UINT16::type::value_type> const& value)
{
object_[sfTransferFee] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfAssetScale (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setAssetScale(std::decay_t<typename SF_UINT8::type::value_type> const& value)
{
object_[sfAssetScale] = value;
return *this;
}
/**
* Set sfMaximumAmount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setMaximumAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMaximumAmount] = value;
return *this;
}
/**
* Set sfOutstandingAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setOutstandingAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOutstandingAmount] = value;
return *this;
}
/**
* Set sfLockedAmount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setLockedAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfLockedAmount] = value;
return *this;
}
/**
* Set sfMPTokenMetadata (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setMPTokenMetadata(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfMPTokenMetadata] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfDomainID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setDomainID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfDomainID] = value;
return *this;
}
/**
* Set sfMutableFlags (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setMutableFlags(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfMutableFlags] = value;
return *this;
}
/**
* Build and return the completed MPTokenIssuance wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
MPTokenIssuance
build(uint256 const& index)
{
return MPTokenIssuance{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,299 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class NFTokenOfferBuilder;
/**
* Ledger Entry: NFTokenOffer
* Type: ltNFTOKEN_OFFER (0x0037)
* RPC Name: nft_offer
*
* Immutable wrapper around SLE providing type-safe field access.
* Use NFTokenOfferBuilder to construct new ledger entries.
*/
class NFTokenOffer : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltNFTOKEN_OFFER;
/**
* Construct a NFTokenOffer ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit NFTokenOffer(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for NFTokenOffer");
}
}
// Ledger entry-specific field getters
/**
* Get sfOwner (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOwner() const
{
return this->sle_.at(sfOwner);
}
/**
* Get sfNFTokenID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getNFTokenID() const
{
return this->sle_.at(sfNFTokenID);
}
/**
* Get sfAmount (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->sle_.at(sfAmount);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfNFTokenOfferNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getNFTokenOfferNode() const
{
return this->sle_.at(sfNFTokenOfferNode);
}
/**
* Get sfDestination (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getDestination() const
{
if (hasDestination())
return this->sle_.at(sfDestination);
return std::nullopt;
}
[[nodiscard]]
bool
hasDestination() const
{
return this->sle_.isFieldPresent(sfDestination);
}
/**
* Get sfExpiration (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getExpiration() const
{
if (hasExpiration())
return this->sle_.at(sfExpiration);
return std::nullopt;
}
[[nodiscard]]
bool
hasExpiration() const
{
return this->sle_.isFieldPresent(sfExpiration);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for NFTokenOffer ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class NFTokenOfferBuilder : public LedgerEntryBuilderBase<NFTokenOfferBuilder>
{
public:
NFTokenOfferBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& owner,std::decay_t<typename SF_UINT256::type::value_type> const& nFTokenID,std::decay_t<typename SF_AMOUNT::type::value_type> const& amount,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT64::type::value_type> const& nFTokenOfferNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<NFTokenOfferBuilder>(ltNFTOKEN_OFFER)
{
setOwner(owner);
setNFTokenID(nFTokenID);
setAmount(amount);
setOwnerNode(ownerNode);
setNFTokenOfferNode(nFTokenOfferNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
NFTokenOfferBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltNFTOKEN_OFFER)
{
throw std::runtime_error("Invalid ledger entry type for NFTokenOffer");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfOwner (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setOwner(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOwner] = value;
return *this;
}
/**
* Set sfNFTokenID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setNFTokenID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfNFTokenID] = value;
return *this;
}
/**
* Set sfAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfNFTokenOfferNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setNFTokenOfferNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfNFTokenOfferNode] = value;
return *this;
}
/**
* Set sfDestination (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* Set sfExpiration (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setExpiration(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfExpiration] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenOfferBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed NFTokenOffer wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
NFTokenOffer
build(uint256 const& index)
{
return NFTokenOffer{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,212 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class NFTokenPageBuilder;
/**
* Ledger Entry: NFTokenPage
* Type: ltNFTOKEN_PAGE (0x0050)
* RPC Name: nft_page
*
* Immutable wrapper around SLE providing type-safe field access.
* Use NFTokenPageBuilder to construct new ledger entries.
*/
class NFTokenPage : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltNFTOKEN_PAGE;
/**
* Construct a NFTokenPage ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit NFTokenPage(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for NFTokenPage");
}
}
// Ledger entry-specific field getters
/**
* Get sfPreviousPageMin (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getPreviousPageMin() const
{
if (hasPreviousPageMin())
return this->sle_.at(sfPreviousPageMin);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousPageMin() const
{
return this->sle_.isFieldPresent(sfPreviousPageMin);
}
/**
* Get sfNextPageMin (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getNextPageMin() const
{
if (hasNextPageMin())
return this->sle_.at(sfNextPageMin);
return std::nullopt;
}
[[nodiscard]]
bool
hasNextPageMin() const
{
return this->sle_.isFieldPresent(sfNextPageMin);
}
/**
* Get sfNFTokens (soeREQUIRED)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
STArray const&
getNFTokens() const
{
return this->sle_.getFieldArray(sfNFTokens);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for NFTokenPage ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class NFTokenPageBuilder : public LedgerEntryBuilderBase<NFTokenPageBuilder>
{
public:
NFTokenPageBuilder(STArray const& nFTokens,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<NFTokenPageBuilder>(ltNFTOKEN_PAGE)
{
setNFTokens(nFTokens);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
NFTokenPageBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltNFTOKEN_PAGE)
{
throw std::runtime_error("Invalid ledger entry type for NFTokenPage");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfPreviousPageMin (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NFTokenPageBuilder&
setPreviousPageMin(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousPageMin] = value;
return *this;
}
/**
* Set sfNextPageMin (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NFTokenPageBuilder&
setNextPageMin(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfNextPageMin] = value;
return *this;
}
/**
* Set sfNFTokens (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenPageBuilder&
setNFTokens(STArray const& value)
{
object_.setFieldArray(sfNFTokens, value);
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenPageBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
NFTokenPageBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed NFTokenPage wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
NFTokenPage
build(uint256 const& index)
{
return NFTokenPage{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,236 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class NegativeUNLBuilder;
/**
* Ledger Entry: NegativeUNL
* Type: ltNEGATIVE_UNL (0x004e)
* RPC Name: nunl
*
* Immutable wrapper around SLE providing type-safe field access.
* Use NegativeUNLBuilder to construct new ledger entries.
*/
class NegativeUNL : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltNEGATIVE_UNL;
/**
* Construct a NegativeUNL ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit NegativeUNL(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for NegativeUNL");
}
}
// Ledger entry-specific field getters
/**
* Get sfDisabledValidators (soeOPTIONAL)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
std::optional<std::reference_wrapper<STArray const>>
getDisabledValidators() const
{
if (this->sle_.isFieldPresent(sfDisabledValidators))
return this->sle_.getFieldArray(sfDisabledValidators);
return std::nullopt;
}
[[nodiscard]]
bool
hasDisabledValidators() const
{
return this->sle_.isFieldPresent(sfDisabledValidators);
}
/**
* Get sfValidatorToDisable (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getValidatorToDisable() const
{
if (hasValidatorToDisable())
return this->sle_.at(sfValidatorToDisable);
return std::nullopt;
}
[[nodiscard]]
bool
hasValidatorToDisable() const
{
return this->sle_.isFieldPresent(sfValidatorToDisable);
}
/**
* Get sfValidatorToReEnable (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getValidatorToReEnable() const
{
if (hasValidatorToReEnable())
return this->sle_.at(sfValidatorToReEnable);
return std::nullopt;
}
[[nodiscard]]
bool
hasValidatorToReEnable() const
{
return this->sle_.isFieldPresent(sfValidatorToReEnable);
}
/**
* Get sfPreviousTxnID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getPreviousTxnID() const
{
if (hasPreviousTxnID())
return this->sle_.at(sfPreviousTxnID);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnID() const
{
return this->sle_.isFieldPresent(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getPreviousTxnLgrSeq() const
{
if (hasPreviousTxnLgrSeq())
return this->sle_.at(sfPreviousTxnLgrSeq);
return std::nullopt;
}
[[nodiscard]]
bool
hasPreviousTxnLgrSeq() const
{
return this->sle_.isFieldPresent(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for NegativeUNL ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class NegativeUNLBuilder : public LedgerEntryBuilderBase<NegativeUNLBuilder>
{
public:
NegativeUNLBuilder()
: LedgerEntryBuilderBase<NegativeUNLBuilder>(ltNEGATIVE_UNL)
{
}
NegativeUNLBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltNEGATIVE_UNL)
{
throw std::runtime_error("Invalid ledger entry type for NegativeUNL");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfDisabledValidators (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NegativeUNLBuilder&
setDisabledValidators(STArray const& value)
{
object_.setFieldArray(sfDisabledValidators, value);
return *this;
}
/**
* Set sfValidatorToDisable (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NegativeUNLBuilder&
setValidatorToDisable(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfValidatorToDisable] = value;
return *this;
}
/**
* Set sfValidatorToReEnable (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NegativeUNLBuilder&
setValidatorToReEnable(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfValidatorToReEnable] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NegativeUNLBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
NegativeUNLBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed NegativeUNL wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
NegativeUNL
build(uint256 const& index)
{
return NegativeUNL{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,374 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class OfferBuilder;
/**
* Ledger Entry: Offer
* Type: ltOFFER (0x006f)
* RPC Name: offer
*
* Immutable wrapper around SLE providing type-safe field access.
* Use OfferBuilder to construct new ledger entries.
*/
class Offer : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltOFFER;
/**
* Construct a Offer ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Offer(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Offer");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSequence() const
{
return this->sle_.at(sfSequence);
}
/**
* Get sfTakerPays (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getTakerPays() const
{
return this->sle_.at(sfTakerPays);
}
/**
* Get sfTakerGets (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getTakerGets() const
{
return this->sle_.at(sfTakerGets);
}
/**
* Get sfBookDirectory (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getBookDirectory() const
{
return this->sle_.at(sfBookDirectory);
}
/**
* Get sfBookNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getBookNode() const
{
return this->sle_.at(sfBookNode);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfExpiration (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getExpiration() const
{
if (hasExpiration())
return this->sle_.at(sfExpiration);
return std::nullopt;
}
[[nodiscard]]
bool
hasExpiration() const
{
return this->sle_.isFieldPresent(sfExpiration);
}
/**
* Get sfDomainID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getDomainID() const
{
if (hasDomainID())
return this->sle_.at(sfDomainID);
return std::nullopt;
}
[[nodiscard]]
bool
hasDomainID() const
{
return this->sle_.isFieldPresent(sfDomainID);
}
/**
* Get sfAdditionalBooks (soeOPTIONAL)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
std::optional<std::reference_wrapper<STArray const>>
getAdditionalBooks() const
{
if (this->sle_.isFieldPresent(sfAdditionalBooks))
return this->sle_.getFieldArray(sfAdditionalBooks);
return std::nullopt;
}
[[nodiscard]]
bool
hasAdditionalBooks() const
{
return this->sle_.isFieldPresent(sfAdditionalBooks);
}
};
/**
* Builder for Offer ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class OfferBuilder : public LedgerEntryBuilderBase<OfferBuilder>
{
public:
OfferBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_UINT32::type::value_type> const& sequence,std::decay_t<typename SF_AMOUNT::type::value_type> const& takerPays,std::decay_t<typename SF_AMOUNT::type::value_type> const& takerGets,std::decay_t<typename SF_UINT256::type::value_type> const& bookDirectory,std::decay_t<typename SF_UINT64::type::value_type> const& bookNode,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<OfferBuilder>(ltOFFER)
{
setAccount(account);
setSequence(sequence);
setTakerPays(takerPays);
setTakerGets(takerGets);
setBookDirectory(bookDirectory);
setBookNode(bookNode);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
OfferBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltOFFER)
{
throw std::runtime_error("Invalid ledger entry type for Offer");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfTakerPays (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setTakerPays(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfTakerPays] = value;
return *this;
}
/**
* Set sfTakerGets (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setTakerGets(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfTakerGets] = value;
return *this;
}
/**
* Set sfBookDirectory (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setBookDirectory(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBookDirectory] = value;
return *this;
}
/**
* Set sfBookNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setBookNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfBookNode] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfExpiration (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setExpiration(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfExpiration] = value;
return *this;
}
/**
* Set sfDomainID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setDomainID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfDomainID] = value;
return *this;
}
/**
* Set sfAdditionalBooks (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setAdditionalBooks(STArray const& value)
{
object_.setFieldArray(sfAdditionalBooks, value);
return *this;
}
/**
* Build and return the completed Offer wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Offer
build(uint256 const& index)
{
return Offer{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,322 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class OracleBuilder;
/**
* Ledger Entry: Oracle
* Type: ltORACLE (0x0080)
* RPC Name: oracle
*
* Immutable wrapper around SLE providing type-safe field access.
* Use OracleBuilder to construct new ledger entries.
*/
class Oracle : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltORACLE;
/**
* Construct a Oracle ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Oracle(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Oracle");
}
}
// Ledger entry-specific field getters
/**
* Get sfOwner (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOwner() const
{
return this->sle_.at(sfOwner);
}
/**
* Get sfOracleDocumentID (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getOracleDocumentID() const
{
if (hasOracleDocumentID())
return this->sle_.at(sfOracleDocumentID);
return std::nullopt;
}
[[nodiscard]]
bool
hasOracleDocumentID() const
{
return this->sle_.isFieldPresent(sfOracleDocumentID);
}
/**
* Get sfProvider (soeREQUIRED)
*/
[[nodiscard]]
SF_VL::type::value_type
getProvider() const
{
return this->sle_.at(sfProvider);
}
/**
* Get sfPriceDataSeries (soeREQUIRED)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
STArray const&
getPriceDataSeries() const
{
return this->sle_.getFieldArray(sfPriceDataSeries);
}
/**
* Get sfAssetClass (soeREQUIRED)
*/
[[nodiscard]]
SF_VL::type::value_type
getAssetClass() const
{
return this->sle_.at(sfAssetClass);
}
/**
* Get sfLastUpdateTime (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getLastUpdateTime() const
{
return this->sle_.at(sfLastUpdateTime);
}
/**
* Get sfURI (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getURI() const
{
if (hasURI())
return this->sle_.at(sfURI);
return std::nullopt;
}
[[nodiscard]]
bool
hasURI() const
{
return this->sle_.isFieldPresent(sfURI);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for Oracle ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class OracleBuilder : public LedgerEntryBuilderBase<OracleBuilder>
{
public:
OracleBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& owner,std::decay_t<typename SF_VL::type::value_type> const& provider,STArray const& priceDataSeries,std::decay_t<typename SF_VL::type::value_type> const& assetClass,std::decay_t<typename SF_UINT32::type::value_type> const& lastUpdateTime,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<OracleBuilder>(ltORACLE)
{
setOwner(owner);
setProvider(provider);
setPriceDataSeries(priceDataSeries);
setAssetClass(assetClass);
setLastUpdateTime(lastUpdateTime);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
OracleBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltORACLE)
{
throw std::runtime_error("Invalid ledger entry type for Oracle");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfOwner (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setOwner(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOwner] = value;
return *this;
}
/**
* Set sfOracleDocumentID (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setOracleDocumentID(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfOracleDocumentID] = value;
return *this;
}
/**
* Set sfProvider (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setProvider(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfProvider] = value;
return *this;
}
/**
* Set sfPriceDataSeries (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setPriceDataSeries(STArray const& value)
{
object_.setFieldArray(sfPriceDataSeries, value);
return *this;
}
/**
* Set sfAssetClass (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setAssetClass(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAssetClass] = value;
return *this;
}
/**
* Set sfLastUpdateTime (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setLastUpdateTime(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfLastUpdateTime] = value;
return *this;
}
/**
* Set sfURI (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setURI(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfURI] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
OracleBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed Oracle wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Oracle
build(uint256 const& index)
{
return Oracle{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,463 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class PayChannelBuilder;
/**
* Ledger Entry: PayChannel
* Type: ltPAYCHAN (0x0078)
* RPC Name: payment_channel
*
* Immutable wrapper around SLE providing type-safe field access.
* Use PayChannelBuilder to construct new ledger entries.
*/
class PayChannel : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltPAYCHAN;
/**
* Construct a PayChannel ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit PayChannel(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for PayChannel");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfDestination (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->sle_.at(sfDestination);
}
/**
* Get sfSequence (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getSequence() const
{
if (hasSequence())
return this->sle_.at(sfSequence);
return std::nullopt;
}
[[nodiscard]]
bool
hasSequence() const
{
return this->sle_.isFieldPresent(sfSequence);
}
/**
* Get sfAmount (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->sle_.at(sfAmount);
}
/**
* Get sfBalance (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getBalance() const
{
return this->sle_.at(sfBalance);
}
/**
* Get sfPublicKey (soeREQUIRED)
*/
[[nodiscard]]
SF_VL::type::value_type
getPublicKey() const
{
return this->sle_.at(sfPublicKey);
}
/**
* Get sfSettleDelay (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSettleDelay() const
{
return this->sle_.at(sfSettleDelay);
}
/**
* Get sfExpiration (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getExpiration() const
{
if (hasExpiration())
return this->sle_.at(sfExpiration);
return std::nullopt;
}
[[nodiscard]]
bool
hasExpiration() const
{
return this->sle_.isFieldPresent(sfExpiration);
}
/**
* Get sfCancelAfter (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getCancelAfter() const
{
if (hasCancelAfter())
return this->sle_.at(sfCancelAfter);
return std::nullopt;
}
[[nodiscard]]
bool
hasCancelAfter() const
{
return this->sle_.isFieldPresent(sfCancelAfter);
}
/**
* Get sfSourceTag (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getSourceTag() const
{
if (hasSourceTag())
return this->sle_.at(sfSourceTag);
return std::nullopt;
}
[[nodiscard]]
bool
hasSourceTag() const
{
return this->sle_.isFieldPresent(sfSourceTag);
}
/**
* Get sfDestinationTag (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
{
if (hasDestinationTag())
return this->sle_.at(sfDestinationTag);
return std::nullopt;
}
[[nodiscard]]
bool
hasDestinationTag() const
{
return this->sle_.isFieldPresent(sfDestinationTag);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfDestinationNode (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getDestinationNode() const
{
if (hasDestinationNode())
return this->sle_.at(sfDestinationNode);
return std::nullopt;
}
[[nodiscard]]
bool
hasDestinationNode() const
{
return this->sle_.isFieldPresent(sfDestinationNode);
}
};
/**
* Builder for PayChannel ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class PayChannelBuilder : public LedgerEntryBuilderBase<PayChannelBuilder>
{
public:
PayChannelBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination,std::decay_t<typename SF_AMOUNT::type::value_type> const& amount,std::decay_t<typename SF_AMOUNT::type::value_type> const& balance,std::decay_t<typename SF_VL::type::value_type> const& publicKey,std::decay_t<typename SF_UINT32::type::value_type> const& settleDelay,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<PayChannelBuilder>(ltPAYCHAN)
{
setAccount(account);
setDestination(destination);
setAmount(amount);
setBalance(balance);
setPublicKey(publicKey);
setSettleDelay(settleDelay);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
PayChannelBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltPAYCHAN)
{
throw std::runtime_error("Invalid ledger entry type for PayChannel");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfDestination (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* Set sfSequence (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* Set sfBalance (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setBalance(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfBalance] = value;
return *this;
}
/**
* Set sfPublicKey (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setPublicKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfPublicKey] = value;
return *this;
}
/**
* Set sfSettleDelay (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setSettleDelay(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSettleDelay] = value;
return *this;
}
/**
* Set sfExpiration (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setExpiration(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfExpiration] = value;
return *this;
}
/**
* Set sfCancelAfter (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setCancelAfter(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfCancelAfter] = value;
return *this;
}
/**
* Set sfSourceTag (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setSourceTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSourceTag] = value;
return *this;
}
/**
* Set sfDestinationTag (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfDestinationNode (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
PayChannelBuilder&
setDestinationNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfDestinationNode] = value;
return *this;
}
/**
* Build and return the completed PayChannel wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
PayChannel
build(uint256 const& index)
{
return PayChannel{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,218 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class PermissionedDomainBuilder;
/**
* Ledger Entry: PermissionedDomain
* Type: ltPERMISSIONED_DOMAIN (0x0082)
* RPC Name: permissioned_domain
*
* Immutable wrapper around SLE providing type-safe field access.
* Use PermissionedDomainBuilder to construct new ledger entries.
*/
class PermissionedDomain : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltPERMISSIONED_DOMAIN;
/**
* Construct a PermissionedDomain ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit PermissionedDomain(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for PermissionedDomain");
}
}
// Ledger entry-specific field getters
/**
* Get sfOwner (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOwner() const
{
return this->sle_.at(sfOwner);
}
/**
* Get sfSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSequence() const
{
return this->sle_.at(sfSequence);
}
/**
* Get sfAcceptedCredentials (soeREQUIRED)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
STArray const&
getAcceptedCredentials() const
{
return this->sle_.getFieldArray(sfAcceptedCredentials);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for PermissionedDomain ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class PermissionedDomainBuilder : public LedgerEntryBuilderBase<PermissionedDomainBuilder>
{
public:
PermissionedDomainBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& owner,std::decay_t<typename SF_UINT32::type::value_type> const& sequence,STArray const& acceptedCredentials,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<PermissionedDomainBuilder>(ltPERMISSIONED_DOMAIN)
{
setOwner(owner);
setSequence(sequence);
setAcceptedCredentials(acceptedCredentials);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
PermissionedDomainBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltPERMISSIONED_DOMAIN)
{
throw std::runtime_error("Invalid ledger entry type for PermissionedDomain");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfOwner (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PermissionedDomainBuilder&
setOwner(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOwner] = value;
return *this;
}
/**
* Set sfSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PermissionedDomainBuilder&
setSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSequence] = value;
return *this;
}
/**
* Set sfAcceptedCredentials (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PermissionedDomainBuilder&
setAcceptedCredentials(STArray const& value)
{
object_.setFieldArray(sfAcceptedCredentials, value);
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PermissionedDomainBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PermissionedDomainBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
PermissionedDomainBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed PermissionedDomain wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
PermissionedDomain
build(uint256 const& index)
{
return PermissionedDomain{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,375 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class RippleStateBuilder;
/**
* Ledger Entry: RippleState
* Type: ltRIPPLE_STATE (0x0072)
* RPC Name: state
*
* Immutable wrapper around SLE providing type-safe field access.
* Use RippleStateBuilder to construct new ledger entries.
*/
class RippleState : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltRIPPLE_STATE;
/**
* Construct a RippleState ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit RippleState(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for RippleState");
}
}
// Ledger entry-specific field getters
/**
* Get sfBalance (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getBalance() const
{
return this->sle_.at(sfBalance);
}
/**
* Get sfLowLimit (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getLowLimit() const
{
return this->sle_.at(sfLowLimit);
}
/**
* Get sfHighLimit (soeREQUIRED)
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getHighLimit() const
{
return this->sle_.at(sfHighLimit);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
/**
* Get sfLowNode (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getLowNode() const
{
if (hasLowNode())
return this->sle_.at(sfLowNode);
return std::nullopt;
}
[[nodiscard]]
bool
hasLowNode() const
{
return this->sle_.isFieldPresent(sfLowNode);
}
/**
* Get sfLowQualityIn (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getLowQualityIn() const
{
if (hasLowQualityIn())
return this->sle_.at(sfLowQualityIn);
return std::nullopt;
}
[[nodiscard]]
bool
hasLowQualityIn() const
{
return this->sle_.isFieldPresent(sfLowQualityIn);
}
/**
* Get sfLowQualityOut (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getLowQualityOut() const
{
if (hasLowQualityOut())
return this->sle_.at(sfLowQualityOut);
return std::nullopt;
}
[[nodiscard]]
bool
hasLowQualityOut() const
{
return this->sle_.isFieldPresent(sfLowQualityOut);
}
/**
* Get sfHighNode (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getHighNode() const
{
if (hasHighNode())
return this->sle_.at(sfHighNode);
return std::nullopt;
}
[[nodiscard]]
bool
hasHighNode() const
{
return this->sle_.isFieldPresent(sfHighNode);
}
/**
* Get sfHighQualityIn (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getHighQualityIn() const
{
if (hasHighQualityIn())
return this->sle_.at(sfHighQualityIn);
return std::nullopt;
}
[[nodiscard]]
bool
hasHighQualityIn() const
{
return this->sle_.isFieldPresent(sfHighQualityIn);
}
/**
* Get sfHighQualityOut (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getHighQualityOut() const
{
if (hasHighQualityOut())
return this->sle_.at(sfHighQualityOut);
return std::nullopt;
}
[[nodiscard]]
bool
hasHighQualityOut() const
{
return this->sle_.isFieldPresent(sfHighQualityOut);
}
};
/**
* Builder for RippleState ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class RippleStateBuilder : public LedgerEntryBuilderBase<RippleStateBuilder>
{
public:
RippleStateBuilder(std::decay_t<typename SF_AMOUNT::type::value_type> const& balance,std::decay_t<typename SF_AMOUNT::type::value_type> const& lowLimit,std::decay_t<typename SF_AMOUNT::type::value_type> const& highLimit,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<RippleStateBuilder>(ltRIPPLE_STATE)
{
setBalance(balance);
setLowLimit(lowLimit);
setHighLimit(highLimit);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
RippleStateBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltRIPPLE_STATE)
{
throw std::runtime_error("Invalid ledger entry type for RippleState");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfBalance (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setBalance(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfBalance] = value;
return *this;
}
/**
* Set sfLowLimit (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setLowLimit(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfLowLimit] = value;
return *this;
}
/**
* Set sfHighLimit (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setHighLimit(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfHighLimit] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Set sfLowNode (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setLowNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfLowNode] = value;
return *this;
}
/**
* Set sfLowQualityIn (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setLowQualityIn(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfLowQualityIn] = value;
return *this;
}
/**
* Set sfLowQualityOut (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setLowQualityOut(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfLowQualityOut] = value;
return *this;
}
/**
* Set sfHighNode (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setHighNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfHighNode] = value;
return *this;
}
/**
* Set sfHighQualityIn (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setHighQualityIn(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfHighQualityIn] = value;
return *this;
}
/**
* Set sfHighQualityOut (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
RippleStateBuilder&
setHighQualityOut(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfHighQualityOut] = value;
return *this;
}
/**
* Build and return the completed RippleState wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
RippleState
build(uint256 const& index)
{
return RippleState{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,248 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class SignerListBuilder;
/**
* Ledger Entry: SignerList
* Type: ltSIGNER_LIST (0x0053)
* RPC Name: signer_list
*
* Immutable wrapper around SLE providing type-safe field access.
* Use SignerListBuilder to construct new ledger entries.
*/
class SignerList : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltSIGNER_LIST;
/**
* Construct a SignerList ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit SignerList(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for SignerList");
}
}
// Ledger entry-specific field getters
/**
* Get sfOwner (soeOPTIONAL)
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getOwner() const
{
if (hasOwner())
return this->sle_.at(sfOwner);
return std::nullopt;
}
[[nodiscard]]
bool
hasOwner() const
{
return this->sle_.isFieldPresent(sfOwner);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfSignerQuorum (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSignerQuorum() const
{
return this->sle_.at(sfSignerQuorum);
}
/**
* Get sfSignerEntries (soeREQUIRED)
* Note: This is an untyped field (unknown).
*/
[[nodiscard]]
STArray const&
getSignerEntries() const
{
return this->sle_.getFieldArray(sfSignerEntries);
}
/**
* Get sfSignerListID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getSignerListID() const
{
return this->sle_.at(sfSignerListID);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for SignerList ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class SignerListBuilder : public LedgerEntryBuilderBase<SignerListBuilder>
{
public:
SignerListBuilder(std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT32::type::value_type> const& signerQuorum,STArray const& signerEntries,std::decay_t<typename SF_UINT32::type::value_type> const& signerListID,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<SignerListBuilder>(ltSIGNER_LIST)
{
setOwnerNode(ownerNode);
setSignerQuorum(signerQuorum);
setSignerEntries(signerEntries);
setSignerListID(signerListID);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
SignerListBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltSIGNER_LIST)
{
throw std::runtime_error("Invalid ledger entry type for SignerList");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfOwner (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
SignerListBuilder&
setOwner(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOwner] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
SignerListBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfSignerQuorum (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
SignerListBuilder&
setSignerQuorum(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSignerQuorum] = value;
return *this;
}
/**
* Set sfSignerEntries (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
SignerListBuilder&
setSignerEntries(STArray const& value)
{
object_.setFieldArray(sfSignerEntries, value);
return *this;
}
/**
* Set sfSignerListID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
SignerListBuilder&
setSignerListID(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfSignerListID] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
SignerListBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
SignerListBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed SignerList wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
SignerList
build(uint256 const& index)
{
return SignerList{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

View File

@@ -1,195 +0,0 @@
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
// Forward declaration
class TicketBuilder;
/**
* Ledger Entry: Ticket
* Type: ltTICKET (0x0054)
* RPC Name: ticket
*
* Immutable wrapper around SLE providing type-safe field access.
* Use TicketBuilder to construct new ledger entries.
*/
class Ticket : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltTICKET;
/**
* Construct a Ticket ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Ticket(SLE const& sle)
: LedgerEntryBase(sle)
{
// Verify ledger entry type
if (sle.getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Ticket");
}
}
// Ledger entry-specific field getters
/**
* Get sfAccount (soeREQUIRED)
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_.at(sfAccount);
}
/**
* Get sfOwnerNode (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_.at(sfOwnerNode);
}
/**
* Get sfTicketSequence (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getTicketSequence() const
{
return this->sle_.at(sfTicketSequence);
}
/**
* Get sfPreviousTxnID (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_.at(sfPreviousTxnID);
}
/**
* Get sfPreviousTxnLgrSeq (soeREQUIRED)
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_.at(sfPreviousTxnLgrSeq);
}
};
/**
* Builder for Ticket ledger entries.
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses Json::Value internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class TicketBuilder : public LedgerEntryBuilderBase<TicketBuilder>
{
public:
TicketBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT32::type::value_type> const& ticketSequence,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<TicketBuilder>(ltTICKET)
{
setAccount(account);
setOwnerNode(ownerNode);
setTicketSequence(ticketSequence);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
TicketBuilder(SLE const& sle)
{
if (sle[sfLedgerEntryType] != ltTICKET)
{
throw std::runtime_error("Invalid ledger entry type for Ticket");
}
object_ = sle;
}
// Ledger entry-specific field setters
/**
* Set sfAccount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
TicketBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* Set sfOwnerNode (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
TicketBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* Set sfTicketSequence (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
TicketBuilder&
setTicketSequence(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfTicketSequence] = value;
return *this;
}
/**
* Set sfPreviousTxnID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
TicketBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* Set sfPreviousTxnLgrSeq (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
TicketBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* Build and return the completed Ticket wrapper.
* @return The constructed ledger entry wrapper.
* @throws std::runtime_error if the JSON cannot be parsed into a valid ledger entry.
*/
Ticket
build(uint256 const& index)
{
return Ticket{SLE(object_, index)};
}
};
} // namespace xrpl::ledger_entries

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