Compare commits

..

3 Commits

Author SHA1 Message Date
Mayukha Vadari
537c520e32 Merge branch 'develop' into mvadari/test-debugging 2026-06-24 20:51:37 -04:00
Mayukha Vadari
310bfc7b94 fix: Improve test debuggability 2026-06-24 20:44:47 -04:00
Mayukha Vadari
afc0b7ab8c add build errors 2026-06-24 20:14:13 -04:00
246 changed files with 2915 additions and 26951 deletions

View File

@@ -1,140 +1,161 @@
---
Checks: "-*,
bugprone-*,
-bugprone-easily-swappable-parameters,
-bugprone-exception-escape,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
-bugprone-throwing-static-initialization,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-capturing-lambda-coroutines,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-avoid-reference-coroutine-parameters,
-cppcoreguidelines-c-copy-assignment-signature,
-cppcoreguidelines-explicit-virtual-functions,
-cppcoreguidelines-interfaces-global-init,
-cppcoreguidelines-macro-to-enum,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-missing-std-forward,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-no-malloc,
-cppcoreguidelines-noexcept-destructor,
-cppcoreguidelines-noexcept-move-operations,
-cppcoreguidelines-noexcept-swap,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-prefer-member-initializer,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-const-cast,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-slicing,
-cppcoreguidelines-special-member-functions,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-bad-signal-to-kill-thread,
bugprone-bool-pointer-implicit-conversion,
bugprone-capturing-this-in-member-variable,
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-derived-method-shadowing-base-method,
bugprone-dynamic-static-initializers,
bugprone-empty-catch,
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-inaccurate-erase,
bugprone-inc-dec-in-conditions,
bugprone-incorrect-enable-if,
bugprone-incorrect-roundings,
bugprone-infinite-loop,
bugprone-integer-division,
bugprone-invalid-enum-default-initialization,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misleading-setter-of-reference,
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-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-unchecked-optional-access,
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-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-rvalue-reference-param-not-moved,
cppcoreguidelines-use-default-member-init,
cppcoreguidelines-use-enum-class,
cppcoreguidelines-virtual-class-destructor,
hicpp-ignored-remove-result,
llvm-namespace-comment,
misc-*,
-misc-anonymous-namespace-in-header,
-misc-confusable-identifiers,
-misc-coroutine-hostile-raii,
-misc-misleading-bidirectional,
-misc-misleading-identifier,
-misc-multiple-inheritance,
-misc-new-delete-overloads,
-misc-no-recursion,
-misc-non-copyable-objects,
-misc-non-private-member-variables-in-classes,
-misc-override-with-different-visibility,
-misc-predictable-rand,
-misc-unconventional-assign-operator,
-misc-uniqueptr-reset-release,
-misc-unused-parameters,
-misc-use-anonymous-namespace,
-misc-use-internal-linkage,
modernize-*,
-modernize-avoid-bind,
-modernize-avoid-c-arrays,
-modernize-avoid-c-style-cast,
-modernize-avoid-setjmp-longjmp,
-modernize-avoid-variadic-functions,
-modernize-deprecated-ios-base-aliases,
-modernize-loop-convert,
-modernize-macro-to-enum,
-modernize-min-max-use-initializer-list,
-modernize-raw-string-literal,
-modernize-redundant-void-arg,
-modernize-replace-auto-ptr,
-modernize-replace-disallow-copy-and-assign-macro,
-modernize-replace-random-shuffle,
-modernize-return-braced-init-list,
-modernize-shrink-to-fit,
-modernize-unary-static-assert,
-modernize-use-auto,
-modernize-use-bool-literals,
-modernize-use-constraints,
-modernize-use-default-member-init,
-modernize-use-integer-sign-comparison,
-modernize-use-noexcept,
-modernize-use-nullptr,
-modernize-use-std-format,
-modernize-use-std-print,
-modernize-use-trailing-return-type,
-modernize-use-transparent-functors,
-modernize-use-uncaught-exceptions,
performance-*,
-performance-avoid-endl,
-performance-enum-size,
-performance-inefficient-algorithm,
-performance-inefficient-string-concatenation,
-performance-no-int-to-ptr,
-performance-noexcept-destructor,
-performance-noexcept-move-constructor,
-performance-noexcept-swap,
-performance-type-promotion-in-math-fn,
-performance-unnecessary-copy-initialization,
-performance-unnecessary-value-param,
readability-*,
-readability-avoid-const-params-in-decls,
-readability-avoid-unconditional-preprocessor-if,
-readability-container-data-pointer,
-readability-delete-null-pointer,
-readability-function-cognitive-complexity,
-readability-function-size,
-readability-identifier-length,
-readability-inconsistent-declaration-parameter-name,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-misplaced-array-index,
-readability-named-parameter,
-readability-operators-representation,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-control-flow,
-readability-redundant-function-ptr-dereference,
-readability-redundant-preprocessor,
-readability-redundant-smartptr-get,
-readability-redundant-string-cstr,
-readability-simplify-subscript-expr,
-readability-static-accessed-through-instance,
-readability-string-compare,
-readability-uniqueptr-delete-release,
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-readability-use-concise-preprocessor-directives
misc-const-correctness,
misc-definitions-in-headers,
misc-header-include-cycle,
misc-include-cleaner,
misc-misplaced-const,
misc-redundant-expression,
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,
modernize-use-emplace,
modernize-use-equals-default,
modernize-use-equals-delete,
modernize-use-nodiscard,
modernize-use-override,
modernize-use-ranges,
modernize-use-scoped-lock,
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-ambiguous-smartptr-reset-call,
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-identifier-naming,
readability-implicit-bool-conversion,
readability-inconsistent-ifelse-braces,
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-parentheses,
readability-redundant-string-init,
readability-redundant-typename,
readability-reference-to-constructed-temporary,
readability-simplify-boolean-expr,
readability-static-definition-in-anonymous-namespace,
readability-suspicious-call-argument,
readability-use-std-min-max
"
# ---
# bugprone-narrowing-conversions, # This will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs

View File

@@ -9,7 +9,7 @@ inputs:
remote_url:
description: "The URL of the Conan endpoint to use."
required: false
default: https://conan.xrplf.org/repository/conan/
default: https://conan.ripplex.io
runs:
using: composite

View File

@@ -1,6 +1,6 @@
{
"platform": "macos/arm64",
"runner": ["self-hosted", "macOS", "ARM64", "macos-26-apple-clang-21"],
"runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"],
"configs": [
{
"build_type": "Release",

View File

@@ -122,6 +122,7 @@ jobs:
issues: write
contents: read
with:
check_only_changed: true
create_issue_on_failure: false
build-test:
@@ -153,8 +154,8 @@ jobs:
if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release') }}
uses: ./.github/workflows/reusable-upload-recipe.yml
secrets:
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
notify-clio:
needs: upload-recipe

View File

@@ -20,8 +20,8 @@ jobs:
if: ${{ github.repository == 'XRPLF/rippled' }}
uses: ./.github/workflows/reusable-upload-recipe.yml
secrets:
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
build-test:
if: ${{ github.repository == 'XRPLF/rippled' }}

View File

@@ -72,6 +72,7 @@ jobs:
issues: write
contents: read
with:
check_only_changed: false
create_issue_on_failure: ${{ github.event_name == 'schedule' }}
build-test:
@@ -97,8 +98,8 @@ jobs:
if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
uses: ./.github/workflows/reusable-upload-recipe.yml
secrets:
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
package:
needs: build-test

View File

@@ -47,7 +47,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with:
enable_ccache: false

View File

@@ -113,7 +113,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with:
enable_ccache: ${{ inputs.ccache_enabled }}
@@ -227,7 +227,8 @@ jobs:
--build . \
--config "${BUILD_TYPE}" \
--parallel "${BUILD_NPROC}" \
--target "${CMAKE_TARGET}"
--target "${CMAKE_TARGET}" \
2>&1 | tee build.log
- name: Show ccache statistics
if: ${{ inputs.ccache_enabled }}
@@ -325,7 +326,7 @@ jobs:
LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
- name: Show test failure summary
if: ${{ failure() && !inputs.build_only }}
if: ${{ failure() }}
env:
WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
run: |
@@ -336,13 +337,17 @@ jobs:
cd "${WORKING_DIR}"
if [ ! -f unittest.log ]; then
if [ -f unittest.log ]; then
if ! grep -E "failed" unittest.log | grep -vE "^I[0-9]|^[0-9]+> (ERR:|FTL:)"; then
echo "unittest.log present but no failure lines found."
fi
else
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."
if [ -f build.log ]; then
if ! grep -E "error:" build.log; then
echo "build.log present but no compile errors found."
fi
fi
fi
- name: Debug failure (Linux)
if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }}

View File

@@ -3,6 +3,10 @@ name: Run clang-tidy on files
on:
workflow_call:
inputs:
check_only_changed:
description: "Check only changed files in PR. If false, checks all files in the repository."
type: boolean
default: false
create_issue_on_failure:
description: "Whether to create an issue if the check failed"
type: boolean
@@ -25,14 +29,15 @@ env:
jobs:
determine-files:
if: ${{ inputs.check_only_changed }}
permissions:
contents: read
uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@d041ac9f1fa9f07a4ba335eb4c1c82233fb3fef6
uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@c7045074aafe9fb92fa537aa4446f81fbfc17e8b
run-clang-tidy:
name: Run clang tidy
needs: [determine-files]
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }}
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523"
permissions:
@@ -43,7 +48,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with:
enable_ccache: false
@@ -91,15 +96,15 @@ jobs:
id: run_clang_tidy
continue-on-error: true
env:
TARGETS: ${{ needs.determine-files.outputs.need_full_run != 'true' && needs.determine-files.outputs.cpp_changed_files || 'src tests' }}
TARGETS: ${{ (needs.determine-files.outputs.clang_tidy_config_changed != 'true' && inputs.check_only_changed) && needs.determine-files.outputs.cpp_changed_files || 'src tests' }}
run: |
set -o pipefail
run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" -quiet -fix -allow-no-checks ${TARGETS} 2>&1 | tee "${OUTPUT_FILE}"
- name: Print filtered clang-tidy errors
- name: Print errors
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: |
bin/filter-clang-tidy.py "${OUTPUT_FILE}"
sed '/error\||/!d' "${OUTPUT_FILE}"
- name: Upload clang-tidy output
if: ${{ github.event.repository.visibility == 'public' && steps.run_clang_tidy.outcome != 'success' }}
@@ -143,12 +148,12 @@ jobs:
\`\`\`
EOF
- name: Append filtered clang-tidy output to issue body
- name: Append clang-tidy output to issue body (filter for errors and warnings)
if: ${{ steps.run_clang_tidy.outcome != 'success' }}
run: |
if [ -f "${OUTPUT_FILE}" ]; then
# Filter to the unique errors with their source context.
bin/filter-clang-tidy.py "${OUTPUT_FILE}" >"${FILTERED_OUTPUT_FILE}" || true
# Extract lines containing 'error:', 'warning:', or 'note:'
grep -E '(error:|warning:|note:)' "${OUTPUT_FILE}" >"${FILTERED_OUTPUT_FILE}" || true
# If filtered output is empty, use original (might be a different error format)
if [ ! -s "${FILTERED_OUTPUT_FILE}" ]; then

View File

@@ -14,7 +14,7 @@ on:
description: "The URL of the Conan endpoint to use."
required: false
type: string
default: https://conan.xrplf.org/repository/conan/
default: https://conan.ripplex.io
secrets:
remote_username:
@@ -41,10 +41,6 @@ jobs:
upload:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523
env:
REMOTE_NAME: ${{ inputs.remote_name }}
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
CONAN_PASSWORD_XRPLF: ${{ secrets.remote_password }}
steps:
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -60,9 +56,15 @@ jobs:
remote_url: ${{ inputs.remote_url }}
- name: Log into Conan remote
run: conan remote login "${REMOTE_NAME}" "${CONAN_LOGIN_USERNAME_XRPLF}" --password "${CONAN_PASSWORD_XRPLF}"
env:
REMOTE_NAME: ${{ inputs.remote_name }}
REMOTE_USERNAME: ${{ secrets.remote_username }}
REMOTE_PASSWORD: ${{ secrets.remote_password }}
run: conan remote login "${REMOTE_NAME}" "${REMOTE_USERNAME}" --password "${REMOTE_PASSWORD}"
- name: Upload Conan recipe (version)
env:
REMOTE_NAME: ${{ inputs.remote_name }}
run: |
conan export . --version=${{ steps.version.outputs.version }}
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
@@ -71,6 +73,8 @@ jobs:
# 'develop' branch, see on-trigger.yml.
- name: Upload Conan recipe (develop)
if: ${{ github.event_name == 'push' }}
env:
REMOTE_NAME: ${{ inputs.remote_name }}
run: |
conan export . --version=develop
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop
@@ -79,6 +83,8 @@ jobs:
# one of the 'release' branches, see on-pr.yml.
- name: Upload Conan recipe (rc)
if: ${{ github.event_name == 'pull_request' }}
env:
REMOTE_NAME: ${{ inputs.remote_name }}
run: |
conan export . --version=rc
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc
@@ -87,6 +93,8 @@ jobs:
# release, see on-tag.yml.
- name: Upload Conan recipe (release)
if: ${{ startsWith(github.ref, 'refs/tags/') }}
env:
REMOTE_NAME: ${{ inputs.remote_name }}
run: |
conan export . --version=release
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release

View File

@@ -34,7 +34,7 @@ on:
env:
CONAN_REMOTE_NAME: xrplf
CONAN_REMOTE_URL: https://conan.xrplf.org/repository/conan/
CONAN_REMOTE_URL: https://conan.ripplex.io
NPROC_SUBTRACT: 2
concurrency:
@@ -68,7 +68,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Prepare runner
uses: XRPLF/actions/prepare-runner@9355d190fd7d4de80fadfd161e6edddc9702cd9f
uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8
with:
enable_ccache: false
@@ -108,12 +108,10 @@ jobs:
- name: Log into Conan remote
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.NEXUS_REMOTE_USERNAME }}" --password "${{ secrets.NEXUS_REMOTE_PASSWORD }}"
run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}"
- name: Upload Conan packages
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
env:
FORCE_OPTION: ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.NEXUS_REMOTE_USERNAME }}
CONAN_PASSWORD_XRPLF: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
run: conan upload "*" --remote="${CONAN_REMOTE_NAME}" --confirm ${FORCE_OPTION}

View File

@@ -101,7 +101,7 @@ More information on customizing Conan can be found in the [Advanced Conan config
Run the following command to add the `xrplf` remote, which hosts some of our dependencies:
```bash
conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
conan remote add --index 0 --force xrplf https://conan.ripplex.io
```
### Set Up Ccache

View File

@@ -90,7 +90,6 @@ find_package(ed25519 REQUIRED)
find_package(gRPC REQUIRED)
find_package(LibArchive REQUIRED)
find_package(lz4 REQUIRED)
find_package(mpt-crypto REQUIRED)
find_package(nudb REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(secp256k1 REQUIRED)
@@ -103,7 +102,6 @@ target_link_libraries(
INTERFACE
ed25519::ed25519
lz4::lz4
mpt-crypto::mpt-crypto
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1

View File

@@ -1,102 +0,0 @@
#!/usr/bin/env python3
"""
Reduce run-clang-tidy output to its unique errors.
It does two things:
1. Filters the raw output down to diagnostics and their source-context lines
(the indented " 103 | ..." / " | ^" lines clang-tidy prints),
matching the "path:line:col: error:" diagnostic shape.
2. Deduplicates. The same diagnostic in a header is reported once per
translation unit that includes it, so identical error blocks are collapsed
to their first occurrence.
An "error block" is an "error:" line together with the indented context lines
and any "note:" lines that follow it (up to the next "error:" line). Blocks are
compared as a whole, so an error stays attached to its own context, and
first-occurrence order is preserved.
The deduplicated output goes to stdout; a summary of unique error counts per
check is printed to stderr.
Usage:
bin/filter-clang-tidy.py [INPUT_FILE] # read from file, or
run-clang-tidy ... | bin/filter-clang-tidy.py # read from stdin
"""
import re
import sys
from collections import Counter
# A clang-tidy diagnostic line looks like "path:line:col: error: msg [check]".
# Matching on that shape (rather than a loose "error" substring) avoids treating
# progress lines whose paths contain "error" as diagnostics, e.g.
# [284/850][0.7s] /nix/.../clang-tidy ... src/.../error.cpp
DIAG_RE = re.compile(r":\d+:\d+: (?:error|warning|note):")
ERROR_RE = re.compile(r":\d+:\d+: error:")
CHECK_RE = re.compile(r" error: .*\[([^\],]+)")
def filter_and_dedup(lines: list[str]) -> list[str]:
"""Keep diagnostics with their context, then drop duplicate error blocks."""
blocks: list[str] = []
seen: set[str] = set()
current: list[str] = []
def flush() -> None:
if not current:
return
block = "".join(current)
if block not in seen:
seen.add(block)
blocks.append(block)
for line in lines:
# Keep only diagnostics and their indented source-context lines; drop
# progress/status output and blank lines.
if not (DIAG_RE.search(line) or line[:1] in (" ", "\t")):
continue
# An "error:" line starts a new block; its context and any following
# "note:" lines (and their context) belong to it.
if ERROR_RE.search(line):
flush()
current = []
current.append(line)
flush()
return blocks
def summarize(blocks: list[str]) -> Counter[str]:
"""Count unique errors per check name (e.g. "bugprone-branch-clone")."""
counts: Counter[str] = Counter()
for block in blocks:
# The error line is the first line of the block.
match = CHECK_RE.search(block.splitlines()[0])
if match:
counts[match.group(1)] += 1
return counts
def main() -> int:
if len(sys.argv) > 1 and sys.argv[1] != "-":
with open(sys.argv[1], encoding="utf-8") as f:
lines = f.readlines()
else:
lines = sys.stdin.readlines()
blocks = filter_and_dedup(lines)
# Blank line between blocks so distinct errors are easy to tell apart.
sys.stdout.write("\n".join(blocks))
print("\nUnique errors per check:", file=sys.stderr)
for check, count in summarize(blocks).most_common():
print(f"{count:>4} {check}", file=sys.stderr)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -1,44 +1,43 @@
{
"version": "0.5",
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
"secp256k1/0.7.1#b1f450b7f78a36fff75bb6934a356f3a%1782338841.3729",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1782392413.075713",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
"openssl/3.6.3#1163d4ddc603907084d08a6a0c6e580f%1782307150.583886",
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1782392402.297166",
"mpt-crypto/0.4.0-rc2#a580f2f9ad0e795de696aa62d54fb9af%1782425834.488828",
"lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1782392402.164188",
"libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1782392792.775744",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1782392402.420732",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311",
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
"secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f%1778071755.506",
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
"grpc/1.81.1#5217e6ef0544c42b46f4af35d5e7f649%1782307148.845616",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1782392402.296732",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1782392419.475605",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
"grpc/1.81.0#2fb144aeb47e7f35c6ebb0e5f35bed31%1781620605.685",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
],
"build_requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1782395692.540639",
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162",
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259",
"cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
"m4/1.4.19#4523e4347b55cd26ae918bd5770cab9a%1778062762.471",
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
],
"python_requires": [],
"overrides": {
@@ -58,7 +57,7 @@
"boost/1.91.0"
],
"lz4/[>=1.9.4 <2]": [
"lz4/1.10.0#982d9b673900f665a1da109e09c17cab"
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
]
},
"config_requires": []

View File

@@ -14,7 +14,7 @@ export CONAN_HOME="$TEMP_DIR"
# Ensure that the xrplf remote is the first to be consulted, so any recipes we
# patched are used. We also add it there to not created huge diff when the
# official Conan Center Index is updated.
conan remote add --force --index 0 xrplf https://conan.xrplf.org/repository/conan/
conan remote add --force --index 0 xrplf https://conan.ripplex.io
# Delete any existing lockfile.
rm -f conan.lock

View File

@@ -28,10 +28,11 @@ class Xrpl(ConanFile):
requires = [
"ed25519/2015.03",
"grpc/1.81.1",
"grpc/1.81.0",
"libarchive/3.8.7",
"nudb/2.0.9",
"openssl/3.6.3",
"openssl/3.6.2",
"secp256k1/0.7.1",
"soci/4.0.3",
"zlib/1.3.2",
]
@@ -131,15 +132,13 @@ class Xrpl(ConanFile):
def requirements(self):
self.requires("boost/1.91.0", force=True, transitive_headers=True)
self.requires("date/3.0.4", transitive_headers=True)
self.requires("lz4/1.10.0", force=True)
self.requires("protobuf/6.33.5", force=True)
self.requires("sqlite3/3.53.0", force=True)
if self.options.jemalloc:
self.requires("jemalloc/5.3.1")
self.requires("lz4/1.10.0", force=True)
self.requires("mpt-crypto/0.4.0-rc2", transitive_headers=True)
self.requires("protobuf/6.33.5", force=True)
if self.options.rocksdb:
self.requires("rocksdb/10.5.1")
self.requires("secp256k1/0.7.1", transitive_headers=True)
self.requires("sqlite3/3.53.0", force=True)
self.requires("xxhash/0.8.3", transitive_headers=True)
exports_sources = (
@@ -209,7 +208,6 @@ class Xrpl(ConanFile):
"grpc::grpc++",
"libarchive::libarchive",
"lz4::lz4",
"mpt-crypto::mpt-crypto",
"nudb::nudb",
"openssl::crypto",
"protobuf::libprotobuf",

View File

@@ -60,7 +60,6 @@ words:
- autobridging
- bimap
- bindir
- blindings
- bookdir
- Bougalis
- Britto
@@ -96,7 +95,6 @@ words:
- daria
- dcmake
- dearmor
- decryptor
- dedented
- deleteme
- demultiplexer
@@ -108,7 +106,6 @@ words:
- distro
- doxyfile
- dxrpl
- elgamal
- enabled
- enablerepo
- endmacro
@@ -122,7 +119,6 @@ words:
- fmtdur
- fsanitize
- funclets
- Gamal
- gcov
- gcovr
- ghead
@@ -220,7 +216,6 @@ words:
- partitioner
- paychan
- paychans
- Pedersen
- permdex
- perminute
- permissioned
@@ -244,10 +239,6 @@ words:
- Raphson
- rcflags
- replayer
- rerandomize
- rerandomization
- rerandomized
- rerandomizes
- rerere
- retriable
- RIPD
@@ -264,7 +255,6 @@ words:
- sahyadri
- Satoshi
- scons
- Schnorr
- secp
- sendq
- seqit
@@ -295,7 +285,6 @@ words:
- stvar
- stvector
- stxchainattestations
- summands
- superpeer
- superpeers
- takergets

View File

@@ -34,7 +34,7 @@ higher index than the default Conan Center remote, so it is consulted first. You
can do this by running:
```bash
conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
conan remote add --index 0 --force xrplf https://conan.ripplex.io
```
Alternatively, you can pull our recipes from the repository and export them locally:

View File

@@ -298,8 +298,7 @@ set(T& target, std::string const& name, Section const& section)
try
{
auto const val = section.get<T>(name);
foundAndValid = val.has_value();
if (foundAndValid)
if ((foundAndValid = val.has_value()))
target = *val;
}
catch (boost::bad_lexical_cast const&) // NOLINT(bugprone-empty-catch)

View File

@@ -19,14 +19,12 @@ enum class HashRouterFlags : std::uint16_t {
HELD = 0x08, // Held by LedgerMaster after potential processing failure
TRUSTED = 0x10, // Comes from a trusted source
// Private flags. Each group is owned by one file; do not read, set, or
// reuse a flag outside the file noted.
// Used in apply.cpp
// Private flags (used internally in apply.cpp)
// Do not attempt to read, set, or reuse.
PRIVATE1 = 0x0100,
PRIVATE2 = 0x0200,
PRIVATE3 = 0x0400,
PRIVATE4 = 0x0800,
// Used in EscrowFinish.cpp
PRIVATE5 = 0x1000,
PRIVATE6 = 0x2000
};

View File

@@ -28,7 +28,7 @@ inline constexpr struct OpenLedgerT
/** Batch view construction tag.
Views constructed with this tag are part of a stack of views
used during batch transaction application.
used during batch transaction applied.
*/
inline constexpr struct BatchViewT
{

View File

@@ -63,39 +63,6 @@ checkArray(STArray const& credentials, unsigned maxSize, beast::Journal j);
TER
verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, beast::Journal j);
/**
* @brief Check whether src is authorized to deposit to dst.
*
* @param tx Transaction containing optional credential IDs.
* @param view Read-only ledger view.
* @param src Source account.
* @param dst Destination account.
* @param sleDst Destination AccountRoot, if it exists.
* @param j Journal for diagnostics.
* @return tesSUCCESS if the deposit is allowed, otherwise an authorization
* error.
*/
TER
checkDepositPreauth(
STTx const& tx,
ReadView const& view,
AccountID const& src,
AccountID const& dst,
std::shared_ptr<SLE const> const& sleDst,
beast::Journal j);
/**
* @brief Remove expired credentials referenced by the transaction.
*
* @param tx Transaction containing optional sfCredentialIDs.
* @param view Mutable ledger view.
* @param j Journal for diagnostics.
* @return tesSUCCESS if no referenced credentials expired, tecEXPIRED if any
* were removed, or an error from credential deletion.
*/
TER
cleanupExpiredCredentials(STTx const& tx, ApplyView& view, beast::Journal j);
// Check expired credentials and for existing DepositPreauth ledger object
TER
verifyDepositPreauth(

View File

@@ -42,7 +42,7 @@ escrowUnlockApplyHelper<Issue>(
beast::Journal journal)
{
Issue const& issue = amount.get<Issue>();
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
Keylet const trustLineKey = keylet::line(receiver, issue);
bool const recvLow = issuer > receiver;
bool const senderIssuer = issuer == sender;
bool const receiverIssuer = issuer == receiver;
@@ -175,7 +175,7 @@ escrowUnlockApplyHelper<MPTIssue>(
bool const receiverIssuer = issuer == receiver;
auto const mptID = amount.get<MPTIssue>().getMptID();
auto const issuanceKey = keylet::mptokenIssuance(mptID);
auto const issuanceKey = keylet::mptIssuance(mptID);
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer)
{
if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};

View File

@@ -131,75 +131,6 @@ checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const&
[[nodiscard]] TER
checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
/**
* Checks freeze compliance for withdrawing an asset from a pseudo-account (e.g. Vault, AMM,
* LoanBroker) to a destination account.
*
* Asserts that sourceAcct is a pseudo-account and that submitterAcct and dstAcct are not.
*
* Issuer exemption: returns tesSUCCESS immediately when dstAcct is the asset issuer — the issuer
* can always receive their own token, even when the pool is frozen. Callers that need to block
* withdrawals from a frozen pool even for the issuer (e.g. because the pool math cannot handle it)
* must check checkFrozen(sourceAcct, asset) separately before calling this function.
*
* Otherwise checks, in order:
* 1. If the asset is globally frozen the remaining checks are redundant.
* 2. For MPT shares: The pseudo-account's vault share must not be transitively frozen via its
* underlying asset.
* 3. The pseudo-account's trustline / MPToken must not be frozen for sending.
* 4. Skipped when submitter == dst (self-withdrawal); a regular freeze should not prevent
* recovering one's own funds.
* 5. The destination must not be deep-frozen (cannot receive under any circumstance).
*
* For IOUs a regular individual freeze on the withdrawer does NOT block self-withdrawal; only deep
* freeze does. For MPTs "locked" is equivalent to deep-frozen, so locked MPT holders are always
* blocked.
*
* @param view Ledger view to read freeze state from.
* @param srcAcct Pseudo-account the funds are withdrawn from (sender).
* @param submitterAcct Account that submitted the withdrawal transaction.
* @param dstAcct Account receiving the withdrawn funds.
* @param asset Asset being withdrawn.
* @return tesSUCCESS if the withdrawal is permitted, otherwise a freeze
* result (tecFROZEN for IOUs, tecLOCKED for MPTs).
*/
[[nodiscard]] TER
checkWithdrawFreeze(
ReadView const& view,
AccountID const& srcAcct,
AccountID const& submitterAcct,
AccountID const& dstAcct,
Asset const& asset);
/**
* Checks freeze compliance for depositing an asset into a pseudo-account (e.g. Vault, AMM,
* LoanBroker).
*
*
* Checks, in order:
* 1. If the asset is globally frozen the remaining checks are redundant.
* 2. For MPT shares: the pseudo-account's vault share must not be transitively frozen via its
* underlying asset (returns tecLOCKED).
* 3. The depositor must not be individually frozen. Skipped when srcAcct is the asset issuer,
* since the issuer can always send its own asset.
* 4. The pseudo-account must not be individually frozen for the asset. Unlike regular accounts,
* pseudo-accounts cannot receive deposits under a regular freeze because the deposited funds
* could not later be withdrawn.
*
* @param view Ledger view to read freeze state from.
* @param srcAcct Depositor sending the funds.
* @param dstAcct Pseudo-account receiving the deposit.
* @param asset Asset being deposited.
* @return tesSUCCESS if the deposit is permitted, otherwise a freeze result
* (tecFROZEN for IOUs, tecLOCKED for MPTs).
*/
[[nodiscard]] TER
checkDepositFreeze(
ReadView const& view,
AccountID const& srcAcct,
AccountID const& dstAcct,
Asset const& asset);
//------------------------------------------------------------------------------
//
// Account balance functions (Asset-based dispatchers)

View File

@@ -1,6 +1,5 @@
#pragma once
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/Serializer.h>
@@ -8,16 +7,9 @@
namespace xrpl {
inline void
serializeBatch(
Serializer& msg,
AccountID const& outerAccount,
std::uint32_t outerSeqValue,
std::uint32_t const& flags,
std::vector<uint256> const& txids)
serializeBatch(Serializer& msg, std::uint32_t const& flags, std::vector<uint256> const& txids)
{
msg.add32(HashPrefix::Batch);
msg.addBitString(outerAccount);
msg.add32(outerSeqValue);
msg.add32(flags);
msg.add32(std::uint32_t(txids.size()));
for (auto const& txid : txids)

View File

@@ -1,431 +0,0 @@
#pragma once
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/detail/secp256k1.h>
#include <secp256k1_mpt.h>
#include <cstdint>
#include <limits>
namespace xrpl {
/**
* @brief Bundles an ElGamal public key with its associated encrypted amount.
*
* Used to represent a recipient in confidential transfers, containing both
* the recipient's ElGamal public key and the ciphertext encrypting the
* transfer amount under that key.
*/
struct ConfidentialRecipient
{
/** @brief The recipient's ElGamal public key (size=xrpl::kEcPubKeyLength). */
Slice publicKey;
/**
* @brief The encrypted amount ciphertext
* (size=xrpl::kEcGamalEncryptedTotalLength).
*/
Slice encryptedAmount;
};
/**
* @brief Holds two secp256k1 public key components representing an ElGamal
* ciphertext (C1, C2).
*/
struct EcPair
{
/** @brief First ElGamal ciphertext component. */
secp256k1_pubkey c1;
/** @brief Second ElGamal ciphertext component. */
secp256k1_pubkey c2;
};
/**
* @brief Increments the confidential balance version counter on an MPToken.
*
* The version counter is used to prevent replay attacks by binding proofs
* to a specific state of the account's confidential balance. Wraps to 0
* on overflow (defined behavior for unsigned integers).
*
* @param mptoken The MPToken ledger entry to update.
*/
inline void
incrementConfidentialVersion(STObject& mptoken)
{
// Retrieve current version and increment, wrapping back to 0 at UINT32_MAX.
// The wrap is computed explicitly rather than relying on unsigned overflow
// of `+ 1u`, as it trips the unsigned-integer-overflow sanitizer in the UBSan CI build.
auto const current = mptoken[~sfConfidentialBalanceVersion].valueOr(0u);
mptoken[sfConfidentialBalanceVersion] =
current == std::numeric_limits<std::uint32_t>::max() ? 0u : current + 1u;
}
/**
* @brief Generates the context hash for ConfidentialMPTSend transactions.
*
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
* this specific send transaction, preventing proof reuse across transactions.
*
* @param account The sender's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
* @param destination The destination account ID.
* @param version The sender's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getSendContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version);
/**
* @brief Generates the context hash for ConfidentialMPTClawback transactions.
*
* Creates a unique 256-bit hash that binds the equality proof to this
* specific clawback transaction.
*
* @param account The issuer's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
* @param holder The holder's account ID being clawed back from.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getClawbackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& holder);
/**
* @brief Generates the context hash for ConfidentialMPTConvert transactions.
*
* Creates a unique 256-bit hash that binds the Schnorr proof (for key
* registration) to this specific convert transaction.
*
* @param account The holder's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or a ticket number.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence);
/**
* @brief Generates the context hash for ConfidentialMPTConvertBack transactions.
*
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
* this specific convert-back transaction.
*
* @param account The holder's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or a ticket number.
* @param version The holder's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertBackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
std::uint32_t version);
/**
* @brief Parses an ElGamal ciphertext into two secp256k1 public key components.
*
* Breaks an encrypted amount (size=xrpl::kEcGamalEncryptedTotalLength, two
* compressed EC points of size=xrpl::kEcCiphertextComponentLength) into
* a pair containing (C1, C2) for use in cryptographic operations.
*
* @param buffer The buffer containing the compressed ciphertext
* (size=xrpl::kEcGamalEncryptedTotalLength).
* @return The parsed pair (c1, c2) if successful, std::nullopt if the buffer is invalid.
*/
std::optional<EcPair>
makeEcPair(Slice const& buffer);
/**
* @brief Serializes an EcPair into compressed form.
*
* Converts an EcPair (C1, C2) back into a buffer
* (size=xrpl::kEcGamalEncryptedTotalLength) containing two compressed EC
* points (size=xrpl::kEcCiphertextComponentLength each).
*
* @param pair The EcPair to serialize.
* @return The buffer (size=xrpl::kEcGamalEncryptedTotalLength), or std::nullopt
* if serialization fails.
*/
std::optional<Buffer>
serializeEcPair(EcPair const& pair);
/**
* @brief Verifies that a buffer contains two valid, parsable EC public keys.
*
* @param buffer The input buffer containing two concatenated components.
* @return true if both components can be parsed successfully, false otherwise.
*/
bool
isValidCiphertext(Slice const& buffer);
/**
* @brief Verifies that a buffer contains a valid, parsable compressed EC point.
*
* Can be used to validate both compressed public keys and Pedersen commitments.
* Fails early if the prefix byte is not 0x02 or 0x03.
*
* @param buffer The input buffer containing a compressed EC point
* (size=xrpl::kCompressedEcPointLength).
* @return true if the point can be parsed successfully, false otherwise.
*/
bool
isValidCompressedECPoint(Slice const& buffer);
/**
* @brief Homomorphically adds two ElGamal ciphertexts.
*
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a + b) from Enc(a) and Enc(b) without decryption.
*
* @param a The first ciphertext (size=xrpl::kEcGamalEncryptedTotalLength).
* @param b The second ciphertext (size=xrpl::kEcGamalEncryptedTotalLength).
* @return The resulting ciphertext Enc(a + b), or std::nullopt on failure.
*/
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b);
/**
* @brief Homomorphically subtracts two ElGamal ciphertexts.
*
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a - b) from Enc(a) and Enc(b) without decryption.
*
* @param a The minuend ciphertext (size=xrpl::kEcGamalEncryptedTotalLength).
* @param b The subtrahend ciphertext (size=xrpl::kEcGamalEncryptedTotalLength).
* @return The resulting ciphertext Enc(a - b), or std::nullopt on failure.
*/
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b);
/**
* @brief Re-randomizes an ElGamal ciphertext without changing its plaintext.
*
* Adds Enc(0; randomness) under the supplied public key to the ciphertext.
* This is used when a public, deterministic scalar must perturb ciphertext
* randomness while preserving ledger reproducibility.
*
* @param ciphertext The ciphertext to re-randomize
* (size=xrpl::kEcGamalEncryptedTotalLength).
* @param pubKeySlice The ElGamal public key matching the ciphertext recipient.
* @param randomness The scalar used as zero-encryption randomness
* (size=xrpl::kEcScalarLength).
* @return The re-randomized ciphertext, or std::nullopt on failure.
*/
std::optional<Buffer>
rerandomizeCiphertext(Slice const& ciphertext, Slice const& pubKeySlice, Slice const& randomness);
/**
* @brief Encrypts an amount using ElGamal encryption.
*
* Produces a ciphertext C = (C1, C2) where C1 = r*G and C2 = m*G + r*Pk,
* using the provided blinding factor r.
*
* @param amt The plaintext amount to encrypt.
* @param pubKeySlice The recipient's ElGamal public key (size=xrpl::kEcPubKeyLength).
* @param blindingFactor The randomness used as blinding factor r
* (size=xrpl::ecBlindingFactorLength).
* @return The ciphertext (size=xrpl::kEcGamalEncryptedTotalLength), or std::nullopt on failure.
*/
std::optional<Buffer>
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor);
/**
* @brief Generates the canonical zero encryption for a specific MPToken.
*
* Creates a deterministic encryption of zero that is unique to the account
* and MPT issuance. Used to initialize confidential balance fields.
*
* @param pubKeySlice The holder's ElGamal public key (size=xrpl::kEcPubKeyLength).
* @param account The account ID of the token holder.
* @param mptId The MPToken Issuance ID.
* @return The canonical zero ciphertext (size=xrpl::kEcGamalEncryptedTotalLength), or std::nullopt
* on failure.
*/
std::optional<Buffer>
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId);
/**
* @brief Verifies a Schnorr proof of knowledge of an ElGamal private key.
*
* Proves that the submitter knows the secret key corresponding to the
* provided public key, without revealing the secret key itself.
*
* @param pubKeySlice The ElGamal public key (size=xrpl::kEcPubKeyLength).
* @param proofSlice The Schnorr proof (size=xrpl::ecSchnorrProofLength).
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if valid, or an error code otherwise.
*/
TER
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash);
/**
* @brief Validates the format of encrypted amount fields in a transaction.
*
* Checks that all ciphertext fields in the transaction object have the
* correct length and contain valid EC points. This function is only used
* by ConfidentialMPTConvert and ConfidentialMPTConvertBack transactions.
*
* @param object The transaction object containing encrypted amount fields.
* @return tesSUCCESS if all formats are valid, temMALFORMED if required fields
* are missing, or temBAD_CIPHERTEXT if format validation fails.
*/
NotTEC
checkEncryptedAmountFormat(STObject const& object);
/**
* @brief Verifies revealed amount encryptions for all recipients.
*
* Validates that the same amount was correctly encrypted for the holder,
* issuer, and optionally the auditor using their respective public keys.
*
* @param amount The revealed plaintext amount.
* @param blindingFactor The blinding factor used in all encryptions
* (size=xrpl::ecBlindingFactorLength).
* @param holder The holder's public key and encrypted amount.
* @param issuer The issuer's public key and encrypted amount.
* @param auditor Optional auditor's public key and encrypted amount.
* @return tesSUCCESS if all encryptions are valid, or an error code otherwise.
*/
TER
verifyRevealedAmount(
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor);
/**
* @brief Returns the number of recipients in a confidential transfer.
*
* Returns 4 if an auditor is present (sender, destination, issuer, auditor),
* or 3 if no auditor (sender, destination, issuer).
*
* @param hasAuditor Whether the issuance has an auditor configured.
* @return The number of recipients (3 or 4).
*/
constexpr uint8_t
getConfidentialRecipientCount(bool hasAuditor)
{
return hasAuditor ? 4 : 3;
}
/**
* @brief Verifies a compact sigma clawback proof.
*
* Proves that the issuer knows the exact amount encrypted in the holder's
* balance ciphertext. Used in ConfidentialMPTClawback to verify the issuer
* can decrypt the balance using their private key.
*
* @param amount The revealed plaintext amount.
* @param proof The zero-knowledge proof bytes (ecClawbackProofLength).
* @param pubKeySlice The issuer's ElGamal public key (kEcPubKeyLength bytes).
* @param ciphertext The issuer's encrypted balance on the holder's account
* (kEcGamalEncryptedTotalLength bytes).
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
*/
TER
verifyClawbackProof(
uint64_t const amount,
Slice const& proof,
Slice const& pubKeySlice,
Slice const& ciphertext,
uint256 const& contextHash);
/**
* @brief Generates a cryptographically secure blinding factor
* (size=xrpl::kEcBlindingFactorLength).
*
* Produces random bytes suitable for use as an ElGamal blinding factor
* or Pedersen commitment randomness.
*
* @return A buffer containing the random blinding factor
* (size=xrpl::kEcBlindingFactorLength).
*/
Buffer
generateBlindingFactor();
/**
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTSend transaction.
*
* This function calls mpt_verify_send_proof API in the mpt-crypto utility lib, which verifies the
* equality proof, amount linkage, balance linkage, and range proof.
* Equality proof: Proves the same value is encrypted for the sender, receiver, issuer, and auditor.
* Amount linkage: Proves the send amount matches the amount Pedersen commitment.
* Balance linkage: Proves the sender's balance matches the balance Pedersen
* commitment.
* Range proof: Proves the amount and the remaining balance are within range [0, 2^64-1].
*
* @param proof The full proof blob.
* @param sender The sender's public key and encrypted amount.
* @param destination The destination's public key and encrypted amount.
* @param issuer The issuer's public key and encrypted amount.
* @param auditor The auditor's public key and encrypted amount if present.
* @param spendingBalance The sender's current spending balance ciphertext.
* @param amountCommitment The Pedersen commitment to the send amount.
* @param balanceCommitment The Pedersen commitment to the sender's balance.
* @param contextHash The context hash binding the proof.
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
*/
TER
verifySendProof(
Slice const& proof,
ConfidentialRecipient const& sender,
ConfidentialRecipient const& destination,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor,
Slice const& spendingBalance,
Slice const& amountCommitment,
Slice const& balanceCommitment,
uint256 const& contextHash);
/**
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTConvertBack transaction.
*
* This function calls mpt_verify_convert_back_proof API in the mpt-crypto utility lib, which
* verifies the balance linkage proof and range proof. Balance linkage proof: proves the balance
* commitment matches the spending ciphertext. Range proof: proves the remaining balance after
* convert back is within range [0, 2^64-1].
*
* @param proof The full proof blob.
* @param pubKeySlice The holder's public key.
* @param spendingBalance The holder's spending balance ciphertext.
* @param balanceCommitment The Pedersen commitment to the balance.
* @param amount The amount being converted back to public.
* @param contextHash The context hash binding the proof.
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
*/
TER
verifyConvertBackProof(
Slice const& proof,
Slice const& pubKeySlice,
Slice const& spendingBalance,
Slice const& balanceCommitment,
uint64_t amount,
uint256 const& contextHash);
} // namespace xrpl

View File

@@ -68,15 +68,21 @@ skip(LedgerIndex ledger) noexcept;
/** The (fixed) index of the object containing the ledger fees. */
Keylet const&
feeSettings() noexcept;
fees() noexcept;
/** The (fixed) index of the object containing the ledger negativeUNL. */
Keylet const&
negativeUNL() noexcept;
/** The beginning of an order book */
Keylet
book(Book const& b);
struct BookT
{
explicit BookT() = default;
Keylet
operator()(Book const& b) const;
};
static BookT const kBook{};
/** The index of a trust line for a given currency
@@ -87,12 +93,12 @@ book(Book const& b);
*/
/** @{ */
Keylet
trustLine(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept;
line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept;
inline Keylet
trustLine(AccountID const& id, Issue const& issue) noexcept
line(AccountID const& id, Issue const& issue) noexcept
{
return trustLine(id, issue.account, issue.currency);
return line(id, issue.account, issue.currency);
}
/** @} */
@@ -113,27 +119,37 @@ Keylet
quality(Keylet const& k, std::uint64_t q) noexcept;
/** The directory for the next lower quality */
Keylet
next(Keylet const& k);
struct NextT
{
explicit NextT() = default;
Keylet
operator()(Keylet const& k) const;
};
static NextT const kNext{};
/** A ticket belonging to an account */
/** @{ */
Keylet
ticket(AccountID const& id, std::uint32_t ticketSeq);
Keylet
ticket(AccountID const& id, SeqProxy ticketSeq);
inline Keylet
ticket(uint256 const& key)
struct TicketT
{
return {ltTICKET, key};
}
/** @} */
explicit TicketT() = default;
Keylet
operator()(AccountID const& id, std::uint32_t ticketSeq) const;
Keylet
operator()(AccountID const& id, SeqProxy ticketSeq) const;
Keylet
operator()(uint256 const& key) const
{
return {ltTICKET, key};
}
};
static TicketT const kTicket{};
/** A SignerList */
Keylet
signerList(AccountID const& account) noexcept;
signers(AccountID const& account) noexcept;
/** A Check */
/** @{ */
@@ -193,7 +209,7 @@ escrow(AccountID const& src, std::uint32_t seq) noexcept;
/** A PaymentChannel */
Keylet
payChannel(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
/** NFT page keylets
@@ -205,22 +221,22 @@ payChannel(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexce
/** @{ */
/** A keylet for the owner's first possible NFT page. */
Keylet
nftokenPageMin(AccountID const& owner);
nftpageMin(AccountID const& owner);
/** A keylet for the owner's last possible NFT page. */
Keylet
nftokenPageMax(AccountID const& owner);
nftpageMax(AccountID const& owner);
Keylet
nftokenPage(Keylet const& k, uint256 const& token);
nftpage(Keylet const& k, uint256 const& token);
/** @} */
/** An offer from an account to buy or sell an NFT */
Keylet
nftokenOffer(AccountID const& owner, std::uint32_t seq);
nftoffer(AccountID const& owner, std::uint32_t seq);
inline Keylet
nftokenOffer(uint256 const& offer)
nftoffer(uint256 const& offer)
{
return {ltNFTOKEN_OFFER, offer};
}
@@ -271,13 +287,13 @@ credential(uint256 const& key) noexcept
}
Keylet
mptokenIssuance(std::uint32_t seq, AccountID const& issuer) noexcept;
mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept;
Keylet
mptokenIssuance(MPTID const& issuanceID) noexcept;
mptIssuance(MPTID const& issuanceID) noexcept;
inline Keylet
mptokenIssuance(uint256 const& issuanceKey)
mptIssuance(uint256 const& issuanceKey)
{
return {ltMPTOKEN_ISSUANCE, issuanceKey};
}
@@ -304,10 +320,10 @@ vault(uint256 const& vaultKey)
}
Keylet
loanBroker(AccountID const& owner, std::uint32_t seq) noexcept;
loanbroker(AccountID const& owner, std::uint32_t seq) noexcept;
inline Keylet
loanBroker(uint256 const& key)
loanbroker(uint256 const& key)
{
return {ltLOAN_BROKER, key};
}
@@ -360,15 +376,11 @@ struct KeyletDesc
std::array<KeyletDesc<AccountID const&>, 6> const kDirectAccountKeylets{
{{.function = &keylet::account, .expectedLEName = jss::AccountRoot, .includeInTests = false},
{.function = &keylet::ownerDir, .expectedLEName = jss::DirectoryNode, .includeInTests = true},
{.function = &keylet::signerList, .expectedLEName = jss::SignerList, .includeInTests = true},
{.function = &keylet::signers, .expectedLEName = jss::SignerList, .includeInTests = true},
// It's normally impossible to create an item at nftpage_min, but
// test it anyway, since the invariant checks for it.
{.function = &keylet::nftokenPageMin,
.expectedLEName = jss::NFTokenPage,
.includeInTests = true},
{.function = &keylet::nftokenPageMax,
.expectedLEName = jss::NFTokenPage,
.includeInTests = true},
{.function = &keylet::nftpageMin, .expectedLEName = jss::NFTokenPage, .includeInTests = true},
{.function = &keylet::nftpageMax, .expectedLEName = jss::NFTokenPage, .includeInTests = true},
{.function = &keylet::did, .expectedLEName = jss::DID, .includeInTests = true}}};
MPTID

View File

@@ -177,8 +177,7 @@ enum LedgerEntryType : std::uint16_t {
LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040) \
LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080)) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \
\
LEDGER_OBJECT(MPTokenIssuanceMutable, \
LSF_FLAG(lsmfMPTCanEnableCanLock, 0x00000002) \
@@ -187,9 +186,8 @@ enum LedgerEntryType : std::uint16_t {
LSF_FLAG(lsmfMPTCanEnableCanTrade, 0x00000010) \
LSF_FLAG(lsmfMPTCanEnableCanTransfer, 0x00000020) \
LSF_FLAG(lsmfMPTCanEnableCanClawback, 0x00000040) \
LSF_FLAG(lsmfMPTCannotEnableCanHoldConfidentialBalance, 0x00000080) \
LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \
\
LEDGER_OBJECT(MPToken, \
LSF_FLAG2(lsfMPTLocked, 0x00000001) \

View File

@@ -106,7 +106,7 @@ public:
txToPermissionType(TxType type);
// tx type value is permission value minus one
[[nodiscard]] static std::optional<TxType>
[[nodiscard]] static TxType
permissionToTxType(std::uint32_t value);
/**

View File

@@ -4,10 +4,6 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/Units.h>
#include <mpt_protocol.h>
#include <secp256k1_mpt.h>
#include <cstddef>
#include <cstdint>
namespace xrpl {
@@ -311,65 +307,4 @@ constexpr std::size_t kPermissionMaxSize = 10;
/** The maximum number of transactions that can be in a batch. */
constexpr std::size_t kMaxBatchTxCount = 8;
/** The maximum number of batch signers. */
constexpr std::size_t kMaxBatchSigners = kMaxBatchTxCount * 3;
/** Length of a secp256k1 scalar in bytes. */
constexpr std::size_t kEcScalarLength = kMPT_SCALAR_SIZE;
/** Length of EC point (compressed) */
constexpr std::size_t kCompressedEcPointLength = 33;
/** Length of one compressed EC point component in an EC ElGamal ciphertext. */
constexpr std::size_t kEcCiphertextComponentLength = kMPT_ELGAMAL_CIPHER_SIZE;
/** EC ElGamal ciphertext length: two compressed EC points concatenated. */
constexpr std::size_t kEcGamalEncryptedTotalLength = kMPT_ELGAMAL_TOTAL_SIZE;
/** Length of EC public key (compressed) */
constexpr std::size_t kEcPubKeyLength = kMPT_PUBKEY_SIZE;
/** Length of EC private key in bytes */
constexpr std::size_t kEcPrivKeyLength = kMPT_PRIVKEY_SIZE;
/** Length of the EC blinding factor in bytes */
constexpr std::size_t kEcBlindingFactorLength = kMPT_BLINDING_FACTOR_SIZE;
/** Length of Schnorr ZKProof for public key registration (compact form) in bytes */
constexpr std::size_t kEcSchnorrProofLength = kMPT_SCHNORR_PROOF_SIZE;
/** Length of Pedersen Commitment (compressed) */
constexpr std::size_t kEcPedersenCommitmentLength = kMPT_PEDERSEN_COMMIT_SIZE;
/** Length of single bulletproof (range proof for 1 commitment) in bytes */
constexpr std::size_t kEcSingleBulletproofLength = kMPT_SINGLE_BULLETPROOF_SIZE;
/** Length of double bulletproof (range proof for 2 commitments) in bytes */
constexpr std::size_t kEcDoubleBulletproofLength = kMPT_DOUBLE_BULLETPROOF_SIZE;
/** Length of the compact sigma proof component for ConfidentialMPTSend. */
constexpr std::size_t kEcSendSigmaProofLength = SECP256K1_COMPACT_STANDARD_PROOF_SIZE;
/** 192 bytes compact sigma proof + 754 bytes double bulletproof. */
constexpr std::size_t kEcSendProofLength = kEcSendSigmaProofLength + kEcDoubleBulletproofLength;
/** Length of the compact sigma proof component for ConfidentialMPTConvertBack. */
constexpr std::size_t kEcConvertBackSigmaProofLength = SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE;
/** 128 bytes compact sigma proof + 688 bytes single bulletproof. */
constexpr std::size_t kEcConvertBackProofLength =
kEcConvertBackSigmaProofLength + kEcSingleBulletproofLength;
/** Length of the ZKProof for ConfidentialMPTClawback. */
constexpr std::size_t kEcClawbackProofLength = SECP256K1_COMPACT_CLAWBACK_PROOF_SIZE;
/** Extra base fee multiplier charged to confidential MPT transactions. */
constexpr std::uint32_t kConfidentialFeeMultiplier = 9;
/** Compressed EC point prefix for even y-coordinate */
constexpr std::uint8_t kEcCompressedPrefixEvenY = 0x02;
/** Compressed EC point prefix for odd y-coordinate */
constexpr std::uint8_t kEcCompressedPrefixOddY = 0x03;
} // namespace xrpl

View File

@@ -217,11 +217,6 @@ public:
[[nodiscard]] AccountID
getAccountID(SField const& field) const;
/** The account responsible for the fee and authorization: the delegate when
sfDelegate is present, otherwise the account. */
[[nodiscard]] AccountID
getFeePayer() const;
[[nodiscard]] Blob
getFieldVL(SField const& field) const;
[[nodiscard]] STAmount const&

View File

@@ -12,7 +12,6 @@
#include <expected>
#include <functional>
#include <optional>
namespace xrpl {
@@ -52,48 +51,51 @@ public:
STTx(TxType type, std::function<void(STObject&)> assembler);
// STObject functions.
[[nodiscard]] SerializedTypeID
SerializedTypeID
getSType() const override;
[[nodiscard]] std::string
std::string
getFullText() const override;
// Outer transaction functions / signature functions.
static Blob
getSignature(STObject const& sigObject);
[[nodiscard]] Blob
Blob
getSignature() const
{
return getSignature(*this);
}
[[nodiscard]] uint256
uint256
getSigningHash() const;
[[nodiscard]] TxType
TxType
getTxnType() const;
[[nodiscard]] Blob
Blob
getSigningPubKey() const;
[[nodiscard]] SeqProxy
SeqProxy
getSeqProxy() const;
/** Returns the first non-zero value of (Sequence, TicketSequence). */
[[nodiscard]] std::uint32_t
std::uint32_t
getSeqValue() const;
[[nodiscard]] boost::container::flat_set<AccountID>
AccountID
getFeePayer() const;
boost::container::flat_set<AccountID>
getMentionedAccounts() const;
[[nodiscard]] uint256
uint256
getTransactionID() const;
[[nodiscard]] json::Value
json::Value
getJson(JsonOptions options) const override;
[[nodiscard]] json::Value
json::Value
getJson(JsonOptions options, bool binary) const;
void
@@ -106,27 +108,27 @@ public:
@param rules The current ledger rules.
@return `true` if valid signature. If invalid, the error message string.
*/
[[nodiscard]] std::expected<void, std::string>
std::expected<void, std::string>
checkSign(Rules const& rules) const;
[[nodiscard]] std::expected<void, std::string>
std::expected<void, std::string>
checkBatchSign(Rules const& rules) const;
// SQL Functions with metadata.
static std::string const&
getMetaSQLInsertReplaceHeader();
[[nodiscard]] std::string
std::string
getMetaSQL(std::uint32_t inLedger, std::string const& escapedMetaData) const;
[[nodiscard]] std::string
std::string
getMetaSQL(
Serializer rawTxn,
std::uint32_t inLedger,
TxnSql status,
std::string const& escapedMetaData) const;
[[nodiscard]] std::vector<uint256> const&
std::vector<uint256> const&
getBatchTransactionIDs() const;
private:
@@ -136,31 +138,28 @@ private:
Will be *this more often than not.
@return `true` if valid signature. If invalid, the error message string.
*/
[[nodiscard]] std::expected<void, std::string>
std::expected<void, std::string>
checkSign(Rules const& rules, STObject const& sigObject) const;
[[nodiscard]] std::expected<void, std::string>
std::expected<void, std::string>
checkSingleSign(STObject const& sigObject) const;
[[nodiscard]] std::expected<void, std::string>
std::expected<void, std::string>
checkMultiSign(Rules const& rules, STObject const& sigObject) const;
[[nodiscard]] std::expected<void, std::string>
std::expected<void, std::string>
checkBatchSingleSign(STObject const& batchSigner) const;
[[nodiscard]] std::expected<void, std::string>
std::expected<void, std::string>
checkBatchMultiSign(STObject const& batchSigner, Rules const& rules) const;
void
buildBatchTxnIds();
STBase*
copy(std::size_t n, void* buf) const override;
STBase*
move(std::size_t n, void* buf) override;
friend class detail::STVar;
std::optional<std::vector<uint256>> batchTxnIds_;
mutable std::vector<uint256> batchTxnIds_;
};
bool

View File

@@ -128,7 +128,6 @@ enum TEMcodes : TERUnderlyingType {
temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH,
temBAD_MPT,
temBAD_CIPHERTEXT,
};
//------------------------------------------------------------------------------
@@ -175,8 +174,6 @@ enum TEFcodes : TERUnderlyingType {
tefNO_TICKET,
tefNFTOKEN_IS_NOT_TRANSFERABLE,
tefINVALID_LEDGER_FIX_TYPE,
tefNO_DST_PARTIAL,
tefBAD_PATH_COUNT,
};
//------------------------------------------------------------------------------
@@ -361,11 +358,6 @@ enum TECcodes : TERUnderlyingType {
tecLIMIT_EXCEEDED = 195,
tecPSEUDO_ACCOUNT = 196,
tecPRECISION_LOSS = 197,
// DEPRECATED: This error code tecNO_DELEGATE_PERMISSION is reserved for
// backward compatibility with historical data on non-prod networks, can be
// reclaimed after those networks reset.
tecNO_DELEGATE_PERMISSION = 198,
tecBAD_PROOF = 199,
};
//------------------------------------------------------------------------------

View File

@@ -140,8 +140,7 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \
TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \
TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback) \
TF_FLAG(tfMPTCanHoldConfidentialBalance, lsfMPTCanHoldConfidentialBalance), \
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \
MASK_ADJ(0)) \
\
TRANSACTION(MPTokenAuthorize, \
@@ -350,13 +349,10 @@ inline constexpr FlagValue tmfMPTCanEnableCanTransfer = lsmfMPTCanEnableCanTrans
inline constexpr FlagValue tmfMPTCanEnableCanClawback = lsmfMPTCanEnableCanClawback;
inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
inline constexpr FlagValue tmfMPTCannotEnableCanHoldConfidentialBalance =
lsmfMPTCannotEnableCanHoldConfidentialBalance;
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask =
~(tmfMPTCanEnableCanLock | tmfMPTCanEnableRequireAuth | tmfMPTCanEnableCanEscrow |
tmfMPTCanEnableCanTrade | tmfMPTCanEnableCanTransfer | tmfMPTCanEnableCanClawback |
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee |
tmfMPTCannotEnableCanHoldConfidentialBalance);
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
// MPTokenIssuanceSet MutableFlags:
// Enable mutable capability flags. These flags are one-way: once enabled,
@@ -368,10 +364,9 @@ inline constexpr FlagValue tmfMPTSetCanEscrow = 0x00000004;
inline constexpr FlagValue tmfMPTSetCanTrade = 0x00000008;
inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000010;
inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000020;
inline constexpr FlagValue tmfMPTSetCanHoldConfidentialBalance = 0x00000040;
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask =
~(tmfMPTSetCanLock | tmfMPTSetRequireAuth | tmfMPTSetCanEscrow | tmfMPTSetCanTrade |
tmfMPTSetCanTransfer | tmfMPTSetCanClawback | tmfMPTSetCanHoldConfidentialBalance);
tmfMPTSetCanTransfer | tmfMPTSetCanClawback);
// 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.

View File

@@ -15,13 +15,11 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FEATURE(BatchV1_1, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocolV1_1, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(ConfidentialTransfer, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_3_0, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_2_0, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(MPTokensV2, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes)
XRPL_FIX (BatchInnerSigs, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocol, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegationV1_1, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (DirectoryLimit, Supported::Yes, VoteBehavior::DefaultNo)
@@ -35,6 +33,7 @@ XRPL_FEATURE(TokenEscrow, Supported::Yes, VoteBehavior::DefaultN
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(SingleAssetVault, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (PayChanCancelAfter, Supported::Yes, VoteBehavior::DefaultNo)
// Check flags in Credential transactions
@@ -59,11 +58,13 @@ XRPL_FIX (EmptyDID, Supported::Yes, VoteBehavior::DefaultNo
XRPL_FEATURE(PriceOracle, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (AMMOverflowOffer, Supported::Yes, VoteBehavior::DefaultYes)
XRPL_FIX (InnerObjTemplate, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (NFTokenReserve, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (FillOrKill, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(DID, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (DisallowIncomingV1, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(XChainBridge, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMM, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Clawback, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(XRPFees, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (RemoveNFTokenAutoTrustLine, Supported::Yes, VoteBehavior::DefaultYes)
@@ -104,7 +105,6 @@ XRPL_RETIRE_FIX(CheckThreading)
XRPL_RETIRE_FIX(MasterKeyAsRegularKey)
XRPL_RETIRE_FIX(NonFungibleTokensV1_2)
XRPL_RETIRE_FIX(NFTokenRemint)
XRPL_RETIRE_FIX(NFTokenReserve)
XRPL_RETIRE_FIX(PayChanRecipientOwnerDir)
XRPL_RETIRE_FIX(QualityUpperBound)
XRPL_RETIRE_FIX(ReducedOffersV1)
@@ -116,7 +116,6 @@ XRPL_RETIRE_FIX(UniversalNumber)
XRPL_RETIRE_FEATURE(Checks)
XRPL_RETIRE_FEATURE(CheckCashMakesTrustLine)
XRPL_RETIRE_FEATURE(Clawback)
XRPL_RETIRE_FEATURE(CryptoConditions)
XRPL_RETIRE_FEATURE(CryptoConditionsSuite)
XRPL_RETIRE_FEATURE(DeletableAccounts)

View File

@@ -21,7 +21,7 @@
/** A ledger object which identifies an offer to buy or sell an NFT.
\sa keylet::nftokenOffer
\sa keylet::nftoffer
*/
LEDGER_ENTRY(ltNFTOKEN_OFFER, 0x0037, NFTokenOffer, nft_offer, ({
{sfOwner, SoeRequired},
@@ -84,7 +84,7 @@ LEDGER_ENTRY(ltNEGATIVE_UNL, 0x004e, NegativeUNL, nunl, ({
/** A ledger object which contains a list of NFTs
\sa keylet::nftokenPageMin, keylet::nftokenPageMax, keylet::nftokenPage
\sa keylet::nftpageMin, keylet::nftpageMax, keylet::nftpage
*/
LEDGER_ENTRY(ltNFTOKEN_PAGE, 0x0050, NFTokenPage, nft_page, ({
{sfPreviousPageMin, SoeOptional},
@@ -96,7 +96,7 @@ LEDGER_ENTRY(ltNFTOKEN_PAGE, 0x0050, NFTokenPage, nft_page, ({
/** A ledger object which contains a signer list for an account.
\sa keylet::signerList
\sa keylet::signers
*/
// All fields are SoeRequired because there is always a SignerEntries.
// If there are no SignerEntries the node is deleted.
@@ -112,7 +112,7 @@ LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
/** A ledger object which describes a ticket.
\sa keylet::ticket
\sa keylet::kTicket
*/
LEDGER_ENTRY(ltTICKET, 0x0054, Ticket, ticket, ({
{sfAccount, SoeRequired},
@@ -272,7 +272,7 @@ LEDGER_ENTRY(ltXCHAIN_OWNED_CLAIM_ID, 0x0071, XChainOwnedClaimID, xchain_owned_c
@note Per Vinnie Falco this should be renamed to ltTRUST_LINE
\sa keylet::trustLine
\sa keylet::line
*/
LEDGER_ENTRY(ltRIPPLE_STATE, 0x0072, RippleState, state, ({
{sfBalance, SoeRequired},
@@ -292,7 +292,7 @@ LEDGER_ENTRY(ltRIPPLE_STATE, 0x0072, RippleState, state, ({
\note This is a singleton: only one such object exists in the ledger.
\sa keylet::feeSettings
\sa keylet::fees
*/
LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({
// Old version uses raw numbers
@@ -346,7 +346,7 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
/** A ledger object describing a single unidirectional XRP payment channel.
\sa keylet::payChannel
\sa keylet::payChan
*/
LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
{sfAccount, SoeRequired},
@@ -384,7 +384,7 @@ LEDGER_ENTRY(ltAMM, 0x0079, AMM, amm, ({
}))
/** A ledger object which tracks MPTokenIssuance
\sa keylet::mptokenIssuance
\sa keylet::mptIssuance
*/
LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfIssuer, SoeRequired},
@@ -401,28 +401,19 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfDomainID, SoeOptional},
{sfMutableFlags, SoeDefault},
{sfReferenceHolding, SoeOptional},
{sfIssuerEncryptionKey, SoeOptional},
{sfAuditorEncryptionKey, SoeOptional},
{sfConfidentialOutstandingAmount, SoeDefault},
}))
/** A ledger object which tracks MPToken
\sa keylet::mptoken
*/
LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfAccount, SoeRequired},
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeDefault},
{sfLockedAmount, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfConfidentialBalanceInbox, SoeOptional},
{sfConfidentialBalanceSpending, SoeOptional},
{sfConfidentialBalanceVersion, SoeDefault},
{sfIssuerEncryptedBalance, SoeOptional},
{sfAuditorEncryptedBalance, SoeOptional},
{sfHolderEncryptionKey, SoeOptional},
{sfAccount, SoeRequired},
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeDefault},
{sfLockedAmount, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
/** A ledger object which tracks Oracle
@@ -508,7 +499,7 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
/** A ledger object representing a loan broker
\sa keylet::loanBroker
\sa keylet::loanbroker
*/
LEDGER_ENTRY(ltLOAN_BROKER, 0x0088, LoanBroker, loan_broker, ({
{sfPreviousTxnID, SoeRequired},

View File

@@ -11,10 +11,7 @@ secp256k1Context()
struct Holder
{
secp256k1_context* impl;
// SECP256K1_CONTEXT_SIGN and SECP256K1_CONTEXT_VERIFY were deprecated.
// All contexts support both signing and verification, so
// SECP256K1_CONTEXT_NONE is the correct flag to use.
Holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_NONE))
Holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
{
}

View File

@@ -113,7 +113,6 @@ TYPED_SFIELD(sfInterestRate, UINT32, 65) // 1/10 basis points (bi
TYPED_SFIELD(sfLateInterestRate, UINT32, 66) // 1/10 basis points (bips)
TYPED_SFIELD(sfCloseInterestRate, UINT32, 67) // 1/10 basis points (bips)
TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 68) // 1/10 basis points (bips)
TYPED_SFIELD(sfConfidentialBalanceVersion, UINT32, 69)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)
@@ -147,7 +146,6 @@ TYPED_SFIELD(sfSubjectNode, UINT64, 28)
TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::kSmdBaseTen|SField::kSmdDefault)
TYPED_SFIELD(sfVaultNode, UINT64, 30)
TYPED_SFIELD(sfLoanBrokerNode, UINT64, 31)
TYPED_SFIELD(sfConfidentialOutstandingAmount, UINT64, 32, SField::kSmdBaseTen|SField::kSmdDefault)
// 128-bit
TYPED_SFIELD(sfEmailHash, UINT128, 1)
@@ -208,7 +206,6 @@ TYPED_SFIELD(sfLoanBrokerID, UINT256, 37,
SField::kSmdPseudoAccount | SField::kSmdDefault)
TYPED_SFIELD(sfLoanID, UINT256, 38)
TYPED_SFIELD(sfReferenceHolding, UINT256, 39)
TYPED_SFIELD(sfBlindingFactor, UINT256, 40)
// number (common)
TYPED_SFIELD(sfNumber, NUMBER, 1)
@@ -302,21 +299,6 @@ TYPED_SFIELD(sfAssetClass, VL, 28)
TYPED_SFIELD(sfProvider, VL, 29)
TYPED_SFIELD(sfMPTokenMetadata, VL, 30)
TYPED_SFIELD(sfCredentialType, VL, 31)
TYPED_SFIELD(sfConfidentialBalanceInbox, VL, 32)
TYPED_SFIELD(sfConfidentialBalanceSpending, VL, 33)
TYPED_SFIELD(sfIssuerEncryptedBalance, VL, 34)
TYPED_SFIELD(sfIssuerEncryptionKey, VL, 35)
TYPED_SFIELD(sfHolderEncryptionKey, VL, 36)
TYPED_SFIELD(sfZKProof, VL, 37)
TYPED_SFIELD(sfHolderEncryptedAmount, VL, 38)
TYPED_SFIELD(sfIssuerEncryptedAmount, VL, 39)
TYPED_SFIELD(sfSenderEncryptedAmount, VL, 40)
TYPED_SFIELD(sfDestinationEncryptedAmount, VL, 41)
TYPED_SFIELD(sfAuditorEncryptedBalance, VL, 42)
TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)

View File

@@ -58,6 +58,7 @@ public:
case TokenCodecErrc::InvalidEncodingChar:
return "invalid encoding char";
case TokenCodecErrc::Unknown:
return "unknown";
default:
return "unknown";
}

View File

@@ -395,7 +395,7 @@ TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
#endif
TRANSACTION(ttCLAWBACK, 30, Clawback,
Delegation::Delegable,
uint256{},
featureClawback,
NoPriv,
({
{sfAmount, SoeRequired, SoeMptSupported},
@@ -735,8 +735,6 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
{sfMPTokenMetadata, SoeOptional},
{sfTransferFee, SoeOptional},
{sfMutableFlags, SoeOptional},
{sfIssuerEncryptionKey, SoeOptional},
{sfAuditorEncryptionKey, SoeOptional},
}))
/** This transaction type authorizes a MPToken instance */
@@ -889,7 +887,6 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
MustDeleteAcct | DestroyMptIssuance | MustModifyVault,
({
{sfVaultID, SoeRequired},
{sfMemoData, SoeOptional},
}))
/** This transaction trades assets for shares with a vault. */
@@ -940,7 +937,7 @@ TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
#endif
TRANSACTION(ttBATCH, 71, Batch,
Delegation::NotDelegable,
featureBatchV1_1,
featureBatch,
NoPriv,
({
{sfRawTransactions, SoeRequired},
@@ -1080,91 +1077,6 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
{sfAmount, SoeRequired, SoeMptSupported},
}))
/** This transaction type converts into confidential MPT balance. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTConvert.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeRequired},
{sfHolderEncryptionKey, SoeOptional},
{sfHolderEncryptedAmount, SoeRequired},
{sfIssuerEncryptedAmount, SoeRequired},
{sfAuditorEncryptedAmount, SoeOptional},
{sfBlindingFactor, SoeRequired},
{sfZKProof, SoeOptional},
}))
/** This transaction type merges MPT inbox. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTMergeInbox.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_MERGE_INBOX, 86, ConfidentialMPTMergeInbox,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
}))
/** This transaction type converts back into public MPT balance. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTConvertBack.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT_BACK, 87, ConfidentialMPTConvertBack,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfMPTAmount, SoeRequired},
{sfHolderEncryptedAmount, SoeRequired},
{sfIssuerEncryptedAmount, SoeRequired},
{sfAuditorEncryptedAmount, SoeOptional},
{sfBlindingFactor, SoeRequired},
{sfZKProof, SoeRequired},
{sfBalanceCommitment, SoeRequired},
}))
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_SEND, 88, ConfidentialMPTSend,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfDestination, SoeRequired},
{sfDestinationTag, SoeOptional},
{sfSenderEncryptedAmount, SoeRequired},
{sfDestinationEncryptedAmount, SoeRequired},
{sfIssuerEncryptedAmount, SoeRequired},
{sfAuditorEncryptedAmount, SoeOptional},
{sfZKProof, SoeRequired},
{sfAmountCommitment, SoeRequired},
{sfBalanceCommitment, SoeRequired},
{sfCredentialIDs, SoeOptional},
}))
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTClawback.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback,
Delegation::Delegable,
featureConfidentialTransfer,
NoPriv,
({
{sfMPTokenIssuanceID, SoeRequired},
{sfHolder, SoeRequired},
{sfMPTAmount, SoeRequired},
{sfZKProof, SoeRequired},
}))
/** This system-generated transaction type is used to update the status of the various amendments.
For details, see: https://xrpl.org/amendments.html

View File

@@ -137,7 +137,6 @@ JSS(authorized_credentials); // in: ledger_entry DepositPreauth
JSS(auth_accounts); // out: amm_info
JSS(auth_change); // out: AccountInfo
JSS(auth_change_queued); // out: AccountInfo
JSS(auditor_encrypted_balance); // out: mpt_holders (confidential MPT)
JSS(available); // out: ValidatorList
JSS(avg_bps_recv); // out: Peers
JSS(avg_bps_sent); // out: Peers
@@ -162,6 +161,9 @@ JSS(build_path); // in: TransactionSign
JSS(build_version); // out: NetworkOPs
JSS(cancel_after); // out: AccountChannels
JSS(can_delete); // out: CanDelete
JSS(mpt_amount); // out: mpt_holders
JSS(mpt_issuance_id); // in: Payment, mpt_holders
JSS(mptoken_index); // out: mpt_holders
JSS(changes); // out: BookChanges
JSS(channel_id); // out: AccountChannels
JSS(channels); // out: AccountChannels
@@ -183,170 +185,165 @@ JSS(command); // in: RPCHandler
JSS(common); // out: RPC server_definitions
JSS(complete); // out: NetworkOPs, InboundLedger
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
JSS(confidential_balance_inbox); // out: mpt_holders (confidential MPT)
JSS(confidential_balance_spending); // out: mpt_holders (confidential MPT)
JSS(confidential_balance_version); // out: mpt_holders (confidential MPT)
JSS(consensus); // out: NetworkOPs, LedgerConsensus
JSS(converge_time); // out: NetworkOPs
JSS(converge_time_s); // out: NetworkOPs
JSS(cookie); // out: NetworkOPs
JSS(count); // in: AccountTx*, ValidatorList
JSS(counters); // in/out: retrieve counters
JSS(credentials); // in: deposit_authorized
JSS(credential_type); // in: LedgerEntry DepositPreauth
JSS(ctid); // in/out: Tx RPC
JSS(currency_a); // out: BookChanges
JSS(currency_b); // out: BookChanges
JSS(currency); // in: paths/PathRequest, STAmount
// out: STPathSet, STAmount, AccountLines
JSS(current); // out: OwnerInfo
JSS(current_activities); //
JSS(current_ledger_size); // out: TxQ
JSS(current_queue_size); // out: TxQ
JSS(data); // out: LedgerData
JSS(date); // out: tx/Transaction, NetworkOPs
JSS(dbKBLedger); // out: getCounts
JSS(dbKBTotal); // out: getCounts
JSS(dbKBTransaction); // out: getCounts
JSS(debug_signing); // in: TransactionSign
JSS(deletion_blockers_only); // in: AccountObjects
JSS(delivered_amount); // out: insertDeliveredAmount
JSS(deposit_authorized); // out: deposit_authorized
JSS(deprecated); //
JSS(descending); // in: AccountTx*
JSS(description); // in/out: Reservations
JSS(destination); // in: nft_buy_offers, nft_sell_offers
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
// out: AccountChannels
JSS(destination_amount); // in: PathRequest, RipplePathFind
JSS(destination_currencies); // in: PathRequest, RipplePathFind
JSS(destination_tag); // in: PathRequest
// out: AccountChannels
JSS(details); // out: Manifest, server_info
JSS(dir_entry); // out: DirectoryEntryIterator
JSS(dir_index); // out: DirectoryEntryIterator
JSS(dir_root); // out: DirectoryEntryIterator
JSS(discounted_fee); // out: amm_info
JSS(domain); // out: ValidatorInfo, Manifest
JSS(drops); // out: TxQ
JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(enabled); // out: AmendmentTable
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
JSS(entire_set); // out: get_aggregate_price
JSS(ephemeral_key); // out: ValidatorInfo
// in/out: Manifest
JSS(error); // out: error
JSS(errored); //
JSS(error_code); // out: error
JSS(error_exception); // out: Submit
JSS(error_message); // out: error
JSS(expand); // in: handler/Ledger
JSS(expected_date); // out: any (warnings)
JSS(expected_date_UTC); // out: any (warnings)
JSS(expected_ledger_size); // out: TxQ
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
JSS(fail_hard); // in: Sign, Submit
JSS(failed); // out: InboundLedger
JSS(feature); // in: Feature
JSS(features); // out: Feature
JSS(fee_base); // out: NetworkOPs
JSS(fee_div_max); // in: TransactionSign
JSS(fee_level); // out: AccountInfo
JSS(fee_mult_max); // in: TransactionSign
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
JSS(fetch_pack); // out: NetworkOPs
JSS(FIELDS); // out: RPC server_definitions
// matches definitions.json format
JSS(first); // out: rpc/Version
JSS(finished); //
JSS(fix_txns); // in: LedgerCleaner
JSS(flags); // out: AccountOffers, NetworkOPs
JSS(forward); // in: AccountTx
JSS(freeze); // out: AccountLines
JSS(freeze_peer); // out: AccountLines
JSS(deep_freeze); // out: AccountLines
JSS(deep_freeze_peer); // out: AccountLines
JSS(frozen_balances); // out: GatewayBalances
JSS(full); // in: LedgerClearer, handlers/Ledger
JSS(full_reply); // out: PathFind
JSS(fullbelow_size); // out: GetCounts
JSS(git); // out: server_info
JSS(good); // out: RPCVersion
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
JSS(have_header); // out: InboundLedger
JSS(have_state); // out: InboundLedger
JSS(have_transactions); // out: InboundLedger
JSS(high); // out: BookChanges
JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
JSS(historical_perminute); // historical_perminute.
JSS(holders); // out: MPTHolders
JSS(holder_encryption_key); // out: mpt_holders (confidential MPT)
JSS(hostid); // out: NetworkOPs
JSS(hotwallet); // in: GatewayBalances
JSS(id); // websocket.
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(in); // out: OverlayImpl
JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us); //
JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid version
JSS(io_latency_ms); // out: NetworkOPs
JSS(ip); // in: Connect, out: OverlayImpl
JSS(is_burned); // out: nft_info (clio)
JSS(isSerialized); // out: RPC server_definitions
// matches definitions.json format
JSS(isSigningField); // out: RPC server_definitions
// matches definitions.json format
JSS(isVLEncoded); // out: RPC server_definitions
// matches definitions.json format
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
// out: STPathSet, STAmount
JSS(issuer_encrypted_balance); // out: mpt_holders (confidential MPT)
JSS(job); //
JSS(job_queue); //
JSS(jobs); //
JSS(jsonrpc); // json version
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
JSS(kept); // out: SubmitTransaction
JSS(key); // out
JSS(key_type); // in/out: WalletPropose, TransactionSign
JSS(latency); // out: PeerImp
JSS(last); // out: RPCVersion
JSS(last_close); // out: NetworkOPs
JSS(last_refresh_time); // out: ValidatorSite
JSS(last_refresh_status); // out: ValidatorSite
JSS(last_refresh_message); // out: ValidatorSite
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
// out: NetworkOPs, PeerImp
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
// AccountLines
JSS(ledger_data); // out: LedgerHeader
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
// TransactionEntry, handlers/Ledger
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
// AccountLines
JSS(ledger_hit_rate); // out: GetCounts
JSS(ledger_index); // in/out: many
JSS(ledger_index_max); // in, out: AccountTx*
JSS(ledger_index_min); // in, out: AccountTx*
JSS(ledger_max); // in, out: AccountTx*
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(consensus); // out: NetworkOPs, LedgerConsensus
JSS(converge_time); // out: NetworkOPs
JSS(converge_time_s); // out: NetworkOPs
JSS(cookie); // out: NetworkOPs
JSS(count); // in: AccountTx*, ValidatorList
JSS(counters); // in/out: retrieve counters
JSS(credentials); // in: deposit_authorized
JSS(credential_type); // in: LedgerEntry DepositPreauth
JSS(ctid); // in/out: Tx RPC
JSS(currency_a); // out: BookChanges
JSS(currency_b); // out: BookChanges
JSS(currency); // in: paths/PathRequest, STAmount
// out: STPathSet, STAmount, AccountLines
JSS(current); // out: OwnerInfo
JSS(current_activities); //
JSS(current_ledger_size); // out: TxQ
JSS(current_queue_size); // out: TxQ
JSS(data); // out: LedgerData
JSS(date); // out: tx/Transaction, NetworkOPs
JSS(dbKBLedger); // out: getCounts
JSS(dbKBTotal); // out: getCounts
JSS(dbKBTransaction); // out: getCounts
JSS(debug_signing); // in: TransactionSign
JSS(deletion_blockers_only); // in: AccountObjects
JSS(delivered_amount); // out: insertDeliveredAmount
JSS(deposit_authorized); // out: deposit_authorized
JSS(deprecated); //
JSS(descending); // in: AccountTx*
JSS(description); // in/out: Reservations
JSS(destination); // in: nft_buy_offers, nft_sell_offers
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
// out: AccountChannels
JSS(destination_amount); // in: PathRequest, RipplePathFind
JSS(destination_currencies); // in: PathRequest, RipplePathFind
JSS(destination_tag); // in: PathRequest
// out: AccountChannels
JSS(details); // out: Manifest, server_info
JSS(dir_entry); // out: DirectoryEntryIterator
JSS(dir_index); // out: DirectoryEntryIterator
JSS(dir_root); // out: DirectoryEntryIterator
JSS(discounted_fee); // out: amm_info
JSS(domain); // out: ValidatorInfo, Manifest
JSS(drops); // out: TxQ
JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(enabled); // out: AmendmentTable
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
JSS(entire_set); // out: get_aggregate_price
JSS(ephemeral_key); // out: ValidatorInfo
// in/out: Manifest
JSS(error); // out: error
JSS(errored); //
JSS(error_code); // out: error
JSS(error_exception); // out: Submit
JSS(error_message); // out: error
JSS(expand); // in: handler/Ledger
JSS(expected_date); // out: any (warnings)
JSS(expected_date_UTC); // out: any (warnings)
JSS(expected_ledger_size); // out: TxQ
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
JSS(fail_hard); // in: Sign, Submit
JSS(failed); // out: InboundLedger
JSS(feature); // in: Feature
JSS(features); // out: Feature
JSS(fee_base); // out: NetworkOPs
JSS(fee_div_max); // in: TransactionSign
JSS(fee_level); // out: AccountInfo
JSS(fee_mult_max); // in: TransactionSign
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
JSS(fetch_pack); // out: NetworkOPs
JSS(FIELDS); // out: RPC server_definitions
// matches definitions.json format
JSS(first); // out: rpc/Version
JSS(finished); //
JSS(fix_txns); // in: LedgerCleaner
JSS(flags); // out: AccountOffers, NetworkOPs
JSS(forward); // in: AccountTx
JSS(freeze); // out: AccountLines
JSS(freeze_peer); // out: AccountLines
JSS(deep_freeze); // out: AccountLines
JSS(deep_freeze_peer); // out: AccountLines
JSS(frozen_balances); // out: GatewayBalances
JSS(full); // in: LedgerClearer, handlers/Ledger
JSS(full_reply); // out: PathFind
JSS(fullbelow_size); // out: GetCounts
JSS(git); // out: server_info
JSS(good); // out: RPCVersion
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
JSS(have_header); // out: InboundLedger
JSS(have_state); // out: InboundLedger
JSS(have_transactions); // out: InboundLedger
JSS(high); // out: BookChanges
JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
JSS(historical_perminute); // historical_perminute.
JSS(holders); // out: MPTHolders
JSS(hostid); // out: NetworkOPs
JSS(hotwallet); // in: GatewayBalances
JSS(id); // websocket.
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(in); // out: OverlayImpl
JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us); //
JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid version
JSS(io_latency_ms); // out: NetworkOPs
JSS(ip); // in: Connect, out: OverlayImpl
JSS(is_burned); // out: nft_info (clio)
JSS(isSerialized); // out: RPC server_definitions
// matches definitions.json format
JSS(isSigningField); // out: RPC server_definitions
// matches definitions.json format
JSS(isVLEncoded); // out: RPC server_definitions
// matches definitions.json format
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
// out: STPathSet, STAmount
JSS(job); //
JSS(job_queue); //
JSS(jobs); //
JSS(jsonrpc); // json version
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
JSS(kept); // out: SubmitTransaction
JSS(key); // out
JSS(key_type); // in/out: WalletPropose, TransactionSign
JSS(latency); // out: PeerImp
JSS(last); // out: RPCVersion
JSS(last_close); // out: NetworkOPs
JSS(last_refresh_time); // out: ValidatorSite
JSS(last_refresh_status); // out: ValidatorSite
JSS(last_refresh_message); // out: ValidatorSite
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
// out: NetworkOPs, PeerImp
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
// AccountLines
JSS(ledger_data); // out: LedgerHeader
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
// TransactionEntry, handlers/Ledger
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
// AccountLines
JSS(ledger_hit_rate); // out: GetCounts
JSS(ledger_index); // in/out: many
JSS(ledger_index_max); // in, out: AccountTx*
JSS(ledger_index_min); // in, out: AccountTx*
JSS(ledger_max); // in, out: AccountTx*
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
// in: LedgerData, BookOffers
JSS(limit_peer); // out: AccountLines
@@ -404,9 +401,6 @@ JSS(min_ledger); // in: LedgerCleaner
JSS(minimum_fee); // out: TxQ
JSS(minimum_level); // out: TxQ
JSS(missingCommand); // error
JSS(mpt_amount); // out: mpt_holders
JSS(mpt_issuance_id); // in: Payment, mpt_holders
JSS(mptoken_index); // out: mpt_holders
JSS(mpt_issuance_id_a); // out: BookChanges
JSS(mpt_issuance_id_b); // out: BookChanges
JSS(name); // out: AmendmentTableImpl, PeerImp

View File

@@ -52,7 +52,7 @@ getTransferFee(uint256 const& id)
}
inline std::uint32_t
getSequence(uint256 const& id)
getSerial(uint256 const& id)
{
std::uint32_t seq = 0;
memcpy(&seq, id.begin() + 28, 4);
@@ -92,7 +92,7 @@ getTaxon(uint256 const& id)
// The taxon cipher is just an XOR, so it is reversible by applying the
// XOR a second time.
return cipheredTaxon(getSequence(id), toTaxon(taxon));
return cipheredTaxon(getSerial(id), toTaxon(taxon));
}
inline AccountID

View File

@@ -147,150 +147,6 @@ public:
{
return this->sle_->at(sfPreviousTxnLgrSeq);
}
/**
* @brief Get sfConfidentialBalanceInbox (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceInbox() const
{
if (hasConfidentialBalanceInbox())
return this->sle_->at(sfConfidentialBalanceInbox);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceInbox is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceInbox() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceInbox);
}
/**
* @brief Get sfConfidentialBalanceSpending (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceSpending() const
{
if (hasConfidentialBalanceSpending())
return this->sle_->at(sfConfidentialBalanceSpending);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceSpending is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceSpending() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceSpending);
}
/**
* @brief Get sfConfidentialBalanceVersion (SoeDefault)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getConfidentialBalanceVersion() const
{
if (hasConfidentialBalanceVersion())
return this->sle_->at(sfConfidentialBalanceVersion);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceVersion is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceVersion() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceVersion);
}
/**
* @brief Get sfIssuerEncryptedBalance (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptedBalance() const
{
if (hasIssuerEncryptedBalance())
return this->sle_->at(sfIssuerEncryptedBalance);
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptedBalance is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptedBalance() const
{
return this->sle_->isFieldPresent(sfIssuerEncryptedBalance);
}
/**
* @brief Get sfAuditorEncryptedBalance (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedBalance() const
{
if (hasAuditorEncryptedBalance())
return this->sle_->at(sfAuditorEncryptedBalance);
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedBalance is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedBalance() const
{
return this->sle_->isFieldPresent(sfAuditorEncryptedBalance);
}
/**
* @brief Get sfHolderEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getHolderEncryptionKey() const
{
if (hasHolderEncryptionKey())
return this->sle_->at(sfHolderEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfHolderEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasHolderEncryptionKey() const
{
return this->sle_->isFieldPresent(sfHolderEncryptionKey);
}
};
/**
@@ -414,72 +270,6 @@ public:
return *this;
}
/**
* @brief Set sfConfidentialBalanceInbox (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceInbox(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceInbox] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceSpending (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceSpending(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceSpending] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceVersion (SoeDefault)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceVersion(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfConfidentialBalanceVersion] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedBalance (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setIssuerEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedBalance] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedBalance (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setAuditorEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedBalance] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptionKey] = value;
return *this;
}
/**
* @brief Build and return the completed MPToken wrapper.
* @param index The ledger entry index.

View File

@@ -302,78 +302,6 @@ public:
{
return this->sle_->isFieldPresent(sfReferenceHolding);
}
/**
* @brief Get sfIssuerEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptionKey() const
{
if (hasIssuerEncryptionKey())
return this->sle_->at(sfIssuerEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptionKey() const
{
return this->sle_->isFieldPresent(sfIssuerEncryptionKey);
}
/**
* @brief Get sfAuditorEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptionKey() const
{
if (hasAuditorEncryptionKey())
return this->sle_->at(sfAuditorEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptionKey() const
{
return this->sle_->isFieldPresent(sfAuditorEncryptionKey);
}
/**
* @brief Get sfConfidentialOutstandingAmount (SoeDefault)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getConfidentialOutstandingAmount() const
{
if (hasConfidentialOutstandingAmount())
return this->sle_->at(sfConfidentialOutstandingAmount);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialOutstandingAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialOutstandingAmount() const
{
return this->sle_->isFieldPresent(sfConfidentialOutstandingAmount);
}
};
/**
@@ -576,39 +504,6 @@ public:
return *this;
}
/**
* @brief Set sfIssuerEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfConfidentialOutstandingAmount (SoeDefault)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setConfidentialOutstandingAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfConfidentialOutstandingAmount] = value;
return *this;
}
/**
* @brief Build and return the completed MPTokenIssuance wrapper.
* @param index The ledger entry index.

View File

@@ -20,7 +20,7 @@ class BatchBuilder;
*
* Type: ttBATCH (71)
* Delegable: Delegation::NotDelegable
* Amendment: featureBatchV1_1
* Amendment: featureBatch
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.

View File

@@ -20,7 +20,7 @@ class ClawbackBuilder;
*
* Type: ttCLAWBACK (30)
* Delegable: Delegation::Delegable
* Amendment: uint256{}
* Amendment: featureClawback
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.

View File

@@ -1,201 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTClawbackBuilder;
/**
* @brief Transaction: ConfidentialMPTClawback
*
* Type: ttCONFIDENTIAL_MPT_CLAWBACK (89)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTClawbackBuilder to construct new transactions.
*/
class ConfidentialMPTClawback : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CLAWBACK;
/**
* @brief Construct a ConfidentialMPTClawback transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTClawback(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawback");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfHolder (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getHolder() const
{
return this->tx_->at(sfHolder);
}
/**
* @brief Get sfMPTAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfZKProof (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
};
/**
* @brief Builder for ConfidentialMPTClawback transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTClawbackBuilder : public TransactionBuilderBase<ConfidentialMPTClawbackBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTClawbackBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param holder The sfHolder field value.
* @param mPTAmount The sfMPTAmount field value.
* @param zKProof The sfZKProof field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTClawbackBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& holder, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTClawbackBuilder>(ttCONFIDENTIAL_MPT_CLAWBACK, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setHolder(holder);
setMPTAmount(mPTAmount);
setZKProof(zKProof);
}
/**
* @brief Construct a ConfidentialMPTClawbackBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTClawbackBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CLAWBACK)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawbackBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfHolder (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setHolder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfHolder] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTClawback wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTClawback
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTClawback{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -1,336 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTConvertBuilder;
/**
* @brief Transaction: ConfidentialMPTConvert
*
* Type: ttCONFIDENTIAL_MPT_CONVERT (85)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTConvertBuilder to construct new transactions.
*/
class ConfidentialMPTConvert : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT;
/**
* @brief Construct a ConfidentialMPTConvert transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTConvert(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvert");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfMPTAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfHolderEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getHolderEncryptionKey() const
{
if (hasHolderEncryptionKey())
{
return this->tx_->at(sfHolderEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfHolderEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasHolderEncryptionKey() const
{
return this->tx_->isFieldPresent(sfHolderEncryptionKey);
}
/**
* @brief Get sfHolderEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getHolderEncryptedAmount() const
{
return this->tx_->at(sfHolderEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfBlindingFactor (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getBlindingFactor() const
{
return this->tx_->at(sfBlindingFactor);
}
/**
* @brief Get sfZKProof (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getZKProof() const
{
if (hasZKProof())
{
return this->tx_->at(sfZKProof);
}
return std::nullopt;
}
/**
* @brief Check if sfZKProof is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasZKProof() const
{
return this->tx_->isFieldPresent(sfZKProof);
}
};
/**
* @brief Builder for ConfidentialMPTConvert transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTConvertBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTConvertBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param mPTAmount The sfMPTAmount field value.
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param blindingFactor The sfBlindingFactor field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTConvertBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTConvertBuilder>(ttCONFIDENTIAL_MPT_CONVERT, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setMPTAmount(mPTAmount);
setHolderEncryptedAmount(holderEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setBlindingFactor(blindingFactor);
}
/**
* @brief Construct a ConfidentialMPTConvertBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTConvertBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfBlindingFactor (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBlindingFactor] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTConvert wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTConvert
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTConvert{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -1,310 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTConvertBackBuilder;
/**
* @brief Transaction: ConfidentialMPTConvertBack
*
* Type: ttCONFIDENTIAL_MPT_CONVERT_BACK (87)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTConvertBackBuilder to construct new transactions.
*/
class ConfidentialMPTConvertBack : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT_BACK;
/**
* @brief Construct a ConfidentialMPTConvertBack transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTConvertBack(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBack");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfMPTAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfHolderEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getHolderEncryptedAmount() const
{
return this->tx_->at(sfHolderEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfBlindingFactor (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getBlindingFactor() const
{
return this->tx_->at(sfBlindingFactor);
}
/**
* @brief Get sfZKProof (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
/**
* @brief Get sfBalanceCommitment (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getBalanceCommitment() const
{
return this->tx_->at(sfBalanceCommitment);
}
};
/**
* @brief Builder for ConfidentialMPTConvertBack transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTConvertBackBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTConvertBackBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param mPTAmount The sfMPTAmount field value.
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param blindingFactor The sfBlindingFactor field value.
* @param zKProof The sfZKProof field value.
* @param balanceCommitment The sfBalanceCommitment field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTConvertBackBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>(ttCONFIDENTIAL_MPT_CONVERT_BACK, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setMPTAmount(mPTAmount);
setHolderEncryptedAmount(holderEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setBlindingFactor(blindingFactor);
setZKProof(zKProof);
setBalanceCommitment(balanceCommitment);
}
/**
* @brief Construct a ConfidentialMPTConvertBackBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTConvertBackBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT_BACK)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBackBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfBlindingFactor (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBlindingFactor] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Set sfBalanceCommitment (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfBalanceCommitment] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTConvertBack wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTConvertBack
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTConvertBack{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -1,129 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTMergeInboxBuilder;
/**
* @brief Transaction: ConfidentialMPTMergeInbox
*
* Type: ttCONFIDENTIAL_MPT_MERGE_INBOX (86)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTMergeInboxBuilder to construct new transactions.
*/
class ConfidentialMPTMergeInbox : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_MERGE_INBOX;
/**
* @brief Construct a ConfidentialMPTMergeInbox transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTMergeInbox(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInbox");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
};
/**
* @brief Builder for ConfidentialMPTMergeInbox transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTMergeInboxBuilder : public TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTMergeInboxBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTMergeInboxBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>(ttCONFIDENTIAL_MPT_MERGE_INBOX, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
}
/**
* @brief Construct a ConfidentialMPTMergeInboxBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTMergeInboxBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_MERGE_INBOX)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInboxBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTMergeInboxBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTMergeInbox wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTMergeInbox
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTMergeInbox{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -1,408 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTSendBuilder;
/**
* @brief Transaction: ConfidentialMPTSend
*
* Type: ttCONFIDENTIAL_MPT_SEND (88)
* Delegable: Delegation::Delegable
* Amendment: featureConfidentialTransfer
* Privileges: NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTSendBuilder to construct new transactions.
*/
class ConfidentialMPTSend : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_SEND;
/**
* @brief Construct a ConfidentialMPTSend transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTSend(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTSend");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfDestination (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->tx_->at(sfDestination);
}
/**
* @brief Get sfDestinationTag (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
{
if (hasDestinationTag())
{
return this->tx_->at(sfDestinationTag);
}
return std::nullopt;
}
/**
* @brief Check if sfDestinationTag is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasDestinationTag() const
{
return this->tx_->isFieldPresent(sfDestinationTag);
}
/**
* @brief Get sfSenderEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getSenderEncryptedAmount() const
{
return this->tx_->at(sfSenderEncryptedAmount);
}
/**
* @brief Get sfDestinationEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getDestinationEncryptedAmount() const
{
return this->tx_->at(sfDestinationEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfZKProof (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
/**
* @brief Get sfAmountCommitment (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getAmountCommitment() const
{
return this->tx_->at(sfAmountCommitment);
}
/**
* @brief Get sfBalanceCommitment (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getBalanceCommitment() const
{
return this->tx_->at(sfBalanceCommitment);
}
/**
* @brief Get sfCredentialIDs (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VECTOR256::type::value_type>
getCredentialIDs() const
{
if (hasCredentialIDs())
{
return this->tx_->at(sfCredentialIDs);
}
return std::nullopt;
}
/**
* @brief Check if sfCredentialIDs is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasCredentialIDs() const
{
return this->tx_->isFieldPresent(sfCredentialIDs);
}
};
/**
* @brief Builder for ConfidentialMPTSend transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTSendBuilder : public TransactionBuilderBase<ConfidentialMPTSendBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTSendBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param destination The sfDestination field value.
* @param senderEncryptedAmount The sfSenderEncryptedAmount field value.
* @param destinationEncryptedAmount The sfDestinationEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param zKProof The sfZKProof field value.
* @param amountCommitment The sfAmountCommitment field value.
* @param balanceCommitment The sfBalanceCommitment field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTSendBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination, std::decay_t<typename SF_VL::type::value_type> const& senderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& destinationEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& amountCommitment, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTSendBuilder>(ttCONFIDENTIAL_MPT_SEND, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setDestination(destination);
setSenderEncryptedAmount(senderEncryptedAmount);
setDestinationEncryptedAmount(destinationEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setZKProof(zKProof);
setAmountCommitment(amountCommitment);
setBalanceCommitment(balanceCommitment);
}
/**
* @brief Construct a ConfidentialMPTSendBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTSendBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_SEND)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTSendBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfDestination (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* @brief Set sfDestinationTag (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
return *this;
}
/**
* @brief Set sfSenderEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setSenderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfSenderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfDestinationEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestinationEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfDestinationEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Set sfAmountCommitment (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setAmountCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAmountCommitment] = value;
return *this;
}
/**
* @brief Set sfBalanceCommitment (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfBalanceCommitment] = value;
return *this;
}
/**
* @brief Set sfCredentialIDs (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setCredentialIDs(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfCredentialIDs] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTSend wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTSend
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTSend{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -187,58 +187,6 @@ public:
{
return this->tx_->isFieldPresent(sfMutableFlags);
}
/**
* @brief Get sfIssuerEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptionKey() const
{
if (hasIssuerEncryptionKey())
{
return this->tx_->at(sfIssuerEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptionKey() const
{
return this->tx_->isFieldPresent(sfIssuerEncryptionKey);
}
/**
* @brief Get sfAuditorEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptionKey() const
{
if (hasAuditorEncryptionKey())
{
return this->tx_->at(sfAuditorEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptionKey() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptionKey);
}
};
/**
@@ -349,28 +297,6 @@ public:
return *this;
}
/**
* @brief Set sfIssuerEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceSetBuilder&
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceSetBuilder&
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptionKey] = value;
return *this;
}
/**
* @brief Build and return the MPTokenIssuanceSet wrapper.
* @param publicKey The public key for signing.

View File

@@ -57,32 +57,6 @@ public:
{
return this->tx_->at(sfVaultID);
}
/**
* @brief Get sfMemoData (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getMemoData() const
{
if (hasMemoData())
{
return this->tx_->at(sfMemoData);
}
return std::nullopt;
}
/**
* @brief Check if sfMemoData is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMemoData() const
{
return this->tx_->isFieldPresent(sfMemoData);
}
};
/**
@@ -138,17 +112,6 @@ public:
return *this;
}
/**
* @brief Set sfMemoData (SoeOptional)
* @return Reference to this builder for method chaining.
*/
VaultDeleteBuilder&
setMemoData(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfMemoData] = value;
return *this;
}
/**
* @brief Build and return the VaultDelete wrapper.
* @param publicKey The public key for signing.

View File

@@ -24,10 +24,6 @@ public:
ApplyFlags flags,
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()});
// Convenience constructor used only by tests that build an ApplyContext
// directly (e.g. invariant checks). Production always uses the parentBatchId
// constructor above; this one fixes parentBatchId to std::nullopt and so is
// never valid for a batch inner (hence the TapBatch assert).
explicit ApplyContext(
ServiceRegistry& registry,
OpenView& base,
@@ -128,7 +124,7 @@ private:
ApplyFlags flags_;
std::optional<ApplyViewImpl> view_;
// The ID of the batch transaction we are executing under, if set.
// The ID of the batch transaction we are executing under, if seated.
std::optional<uint256 const> parentBatchId_;
};

View File

@@ -7,7 +7,6 @@
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/applySteps.h>
#include <cstdint>
#include <tuple>
#include <utility>
@@ -180,14 +179,13 @@ public:
static NotTEC
checkSign(PreclaimContext const& ctx);
static NotTEC
checkBatchSign(PreclaimContext const& ctx);
// Returns the fee in fee units, not scaled for load.
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
// Returns the base fee plus extra base fee units, not scaled for load.
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx, std::uint32_t extraBaseFeeMultiplier);
/* Do NOT define an invokePreflight function in a derived class.
Instead, define:
@@ -370,12 +368,7 @@ protected:
std::optional<uint256 const> const& parentBatchId,
AccountID const& idAccount,
STObject const& sigObject,
beast::Journal const j,
// A batch may carry an inner from an account that an earlier inner
// creates, so the signer account need not exist yet; when it does not,
// only its own master key may authorize it. Normal transactions require
// the account to already exist.
bool permitUncreatedAccount = false);
beast::Journal const j);
// Base class always returns true
static bool
@@ -415,8 +408,25 @@ protected:
std::optional<T> value,
unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
// Signature-authorization helpers. protected so the Batch transactor can
// reuse them when validating each BatchSigner in Batch::checkBatchSign.
private:
static NotTEC
checkPermission(
ReadView const& view,
STTx const& tx,
std::unordered_set<GranularPermissionType>& heldGranularPermissions);
std::pair<TER, XRPAmount>
reset(XRPAmount fee);
TER
consumeSeqProxy(SLE::pointer const& sleAccount);
TER
payFee();
std::tuple<TER, XRPAmount, bool>
processPersistentChanges(TER result, XRPAmount fee);
static NotTEC
checkSingleSign(
ReadView const& view,
@@ -433,24 +443,6 @@ protected:
STObject const& sigObject,
beast::Journal const j);
private:
static NotTEC
checkPermission(
ReadView const& view,
STTx const& tx,
std::unordered_set<GranularPermissionType>& heldGranularPermissions);
std::pair<TER, XRPAmount>
reset(XRPAmount fee);
TER
consumeSeqProxy(SLE::pointer const& sleAccount);
TER
payFee();
std::tuple<TER, XRPAmount, bool>
processPersistentChanges(TER result, XRPAmount fee);
void trapTransaction(uint256) const;
/** Performs early sanity checks on the account and fee fields.

View File

@@ -375,35 +375,16 @@ public:
*/
class ValidAmounts
{
std::vector<SLE::const_pointer> afterEntries_;
std::vector<std::shared_ptr<SLE const>> afterEntries_;
public:
void
visitEntry(bool, SLE::const_ref, SLE::const_ref);
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
[[nodiscard]] bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
};
/*
* Verify that when an object with an associated pseudo-account is deleted,
* its pseudo-account is also deleted.
*
* The reverse (pseudo-account deleted → object deleted) is enforced by
* AccountRootsDeletedClean via getPseudoAccountFields().
*/
class ObjectHasPseudoAccount
{
public:
void
visitEntry(bool, SLE::const_ref, SLE::const_ref);
[[nodiscard]] bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
private:
std::vector<SLE::const_pointer> deletedObjSles_;
};
// additional invariant checks can be declared above and then added to this
// tuple
using InvariantChecks = std::tuple<
@@ -432,11 +413,9 @@ using InvariantChecks = std::tuple<
ValidLoanBroker,
ValidLoan,
ValidVault,
ValidConfidentialMPToken,
ValidMPTPayment,
ValidAmounts,
ValidMPTTransfer,
ObjectHasPseudoAccount>;
ValidMPTTransfer>;
/**
* @brief get a tuple of all invariant checks

View File

@@ -36,42 +36,17 @@ class ValidMPTIssuance
std::vector<std::shared_ptr<SLE const>> deletedHoldings_;
public:
/**
* @brief Track MPT issuance and holding creations, deletions, and
* mutations.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
visitEntry(bool, SLE::const_ref, SLE::const_ref);
/**
* @brief Verify MPT issuance invariants after transaction application.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
[[nodiscard]] bool
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j) const;
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
};
/**
* @brief Verify public MPT amount and outstanding amount accounting.
*
* Checks that OutstandingAmount does not exceed MaximumAmount and that
* OutstandingAmount after application equals OutstandingAmount before
* application plus the net holder balance delta.
/** Verify:
* - OutstandingAmount <= MaximumAmount for any MPT
* - OutstandingAmount after = OutstandingAmount before +
* sum (MPT after - MPT before) - this is total MPT credit/debit
*/
class ValidMPTPayment
{
@@ -89,104 +64,11 @@ class ValidMPTPayment
hash_map<uint192, MPTData> data_;
public:
/**
* @brief Track MPT amount and outstanding amount changes.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
visitEntry(bool, SLE::const_ref, SLE::const_ref);
/**
* @brief Verify public MPT payment accounting invariants.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
bool
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j);
};
/**
* @brief Invariants: Confidential MPToken consistency
*
* - Convert/ConvertBack symmetry:
* Regular MPToken balance change (±X) == COA (Confidential Outstanding Amount) change (∓X)
* - Cannot delete MPToken with non-zero confidential state:
* Cannot delete if sfIssuerEncryptedBalance exists
* Cannot delete if sfConfidentialBalanceInbox and sfConfidentialBalanceSpending exist
* - Privacy flag consistency:
* MPToken confidential balance fields can only be created or changed if
* lsfMPTCanHoldConfidentialBalance is set on the issuance.
* - Encrypted field existence consistency:
* If sfConfidentialBalanceSpending/sfConfidentialBalanceInbox exists, then
* sfIssuerEncryptedBalance must also exist (and vice versa). If
* sfAuditorEncryptedBalance exists, then those core encrypted balance fields
* must also exist.
* - COA <= OutstandingAmount:
* Confidential outstanding balance cannot exceed total outstanding.
* - Verifies sfConfidentialBalanceVersion is changed whenever sfConfidentialBalanceSpending is
* modified on an MPToken.
*/
class ValidConfidentialMPToken
{
struct Changes
{
std::int64_t mptAmountDelta = 0;
std::int64_t coaDelta = 0;
std::int64_t outstandingDelta = 0;
SLE::const_pointer issuance;
bool deletedWithEncrypted = false;
bool badConsistency = false;
bool badCOA = false;
bool changesConfidentialFields = false;
bool badVersion = false;
};
std::map<uint192, Changes> changes_;
public:
/**
* @brief Track confidential MPT balance, issuance, and version changes.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after);
/**
* @brief Verify confidential MPT accounting and encrypted-field
* invariants.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
bool
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j);
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
class ValidMPTTransfer
@@ -203,36 +85,11 @@ class ValidMPTTransfer
hash_map<uint256, bool> deletedAuthorized_;
public:
/**
* @brief Track MPT balance changes and deleted authorization state.
*
* @param isDelete Whether the ledger entry is being deleted.
* @param before The ledger entry before transaction application.
* @param after The ledger entry after transaction application.
*/
void
visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after);
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
/**
* @brief Verify MPT transfer authorization invariants.
*
* @param tx The transaction being checked.
* @param result The transaction result code.
* @param fee The fee charged by the transaction.
* @param view The ledger view after transaction application.
* @param j Journal used for diagnostics.
* @return true if the invariant checks pass, otherwise false.
*/
bool
finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
ReadView const& view,
beast::Journal const& j);
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
private:
/**
@@ -242,13 +99,7 @@ private:
* finalize() runs, so their authorization state is captured during
* visitEntry() and stored in deletedAuthorized_. For deleted MPTokens,
* returns true if reqAuth is false or lsfMPTAuthorized was set at deletion.
* For existing MPTokens, returns the result of requireAuth().
*
* @param view The ledger view after transaction application.
* @param mptid The MPToken issuance ID.
* @param holder The holder account being checked.
* @param requireAuth Whether the issuance requires explicit authorization.
* @return true if the holder is authorized, otherwise false.
* For existing MPTokens, returns the result of requireAuth()
*/
[[nodiscard]] bool
isAuthorized(

View File

@@ -27,7 +27,7 @@ checkFreeze(
}
}
if (auto sle = view.read(keylet::trustLine(src, dst, currency)))
if (auto sle = view.read(keylet::line(src, dst, currency)))
{
if (sle->isFlag((dst > src) ? lsfHighFreeze : lsfLowFreeze))
{
@@ -71,8 +71,8 @@ checkNoRipple(
beast::Journal j)
{
// fetch the ripple lines into and out of this node
auto sleIn = view.read(keylet::trustLine(prev, cur, currency));
auto sleOut = view.read(keylet::trustLine(cur, next, currency));
auto sleIn = view.read(keylet::line(prev, cur, currency));
auto sleOut = view.read(keylet::line(cur, next, currency));
if (!sleIn || !sleOut)
return terNO_LINE;

View File

@@ -159,11 +159,6 @@ public:
beast::Journal const& journal);
private:
/** Returns IgnoreFreeze when the withdrawer is the issuer of a pool
* asset (post-fixCleanup3_3_0), ZeroIfFrozen otherwise. */
[[nodiscard]] FreezeHandling
issuerFreezeHandling() const;
std::pair<TER, bool>
applyGuts(Sandbox& view);

View File

@@ -1,5 +1,7 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/Transactor.h>
namespace xrpl {
@@ -59,12 +61,6 @@ public:
ttLOAN_MANAGE,
ttLOAN_PAY,
});
private:
// Skips signature verification for inner txns, so keep it private: it must
// only be reached through Batch::checkSign.
static NotTEC
checkBatchSign(PreclaimContext const& ctx);
};
} // namespace xrpl

View File

@@ -1,59 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Allows an MPT issuer to clawback confidential balances from a holder.
*
* This transaction enables the issuer of an MPToken Issuance (with clawback
* enabled) to reclaim confidential tokens from a holder's account. Unlike
* regular clawback, the issuer cannot see the holder's balance directly.
* Instead, the issuer must provide a zero-knowledge proof that demonstrates
* they know the exact encrypted balance amount.
*
* @par Cryptographic Operations:
* - **Equality Proof Verification**: Verifies that the issuer's revealed
* amount matches the holder's encrypted balance using the issuer's
* ElGamal private key.
*
* @see ConfidentialMPTSend, ConfidentialMPTConvert
*/
class ConfidentialMPTClawback : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTClawback(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -1,61 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Converts public (plaintext) MPT balance to confidential (encrypted)
* balance.
*
* This transaction allows a token holder to convert their publicly visible
* MPToken balance into an encrypted confidential balance. Once converted,
* the balance can only be spent using ConfidentialMPTSend transactions and
* remains hidden from public view on the ledger.
*
* @par Cryptographic Operations:
* - **Schnorr Proof Verification**: When registering a new ElGamal public key,
* verifies proof of knowledge of the corresponding private key.
* - **Revealed Amount Verification**: Verifies that the provided encrypted
* amounts (for holder, issuer, and optionally auditor) all encrypt the
* same plaintext amount using the provided blinding factor.
*
* @see ConfidentialMPTConvertBack, ConfidentialMPTSend
*/
class ConfidentialMPTConvert : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTConvert(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -1,62 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Converts confidential (encrypted) MPT balance back to public
* (plaintext) balance.
*
* This transaction allows a token holder to convert their encrypted
* confidential balance back into a publicly visible MPToken balance. The
* holder must prove they have sufficient confidential balance without
* revealing the actual balance amount.
*
* @par Cryptographic Operations:
* - **Revealed Amount Verification**: Verifies that the provided encrypted
* amounts correctly encrypt the conversion amount.
* - **Pedersen Linkage Proof**: Verifies that the provided balance commitment
* correctly links to the holder's encrypted spending balance.
* - **Bulletproof Range Proof**: Verifies that the remaining balance (after
* conversion) is non-negative, ensuring the holder has sufficient funds.
*
* @see ConfidentialMPTConvert, ConfidentialMPTSend
*/
class ConfidentialMPTConvertBack : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTConvertBack(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -1,63 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Merges the confidential inbox balance into the spending balance.
*
* In the confidential transfer system, incoming funds are deposited into an
* "inbox" balance that the recipient cannot immediately spend. This prevents
* front-running attacks where an attacker could invalidate a pending
* transaction by sending funds to the sender. This transaction merges the
* inbox into the spending balance, making those funds available for spending.
*
* @par Cryptographic Operations:
* - **Homomorphic Addition**: Adds the encrypted inbox balance to the
* encrypted spending balance using ElGamal homomorphic properties.
* - **Zero Encryption**: Resets the inbox to an encryption of zero.
*
* @note This transaction requires no zero-knowledge proofs because it only
* combines encrypted values that the holder already owns. The
* homomorphic properties of ElGamal encryption ensure correctness.
*
* @see ConfidentialMPTSend, ConfidentialMPTConvert
*/
class ConfidentialMPTMergeInbox : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTMergeInbox(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -1,72 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Transfers confidential MPT tokens between holders privately.
*
* This transaction enables private token transfers where the transfer amount
* is hidden from public view. Both sender and recipient must have initialized
* confidential balances. The transaction provides encrypted amounts for all
* parties (sender, destination, issuer, and optionally auditor) along with
* zero-knowledge proofs that verify correctness without revealing the amount.
*
* @par Cryptographic Operations:
* - **Multi-Ciphertext Equality Proof**: Verifies that all encrypted amounts
* (sender, destination, issuer, auditor) encrypt the same plaintext value.
* - **Amount Pedersen Linkage Proof**: Verifies that the amount commitment
* correctly links to the sender's encrypted amount.
* - **Balance Pedersen Linkage Proof**: Verifies that the balance commitment
* correctly links to the sender's encrypted spending balance.
* - **Bulletproof Range Proof**: Verifies remaining balance and
* transfer amount are non-negative.
*
* @note Funds are deposited into the destination's inbox, not spending
* balance. The recipient must call ConfidentialMPTMergeInbox to make
* received funds spendable.
*
* @see ConfidentialMPTMergeInbox, ConfidentialMPTConvert,
* ConfidentialMPTConvertBack
*/
class ConfidentialMPTSend : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTSend(ApplyContext& ctx) : Transactor(ctx)
{
}
static bool
checkExtraFeatures(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl

View File

@@ -190,8 +190,17 @@ Condition::deserialize(Slice s, std::error_code& ec)
break;
case 1: // PrefixSha256
ec = Error::UnsupportedType;
return {};
case 2: // ThresholdSha256
ec = Error::UnsupportedType;
return {};
case 3: // RsaSha256
ec = Error::UnsupportedType;
return {};
case 4: // Ed25519Sha256
ec = Error::UnsupportedType;
return {};

View File

@@ -101,8 +101,20 @@ Fulfillment::deserialize(Slice s, std::error_code& ec)
break;
case safeCast<TagType>(Type::PrefixSha256):
ec = Error::UnsupportedType;
return {};
break;
case safeCast<TagType>(Type::ThresholdSha256):
ec = Error::UnsupportedType;
return {};
break;
case safeCast<TagType>(Type::RsaSha256):
ec = Error::UnsupportedType;
return {};
break;
case safeCast<TagType>(Type::Ed25519Sha256):
ec = Error::UnsupportedType;
return {};

View File

@@ -42,8 +42,7 @@ CanonicalTXSet::accountKey(AccountID const& account)
void
CanonicalTXSet::insert(std::shared_ptr<STTx const> txn)
{
Key const key(
accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID());
Key key(accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID());
map_.emplace(key, std::move(txn));
}

View File

@@ -180,7 +180,7 @@ Ledger::Ledger(
}
{
auto sle = std::make_shared<SLE>(keylet::feeSettings());
auto sle = std::make_shared<SLE>(keylet::fees());
// Whether featureXRPFees is supported will depend on startup options.
if (std::ranges::find(amendments, featureXRPFees) != amendments.end())
{
@@ -560,7 +560,7 @@ Ledger::setup()
try
{
if (auto const sle = read(keylet::feeSettings()))
if (auto const sle = read(keylet::fees()))
{
bool oldFees = false;
bool newFees = false;

View File

@@ -70,7 +70,7 @@ isVaultPseudoAccountFrozen(
// LCOV_EXCL_STOP
}
auto const mptIssuance = view.read(keylet::mptokenIssuance(mptShare.getMptID()));
auto const mptIssuance = view.read(keylet::mptIssuance(mptShare.getMptID()));
if (mptIssuance == nullptr)
return false; // zero MPToken won't block deletion of MPTokenIssuance

View File

@@ -555,7 +555,7 @@ ammLPHolds(
auto const currency = ammLPTCurrency(asset1, asset2);
STAmount amount;
auto const sle = view.read(keylet::trustLine(lpAccount, ammAccount, currency));
auto const sle = view.read(keylet::line(lpAccount, ammAccount, currency));
if (!sle)
{
amount.clear(Issue{currency, ammAccount});
@@ -647,7 +647,7 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
}
else if (
auto const sle =
view.read(keylet::trustLine(ammAccountID, issue.account, issue.currency));
view.read(keylet::line(ammAccountID, issue.account, issue.currency));
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
{
STAmount amount = (*sle)[sfBalance];

View File

@@ -346,9 +346,9 @@ verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, b
}
TER
checkDepositPreauth(
verifyDepositPreauth(
STTx const& tx,
ReadView const& view,
ApplyView& view,
AccountID const& src,
AccountID const& dst,
SLE::const_ref sleDst,
@@ -360,27 +360,9 @@ checkDepositPreauth(
// 2. If src is deposit preauthorized by dst (either by account or by
// credentials).
if (sleDst && ((sleDst->getFlags() & lsfDepositAuth) != 0u))
{
if (src != dst)
{
if (!view.exists(keylet::depositPreauth(dst, src)))
{
return !tx.isFieldPresent(sfCredentialIDs)
? tecNO_PERMISSION
: credentials::authorizedDepositPreauth(
view, tx.getFieldV256(sfCredentialIDs), dst);
}
}
}
bool const credentialsPresent = tx.isFieldPresent(sfCredentialIDs);
return tesSUCCESS;
}
TER
cleanupExpiredCredentials(STTx const& tx, ApplyView& view, beast::Journal j)
{
if (tx.isFieldPresent(sfCredentialIDs))
if (credentialsPresent)
{
auto const foundExpired =
credentials::removeExpired(view, tx.getFieldV256(sfCredentialIDs), j);
@@ -390,22 +372,20 @@ cleanupExpiredCredentials(STTx const& tx, ApplyView& view, beast::Journal j)
return tecEXPIRED;
}
if (sleDst && sleDst->isFlag(lsfDepositAuth))
{
if (src != dst)
{
if (!view.exists(keylet::depositPreauth(dst, src)))
{
return !credentialsPresent ? tecNO_PERMISSION
: credentials::authorizedDepositPreauth(
view, tx.getFieldV256(sfCredentialIDs), dst);
}
}
}
return tesSUCCESS;
}
TER
verifyDepositPreauth(
STTx const& tx,
ApplyView& view,
AccountID const& src,
AccountID const& dst,
SLE::const_ref sleDst,
beast::Journal j)
{
if (auto const err = cleanupExpiredCredentials(tx, view, j); !isTesSuccess(err))
return err;
return checkDepositPreauth(tx, view, src, dst, sleDst, j);
}
} // namespace xrpl

View File

@@ -40,7 +40,7 @@ namespace xrpl {
bool
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
{
if (auto const sle = view.read(keylet::mptokenIssuance(mptIssue.getMptID())))
if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())))
return sle->isFlag(lsfMPTLocked);
return false;
}
@@ -95,7 +95,7 @@ transferRate(ReadView const& view, MPTID const& issuanceID)
// fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000
// For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000
// which represents 50% of 1,000,000,000
if (auto const sle = view.read(keylet::mptokenIssuance(issuanceID));
if (auto const sle = view.read(keylet::mptIssuance(issuanceID));
sle && sle->isFieldPresent(sfTransferFee))
{
auto const fee = sle->getFieldU16(sfTransferFee);
@@ -110,7 +110,7 @@ transferRate(ReadView const& view, MPTID const& issuanceID)
canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
{
auto mptID = mptIssue.getMptID();
auto issuance = view.read(keylet::mptokenIssuance(mptID));
auto issuance = view.read(keylet::mptIssuance(mptID));
if (!issuance)
{
return tecOBJECT_NOT_FOUND;
@@ -132,7 +132,7 @@ addEmptyHolding(
beast::Journal journal)
{
auto const& mptID = mptIssue.getMptID();
auto const mpt = view.peek(keylet::mptokenIssuance(mptID));
auto const mpt = view.peek(keylet::mptIssuance(mptID));
if (!mpt)
return tefINTERNAL; // LCOV_EXCL_LINE
if (mpt->isFlag(lsfMPTLocked))
@@ -204,7 +204,7 @@ authorizeMPToken(
return tecINSUFFICIENT_RESERVE;
// Defensive check before we attempt to create MPToken for the issuer
auto const mpt = view.read(keylet::mptokenIssuance(mptIssuanceID));
auto const mpt = view.read(keylet::mptIssuance(mptIssuanceID));
if (!mpt || mpt->getAccountID(sfIssuer) == account)
{
// LCOV_EXCL_START
@@ -230,7 +230,7 @@ authorizeMPToken(
return tesSUCCESS;
}
auto const sleMptIssuance = view.read(keylet::mptokenIssuance(mptIssuanceID));
auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleMptIssuance)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -290,15 +290,6 @@ removeEmptyHolding(
(view.rules().enabled(fixCleanup3_1_3) && (*mptoken)[~sfLockedAmount].valueOr(0) != 0))
return tecHAS_OBLIGATIONS;
// Don't delete if the token still has confidential balances
if (mptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
mptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
mptoken->isFieldPresent(sfIssuerEncryptedBalance) ||
mptoken->isFieldPresent(sfAuditorEncryptedBalance))
{
return tecHAS_OBLIGATIONS;
}
return authorizeMPToken(
view,
{}, // priorBalance
@@ -317,19 +308,7 @@ requireAuth(
AuthType authType,
std::uint8_t depth)
{
bool const fix330Enabled = view.rules().enabled(fixCleanup3_3_0);
bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault);
bool const featureMPTV2Enabled = view.rules().enabled(featureMPTokensV2);
// Pseudo-accounts (Vault, LoanBroker, AMM) hold assets on behalf of their participants.
// They are implicitly authorized for any MPT they hold, including vault shares whose
// underlying asset would otherwise require auth.
auto const isPseudoAccountExempt = [&] {
return (featureSAVEnabled || featureMPTV2Enabled) &&
isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID, &sfAMMID});
};
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
auto const sleIssuance = view.read(mptID);
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
@@ -340,9 +319,7 @@ requireAuth(
if (mptIssuer == account) // Issuer won't have MPToken
return tesSUCCESS;
// Post-fix330: exempt before the recursive underlying-asset auth check.
if (fix330Enabled && isPseudoAccountExempt())
return tesSUCCESS;
bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault);
if (featureSAVEnabled)
{
@@ -405,9 +382,13 @@ requireAuth(
// belong to someone who is explicitly authorized e.g. a vault owner.
}
// Pre-fix330: exempt after domain/sleToken checks, preserving prior behavior.
if (!fix330Enabled && isPseudoAccountExempt())
return tesSUCCESS;
bool const featureMPTV2Enabled = view.rules().enabled(featureMPTokensV2);
if (featureSAVEnabled || featureMPTV2Enabled)
{
// Implicitly authorize Vault, LoanBroker, and AMM pseudo-accounts
if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID, &sfAMMID}))
return tesSUCCESS;
}
// mptoken must be authorized if issuance enabled requireAuth
if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
@@ -425,7 +406,7 @@ enforceMPTokenAuthorization(
XRPAmount const& priorBalance, // for MPToken authorization
beast::Journal j)
{
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssuanceID));
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -549,7 +530,7 @@ canTransfer(
WaiveMPTCanTransfer waive,
std::uint8_t depth)
{
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
auto const sleIssuance = view.read(mptID);
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
@@ -603,7 +584,7 @@ canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth)
return asset.visit(
[&](Issue const&) -> TER { return tesSUCCESS; },
[&](MPTIssue const& mptIssue) -> TER {
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanTrade))
@@ -657,7 +638,7 @@ TER
lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount, beast::Journal j)
{
auto const mptIssue = amount.get<MPTIssue>();
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
auto sleIssuance = view.peek(mptID);
if (!sleIssuance)
{ // LCOV_EXCL_START
@@ -762,7 +743,7 @@ unlockEscrowMPT(
auto const& issuer = netAmount.getIssuer();
auto const& mptIssue = netAmount.get<MPTIssue>();
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
auto sleIssuance = view.peek(mptID);
if (!sleIssuance)
{ // LCOV_EXCL_START
@@ -946,7 +927,7 @@ checkCreateMPT(
if (mptIssue.getIssuer() == holder)
return tesSUCCESS;
auto const mptIssuanceID = keylet::mptokenIssuance(mptIssue.getMptID());
auto const mptIssuanceID = keylet::mptIssuance(mptIssue.getMptID());
auto const mptokenID = keylet::mptoken(mptIssuanceID.key, holder);
if (!view.exists(mptokenID))
{
@@ -982,7 +963,7 @@ availableMPTAmount(SLE const& sleIssuance)
std::int64_t
availableMPTAmount(ReadView const& view, MPTID const& mptID)
{
auto const sle = view.read(keylet::mptokenIssuance(mptID));
auto const sle = view.read(keylet::mptIssuance(mptID));
if (!sle)
Throw<std::runtime_error>(transHuman(tecINTERNAL));
return availableMPTAmount(*sle);
@@ -1006,7 +987,7 @@ issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue)
{
STAmount amount{issue};
auto const sle = view.read(keylet::mptokenIssuance(issue));
auto const sle = view.read(keylet::mptIssuance(issue));
if (!sle)
return amount;
auto const available = availableMPTAmount(*sle);

View File

@@ -44,8 +44,8 @@ namespace xrpl::nft {
static SLE::const_pointer
locatePage(ReadView const& view, AccountID const& owner, uint256 const& id)
{
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
auto const last = keylet::nftokenPageMax(owner);
auto const first = keylet::nftpage(keylet::nftpageMin(owner), id);
auto const last = keylet::nftpageMax(owner);
// This NFT can only be found in the first page with a key that's strictly
// greater than `first`, so look for that, up until the maximum possible
@@ -57,8 +57,8 @@ locatePage(ReadView const& view, AccountID const& owner, uint256 const& id)
static SLE::pointer
locatePage(ApplyView& view, AccountID const& owner, uint256 const& id)
{
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
auto const last = keylet::nftokenPageMax(owner);
auto const first = keylet::nftpage(keylet::nftpageMin(owner), id);
auto const last = keylet::nftpageMax(owner);
// This NFT can only be found in the first page with a key that's strictly
// greater than `first`, so look for that, up until the maximum possible
@@ -74,9 +74,9 @@ getPageForToken(
uint256 const& id,
std::function<void(ApplyView&, AccountID const&)> const& createCallback)
{
auto const base = keylet::nftokenPageMin(owner);
auto const first = keylet::nftokenPage(base, id);
auto const last = keylet::nftokenPageMax(owner);
auto const base = keylet::nftpageMin(owner);
auto const first = keylet::nftpage(base, id);
auto const last = keylet::nftpageMax(owner);
// This NFT can only be found in the first page with a key that's strictly
// greater than `first`, so look for that, up until the maximum possible
@@ -182,7 +182,7 @@ getPageForToken(
? narr[kDirMaxTokensPerPage - 1].getFieldH256(sfNFTokenID).next()
: carr[0].getFieldH256(sfNFTokenID);
auto np = std::make_shared<SLE>(keylet::nftokenPage(base, tokenIDForNewPage));
auto np = std::make_shared<SLE>(keylet::nftpage(base, tokenIDForNewPage));
XRPL_ASSERT(np->key() > base.key, "xrpl::nft::getPageForToken : valid NFT page index");
np->setFieldArray(sfNFTokens, narr);
np->setFieldH256(sfNextPageMin, cp->key());
@@ -597,7 +597,7 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
// deleting during iteration.
for (int i = offerIndexes.size() - 1; i >= 0; --i)
{
if (auto const offer = view.peek(keylet::nftokenOffer(offerIndexes[i])))
if (auto const offer = view.peek(keylet::nftoffer(offerIndexes[i])))
{
if (deleteTokenOffer(view, offer))
{
@@ -651,11 +651,11 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner)
{
bool didRepair = false;
auto const last = keylet::nftokenPageMax(owner);
auto const last = keylet::nftpageMax(owner);
SLE::pointer page = view.peek(Keylet(
ltNFTOKEN_PAGE,
view.succ(keylet::nftokenPageMin(owner).key, last.key.next()).value_or(last.key)));
view.succ(keylet::nftpageMin(owner).key, last.key.next()).value_or(last.key)));
if (!page)
return didRepair;
@@ -839,10 +839,10 @@ tokenOfferCreatePreclaim(
if (view.rules().enabled(featureNFTokenMintOffer))
{
if (nftIssuer != amount.getIssuer() &&
!view.read(keylet::trustLine(nftIssuer, amount.get<Issue>())))
!view.read(keylet::line(nftIssuer, amount.get<Issue>())))
return tecNO_LINE;
}
else if (!view.exists(keylet::trustLine(nftIssuer, amount.get<Issue>())))
else if (!view.exists(keylet::line(nftIssuer, amount.get<Issue>())))
{
return tecNO_LINE;
}
@@ -934,7 +934,7 @@ tokenOfferCreateApply(
priorBalance < view.fees().accountReserve((*acct)[sfOwnerCount] + 1))
return tecINSUFFICIENT_RESERVE;
auto const offerID = keylet::nftokenOffer(acctID, seqProxy.value());
auto const offerID = keylet::nftoffer(acctID, seqProxy.value());
// Create the offer:
{
@@ -1020,7 +1020,7 @@ checkTrustlineAuthorized(
if (issuerAccount->isFlag(lsfRequireAuth))
{
auto const trustLine = view.read(keylet::trustLine(id, issue.account, issue.currency));
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
if (!trustLine)
{
@@ -1070,7 +1070,7 @@ checkTrustlineDeepFrozen(
return tesSUCCESS;
}
auto const trustLine = view.read(keylet::trustLine(id, issue.account, issue.currency));
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
if (!trustLine)
{

View File

@@ -47,7 +47,7 @@ creditLimit(
{
STAmount result(Issue{currency, account});
auto sleRippleState = view.read(keylet::trustLine(account, issuer, currency));
auto sleRippleState = view.read(keylet::line(account, issuer, currency));
if (sleRippleState)
{
@@ -78,7 +78,7 @@ creditBalance(
{
STAmount result(Issue{currency, account});
auto sleRippleState = view.read(keylet::trustLine(account, issuer, currency));
auto sleRippleState = view.read(keylet::line(account, issuer, currency));
if (sleRippleState)
{
@@ -114,7 +114,7 @@ isIndividualFrozen(
if (issuer != account)
{
// Check if the issuer froze the line
auto const sle = view.read(keylet::trustLine(account, issuer, currency));
auto const sle = view.read(keylet::line(account, issuer, currency));
if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
return true;
}
@@ -138,7 +138,7 @@ isFrozen(
if (issuer != account)
{
// Check if the issuer froze the line
sle = view.read(keylet::trustLine(account, issuer, currency));
sle = view.read(keylet::line(account, issuer, currency));
if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
return true;
}
@@ -162,7 +162,7 @@ isDeepFrozen(
return false;
}
auto const sle = view.read(keylet::trustLine(account, issuer, currency));
auto const sle = view.read(keylet::line(account, issuer, currency));
if (!sle)
{
return false;
@@ -403,7 +403,7 @@ issueIOU(
bool const bSenderHigh = issue.account > account;
auto const index = keylet::trustLine(issue.account, account, issue.currency);
auto const index = keylet::line(issue.account, account, issue.currency);
if (auto state = view.peek(index))
{
@@ -497,7 +497,7 @@ redeemIOU(
bool const bSenderHigh = account > issue.account;
if (auto state = view.peek(keylet::trustLine(account, issue.account, issue.currency)))
if (auto state = view.peek(keylet::line(account, issue.account, issue.currency)))
{
STAmount finalBalance = state->getFieldAmount(sfBalance);
@@ -558,7 +558,7 @@ requireAuth(ReadView const& view, Issue const& issue, AccountID const& account,
if (isXRP(issue) || issue.account == account)
return tesSUCCESS;
auto const trustLine = view.read(keylet::trustLine(account, issue.account, issue.currency));
auto const trustLine = view.read(keylet::line(account, issue.account, issue.currency));
// If account has no line, and this is a strong check, fail
if (!trustLine && authType == AuthType::StrongAuth)
return tecNO_LINE;
@@ -596,7 +596,7 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
auto const isRippleDisabled = [&](AccountID account) -> bool {
// Line might not exist, but some transfers can create it. If this
// is the case, just check the default ripple on the issuer account.
auto const line = view.read(keylet::trustLine(account, issue));
auto const line = view.read(keylet::line(account, issue));
if (line)
{
bool const issuerHigh = issuerId > account;
@@ -638,7 +638,7 @@ addEmptyHolding(
auto const& srcId = issuerId;
auto const& dstId = accountID;
auto const high = srcId > dstId;
auto const index = keylet::trustLine(srcId, dstId, currency);
auto const index = keylet::line(srcId, dstId, currency);
auto const sleSrc = view.peek(keylet::account(srcId));
auto const sleDst = view.peek(keylet::account(dstId));
if (!sleDst || !sleSrc)
@@ -696,7 +696,7 @@ removeEmptyHolding(
// If the account is the issuer, then no line should exist. Check anyway.
// If a line does exist, it will get deleted. If not, return success.
bool const accountIsIssuer = accountID == issue.account;
auto const line = view.peek(keylet::trustLine(accountID, issue));
auto const line = view.peek(keylet::line(accountID, issue));
if (!line)
return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::kZero)

View File

@@ -6,7 +6,6 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -35,6 +34,14 @@
namespace xrpl {
// Forward declaration for function that remains in View.h/cpp
bool
isLPTokenFrozen(
ReadView const& view,
AccountID const& account,
Asset const& asset,
Asset const& asset2);
//------------------------------------------------------------------------------
//
// Freeze checking (Asset-based)
@@ -157,90 +164,6 @@ checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& ass
[&](auto const& issue) { return checkDeepFrozen(view, account, issue); }, asset.value());
}
[[nodiscard]] TER
checkWithdrawFreeze(
ReadView const& view,
AccountID const& srcAcct,
AccountID const& submitterAcct,
AccountID const& dstAcct,
Asset const& asset)
{
XRPL_ASSERT(
isPseudoAccount(view, srcAcct), "xrpl::checkWithdrawFreeze : source is a pseudo-account");
XRPL_ASSERT(
!isPseudoAccount(view, submitterAcct),
"xrpl::checkWithdrawFreeze : submitter is not a pseudo-account");
XRPL_ASSERT(
!isPseudoAccount(view, dstAcct),
"xrpl::checkWithdrawFreeze : destination is not a pseudo-account");
// Funds can always be sent to the issuer
if (dstAcct == asset.getIssuer())
return tesSUCCESS;
// If the asset is globally frozen, other checks are redundant
if (auto const ret = checkGlobalFrozen(view, asset); !isTesSuccess(ret))
return ret;
// Special case for shares - check if the shares (and the transitive asset) is not frozen
if (asset.holds<MPTIssue>() &&
isVaultPseudoAccountFrozen(view, srcAcct, asset.get<MPTIssue>(), 0))
{
return tecLOCKED;
}
// The transfer is from Submitter to Destination via Source (pseudo-account)
// Both Source and Submitter must not be frozen to allow sending funds
if (auto const ret = checkIndividualFrozen(view, srcAcct, asset); !isTesSuccess(ret))
return ret;
// Check submitter's individual freeze only when Submitter != Destination (a regular freeze
// should not block self-withdrawal).
if (submitterAcct != dstAcct)
{
if (auto const ret = checkIndividualFrozen(view, submitterAcct, asset); !isTesSuccess(ret))
return ret;
}
// The destination account must not be deep frozen to receive the funds
return checkDeepFrozen(view, dstAcct, asset);
}
[[nodiscard]] TER
checkDepositFreeze(
ReadView const& view,
AccountID const& srcAcct,
AccountID const& dstAcct,
Asset const& asset)
{
XRPL_ASSERT(
isPseudoAccount(view, dstAcct),
"xrpl::checkDepositFreeze : destination is a pseudo-account");
XRPL_ASSERT(
!isPseudoAccount(view, srcAcct),
"xrpl::checkDepositFreeze : source is not a pseudo-account");
if (auto const ret = checkGlobalFrozen(view, asset); !isTesSuccess(ret))
return ret;
// Special case for shares - check if the shares and the transitive asset is not frozen
if (asset.holds<MPTIssue>() &&
isVaultPseudoAccountFrozen(view, dstAcct, asset.get<MPTIssue>(), 0))
{
return tecLOCKED;
}
if (srcAcct != asset.getIssuer())
{
if (auto const ret = checkIndividualFrozen(view, srcAcct, asset); !isTesSuccess(ret))
return ret;
}
// Unlike regular accounts, pseudo-accounts cannot receive deposits under a regular freeze
// because those funds cannot be later withdrawn
return checkIndividualFrozen(view, dstAcct, asset);
}
//------------------------------------------------------------------------------
//
// Account balance functions
@@ -256,7 +179,7 @@ getLineIfUsable(
FreezeHandling zeroIfFrozen,
beast::Journal j)
{
auto sle = view.read(keylet::trustLine(account, issuer, currency));
auto sle = view.read(keylet::line(account, issuer, currency));
if (!sle)
{
@@ -397,7 +320,7 @@ accountHolds(
{
// if the account is the issuer, and the issuance exists, their limit is
// the issuance limit minus the outstanding value
auto const issuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
auto const issuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
if (!issuance)
{
@@ -411,8 +334,11 @@ accountHolds(
auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account));
if (!sleMpt ||
(zeroIfFrozen == FreezeHandling::ZeroIfFrozen && isFrozen(view, account, mptIssue)))
if (!sleMpt)
{
amount.clear(mptIssue);
}
else if (zeroIfFrozen == FreezeHandling::ZeroIfFrozen && isFrozen(view, account, mptIssue))
{
amount.clear(mptIssue);
}
@@ -423,8 +349,7 @@ accountHolds(
// Only if auth check is needed, as it needs to do an additional read
// operation. Note featureSingleAssetVault will affect error codes.
if (zeroIfUnauthorized == AuthHandling::ZeroIfUnauthorized &&
(view.rules().enabled(featureSingleAssetVault) ||
view.rules().enabled(featureConfidentialTransfer)))
view.rules().enabled(featureSingleAssetVault))
{
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth);
!isTesSuccess(err))
@@ -432,7 +357,7 @@ accountHolds(
}
else if (zeroIfUnauthorized == AuthHandling::ZeroIfUnauthorized)
{
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
// if auth is enabled on the issuance and mpt is not authorized,
// clear amount
@@ -643,7 +568,7 @@ directSendNoFeeIOU(
XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::directSendNoFeeIOU : sender is not receiver");
bool const bSenderHigh = uSenderID > uReceiverID;
auto const index = keylet::trustLine(uSenderID, uReceiverID, currency);
auto const index = keylet::line(uSenderID, uReceiverID, currency);
XRPL_ASSERT(
!isXRP(uSenderID) && uSenderID != noAccount(),
@@ -851,8 +776,7 @@ directSendNoLimitMultiIOU(
if (senderID == issuer || receiverID == issuer || issuer == noAccount())
{
// Direct send: redeeming IOUs and/or sending own IOUs.
if (auto const ter = directSendNoFeeIOU(view, senderID, receiverID, amount, false, j);
!isTesSuccess(ter))
if (auto const ter = directSendNoFeeIOU(view, senderID, receiverID, amount, false, j))
return ter;
actual += amount;
// Do not add amount to takeFromSender, because directSendNoFeeIOU took
@@ -1142,7 +1066,7 @@ directSendNoFeeMPT(
beast::Journal j)
{
// Do not check MPT authorization here - it must have been checked earlier
auto const mptID = keylet::mptokenIssuance(saAmount.get<MPTIssue>().getMptID());
auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
auto const& issuer = saAmount.getIssuer();
auto sleIssuance = view.peek(mptID);
if (!sleIssuance)
@@ -1234,7 +1158,7 @@ directSendNoLimitMPT(
// Safe to get MPT since directSendNoLimitMPT is only called by accountSendMPT
auto const& issuer = saAmount.getIssuer();
auto const sle = view.read(keylet::mptokenIssuance(saAmount.get<MPTIssue>().getMptID()));
auto const sle = view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
if (!sle)
return tecOBJECT_NOT_FOUND;
@@ -1291,7 +1215,7 @@ directSendNoLimitMultiMPT(
{
auto const& issuer = mptIssue.getIssuer();
auto const sle = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
if (!sle)
return tecOBJECT_NOT_FOUND;

View File

@@ -36,6 +36,7 @@
#include <rocksdb/write_batch.h>
#include <atomic>
#include <bit>
#include <cstddef>
#include <functional>
#include <memory>
@@ -285,7 +286,7 @@ public:
Status status = Status::Ok;
rocksdb::ReadOptions const options;
rocksdb::Slice const slice(reinterpret_cast<char const*>(hash.data()), keyBytes);
rocksdb::Slice const slice(std::bit_cast<char const*>(hash.data()), keyBytes);
std::string string;
@@ -348,9 +349,8 @@ public:
EncodedBlob const encoded(e);
wb.Put(
rocksdb::Slice(reinterpret_cast<char const*>(encoded.getKey()), keyBytes),
rocksdb::Slice(
reinterpret_cast<char const*>(encoded.getData()), encoded.getSize()));
rocksdb::Slice(std::bit_cast<char const*>(encoded.getKey()), keyBytes),
rocksdb::Slice(std::bit_cast<char const*>(encoded.getData()), encoded.getSize()));
}
rocksdb::WriteOptions const options;

View File

@@ -1,487 +0,0 @@
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/basics/Buffer.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <openssl/rand.h>
#include <utility/mpt_utility.h>
#include <mpt_protocol.h>
#include <secp256k1.h>
#include <secp256k1_mpt.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <optional>
#include <stdexcept>
#include <vector>
namespace xrpl {
namespace {
account_id
toAccountId(AccountID const& account)
{
account_id res;
std::memcpy(res.bytes, account.data(), kMPT_ACCOUNT_ID_SIZE);
return res;
}
mpt_issuance_id
toIssuanceId(uint192 const& issuance)
{
mpt_issuance_id res;
std::memcpy(res.bytes, issuance.data(), kMPT_ISSUANCE_ID_SIZE);
return res;
}
/**
* @brief Pack a ConfidentialRecipient (public key + ElGamal ciphertext) into the
* secp256k1-mpt participant struct. Callers MUST have already validated that
* r.publicKey.size() == kEcPubKeyLength and
* r.encryptedAmount.size() == kEcGamalEncryptedTotalLength;
*/
mpt_confidential_participant
toParticipant(ConfidentialRecipient const& r)
{
mpt_confidential_participant p{};
std::memcpy(p.pubkey, r.publicKey.data(), kEcPubKeyLength);
std::memcpy(p.ciphertext, r.encryptedAmount.data(), kEcGamalEncryptedTotalLength);
return p;
}
} // namespace
uint256
getSendContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version)
{
uint256 result;
mpt_get_send_context_hash(
toAccountId(account),
toIssuanceId(issuanceID),
sequence,
toAccountId(destination),
version,
result.data());
return result;
}
uint256
getClawbackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& holder)
{
uint256 result;
mpt_get_clawback_context_hash(
toAccountId(account),
toIssuanceId(issuanceID),
sequence,
toAccountId(holder),
result.data());
return result;
}
uint256
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence)
{
uint256 result;
mpt_get_convert_context_hash(
toAccountId(account), toIssuanceId(issuanceID), sequence, result.data());
return result;
}
uint256
getConvertBackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
std::uint32_t version)
{
uint256 result;
mpt_get_convert_back_context_hash(
toAccountId(account), toIssuanceId(issuanceID), sequence, version, result.data());
return result;
}
std::optional<EcPair>
makeEcPair(Slice const& buffer)
{
if (buffer.length() != 2 * kEcCiphertextComponentLength)
return std::nullopt; // LCOV_EXCL_LINE
auto parsePubKey = [](Slice const& slice, secp256k1_pubkey& out) {
return secp256k1_ec_pubkey_parse(secp256k1Context(), &out, slice.data(), slice.length());
};
Slice const s1{buffer.data(), kEcCiphertextComponentLength};
Slice const s2{buffer.data() + kEcCiphertextComponentLength, kEcCiphertextComponentLength};
EcPair pair{};
if (parsePubKey(s1, pair.c1) != 1 || parsePubKey(s2, pair.c2) != 1)
return std::nullopt;
return pair;
}
std::optional<Buffer>
serializeEcPair(EcPair const& pair)
{
auto serializePubKey = [](secp256k1_pubkey const& pub, unsigned char* out) {
size_t outLen = kEcCiphertextComponentLength; // 33 bytes
auto const ret = secp256k1_ec_pubkey_serialize(
secp256k1Context(), out, &outLen, &pub, SECP256K1_EC_COMPRESSED);
return ret == 1 && outLen == kEcCiphertextComponentLength;
};
Buffer buffer(kEcGamalEncryptedTotalLength);
auto const ptr = buffer.data();
bool const res1 = serializePubKey(pair.c1, ptr);
bool const res2 = serializePubKey(pair.c2, ptr + kEcCiphertextComponentLength);
if (!res1 || !res2)
return std::nullopt;
return buffer;
}
bool
isValidCiphertext(Slice const& buffer)
{
return makeEcPair(buffer).has_value();
}
bool
isValidCompressedECPoint(Slice const& buffer)
{
if (buffer.size() != kCompressedEcPointLength)
return false;
// Compressed EC points must start with 0x02 or 0x03
if (buffer[0] != kEcCompressedPrefixEvenY && buffer[0] != kEcCompressedPrefixOddY)
return false;
secp256k1_pubkey point;
return secp256k1_ec_pubkey_parse(secp256k1Context(), &point, buffer.data(), buffer.size()) == 1;
}
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b)
{
if (a.length() != kEcGamalEncryptedTotalLength || b.length() != kEcGamalEncryptedTotalLength)
return std::nullopt;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!pairA || !pairB)
return std::nullopt;
EcPair sum{};
if (auto res = secp256k1_elgamal_add(
secp256k1Context(), &sum.c1, &sum.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
return serializeEcPair(sum);
}
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b)
{
if (a.length() != kEcGamalEncryptedTotalLength || b.length() != kEcGamalEncryptedTotalLength)
return std::nullopt;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!pairA || !pairB)
return std::nullopt;
EcPair diff{};
if (auto const res = secp256k1_elgamal_subtract(
secp256k1Context(), &diff.c1, &diff.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
return serializeEcPair(diff);
}
std::optional<Buffer>
rerandomizeCiphertext(Slice const& ciphertext, Slice const& pubKeySlice, Slice const& randomness)
{
auto zero = encryptAmount(0, pubKeySlice, randomness);
if (!zero)
return std::nullopt;
return homomorphicAdd(ciphertext, *zero);
}
Buffer
generateBlindingFactor()
{
unsigned char blindingFactor[kEcBlindingFactorLength];
// todo: might need to be updated using another RNG
if (RAND_bytes(blindingFactor, kEcBlindingFactorLength) != 1)
Throw<std::runtime_error>("Failed to generate random number");
return Buffer(blindingFactor, kEcBlindingFactorLength);
}
std::optional<Buffer>
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor)
{
if (blindingFactor.size() != kEcBlindingFactorLength || pubKeySlice.size() != kEcPubKeyLength)
return std::nullopt;
Buffer out(kEcGamalEncryptedTotalLength);
if (mpt_encrypt_amount(amt, pubKeySlice.data(), blindingFactor.data(), out.data()) != 0)
return std::nullopt;
return out;
}
std::optional<Buffer>
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId)
{
if (pubKeySlice.size() != kEcPubKeyLength)
return std::nullopt; // LCOV_EXCL_LINE
EcPair pair{};
secp256k1_pubkey pubKey;
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), kEcPubKeyLength);
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
if (auto res = generate_canonical_encrypted_zero(
secp256k1Context(), &pair.c1, &pair.c2, &pubKey, account.data(), mptId.data());
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
return serializeEcPair(pair);
}
TER
verifyRevealedAmount(
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor)
{
if (blindingFactor.size() != kEcBlindingFactorLength ||
holder.publicKey.size() != kEcPubKeyLength ||
holder.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
issuer.publicKey.size() != kEcPubKeyLength ||
issuer.encryptedAmount.size() != kEcGamalEncryptedTotalLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
auto const holderP = toParticipant(holder);
auto const issuerP = toParticipant(issuer);
mpt_confidential_participant auditorP{};
mpt_confidential_participant const* auditorPtr = nullptr;
if (auditor)
{
if (auditor->publicKey.size() != kEcPubKeyLength ||
auditor->encryptedAmount.size() != kEcGamalEncryptedTotalLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
auditorP = toParticipant(*auditor);
auditorPtr = &auditorP;
}
if (mpt_verify_revealed_amount(amount, blindingFactor.data(), &holderP, &issuerP, auditorPtr) !=
0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
NotTEC
checkEncryptedAmountFormat(STObject const& object)
{
// Current usage of this function is only for ConfidentialMPTConvert and
// ConfidentialMPTConvertBack transactions, which already enforce that these fields
// are present.
if (!object.isFieldPresent(sfHolderEncryptedAmount) ||
!object.isFieldPresent(sfIssuerEncryptedAmount))
{
return temMALFORMED; // LCOV_EXCL_LINE
}
if (object[sfHolderEncryptedAmount].length() != kEcGamalEncryptedTotalLength ||
object[sfIssuerEncryptedAmount].length() != kEcGamalEncryptedTotalLength)
{
return temBAD_CIPHERTEXT;
}
bool const hasAuditor = object.isFieldPresent(sfAuditorEncryptedAmount);
if (hasAuditor && object[sfAuditorEncryptedAmount].length() != kEcGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
if (!isValidCiphertext(object[sfHolderEncryptedAmount]) ||
!isValidCiphertext(object[sfIssuerEncryptedAmount]))
{
return temBAD_CIPHERTEXT;
}
if (hasAuditor && !isValidCiphertext(object[sfAuditorEncryptedAmount]))
return temBAD_CIPHERTEXT;
return tesSUCCESS;
}
TER
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash)
{
if (proofSlice.size() != kEcSchnorrProofLength || pubKeySlice.size() != kEcPubKeyLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (mpt_verify_convert_proof(proofSlice.data(), pubKeySlice.data(), contextHash.data()) != 0)
return tecBAD_PROOF;
return tesSUCCESS;
}
TER
verifyClawbackProof(
uint64_t const amount,
Slice const& proof,
Slice const& pubKeySlice,
Slice const& ciphertext,
uint256 const& contextHash)
{
if (ciphertext.size() != kEcGamalEncryptedTotalLength ||
pubKeySlice.size() != kEcPubKeyLength || proof.size() != kEcClawbackProofLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (mpt_verify_clawback_proof(
proof.data(), amount, pubKeySlice.data(), ciphertext.data(), contextHash.data()) != 0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
TER
verifySendProof(
Slice const& proof,
ConfidentialRecipient const& sender,
ConfidentialRecipient const& destination,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor,
Slice const& spendingBalance,
Slice const& amountCommitment,
Slice const& balanceCommitment,
uint256 const& contextHash)
{
auto const recipientCount = getConfidentialRecipientCount(auditor.has_value());
if (proof.size() != kEcSendProofLength || sender.publicKey.size() != kEcPubKeyLength ||
sender.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
destination.publicKey.size() != kEcPubKeyLength ||
destination.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
issuer.publicKey.size() != kEcPubKeyLength ||
issuer.encryptedAmount.size() != kEcGamalEncryptedTotalLength ||
spendingBalance.size() != kEcGamalEncryptedTotalLength ||
amountCommitment.size() != kEcPedersenCommitmentLength ||
balanceCommitment.size() != kEcPedersenCommitmentLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
std::vector<mpt_confidential_participant> participants;
participants.reserve(recipientCount);
participants.push_back(toParticipant(sender));
participants.push_back(toParticipant(destination));
participants.push_back(toParticipant(issuer));
if (auditor)
{
if (auditor->publicKey.size() != kEcPubKeyLength ||
auditor->encryptedAmount.size() != kEcGamalEncryptedTotalLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
participants.push_back(toParticipant(*auditor));
}
if (participants.size() != recipientCount)
return tecINTERNAL; // LCOV_EXCL_LINE
if (mpt_verify_send_proof(
proof.data(),
participants.data(),
recipientCount,
spendingBalance.data(),
amountCommitment.data(),
balanceCommitment.data(),
contextHash.data()) != 0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
TER
verifyConvertBackProof(
Slice const& proof,
Slice const& pubKeySlice,
Slice const& spendingBalance,
Slice const& balanceCommitment,
uint64_t amount,
uint256 const& contextHash)
{
if (proof.size() != kEcConvertBackProofLength || pubKeySlice.size() != kEcPubKeyLength ||
spendingBalance.size() != kEcGamalEncryptedTotalLength ||
balanceCommitment.size() != kEcPedersenCommitmentLength)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (mpt_verify_convert_back_proof(
proof.data(),
pubKeySlice.data(),
spendingBalance.data(),
balanceCommitment.data(),
amount,
contextHash.data()) != 0)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -218,7 +218,7 @@ amendments() noexcept
}
Keylet const&
feeSettings() noexcept
fees() noexcept
{
static Keylet const kRet{ltFEE_SETTINGS, indexHash(LedgerNameSpace::FeeSettings)};
return kRet;
@@ -232,18 +232,18 @@ negativeUNL() noexcept
}
Keylet
book(Book const& b)
BookT::operator()(Book const& b) const
{
return {ltDIR_NODE, getBookBase(b)};
}
Keylet
trustLine(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept
line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept
{
// There is code in TrustSet that calls us with id0 == id1, to allow users
// to locate and delete such "weird" trustlines. If we remove that code, we
// could enable this assert:
// XRPL_ASSERT(id0 != id1, "xrpl::keylet::trustLine : accounts must be
// XRPL_ASSERT(id0 != id1, "xrpl::keylet::line : accounts must be
// different");
// A trust line is shared between two accounts; while we typically think
@@ -285,20 +285,20 @@ quality(Keylet const& k, std::uint64_t q) noexcept
}
Keylet
next(Keylet const& k)
NextT::operator()(Keylet const& k) const
{
XRPL_ASSERT(k.type == ltDIR_NODE, "xrpl::keylet::next : valid input type");
XRPL_ASSERT(k.type == ltDIR_NODE, "xrpl::keylet::NextT::operator() : valid input type");
return {ltDIR_NODE, getQualityNext(k.key)};
}
Keylet
ticket(AccountID const& id, std::uint32_t ticketSeq)
TicketT::operator()(AccountID const& id, std::uint32_t ticketSeq) const
{
return {ltTICKET, getTicketIndex(id, ticketSeq)};
}
Keylet
ticket(AccountID const& id, SeqProxy ticketSeq)
TicketT::operator()(AccountID const& id, SeqProxy ticketSeq) const
{
return {ltTICKET, getTicketIndex(id, ticketSeq)};
}
@@ -307,15 +307,15 @@ ticket(AccountID const& id, SeqProxy ticketSeq)
// else. If we ever support multiple pages of signer lists, this would be the
// keylet used to locate them.
static Keylet
signerList(AccountID const& account, std::uint32_t page) noexcept
signers(AccountID const& account, std::uint32_t page) noexcept
{
return {ltSIGNER_LIST, indexHash(LedgerNameSpace::SignerList, account, page)};
}
Keylet
signerList(AccountID const& account) noexcept
signers(AccountID const& account) noexcept
{
return signerList(account, 0);
return signers(account, 0);
}
Keylet
@@ -375,13 +375,13 @@ escrow(AccountID const& src, std::uint32_t seq) noexcept
}
Keylet
payChannel(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept
payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept
{
return {ltPAYCHAN, indexHash(LedgerNameSpace::XRPPaymentChannel, src, dst, seq)};
}
Keylet
nftokenPageMin(AccountID const& owner)
nftpageMin(AccountID const& owner)
{
std::array<std::uint8_t, 32> buf{};
std::memcpy(buf.data(), owner.data(), owner.size());
@@ -389,7 +389,7 @@ nftokenPageMin(AccountID const& owner)
}
Keylet
nftokenPageMax(AccountID const& owner)
nftpageMax(AccountID const& owner)
{
uint256 id = nft::kPageMask;
std::memcpy(id.data(), owner.data(), owner.size());
@@ -397,14 +397,14 @@ nftokenPageMax(AccountID const& owner)
}
Keylet
nftokenPage(Keylet const& k, uint256 const& token)
nftpage(Keylet const& k, uint256 const& token)
{
XRPL_ASSERT(k.type == ltNFTOKEN_PAGE, "xrpl::keylet::nftokenPage : valid input type");
XRPL_ASSERT(k.type == ltNFTOKEN_PAGE, "xrpl::keylet::nftpage : valid input type");
return {ltNFTOKEN_PAGE, (k.key & ~nft::kPageMask) + (token & nft::kPageMask)};
}
Keylet
nftokenOffer(AccountID const& owner, std::uint32_t seq)
nftoffer(AccountID const& owner, std::uint32_t seq)
{
return {ltNFTOKEN_OFFER, indexHash(LedgerNameSpace::NftokenOffer, owner, seq)};
}
@@ -518,13 +518,13 @@ oracle(AccountID const& account, std::uint32_t const& documentID) noexcept
}
Keylet
mptokenIssuance(std::uint32_t seq, AccountID const& issuer) noexcept
mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept
{
return mptokenIssuance(makeMptID(seq, issuer));
return mptIssuance(makeMptID(seq, issuer));
}
Keylet
mptokenIssuance(MPTID const& issuanceID) noexcept
mptIssuance(MPTID const& issuanceID) noexcept
{
return {ltMPTOKEN_ISSUANCE, indexHash(LedgerNameSpace::MPTokenIssuance, issuanceID)};
}
@@ -532,7 +532,7 @@ mptokenIssuance(MPTID const& issuanceID) noexcept
Keylet
mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept
{
return mptoken(mptokenIssuance(issuanceID).key, holder);
return mptoken(mptIssuance(issuanceID).key, holder);
}
Keylet
@@ -554,9 +554,9 @@ vault(AccountID const& owner, std::uint32_t seq) noexcept
}
Keylet
loanBroker(AccountID const& owner, std::uint32_t seq) noexcept
loanbroker(AccountID const& owner, std::uint32_t seq) noexcept
{
return loanBroker(indexHash(LedgerNameSpace::LoanBroker, owner, seq));
return loanbroker(indexHash(LedgerNameSpace::LoanBroker, owner, seq));
}
Keylet

View File

@@ -13,7 +13,6 @@
#include <algorithm>
#include <cstdint>
#include <functional>
#include <limits>
#include <optional>
#include <stdexcept>
#include <string>
@@ -153,11 +152,9 @@ Permission::getPermissionName(std::uint32_t value) const
return granular;
// not a granular permission, check if it maps to a transaction type
if (auto const txType = permissionToTxType(value))
{
if (auto const* item = TxFormats::getInstance().findByType(*txType); item != nullptr)
return item->getName();
}
auto const txType = permissionToTxType(value);
if (auto const* item = TxFormats::getInstance().findByType(txType); item != nullptr)
return item->getName();
return std::nullopt;
}
@@ -234,10 +231,7 @@ Permission::isDelegable(std::uint32_t permissionValue, Rules const& rules) const
}
auto const txType = permissionToTxType(permissionValue);
if (!txType)
return false;
auto const txIt = txDelegationMap_.find(*txType);
auto const txIt = txDelegationMap_.find(txType);
// Tx-level permissions require the transaction type itself to be delegable, and
// the corresponding amendment enabled.
@@ -251,14 +245,10 @@ Permission::txToPermissionType(TxType const type)
return static_cast<uint32_t>(type) + 1;
}
std::optional<TxType>
TxType
Permission::permissionToTxType(uint32_t value)
{
// Values outside this range [1, 65536] would silently truncate when cast to
// uint16_t, for example, 65537 would become 1, mapping to the Payment transaction.
if (value == 0 || value > std::numeric_limits<std::uint16_t>::max() + 1u)
return std::nullopt;
XRPL_ASSERT(value > 0, "xrpl::Permission::permissionToTxType : value is greater than 0");
return static_cast<TxType>(value - 1);
}

View File

@@ -5,7 +5,6 @@
#include <xrpl/basics/contract.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/detail/secp256k1.h>
#include <xrpl/protocol/digest.h>
@@ -212,7 +211,7 @@ publicKeyType(Slice const& slice)
if (slice[0] == 0xED)
return KeyType::Ed25519;
if (slice[0] == kEcCompressedPrefixEvenY || slice[0] == kEcCompressedPrefixOddY)
if (slice[0] == 0x02 || slice[0] == 0x03)
return KeyType::Secp256k1;
}

View File

@@ -635,20 +635,6 @@ STObject::getAccountID(SField const& field) const
return getFieldByValue<STAccount>(field);
}
AccountID
STObject::getFeePayer() const
{
// If sfDelegate is present, the delegate account is the payer
// note: if a delegate is specified, its authorization to act on behalf of the account is
// enforced in `Transactor::invokeCheckPermission`
// cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`)
if (isFieldPresent(sfDelegate))
return getAccountID(sfDelegate);
// Default payer
return getAccountID(sfAccount);
}
Blob
STObject::getFieldVL(SField const& field) const
{

View File

@@ -1,6 +1,7 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/StringUtilities.h>
#include <xrpl/basics/base_uint.h>
@@ -71,7 +72,6 @@ STTx::STTx(STObject&& object) : STObject(std::move(object))
txType_ = safeCast<TxType>(getFieldU16(sfTransactionType));
applyTemplate(getTxFormat(txType_)->getSOTemplate()); // may throw
tid_ = getHash(HashPrefix::TransactionId);
buildBatchTxnIds();
}
STTx::STTx(SerialIter& sit) : STObject(sfTransaction)
@@ -88,7 +88,6 @@ STTx::STTx(SerialIter& sit) : STObject(sfTransaction)
applyTemplate(getTxFormat(txType_)->getSOTemplate()); // May throw
tid_ = getHash(HashPrefix::TransactionId);
buildBatchTxnIds();
}
STTx::STTx(TxType type, std::function<void(STObject&)> assembler) : STObject(sfTransaction)
@@ -106,7 +105,6 @@ STTx::STTx(TxType type, std::function<void(STObject&)> assembler) : STObject(sfT
logicError("Transaction type was mutated during assembly");
tid_ = getHash(HashPrefix::TransactionId);
buildBatchTxnIds();
}
STBase*
@@ -214,6 +212,20 @@ STTx::getSeqValue() const
return getSeqProxy().value();
}
AccountID
STTx::getFeePayer() const
{
// If sfDelegate is present, the delegate account is the payer
// note: if a delegate is specified, its authorization to act on behalf of the account is
// enforced in `Transactor::invokeCheckPermission`
// cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`)
if (isFieldPresent(sfDelegate))
return getAccountID(sfDelegate);
// Default payer
return getAccountID(sfAccount);
}
void
STTx::sign(
PublicKey const& publicKey,
@@ -267,17 +279,6 @@ STTx::checkSign(Rules const& rules) const
if (auto const ret = checkSign(rules, counterSig); !ret)
return std::unexpected("Counterparty: " + ret.error());
}
// Verify the batch signer signatures here too, so they are cached with the
// rest of signature checking (checkValidity / SF_SIGGOOD) and stay out of
// the transaction engine. Gated on a batch (batchTxnIds_ seated) that
// actually carries signers; a batch whose inners are all from the outer
// account has no sfBatchSigners and needs no signer crypto.
if (batchTxnIds_ && isFieldPresent(sfBatchSigners))
{
if (auto const ret = checkBatchSign(rules); !ret)
return ret;
}
return {};
}
@@ -286,24 +287,13 @@ STTx::checkBatchSign(Rules const& rules) const
{
try
{
XRPL_ASSERT(getTxnType() == ttBATCH, "STTx::checkBatchSign : not a batch transaction");
if (getTxnType() != ttBATCH)
{
// LCOV_EXCL_START
UNREACHABLE("STTx::checkBatchSign : not a batch transaction");
JLOG(debugLog().fatal()) << "not a batch transaction";
return std::unexpected("Not a batch transaction.");
// LCOV_EXCL_STOP
}
if (!isFieldPresent(sfBatchSigners))
return std::unexpected("Missing BatchSigners field."); // LCOV_EXCL_LINE
STArray const& signers{getFieldArray(sfBatchSigners)};
// Bound signature verification to the protocol cap. This runs in
// checkValidity (via checkSign) at relay / submit time, BEFORE preflight
// and passesLocalChecks enforce the cap. Without this guard a malicious
// peer could put an oversized sfBatchSigners array in a 1 MB blob and
// force one signature verification per entry before any of those checks
// (or the fee charge) runs.
if (signers.size() > kMaxBatchSigners)
return std::unexpected("BatchSigners array exceeds max entries.");
for (auto const& signer : signers)
{
Blob const& signingPubKey = signer.getFieldVL(sfSigningPubKey);
@@ -317,10 +307,9 @@ STTx::checkBatchSign(Rules const& rules) const
}
catch (std::exception const& e)
{
// LCOV_EXCL_START
return std::unexpected(std::string("Internal batch signature check failure: ") + e.what());
// LCOV_EXCL_STOP
JLOG(debugLog().error()) << "Batch signature check failed: " << e.what();
}
return std::unexpected("Internal batch signature check failure.");
}
json::Value
@@ -440,11 +429,8 @@ STTx::checkSingleSign(STObject const& sigObject) const
std::expected<void, std::string>
STTx::checkBatchSingleSign(STObject const& batchSigner) const
{
XRPL_ASSERT(getTxnType() == ttBATCH, "STTx::checkBatchSingleSign : batch transaction");
Serializer msg;
serializeBatch(
msg, getAccountID(sfAccount), getSeqValue(), getFlags(), getBatchTransactionIDs());
finishMultiSigningData(batchSigner.getAccountID(sfAccount), msg);
serializeBatch(msg, getFlags(), getBatchTransactionIDs());
return singleSignHelper(batchSigner, msg.slice());
}
@@ -518,7 +504,7 @@ multiSignHelper(
{
return std::unexpected(
std::string("Invalid signature on account ") + toBase58(accountID) +
(errorWhat ? ": " + *errorWhat : "") + ".");
errorWhat.value_or("") + ".");
}
}
// All signatures verified.
@@ -528,18 +514,14 @@ multiSignHelper(
std::expected<void, std::string>
STTx::checkBatchMultiSign(STObject const& batchSigner, Rules const& rules) const
{
XRPL_ASSERT(getTxnType() == ttBATCH, "STTx::checkBatchMultiSign : batch transaction");
// We can ease the computational load inside the loop a bit by
// pre-constructing part of the data that we hash. Fill a Serializer
// with the stuff that stays constant from signature to signature.
auto const batchSignerAccount = batchSigner.getAccountID(sfAccount);
Serializer dataStart;
serializeBatch(
dataStart, getAccountID(sfAccount), getSeqValue(), getFlags(), getBatchTransactionIDs());
dataStart.addBitString(batchSignerAccount);
serializeBatch(dataStart, getFlags(), getBatchTransactionIDs());
return multiSignHelper(
batchSigner,
batchSignerAccount,
std::nullopt,
[&dataStart](AccountID const& accountID) -> Serializer {
Serializer s = dataStart;
finishMultiSigningData(accountID, s);
@@ -573,48 +555,42 @@ STTx::checkMultiSign(Rules const& rules, STObject const& sigObject) const
rules);
}
void
STTx::buildBatchTxnIds()
{
// Precondition: the template must have been applied first, so the fields
// (including sfRawTransactions) are canonical before the inner txns are
// hashed. The constructors call this immediately after applying the
// template; isFree() being false confirms a template is set.
XRPL_ASSERT(!isFree(), "STTx::buildBatchTxnIds : template applied");
if (getTxnType() != ttBATCH || !isFieldPresent(sfRawTransactions))
return;
auto const& raw = getFieldArray(sfRawTransactions);
// Bound the eager hashing to the protocol cap. This runs in the STTx
// constructor, i.e. at parse time, BEFORE preflight / passesLocalChecks
// enforce the cap and BEFORE checkValidity's signature check. Without this
// guard a malicious peer could put an oversized sfRawTransactions array in
// a 1 MB blob and force hashing of every inner id at deserialization.
// Leaving batchTxnIds_ unseated (nullopt) makes checkSign skip batch-signer
// verification for the oversized batch, which is then dropped by the count
// checks in preflight / passesLocalChecks downstream.
if (raw.size() > kMaxBatchTxCount)
return;
// For an in-cap batch the ids are always fully built, preserving the
// invariant batchTxnIds_->size() == rawTransactions.size().
auto& ids = batchTxnIds_.emplace();
ids.reserve(raw.size());
for (STObject const& rb : raw)
ids.push_back(rb.getHash(HashPrefix::TransactionId));
}
/**
* @brief Retrieves a batch of transaction IDs from the STTx.
*
* This function returns a vector of transaction IDs by extracting them from
* the field array `sfRawTransactions` within the STTx. If the batch
* transaction IDs have already been computed and cached in `batchTxnIds_`,
* it returns the cached vector. Otherwise, it computes the transaction IDs,
* caches them, and then returns the vector.
*
* @return A vector of `uint256` containing the batch transaction IDs.
*
* @note The function asserts that the `sfRawTransactions` field array is not
* empty and that the size of the computed batch transaction IDs matches the
* size of the `sfRawTransactions` field array.
*/
std::vector<uint256> const&
STTx::getBatchTransactionIDs() const
{
XRPL_ASSERT(getTxnType() == ttBATCH, "STTx::getBatchTransactionIDs : batch transaction");
XRPL_ASSERT(getTxnType() == ttBATCH, "STTx::getBatchTransactionIDs : not a batch transaction");
XRPL_ASSERT(
batchTxnIds_.has_value(), "STTx::getBatchTransactionIDs : batch transaction IDs built");
!getFieldArray(sfRawTransactions).empty(),
"STTx::getBatchTransactionIDs : empty raw transactions");
// The list of inner ids is built once, then reused on subsequent calls.
// After the list is built, it must always have the same size as the array
// `sfRawTransactions`. The assert below verifies that.
if (batchTxnIds_.empty())
{
for (STObject const& rb : getFieldArray(sfRawTransactions))
batchTxnIds_.push_back(rb.getHash(HashPrefix::TransactionId));
}
XRPL_ASSERT(
batchTxnIds_->size() == getFieldArray(sfRawTransactions).size(),
batchTxnIds_.size() == getFieldArray(sfRawTransactions).size(),
"STTx::getBatchTransactionIDs : batch transaction IDs size mismatch");
return *batchTxnIds_;
return batchTxnIds_;
}
//------------------------------------------------------------------------------
@@ -751,22 +727,13 @@ invalidMPTAmountInTx(STObject const& tx)
}
static bool
isBatchRawTransactionOkay(STObject const& st, std::string& reason)
isRawTransactionOkay(STObject const& st, std::string& reason)
{
if (!st.isFieldPresent(sfRawTransactions))
return true;
// sfRawTransactions only appears on a Batch. passesLocalChecks runs on
// unverified user and peer input, so reject (rather than assert) a non-batch
// transaction that carries it.
if (st.getFieldU16(sfTransactionType) != ttBATCH)
{
reason = "Only Batch transactions may contain raw transactions.";
return false;
}
if (st.isFieldPresent(sfBatchSigners) &&
st.getFieldArray(sfBatchSigners).size() > kMaxBatchSigners)
st.getFieldArray(sfBatchSigners).size() > kMaxBatchTxCount)
{
reason = "Batch Signers array exceeds max entries.";
return false;
@@ -790,12 +757,6 @@ isBatchRawTransactionOkay(STObject const& st, std::string& reason)
}
raw.applyTemplate(getTxFormat(tt)->getSOTemplate());
// passesLocalChecks recurses back into isBatchRawTransactionOkay,
// but an inner can never be a batch (rejected above), so the
// recursion terminates at depth 1.
if (!passesLocalChecks(raw, reason))
return false;
}
catch (std::exception const& e)
{
@@ -830,7 +791,7 @@ passesLocalChecks(STObject const& st, std::string& reason)
return false;
}
if (!isBatchRawTransactionOkay(st, reason))
if (!isRawTransactionOkay(st, reason))
return false;
return true;

View File

@@ -106,7 +106,6 @@ transResults()
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
MAKE_ERROR(tecBAD_PROOF, "Proof cannot be verified"),
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
@@ -130,8 +129,6 @@ transResults()
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
MAKE_ERROR(tefINVALID_LEDGER_FIX_TYPE, "The LedgerFixType field has an invalid value."),
MAKE_ERROR(tefNO_DST_PARTIAL, "Partial payment to create account not allowed."),
MAKE_ERROR(tefBAD_PATH_COUNT, "Malformed: Too many paths."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
@@ -202,7 +199,6 @@ transResults()
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
MAKE_ERROR(temBAD_CIPHERTEXT, "Malformed: Invalid ciphertext."),
MAKE_ERROR(terRETRY, "Retry transaction."),
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),

View File

@@ -14,7 +14,6 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/InvariantCheck.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <exception>
@@ -115,7 +114,7 @@ ApplyContext::checkInvariantsHelper(
tx, result, fee, *view_, journal)...}}; // NOLINT(bugprone-unchecked-optional-access)
// call each check's finalizer to see that it passes
if (!std::ranges::all_of(finalizers, [](auto const& b) { return b; }))
if (!std::all_of(finalizers.cbegin(), finalizers.cend(), [](auto const& b) { return b; }))
{
JLOG(journal.fatal()) << "Transaction has failed one or more global invariants: "
<< to_string(tx.getJson(JsonOptions::Values::None));

View File

@@ -1,5 +1,6 @@
#include <xrpl/tx/Transactor.h>
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
@@ -220,15 +221,13 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask)
if (ctx.tx.getSeqProxy().isTicket() && ctx.tx.isFieldPresent(sfAccountTxnID))
return temINVALID;
if (ctx.tx.isFlag(tfInnerBatchTxn) && !ctx.rules.enabled(featureBatchV1_1))
if (ctx.tx.isFlag(tfInnerBatchTxn) && !ctx.rules.enabled(featureBatch))
return temINVALID_FLAG;
// Reject if the inner batch flag and parentBatchId are inconsistent.
// A standalone tx with tfInnerBatchTxn but no parentBatchId is an
// attack attempt. A tx with parentBatchId but without tfInnerBatchTxn
// is a programming error.
if (ctx.tx.isFlag(tfInnerBatchTxn) != ctx.parentBatchId.has_value())
return temINVALID_INNER_BATCH;
XRPL_ASSERT(
ctx.tx.isFlag(tfInnerBatchTxn) == ctx.parentBatchId.has_value() ||
!ctx.rules.enabled(featureBatch),
"Inner batch transaction must have a parent batch ID.");
return tesSUCCESS;
}
@@ -244,19 +243,15 @@ Transactor::preflight2(PreflightContext const& ctx)
return *ret;
}
// Skip the signature check on batch inner transactions. preflight1 already
// enforces both conditions; re-checking them as defense in depth guarantees
// we never return success (and so skip signature validation) for an inner
// transaction unless the amendment is enabled and it really sits inside a
// batch.
if (ctx.tx.isFlag(tfInnerBatchTxn))
{
if (!ctx.rules.enabled(featureBatchV1_1))
return temINVALID_FLAG;
if (!ctx.parentBatchId.has_value())
return temINVALID_INNER_BATCH;
// It should be impossible for the InnerBatchTxn flag to be set without
// featureBatch being enabled
XRPL_ASSERT_PARTS(
!ctx.tx.isFlag(tfInnerBatchTxn) || ctx.rules.enabled(featureBatch),
"xrpl::Transactor::preflight2",
"InnerBatch flag only set if feature enabled");
// Skip signature check on batch inner transactions
if (ctx.tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatch))
return tesSUCCESS;
}
// Do not add any checks after this point that are relevant for
// batch inner transactions. They will be skipped.
@@ -361,15 +356,6 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
return baseFee + (signerCount * baseFee);
}
XRPAmount
Transactor::calculateBaseFee(
ReadView const& view,
STTx const& tx,
std::uint32_t extraBaseFeeMultiplier)
{
return calculateBaseFee(view, tx) + view.fees().base * extraBaseFeeMultiplier;
}
// Returns the fee in fee units, not scaled for load.
XRPAmount
Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx)
@@ -542,7 +528,7 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j
}
// Transaction can never succeed if the Ticket is not in the ledger.
if (!view.exists(keylet::ticket(id, tSeqProx)))
if (!view.exists(keylet::kTicket(id, tSeqProx)))
{
JLOG(j.trace()) << "applyTransaction: ticket already used or never created "
<< "a_seq=" << aSeq << " t_seq=" << tSeqProx;
@@ -607,7 +593,7 @@ Transactor::ticketDelete(
{
// Delete the Ticket, adjust the account root ticket count, and
// reduce the owner count.
SLE::pointer const sleTicket = view.peek(keylet::ticket(ticketIndex));
SLE::pointer const sleTicket = view.peek(keylet::kTicket(ticketIndex));
if (!sleTicket)
{
// LCOV_EXCL_START
@@ -713,26 +699,23 @@ Transactor::checkSign(
std::optional<uint256 const> const& parentBatchId,
AccountID const& idAccount,
STObject const& sigObject,
beast::Journal const j,
bool permitUncreatedAccount)
beast::Journal const j)
{
{
auto const sle = view.read(keylet::account(idAccount));
if ((view.rules().enabled(featureLendingProtocol) ||
view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) &&
isPseudoAccount(sle))
if (view.rules().enabled(featureLendingProtocol) && isPseudoAccount(sle))
{
// Pseudo-accounts can't sign transactions. This check is gated on a
// few different amendments so that it takes effect as soon as any of
// them is activated.
// Pseudo-accounts can't sign transactions. This check is gated on
// the Lending Protocol amendment because that's the project it was
// added under, and it doesn't justify another amendment
return tefBAD_AUTH;
}
}
auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey);
// Ignore signature check on batch inner transactions
if (parentBatchId && view.rules().enabled(featureBatchV1_1))
if (parentBatchId && view.rules().enabled(featureBatch))
{
// Defensive Check: These values are also checked in Batch::preflight
if (sigObject.isFieldPresent(sfTxnSignature) || !pkSigner.empty() ||
@@ -770,16 +753,7 @@ Transactor::checkSign(
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const sleAccount = view.read(keylet::account(idAccount));
if (!sleAccount)
{
// An account that does not exist yet can only be authorized by its own
// master key, and only where an un-created signer is permitted (a batch
// whose earlier inner creates the account). Otherwise it cannot sign.
if (!permitUncreatedAccount)
return terNO_ACCOUNT;
if (idAccount != idSigner)
return tefBAD_AUTH;
return tesSUCCESS;
}
return terNO_ACCOUNT;
return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
}
@@ -792,6 +766,50 @@ Transactor::checkSign(PreclaimContext const& ctx)
return checkSign(ctx.view, ctx.flags, ctx.parentBatchId, idAccount, ctx.tx, ctx.j);
}
NotTEC
Transactor::checkBatchSign(PreclaimContext const& ctx)
{
NotTEC ret = tesSUCCESS;
STArray const& signers{ctx.tx.getFieldArray(sfBatchSigners)};
for (auto const& signer : signers)
{
auto const idAccount = signer.getAccountID(sfAccount);
Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey);
if (pkSigner.empty())
{
if (ret = checkMultiSign(ctx.view, ctx.flags, idAccount, signer, ctx.j);
!isTesSuccess(ret))
return ret;
}
else
{
// LCOV_EXCL_START
if (!publicKeyType(makeSlice(pkSigner)))
return tefBAD_AUTH;
// LCOV_EXCL_STOP
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const sleAccount = ctx.view.read(keylet::account(idAccount));
// A batch can include transactions from an un-created account ONLY
// when the account master key is the signer
if (!sleAccount)
{
if (idAccount != idSigner)
return tefBAD_AUTH;
return tesSUCCESS;
}
if (ret = checkSingleSign(ctx.view, idSigner, idAccount, sleAccount, ctx.j);
!isTesSuccess(ret))
return ret;
}
}
return ret;
}
NotTEC
Transactor::checkSingleSign(
ReadView const& view,
@@ -833,7 +851,7 @@ Transactor::checkMultiSign(
beast::Journal const j)
{
// Get id's SignerList and Quorum.
STLedgerEntry::const_pointer const sleAccountSigners = view.read(keylet::signerList(id));
STLedgerEntry::const_pointer const sleAccountSigners = view.read(keylet::signers(id));
// If the signer list doesn't exist the account is not multi-signing.
if (!sleAccountSigners)
{
@@ -1013,7 +1031,7 @@ removeExpiredNFTokenOffers(
for (auto const& index : offers)
{
if (auto const offer = view.peek(keylet::nftokenOffer(index)))
if (auto const offer = view.peek(keylet::nftoffer(index)))
{
nft::deleteTokenOffer(view, offer);
if (++removed == kExpiredOfferRemoveLimit)
@@ -1087,7 +1105,7 @@ Transactor::reset(XRPAmount fee)
// balance should have already been checked in checkFee / preFlight.
XRPL_ASSERT(
(fee == beast::kZero || balance != beast::kZero) && (!view().open() || balance >= fee),
balance != beast::kZero && (!view().open() || balance >= fee),
"xrpl::Transactor::reset : valid balance");
// We retry/reject the transaction if the account balance is zero or

View File

@@ -8,6 +8,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STObject.h>
@@ -38,14 +39,30 @@ checkValidity(HashRouter& router, STTx const& tx, Rules const& rules)
auto const id = tx.getTransactionID();
auto const flags = router.getFlags(id);
// Batch inner transactions are never independently valid: they are applied
// within their batch, not through checkValidity. Reaching here means one was
// relayed or submitted on its own, so mark it bad regardless of the
// amendment (like PeerImp and NetworkOPs).
if (tx.isFlag(tfInnerBatchTxn))
// Ignore signature check on batch inner transactions
if (tx.isFlag(tfInnerBatchTxn) && rules.enabled(featureBatch))
{
router.setFlags(id, kSfSigbad);
return {Validity::SigBad, "Batch inner transactions are never considered validly signed."};
// Defensive Check: These values are also checked in Batch::preflight
if (tx.isFieldPresent(sfTxnSignature) || !tx.getSigningPubKey().empty() ||
tx.isFieldPresent(sfSigners))
return {Validity::SigBad, "Malformed: Invalid inner batch transaction."};
// This block should probably have never been included in the
// original `Batch` implementation. An inner transaction never
// has a valid signature.
bool const neverValid = rules.enabled(fixBatchInnerSigs);
if (!neverValid)
{
std::string reason;
if (!passesLocalChecks(tx, reason))
{
router.setFlags(id, kSfLocalbad);
return {Validity::SigGoodOnly, reason};
}
router.setFlags(id, kSfSiggood);
return {Validity::Valid, ""};
}
}
if (any(flags & kSfSigbad))
@@ -166,9 +183,6 @@ applyBatchTransactions(
// If the transaction should be applied push its changes to the
// whole-batch view.
// NOTE: each inner tx is individually capped at kOversizeMetaDataCap;
// there is no aggregate cap here. Bounded by kMaxBatchTxCount * cap,
// which standalone txns can already produce in one ledger.
if (ret.applied && (isTesSuccess(ret.ter) || isTecClaim(ret.ter)))
perTxBatchView.apply(batchView);

View File

@@ -35,7 +35,6 @@
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
namespace xrpl {
@@ -64,23 +63,6 @@ hasPrivilege(STTx const& tx, Privilege priv)
#undef TRANSACTION
#pragma pop_macro("TRANSACTION")
// Returns the human-readable name of a ledger entry's type, falling back to
// the numeric type if the format is somehow unknown.
static std::string
ledgerEntryTypeName(SLE const& sle)
{
auto const item = LedgerFormats::getInstance().findByType(sle.getType());
if (item == nullptr)
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::ledgerEntryTypeName : ledger entry has no known ledger format");
return std::to_string(sle.getType());
// LCOV_EXCL_STOP
}
return item->getName();
}
void
TransactionFeeCheck::visitEntry(bool, SLE::const_ref, SLE::const_ref)
{
@@ -475,8 +457,16 @@ AccountRootsDeletedClean::finalize(
if (auto const sle = view.read(keylet))
{
// Finding the object is bad
JLOG(j.fatal()) << "Invariant failed: account deletion left behind a "
<< ledgerEntryTypeName(*sle) << " object";
auto const typeName = [&sle]() {
auto item = LedgerFormats::getInstance().findByType(sle->getType());
if (item != nullptr)
return item->getName();
return std::to_string(sle->getType());
}();
JLOG(j.fatal()) << "Invariant failed: account deletion left behind a " << typeName
<< " object";
// The comment above starting with "assert(enforce)" explains this
// assert.
XRPL_ASSERT(
@@ -528,8 +518,8 @@ AccountRootsDeletedClean::finalize(
// checked above as entries in directAccountKeylets. This uses
// view.succ() to check for any NFT pages in between the two
// endpoints.
Keylet const first = keylet::nftokenPageMin(accountID);
Keylet const last = keylet::nftokenPageMax(accountID);
Keylet const first = keylet::nftpageMin(accountID);
Keylet const last = keylet::nftpageMax(accountID);
std::optional<uint256> key = view.succ(first.key, last.key.next());
@@ -886,9 +876,10 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::const_ref before, SLE::const
{
std::vector<SField const*> const& fields = getPseudoAccountFields();
auto const numFields = std::ranges::count_if(
fields,
[&after](SField const* sf) -> bool { return after->isFieldPresent(*sf); });
auto const numFields =
std::count_if(fields.begin(), fields.end(), [&after](SField const* sf) -> bool {
return after->isFieldPresent(*sf);
});
if (numFields != 1)
{
std::stringstream error;
@@ -1080,69 +1071,4 @@ ValidAmounts::finalize(
return true;
}
void
ObjectHasPseudoAccount::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
{
if (!isDelete)
return;
// Before should never be null when isDelete = true
if (!before)
{
// LCOV_EXCL_START
UNREACHABLE(
"xrpl::ObjectHasPseudoAccount::visitEntry : deleted ledger entry missing before state");
return;
// LCOV_EXCL_STOP
}
switch (before->getType())
{
case ltAMM:
case ltVAULT:
case ltLOAN_BROKER:
deletedObjSles_.push_back(before);
break;
default:
return;
}
}
[[nodiscard]] bool
ObjectHasPseudoAccount::finalize(
STTx const&,
TER const,
XRPAmount const,
ReadView const& view,
beast::Journal const& j) const
{
if (!view.rules().enabled(fixCleanup3_3_0))
return true;
if (deletedObjSles_.empty())
return true;
bool failed = false;
for (auto const& sle : deletedObjSles_)
{
if (!sle->isFieldPresent(sfAccount))
{
JLOG(j.fatal()) << "Invariant failed: deleted " << ledgerEntryTypeName(*sle)
<< " is missing pseudo-account field";
failed = true;
continue;
}
// The pseudo-account must NOT exist on the ledger after the object is deleted.
if (view.exists(keylet::account(sle->getAccountID(sfAccount))))
{
JLOG(j.fatal()) << "Invariant failed: deleted " << ledgerEntryTypeName(*sle)
<< " without deleting its pseudo-account";
failed = true;
}
}
return !failed;
}
} // namespace xrpl

View File

@@ -130,7 +130,7 @@ ValidLoanBroker::finalize(
for (auto const& [brokerID, broker] : brokers_)
{
auto const& after =
broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanBroker(brokerID));
broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanbroker(brokerID));
if (!after)
{

View File

@@ -1,13 +1,9 @@
#include <xrpl/tx/invariants/MPTInvariant.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
@@ -25,46 +21,11 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
namespace xrpl {
namespace {
constexpr auto kConfidentialMptTxTypes = std::to_array<TxType>({
ttCONFIDENTIAL_MPT_SEND,
ttCONFIDENTIAL_MPT_CONVERT,
ttCONFIDENTIAL_MPT_CONVERT_BACK,
ttCONFIDENTIAL_MPT_MERGE_INBOX,
ttCONFIDENTIAL_MPT_CLAWBACK,
});
// Clamp to the cap (== INT64_MAX) before the signed conversion. Invariant
// tests can inject INT64_MAX + 1, which would result in undefined behavior
// under UBSan if converted directly.
std::int64_t
toSignedMPTAmount(std::uint64_t amount)
{
return static_cast<std::int64_t>(std::min(amount, kMaxMpTokenAmount));
}
std::int64_t
addMPTAmountDelta(std::int64_t delta, std::uint64_t amount)
{
return delta + toSignedMPTAmount(amount);
}
std::int64_t
subtractMPTAmountDelta(std::int64_t delta, std::uint64_t amount)
{
return delta - toSignedMPTAmount(amount);
}
} // namespace
void
ValidMPTIssuance::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
{
@@ -475,16 +436,6 @@ ValidMPTPayment::finalize(
{
if (isTesSuccess(result))
{
// Confidential transactions are validated by ValidConfidentialMPToken.
// They modify encrypted fields and sfConfidentialOutstandingAmount
// rather than sfMPTAmount/sfOutstandingAmount in the standard way,
// so ValidMPTPayment's accounting does not apply to them.
if (std::ranges::find(kConfidentialMptTxTypes, tx.getTxnType()) !=
kConfidentialMptTxTypes.end())
{
return true;
}
bool const invariantPasses = !view.rules().enabled(featureMPTokensV2);
if (overflow_)
{
@@ -515,261 +466,6 @@ ValidMPTPayment::finalize(
return true;
}
void
ValidConfidentialMPToken::visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
{
// Helper to get MPToken Issuance ID safely
auto const getMptID = [](std::shared_ptr<SLE const> const& sle) -> uint192 {
if (!sle)
return beast::kZero;
if (sle->getType() == ltMPTOKEN)
return sle->getFieldH192(sfMPTokenIssuanceID);
if (sle->getType() == ltMPTOKEN_ISSUANCE)
return makeMptID(sle->getFieldU32(sfSequence), sle->getAccountID(sfIssuer));
return beast::kZero;
};
if (before && before->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(before);
auto& change = changes_[id];
change.mptAmountDelta =
subtractMPTAmountDelta(change.mptAmountDelta, before->getFieldU64(sfMPTAmount));
// Cannot delete MPToken with non-zero confidential state or non-zero public amount
if (isDelete)
{
bool const hasPublicBalance = before->getFieldU64(sfMPTAmount) > 0;
bool const hasEncryptedFields = before->isFieldPresent(sfConfidentialBalanceSpending) ||
before->isFieldPresent(sfConfidentialBalanceInbox) ||
before->isFieldPresent(sfIssuerEncryptedBalance) ||
before->isFieldPresent(sfAuditorEncryptedBalance);
if (hasPublicBalance || hasEncryptedFields)
changes_[id].deletedWithEncrypted = true;
}
}
if (after && after->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(after);
auto& change = changes_[id];
change.mptAmountDelta =
addMPTAmountDelta(change.mptAmountDelta, after->getFieldU64(sfMPTAmount));
// Encrypted field existence consistency
bool const hasIssuerBalance = after->isFieldPresent(sfIssuerEncryptedBalance);
bool const hasHolderInbox = after->isFieldPresent(sfConfidentialBalanceInbox);
bool const hasHolderSpending = after->isFieldPresent(sfConfidentialBalanceSpending);
bool const hasAuditorBalance = after->isFieldPresent(sfAuditorEncryptedBalance);
// The core encrypted balances must all exist or not exist at the same time. The auditor
// balance is optional, but cannot exist without the core fields.
if (hasHolderInbox != hasHolderSpending || hasHolderInbox != hasIssuerBalance ||
(hasAuditorBalance && !hasIssuerBalance))
changes_[id].badConsistency = true;
auto const confidentialBalanceFieldChanged = [&before, &after](auto const& field) {
auto const afterValue = (*after)[~field];
if (!afterValue)
return false;
if (!before || before->getType() != ltMPTOKEN)
return true; // LCOV_EXCL_LINE
return (*before)[~field] != afterValue;
};
if (confidentialBalanceFieldChanged(sfConfidentialBalanceInbox) ||
confidentialBalanceFieldChanged(sfConfidentialBalanceSpending) ||
confidentialBalanceFieldChanged(sfIssuerEncryptedBalance) ||
confidentialBalanceFieldChanged(sfAuditorEncryptedBalance))
{
changes_[id].changesConfidentialFields = true;
}
}
if (before && before->getType() == ltMPTOKEN_ISSUANCE)
{
uint192 const id = getMptID(before);
auto& change = changes_[id];
if (before->isFieldPresent(sfConfidentialOutstandingAmount))
{
change.coaDelta = subtractMPTAmountDelta(
change.coaDelta, before->getFieldU64(sfConfidentialOutstandingAmount));
}
change.outstandingDelta = subtractMPTAmountDelta(
change.outstandingDelta, before->getFieldU64(sfOutstandingAmount));
}
if (after && after->getType() == ltMPTOKEN_ISSUANCE)
{
uint192 const id = getMptID(after);
auto& change = changes_[id];
bool const hasCOA = after->isFieldPresent(sfConfidentialOutstandingAmount);
std::uint64_t const coa = (*after)[~sfConfidentialOutstandingAmount].value_or(0);
std::uint64_t const oa = after->getFieldU64(sfOutstandingAmount);
if (hasCOA)
change.coaDelta = addMPTAmountDelta(change.coaDelta, coa);
change.outstandingDelta = addMPTAmountDelta(change.outstandingDelta, oa);
change.issuance = after;
// COA <= OutstandingAmount
if (coa > oa)
change.badCOA = true;
}
if (before && after && before->getType() == ltMPTOKEN && after->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(after);
// sfConfidentialBalanceVersion must change when spending changes
auto const spendingBefore = (*before)[~sfConfidentialBalanceSpending];
auto const spendingAfter = (*after)[~sfConfidentialBalanceSpending];
auto const versionBefore = (*before)[~sfConfidentialBalanceVersion];
auto const versionAfter = (*after)[~sfConfidentialBalanceVersion];
if (spendingBefore.has_value() && spendingBefore != spendingAfter)
{
if (versionBefore == versionAfter)
changes_[id].badVersion = true;
}
}
}
bool
ValidConfidentialMPToken::finalize(
STTx const& tx,
TER const result,
XRPAmount const,
ReadView const& view,
beast::Journal const& j)
{
if (result != tesSUCCESS)
return true;
for (auto const& [id, checks] : changes_)
{
// Find the MPTokenIssuance
auto const issuance = [&]() -> std::shared_ptr<SLE const> {
if (checks.issuance)
return checks.issuance;
return view.read(keylet::mptokenIssuance(id));
}();
// Skip all invariance checks if issuance doesn't exist because that means the MPT has been
// deleted
if (!issuance)
continue;
// Cannot delete MPToken with non-zero confidential state
if (checks.deletedWithEncrypted)
{
if ((*issuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0)
{
JLOG(j.fatal())
<< "Invariant failed: MPToken deleted with encrypted fields while COA > 0";
return false;
}
}
// Encrypted field existence consistency
if (checks.badConsistency)
{
JLOG(j.fatal()) << "Invariant failed: MPToken encrypted field "
"existence inconsistency";
return false;
}
// COA <= OutstandingAmount
if (checks.badCOA)
{
JLOG(j.fatal()) << "Invariant failed: Confidential outstanding amount "
"exceeds total outstanding amount";
return false;
}
// Confidential balance fields may remain on a holder MPToken after all
// confidential balances have returned to zero. Only creating or
// changing those fields requires the issuance privacy flag.
if (checks.changesConfidentialFields)
{
if (!issuance->isFlag(lsfMPTCanHoldConfidentialBalance))
{
JLOG(j.fatal()) << "Invariant failed: MPToken has encrypted "
"fields but Issuance does not have "
"lsfMPTCanHoldConfidentialBalance set";
return false;
}
}
// We only enforce this when Confidential Outstanding Amount changes (Convert, ConvertBack,
// ConfidentialClawback). This avoids falsely failing on Escrow or AMM operations that lock
// public tokens outside of ltMPTOKEN. Convert / ConvertBack:
// - COA and MPTAmount must have opposite deltas, which cancel each other out to zero.
// - OA remains unchanged.
// - Therefore, the net delta on both sides of the equation is zero.
//
// Clawback:
// - MPTAmount remains unchanged.
// - COA and OA must have identical deltas (mirrored on each side).
// - The equation remains balanced as both sides have equal offsets.
if (checks.coaDelta != 0)
{
if (checks.mptAmountDelta + checks.coaDelta != checks.outstandingDelta)
{
JLOG(j.fatal()) << "Invariant failed: Token conservation "
"violation for MPT "
<< to_string(id);
return false;
}
}
else if (
std::ranges::find(kConfidentialMptTxTypes, tx.getTxnType()) !=
kConfidentialMptTxTypes.end())
{
// Confidential Txns should not modify public MPTAmount balance
// if Confidential Amount Delta is 0
if (checks.mptAmountDelta != 0)
{
JLOG(j.fatal()) << "Invariant failed: MPTAmount changed by confidential "
"transaction that should not modify this field."
<< to_string(id);
return false;
}
// Among confidential MPT transactions, only ConfidentialMPTSend and
// ConfidentialMPTMergeInbox leave coaDelta unmodified. Therefore, if a confidential MPT
// transaction reaches here, it must be one of these two types, neither of which will
// modify sfOutstandingAmount
if (checks.outstandingDelta != 0)
{
JLOG(j.fatal()) << "Invariant failed: OutstandingAmount changed "
"by confidential transaction that should not "
"modify it for MPT "
<< to_string(id);
return false;
}
}
if (checks.badVersion)
{
JLOG(j.fatal())
<< "Invariant failed: MPToken sfConfidentialBalanceVersion not updated when "
"sfConfidentialBalanceSpending changed";
return false;
}
}
return true;
}
void
ValidMPTTransfer::visitEntry(
bool isDelete,
@@ -813,15 +509,6 @@ ValidMPTTransfer::isAuthorized(
AccountID const& holder,
bool reqAuth) const
{
// Pseudo-accounts (Vault, LoanBroker, AMM) hold assets on behalf of their
// participants and are implicitly authorized for any MPT they hold,
// including vault shares whose underlying asset would otherwise require
// auth. Exempt them here rather than relying on requireAuth: the recursive
// share -> underlying descent in requireAuth fails for a pseudo-account
// that holds the share but not the underlying.
if (isPseudoAccount(view, holder, {&sfVaultID, &sfLoanBrokerID, &sfAMMID}))
return true;
auto const key = keylet::mptoken(mptid, holder);
auto const it = deletedAuthorized_.find(key.key);
if (it != deletedAuthorized_.end())
@@ -837,8 +524,6 @@ ValidMPTTransfer::finalize(
ReadView const& view,
beast::Journal const& j)
{
auto const fix330Enabled = view.rules().enabled(fixCleanup3_3_0);
if (hasPrivilege(tx, OverrideFreeze))
return true;
@@ -866,7 +551,7 @@ ValidMPTTransfer::finalize(
std::uint16_t senders = 0;
std::uint16_t receivers = 0;
bool invalidTransfer = false;
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptID));
auto const sleIssuance = view.read(keylet::mptIssuance(mptID));
if (!sleIssuance)
{
continue;
@@ -899,29 +584,12 @@ ValidMPTTransfer::finalize(
++senders;
}
// Check once: if any involved account is frozen, the whole issuance transfer is
// considered frozen. Only need to check for frozen if there is a transfer of funds.
//
// Post-fix330: full isFrozen() applies — vault-share transitive freeze is part of
// the freeze semantics for all changed holders.
//
// Pre-fix330: legacy AMM withdraw only checked individual freeze on the
// destination, not the transitive vault freeze. All other paths (and the AMM
// account itself as sender) did apply the full check.
MPTIssue const issue{mptID};
auto const legacyAccountFrozen = [&] {
if (isGlobalFrozen(view, issue) || isIndividualFrozen(view, account, issue))
return true;
bool const isReceiver =
!value.amtBefore.has_value() || *value.amtAfter > *value.amtBefore;
if (txnType == ttAMM_WITHDRAW && isReceiver)
return false;
return isVaultPseudoAccountFrozen(view, account, issue, 0);
};
bool const accountFrozen =
fix330Enabled ? isFrozen(view, account, issue) : legacyAccountFrozen();
// Check once: if any involved account is frozen, the whole
// issuance transfer is considered frozen. Only need to check for
// frozen if there is a transfer of funds.
if (!invalidTransfer &&
(accountFrozen || !isAuthorized(view, mptID, account, reqAuth)))
(isFrozen(view, account, MPTIssue{mptID}) ||
!isAuthorized(view, mptID, account, reqAuth)))
{
invalidTransfer = true;
}

View File

@@ -199,7 +199,7 @@ ValidVault::deltaAssets(AccountID const& id) const
{
if (isXRP(issue))
return lookup(keylet::account(id).key);
auto result = lookup(keylet::trustLine(id, issue).key);
auto result = lookup(keylet::line(id, issue).key);
// Trust-line balance is stored from the low-account's perspective;
// negate if id is the high account so the delta is in id's terms.
if (result && id > issue.getIssuer())
@@ -238,7 +238,7 @@ ValidVault::deltaShares(AccountID const& id) const
auto const& afterVault = afterVault_[0];
auto const it = [&]() {
if (id == afterVault.pseudoId)
return deltas_.find(keylet::mptokenIssuance(afterVault.shareMPTID).key);
return deltas_.find(keylet::mptIssuance(afterVault.shareMPTID).key);
return deltas_.find(keylet::mptoken(afterVault.shareMPTID, id).key);
}();
@@ -411,7 +411,7 @@ ValidVault::finalize(
return e;
}
auto const sleShares = view.read(keylet::mptokenIssuance(afterVault.shareMPTID));
auto const sleShares = view.read(keylet::mptIssuance(afterVault.shareMPTID));
return sleShares ? std::optional<Shares>(Shares::make(*sleShares)) : std::nullopt;
}();

View File

@@ -1359,7 +1359,7 @@ BookStep<TIn, TOut, TDerived>::check(StrandContext const& ctx) const
auto const err = book_.in.visit(
[&](Issue const& issue) -> std::optional<TER> {
auto sle = view.read(keylet::trustLine(*prev, cur, issue.currency));
auto sle = view.read(keylet::line(*prev, cur, issue.currency));
if (!sle)
return terNO_LINE;
if (sle->isFlag((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))

View File

@@ -344,7 +344,7 @@ DirectIPaymentStep::quality(ReadView const& sb, QualityDirection qDir) const
if (src_ == dst_)
return QUALITY_ONE;
auto const sle = sb.read(keylet::trustLine(dst_, src_, currency_));
auto const sle = sb.read(keylet::line(dst_, src_, currency_));
if (!sle)
return QUALITY_ONE;
@@ -420,7 +420,7 @@ DirectIPaymentStep::check(StrandContext const& ctx, SLE::const_ref sleSrc) const
// Since this is a payment a trust line must be present. Perform all
// trust line related checks.
{
auto const sleLine = ctx.view.read(keylet::trustLine(src_, dst_, currency_));
auto const sleLine = ctx.view.read(keylet::line(src_, dst_, currency_));
if (!sleLine)
{
JLOG(j_.trace()) << "DirectStepI: No credit line. " << *this;

View File

@@ -434,7 +434,7 @@ MPTEndpointStep<TDerived>::maxPaymentFlow(ReadView const& sb) const
return {toAmount<MPTAmount>(maxFlow), DebtDirection::Redeems};
// From an issuer to a holder
if (auto const sle = sb.read(keylet::mptokenIssuance(mptIssue_)))
if (auto const sle = sb.read(keylet::mptIssuance(mptIssue_)))
{
// If issuer is the source account, and it is direct payment then
// MPTEndpointStep is the only step. Provide available maxFlow.

View File

@@ -203,7 +203,7 @@ private:
{
return ctx.strandDeliver.visit(
[&](Issue const& issue) {
if (!ctx.view.exists(keylet::trustLine(acc, issue)))
if (!ctx.view.exists(keylet::line(acc, issue)))
return -1;
return 0;
},

View File

@@ -260,8 +260,8 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
return tecHAS_OBLIGATIONS;
// If the account owns any NFTs it cannot be deleted.
Keylet const first = keylet::nftokenPageMin(account);
Keylet const last = keylet::nftokenPageMax(account);
Keylet const first = keylet::nftpageMin(account);
Keylet const last = keylet::nftpageMax(account);
auto const cp = ctx.view.read(
Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key)));

View File

@@ -194,27 +194,30 @@ AccountSet::preclaim(PreclaimContext const& ctx)
//
// Clawback
//
if (uSetFlag == asfAllowTrustLineClawback)
if (ctx.view.rules().enabled(featureClawback))
{
if (sle->isFlag(lsfNoFreeze))
if (uSetFlag == asfAllowTrustLineClawback)
{
JLOG(ctx.j.trace()) << "Can't set Clawback if NoFreeze is set";
return tecNO_PERMISSION;
}
if (sle->isFlag(lsfNoFreeze))
{
JLOG(ctx.j.trace()) << "Can't set Clawback if NoFreeze is set";
return tecNO_PERMISSION;
}
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
{
JLOG(ctx.j.trace()) << "Owner directory not empty.";
return tecOWNERS;
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
{
JLOG(ctx.j.trace()) << "Owner directory not empty.";
return tecOWNERS;
}
}
}
else if (uSetFlag == asfNoFreeze)
{
// Cannot set NoFreeze if clawback is enabled
if (sle->isFlag(lsfAllowTrustLineClawback))
else if (uSetFlag == asfNoFreeze)
{
JLOG(ctx.j.trace()) << "Can't set NoFreeze if clawback is enabled";
return tecNO_PERMISSION;
// Cannot set NoFreeze if clawback is enabled
if (sle->isFlag(lsfAllowTrustLineClawback))
{
JLOG(ctx.j.trace()) << "Can't set NoFreeze if clawback is enabled";
return tecNO_PERMISSION;
}
}
}
@@ -312,7 +315,7 @@ AccountSet::doApply()
return tecNEED_MASTER_KEY;
}
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signerList(accountID_))))
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signers(accountID_))))
{
// Account has no regular key or multi-signer signer list.
return tecNO_ALTERNATIVE_KEY;
@@ -573,7 +576,7 @@ AccountSet::doApply()
}
// Set flag for clawback
if (uSetFlag == asfAllowTrustLineClawback)
if (ctx_.view().rules().enabled(featureClawback) && uSetFlag == asfAllowTrustLineClawback)
{
JLOG(j_.trace()) << "set allow clawback";
uFlagsOut |= lsfAllowTrustLineClawback;

View File

@@ -67,7 +67,7 @@ SetRegularKey::doApply()
else
{
// Account has disabled master key and no multi-signer signer list.
if (sle->isFlag(lsfDisableMaster) && !view().peek(keylet::signerList(accountID_)))
if (sle->isFlag(lsfDisableMaster) && !view().peek(keylet::signers(accountID_)))
return tecNO_ALTERNATIVE_KEY;
sle->makeFieldAbsent(sfRegularKey);

View File

@@ -232,7 +232,7 @@ SignerListSet::removeFromLedger(
{
auto const accountKeylet = keylet::account(account);
auto const ownerDirKeylet = keylet::ownerDir(account);
auto const signerListKeylet = keylet::signerList(account);
auto const signerListKeylet = keylet::signers(account);
return removeSignersFromLedger(
registry, view, accountKeylet, ownerDirKeylet, signerListKeylet, j);
@@ -302,7 +302,7 @@ SignerListSet::replaceSignerList()
{
auto const accountKeylet = keylet::account(accountID_);
auto const ownerDirKeylet = keylet::ownerDir(accountID_);
auto const signerListKeylet = keylet::signerList(accountID_);
auto const signerListKeylet = keylet::signers(accountID_);
// This may be either a create or a replace. Preemptively remove any
// old signer list. May reduce the reserve, so this is done before
@@ -367,7 +367,7 @@ SignerListSet::destroySignerList()
return tecNO_ALTERNATIVE_KEY;
auto const ownerDirKeylet = keylet::ownerDir(accountID_);
auto const signerListKeylet = keylet::signerList(accountID_);
auto const signerListKeylet = keylet::signers(accountID_);
return removeSignersFromLedger(
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
}

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