mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-01 03:22:19 +00:00
Compare commits
14 Commits
gregtatcam
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e1d5011fc | ||
|
|
768d7603b1 | ||
|
|
fd8a915243 | ||
|
|
3e9f1d0ab8 | ||
|
|
652b5f9af1 | ||
|
|
50fdb38ace | ||
|
|
bb2ab4243b | ||
|
|
2ab43b6fda | ||
|
|
12a5d9014e | ||
|
|
b9eee1d245 | ||
|
|
0711a7b493 | ||
|
|
07c64f07f0 | ||
|
|
3097c157b6 | ||
|
|
556d62a0de |
307
.clang-tidy
307
.clang-tidy
@@ -1,161 +1,158 @@
|
||||
---
|
||||
Checks: "-*,
|
||||
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,
|
||||
bugprone-*,
|
||||
-bugprone-assignment-in-if-condition,
|
||||
-bugprone-bitwise-pointer-cast,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-command-processor,
|
||||
-bugprone-copy-constructor-mutates-argument,
|
||||
-bugprone-default-operator-new-on-overaligned-type,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-exception-copy-constructor-throws,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-float-loop-counter,
|
||||
-bugprone-forwarding-reference-overload,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-incorrect-enable-shared-from-this,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-nondeterministic-pointer-iteration-order,
|
||||
-bugprone-not-null-terminated-result,
|
||||
-bugprone-random-generator-seed,
|
||||
-bugprone-raw-memory-call-on-non-trivial-type,
|
||||
-bugprone-std-namespace-modification,
|
||||
-bugprone-tagged-union-member-count,
|
||||
-bugprone-throwing-static-initialization,
|
||||
-bugprone-unchecked-string-to-number-conversion,
|
||||
-bugprone-unintended-char-ostream-output,
|
||||
|
||||
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,
|
||||
|
||||
llvm-namespace-comment,
|
||||
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
|
||||
|
||||
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
|
||||
"
|
||||
# ---
|
||||
# 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
|
||||
|
||||
2
.github/actions/setup-conan/action.yml
vendored
2
.github/actions/setup-conan/action.yml
vendored
@@ -9,7 +9,7 @@ inputs:
|
||||
remote_url:
|
||||
description: "The URL of the Conan endpoint to use."
|
||||
required: false
|
||||
default: https://conan.ripplex.io
|
||||
default: https://conan.xrplf.org/repository/conan/
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
5
.github/workflows/on-pr.yml
vendored
5
.github/workflows/on-pr.yml
vendored
@@ -122,7 +122,6 @@ jobs:
|
||||
issues: write
|
||||
contents: read
|
||||
with:
|
||||
check_only_changed: true
|
||||
create_issue_on_failure: false
|
||||
|
||||
build-test:
|
||||
@@ -154,8 +153,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.CONAN_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
|
||||
notify-clio:
|
||||
needs: upload-recipe
|
||||
|
||||
4
.github/workflows/on-tag.yml
vendored
4
.github/workflows/on-tag.yml
vendored
@@ -20,8 +20,8 @@ jobs:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
uses: ./.github/workflows/reusable-upload-recipe.yml
|
||||
secrets:
|
||||
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
|
||||
build-test:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
|
||||
5
.github/workflows/on-trigger.yml
vendored
5
.github/workflows/on-trigger.yml
vendored
@@ -72,7 +72,6 @@ jobs:
|
||||
issues: write
|
||||
contents: read
|
||||
with:
|
||||
check_only_changed: false
|
||||
create_issue_on_failure: ${{ github.event_name == 'schedule' }}
|
||||
|
||||
build-test:
|
||||
@@ -98,8 +97,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.CONAN_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
|
||||
package:
|
||||
needs: build-test
|
||||
|
||||
11
.github/workflows/reusable-clang-tidy.yml
vendored
11
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -3,10 +3,6 @@ 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
|
||||
@@ -29,15 +25,14 @@ env:
|
||||
|
||||
jobs:
|
||||
determine-files:
|
||||
if: ${{ inputs.check_only_changed }}
|
||||
permissions:
|
||||
contents: read
|
||||
uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@c7045074aafe9fb92fa537aa4446f81fbfc17e8b
|
||||
uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@d041ac9f1fa9f07a4ba335eb4c1c82233fb3fef6
|
||||
|
||||
run-clang-tidy:
|
||||
name: Run clang tidy
|
||||
needs: [determine-files]
|
||||
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }}
|
||||
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523"
|
||||
permissions:
|
||||
@@ -96,7 +91,7 @@ jobs:
|
||||
id: run_clang_tidy
|
||||
continue-on-error: true
|
||||
env:
|
||||
TARGETS: ${{ (needs.determine-files.outputs.clang_tidy_config_changed != 'true' && inputs.check_only_changed) && needs.determine-files.outputs.cpp_changed_files || 'src tests' }}
|
||||
TARGETS: ${{ needs.determine-files.outputs.need_full_run != 'true' && 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}"
|
||||
|
||||
22
.github/workflows/reusable-package.yml
vendored
22
.github/workflows/reusable-package.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
@@ -39,23 +39,8 @@ jobs:
|
||||
working-directory: .github/scripts/strategy-matrix
|
||||
run: ./generate.py --packaging >>"${GITHUB_OUTPUT}"
|
||||
|
||||
generate-version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
sparse-checkout: |
|
||||
.github/actions/generate-version
|
||||
src/libxrpl/protocol/BuildInfo.cpp
|
||||
- name: Generate version
|
||||
id: version
|
||||
uses: ./.github/actions/generate-version
|
||||
|
||||
package:
|
||||
needs: [generate-matrix, generate-version]
|
||||
needs: [generate-matrix]
|
||||
if: ${{ github.event.repository.visibility == 'public' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -82,14 +67,13 @@ jobs:
|
||||
|
||||
- name: Build package
|
||||
env:
|
||||
PKG_VERSION: ${{ needs.generate-version.outputs.version }}
|
||||
PKG_RELEASE: ${{ inputs.pkg_release }}
|
||||
run: ./package/build_pkg.sh
|
||||
|
||||
- name: Upload package artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }}
|
||||
name: ${{ matrix.artifact_name }}-pkg
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/debbuild/*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/*.ddeb
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
|
||||
20
.github/workflows/reusable-upload-recipe.yml
vendored
20
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
description: "The URL of the Conan endpoint to use."
|
||||
required: false
|
||||
type: string
|
||||
default: https://conan.ripplex.io
|
||||
default: https://conan.xrplf.org/repository/conan/
|
||||
|
||||
secrets:
|
||||
remote_username:
|
||||
@@ -41,6 +41,10 @@ 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
|
||||
@@ -56,15 +60,9 @@ jobs:
|
||||
remote_url: ${{ inputs.remote_url }}
|
||||
|
||||
- name: Log into Conan remote
|
||||
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}"
|
||||
run: conan remote login "${REMOTE_NAME}" "${CONAN_LOGIN_USERNAME_XRPLF}" --password "${CONAN_PASSWORD_XRPLF}"
|
||||
|
||||
- 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 }}
|
||||
@@ -73,8 +71,6 @@ 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
|
||||
@@ -83,8 +79,6 @@ 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
|
||||
@@ -93,8 +87,6 @@ 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
|
||||
|
||||
6
.github/workflows/upload-conan-deps.yml
vendored
6
.github/workflows/upload-conan-deps.yml
vendored
@@ -34,7 +34,7 @@ on:
|
||||
|
||||
env:
|
||||
CONAN_REMOTE_NAME: xrplf
|
||||
CONAN_REMOTE_URL: https://conan.ripplex.io
|
||||
CONAN_REMOTE_URL: https://conan.xrplf.org/repository/conan/
|
||||
NPROC_SUBTRACT: 2
|
||||
|
||||
concurrency:
|
||||
@@ -108,10 +108,12 @@ 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.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}"
|
||||
run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.NEXUS_REMOTE_USERNAME }}" --password "${{ secrets.NEXUS_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}
|
||||
|
||||
2
BUILD.md
2
BUILD.md
@@ -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.ripplex.io
|
||||
conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
|
||||
```
|
||||
|
||||
### Set Up Ccache
|
||||
|
||||
@@ -90,6 +90,7 @@ 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)
|
||||
@@ -102,6 +103,7 @@ target_link_libraries(
|
||||
INTERFACE
|
||||
ed25519::ed25519
|
||||
lz4::lz4
|
||||
mpt-crypto::mpt-crypto
|
||||
OpenSSL::Crypto
|
||||
OpenSSL::SSL
|
||||
secp256k1::secp256k1
|
||||
|
||||
@@ -28,7 +28,6 @@ endif()
|
||||
set(package_env
|
||||
SRC_DIR=${CMAKE_SOURCE_DIR}
|
||||
BUILD_DIR=${CMAKE_BINARY_DIR}
|
||||
PKG_VERSION=${xrpld_version}
|
||||
PKG_RELEASE=${pkg_release}
|
||||
)
|
||||
|
||||
|
||||
65
conan.lock
65
conan.lock
@@ -1,43 +1,44 @@
|
||||
{
|
||||
"version": "0.5",
|
||||
"requires": [
|
||||
"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",
|
||||
"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",
|
||||
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
|
||||
"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"
|
||||
"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"
|
||||
],
|
||||
"build_requires": [
|
||||
"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",
|
||||
"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",
|
||||
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
|
||||
"m4/1.4.19#4523e4347b55cd26ae918bd5770cab9a%1778062762.471",
|
||||
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
|
||||
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
|
||||
"m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259",
|
||||
"cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091",
|
||||
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
|
||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
|
||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
|
||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
|
||||
],
|
||||
"python_requires": [],
|
||||
"overrides": {
|
||||
@@ -57,7 +58,7 @@
|
||||
"boost/1.91.0"
|
||||
],
|
||||
"lz4/[>=1.9.4 <2]": [
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
|
||||
"lz4/1.10.0#982d9b673900f665a1da109e09c17cab"
|
||||
]
|
||||
},
|
||||
"config_requires": []
|
||||
|
||||
@@ -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.ripplex.io
|
||||
conan remote add --force --index 0 xrplf https://conan.xrplf.org/repository/conan/
|
||||
|
||||
# Delete any existing lockfile.
|
||||
rm -f conan.lock
|
||||
|
||||
@@ -28,10 +28,11 @@ class Xrpl(ConanFile):
|
||||
|
||||
requires = [
|
||||
"ed25519/2015.03",
|
||||
"grpc/1.81.0",
|
||||
"grpc/1.81.1",
|
||||
"libarchive/3.8.7",
|
||||
"mpt-crypto/0.4.0-rc2",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.2",
|
||||
"openssl/3.6.3",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"zlib/1.3.2",
|
||||
@@ -208,6 +209,7 @@ class Xrpl(ConanFile):
|
||||
"grpc::grpc++",
|
||||
"libarchive::libarchive",
|
||||
"lz4::lz4",
|
||||
"mpt-crypto::mpt-crypto",
|
||||
"nudb::nudb",
|
||||
"openssl::crypto",
|
||||
"protobuf::libprotobuf",
|
||||
|
||||
@@ -60,6 +60,7 @@ words:
|
||||
- autobridging
|
||||
- bimap
|
||||
- bindir
|
||||
- blindings
|
||||
- bookdir
|
||||
- Bougalis
|
||||
- Britto
|
||||
@@ -95,6 +96,7 @@ words:
|
||||
- daria
|
||||
- dcmake
|
||||
- dearmor
|
||||
- decryptor
|
||||
- dedented
|
||||
- deleteme
|
||||
- demultiplexer
|
||||
@@ -106,6 +108,7 @@ words:
|
||||
- distro
|
||||
- doxyfile
|
||||
- dxrpl
|
||||
- elgamal
|
||||
- enabled
|
||||
- enablerepo
|
||||
- endmacro
|
||||
@@ -119,6 +122,7 @@ words:
|
||||
- fmtdur
|
||||
- fsanitize
|
||||
- funclets
|
||||
- Gamal
|
||||
- gcov
|
||||
- gcovr
|
||||
- ghead
|
||||
@@ -216,6 +220,7 @@ words:
|
||||
- partitioner
|
||||
- paychan
|
||||
- paychans
|
||||
- Pedersen
|
||||
- permdex
|
||||
- perminute
|
||||
- permissioned
|
||||
@@ -239,6 +244,10 @@ words:
|
||||
- Raphson
|
||||
- rcflags
|
||||
- replayer
|
||||
- rerandomize
|
||||
- rerandomization
|
||||
- rerandomized
|
||||
- rerandomizes
|
||||
- rerere
|
||||
- retriable
|
||||
- RIPD
|
||||
@@ -255,6 +264,7 @@ words:
|
||||
- sahyadri
|
||||
- Satoshi
|
||||
- scons
|
||||
- Schnorr
|
||||
- secp
|
||||
- sendq
|
||||
- seqit
|
||||
@@ -285,6 +295,7 @@ words:
|
||||
- stvar
|
||||
- stvector
|
||||
- stxchainattestations
|
||||
- summands
|
||||
- superpeer
|
||||
- superpeers
|
||||
- takergets
|
||||
@@ -301,6 +312,7 @@ words:
|
||||
- txs
|
||||
- ubsan
|
||||
- UBSAN
|
||||
- ufdio
|
||||
- umant
|
||||
- unacquired
|
||||
- unambiguity
|
||||
|
||||
2
docs/build/advanced_conan.md
vendored
2
docs/build/advanced_conan.md
vendored
@@ -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.ripplex.io
|
||||
conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
|
||||
```
|
||||
|
||||
Alternatively, you can pull our recipes from the repository and export them locally:
|
||||
|
||||
@@ -63,6 +63,39 @@ 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(
|
||||
|
||||
@@ -42,7 +42,7 @@ escrowUnlockApplyHelper<Issue>(
|
||||
beast::Journal journal)
|
||||
{
|
||||
Issue const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::line(receiver, issue);
|
||||
Keylet const trustLineKey = keylet::trustLine(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::mptIssuance(mptID);
|
||||
auto const issuanceKey = keylet::mptokenIssuance(mptID);
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer)
|
||||
{
|
||||
if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
|
||||
|
||||
@@ -131,6 +131,75 @@ 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)
|
||||
|
||||
431
include/xrpl/protocol/ConfidentialTransfer.h
Normal file
431
include/xrpl/protocol/ConfidentialTransfer.h
Normal file
@@ -0,0 +1,431 @@
|
||||
#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
|
||||
@@ -68,21 +68,15 @@ skip(LedgerIndex ledger) noexcept;
|
||||
|
||||
/** The (fixed) index of the object containing the ledger fees. */
|
||||
Keylet const&
|
||||
fees() noexcept;
|
||||
feeSettings() noexcept;
|
||||
|
||||
/** The (fixed) index of the object containing the ledger negativeUNL. */
|
||||
Keylet const&
|
||||
negativeUNL() noexcept;
|
||||
|
||||
/** The beginning of an order book */
|
||||
struct BookT
|
||||
{
|
||||
explicit BookT() = default;
|
||||
|
||||
Keylet
|
||||
operator()(Book const& b) const;
|
||||
};
|
||||
static BookT const kBook{};
|
||||
Keylet
|
||||
book(Book const& b);
|
||||
|
||||
/** The index of a trust line for a given currency
|
||||
|
||||
@@ -93,12 +87,12 @@ static BookT const kBook{};
|
||||
*/
|
||||
/** @{ */
|
||||
Keylet
|
||||
line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept;
|
||||
trustLine(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept;
|
||||
|
||||
inline Keylet
|
||||
line(AccountID const& id, Issue const& issue) noexcept
|
||||
trustLine(AccountID const& id, Issue const& issue) noexcept
|
||||
{
|
||||
return line(id, issue.account, issue.currency);
|
||||
return trustLine(id, issue.account, issue.currency);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
@@ -119,37 +113,27 @@ Keylet
|
||||
quality(Keylet const& k, std::uint64_t q) noexcept;
|
||||
|
||||
/** The directory for the next lower quality */
|
||||
struct NextT
|
||||
{
|
||||
explicit NextT() = default;
|
||||
|
||||
Keylet
|
||||
operator()(Keylet const& k) const;
|
||||
};
|
||||
static NextT const kNext{};
|
||||
Keylet
|
||||
next(Keylet const& k);
|
||||
|
||||
/** A ticket belonging to an account */
|
||||
struct TicketT
|
||||
/** @{ */
|
||||
Keylet
|
||||
ticket(AccountID const& id, std::uint32_t ticketSeq);
|
||||
|
||||
Keylet
|
||||
ticket(AccountID const& id, SeqProxy ticketSeq);
|
||||
|
||||
inline Keylet
|
||||
ticket(uint256 const& 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{};
|
||||
return {ltTICKET, key};
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** A SignerList */
|
||||
Keylet
|
||||
signers(AccountID const& account) noexcept;
|
||||
signerList(AccountID const& account) noexcept;
|
||||
|
||||
/** A Check */
|
||||
/** @{ */
|
||||
@@ -209,7 +193,7 @@ escrow(AccountID const& src, std::uint32_t seq) noexcept;
|
||||
|
||||
/** A PaymentChannel */
|
||||
Keylet
|
||||
payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
|
||||
payChannel(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
|
||||
|
||||
/** NFT page keylets
|
||||
|
||||
@@ -221,22 +205,22 @@ payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
|
||||
/** @{ */
|
||||
/** A keylet for the owner's first possible NFT page. */
|
||||
Keylet
|
||||
nftpageMin(AccountID const& owner);
|
||||
nftokenPageMin(AccountID const& owner);
|
||||
|
||||
/** A keylet for the owner's last possible NFT page. */
|
||||
Keylet
|
||||
nftpageMax(AccountID const& owner);
|
||||
nftokenPageMax(AccountID const& owner);
|
||||
|
||||
Keylet
|
||||
nftpage(Keylet const& k, uint256 const& token);
|
||||
nftokenPage(Keylet const& k, uint256 const& token);
|
||||
/** @} */
|
||||
|
||||
/** An offer from an account to buy or sell an NFT */
|
||||
Keylet
|
||||
nftoffer(AccountID const& owner, std::uint32_t seq);
|
||||
nftokenOffer(AccountID const& owner, std::uint32_t seq);
|
||||
|
||||
inline Keylet
|
||||
nftoffer(uint256 const& offer)
|
||||
nftokenOffer(uint256 const& offer)
|
||||
{
|
||||
return {ltNFTOKEN_OFFER, offer};
|
||||
}
|
||||
@@ -287,13 +271,13 @@ credential(uint256 const& key) noexcept
|
||||
}
|
||||
|
||||
Keylet
|
||||
mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept;
|
||||
mptokenIssuance(std::uint32_t seq, AccountID const& issuer) noexcept;
|
||||
|
||||
Keylet
|
||||
mptIssuance(MPTID const& issuanceID) noexcept;
|
||||
mptokenIssuance(MPTID const& issuanceID) noexcept;
|
||||
|
||||
inline Keylet
|
||||
mptIssuance(uint256 const& issuanceKey)
|
||||
mptokenIssuance(uint256 const& issuanceKey)
|
||||
{
|
||||
return {ltMPTOKEN_ISSUANCE, issuanceKey};
|
||||
}
|
||||
@@ -320,10 +304,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};
|
||||
}
|
||||
@@ -376,11 +360,15 @@ 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::signers, .expectedLEName = jss::SignerList, .includeInTests = true},
|
||||
{.function = &keylet::signerList, .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::nftpageMin, .expectedLEName = jss::NFTokenPage, .includeInTests = true},
|
||||
{.function = &keylet::nftpageMax, .expectedLEName = jss::NFTokenPage, .includeInTests = true},
|
||||
{.function = &keylet::nftokenPageMin,
|
||||
.expectedLEName = jss::NFTokenPage,
|
||||
.includeInTests = true},
|
||||
{.function = &keylet::nftokenPageMax,
|
||||
.expectedLEName = jss::NFTokenPage,
|
||||
.includeInTests = true},
|
||||
{.function = &keylet::did, .expectedLEName = jss::DID, .includeInTests = true}}};
|
||||
|
||||
MPTID
|
||||
|
||||
@@ -177,7 +177,8 @@ enum LedgerEntryType : std::uint16_t {
|
||||
LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \
|
||||
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
|
||||
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
|
||||
LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \
|
||||
LSF_FLAG(lsfMPTCanClawback, 0x00000040) \
|
||||
LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080)) \
|
||||
\
|
||||
LEDGER_OBJECT(MPTokenIssuanceMutable, \
|
||||
LSF_FLAG(lsmfMPTCanEnableCanLock, 0x00000002) \
|
||||
@@ -186,8 +187,9 @@ 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) \
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
txToPermissionType(TxType type);
|
||||
|
||||
// tx type value is permission value minus one
|
||||
[[nodiscard]] static TxType
|
||||
[[nodiscard]] static std::optional<TxType>
|
||||
permissionToTxType(std::uint32_t value);
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#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 {
|
||||
@@ -307,4 +311,62 @@ constexpr std::size_t kPermissionMaxSize = 10;
|
||||
/** The maximum number of transactions that can be in a batch. */
|
||||
constexpr std::size_t kMaxBatchTxCount = 8;
|
||||
|
||||
/** 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
|
||||
|
||||
@@ -128,6 +128,7 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
temBAD_MPT,
|
||||
temBAD_CIPHERTEXT,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -358,6 +359,11 @@ 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,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -140,7 +140,8 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
|
||||
TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \
|
||||
TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \
|
||||
TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \
|
||||
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \
|
||||
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback) \
|
||||
TF_FLAG(tfMPTCanHoldConfidentialBalance, lsfMPTCanHoldConfidentialBalance), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(MPTokenAuthorize, \
|
||||
@@ -349,10 +350,13 @@ 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);
|
||||
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee |
|
||||
tmfMPTCannotEnableCanHoldConfidentialBalance);
|
||||
|
||||
// MPTokenIssuanceSet MutableFlags:
|
||||
// Enable mutable capability flags. These flags are one-way: once enabled,
|
||||
@@ -364,9 +368,10 @@ 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);
|
||||
tmfMPTSetCanTransfer | tmfMPTSetCanClawback | tmfMPTSetCanHoldConfidentialBalance);
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
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)
|
||||
@@ -58,13 +58,11 @@ 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)
|
||||
|
||||
@@ -105,6 +103,7 @@ 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,6 +115,7 @@ 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)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
/** A ledger object which identifies an offer to buy or sell an NFT.
|
||||
|
||||
\sa keylet::nftoffer
|
||||
\sa keylet::nftokenOffer
|
||||
*/
|
||||
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::nftpageMin, keylet::nftpageMax, keylet::nftpage
|
||||
\sa keylet::nftokenPageMin, keylet::nftokenPageMax, keylet::nftokenPage
|
||||
*/
|
||||
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::signers
|
||||
\sa keylet::signerList
|
||||
*/
|
||||
// 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::kTicket
|
||||
\sa keylet::ticket
|
||||
*/
|
||||
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::line
|
||||
\sa keylet::trustLine
|
||||
*/
|
||||
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::fees
|
||||
\sa keylet::feeSettings
|
||||
*/
|
||||
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::payChan
|
||||
\sa keylet::payChannel
|
||||
*/
|
||||
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::mptIssuance
|
||||
\sa keylet::mptokenIssuance
|
||||
*/
|
||||
LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
|
||||
{sfIssuer, SoeRequired},
|
||||
@@ -401,19 +401,28 @@ 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},
|
||||
{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},
|
||||
}))
|
||||
|
||||
/** A ledger object which tracks Oracle
|
||||
@@ -499,7 +508,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},
|
||||
|
||||
@@ -11,7 +11,10 @@ secp256k1Context()
|
||||
struct Holder
|
||||
{
|
||||
secp256k1_context* impl;
|
||||
Holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
|
||||
// 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))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ 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)
|
||||
@@ -146,6 +147,7 @@ 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)
|
||||
@@ -206,6 +208,7 @@ 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)
|
||||
@@ -299,6 +302,21 @@ 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)
|
||||
|
||||
@@ -395,7 +395,7 @@ TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
||||
#endif
|
||||
TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||
Delegation::Delegable,
|
||||
featureClawback,
|
||||
uint256{},
|
||||
NoPriv,
|
||||
({
|
||||
{sfAmount, SoeRequired, SoeMptSupported},
|
||||
@@ -735,6 +735,8 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
{sfMPTokenMetadata, SoeOptional},
|
||||
{sfTransferFee, SoeOptional},
|
||||
{sfMutableFlags, SoeOptional},
|
||||
{sfIssuerEncryptionKey, SoeOptional},
|
||||
{sfAuditorEncryptionKey, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This transaction type authorizes a MPToken instance */
|
||||
@@ -1077,6 +1079,91 @@ 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
|
||||
|
||||
@@ -137,6 +137,7 @@ 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
|
||||
@@ -161,9 +162,6 @@ 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
|
||||
@@ -185,165 +183,170 @@ JSS(command); // in: RPCHandler
|
||||
JSS(common); // out: RPC server_definitions
|
||||
JSS(complete); // out: NetworkOPs, InboundLedger
|
||||
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
|
||||
JSS(consensus); // out: NetworkOPs, LedgerConsensus
|
||||
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(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(limit); // in/out: AccountTx*, AccountOffers, AccountLines, AccountObjects
|
||||
// in: LedgerData, BookOffers
|
||||
JSS(limit_peer); // out: AccountLines
|
||||
@@ -401,6 +404,9 @@ 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
|
||||
|
||||
@@ -52,7 +52,7 @@ getTransferFee(uint256 const& id)
|
||||
}
|
||||
|
||||
inline std::uint32_t
|
||||
getSerial(uint256 const& id)
|
||||
getSequence(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(getSerial(id), toTaxon(taxon));
|
||||
return cipheredTaxon(getSequence(id), toTaxon(taxon));
|
||||
}
|
||||
|
||||
inline AccountID
|
||||
|
||||
@@ -147,6 +147,150 @@ 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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -270,6 +414,72 @@ 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.
|
||||
|
||||
@@ -302,6 +302,78 @@ 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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -504,6 +576,39 @@ 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.
|
||||
|
||||
@@ -20,7 +20,7 @@ class ClawbackBuilder;
|
||||
*
|
||||
* Type: ttCLAWBACK (30)
|
||||
* Delegable: Delegation::Delegable
|
||||
* Amendment: featureClawback
|
||||
* Amendment: uint256{}
|
||||
* Privileges: NoPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// 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
|
||||
@@ -0,0 +1,336 @@
|
||||
// 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
|
||||
@@ -0,0 +1,310 @@
|
||||
// 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
|
||||
@@ -0,0 +1,129 @@
|
||||
// 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
|
||||
408
include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h
Normal file
408
include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h
Normal file
@@ -0,0 +1,408 @@
|
||||
// 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
|
||||
@@ -187,6 +187,58 @@ 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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -297,6 +349,28 @@ 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.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
@@ -186,6 +187,10 @@ public:
|
||||
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:
|
||||
|
||||
|
||||
@@ -413,6 +413,7 @@ using InvariantChecks = std::tuple<
|
||||
ValidLoanBroker,
|
||||
ValidLoan,
|
||||
ValidVault,
|
||||
ValidConfidentialMPToken,
|
||||
ValidMPTPayment,
|
||||
ValidAmounts,
|
||||
ValidMPTTransfer>;
|
||||
|
||||
@@ -36,17 +36,42 @@ 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, SLE::const_ref, SLE::const_ref);
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
|
||||
|
||||
/**
|
||||
* @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&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
|
||||
finalize(
|
||||
STTx const& tx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const;
|
||||
};
|
||||
|
||||
/** Verify:
|
||||
* - OutstandingAmount <= MaximumAmount for any MPT
|
||||
* - OutstandingAmount after = OutstandingAmount before +
|
||||
* sum (MPT after - MPT before) - this is total MPT credit/debit
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
class ValidMPTPayment
|
||||
{
|
||||
@@ -64,11 +89,104 @@ 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, SLE::const_ref, SLE::const_ref);
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
|
||||
|
||||
/**
|
||||
* @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&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
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);
|
||||
};
|
||||
|
||||
class ValidMPTTransfer
|
||||
@@ -85,11 +203,36 @@ 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, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
visitEntry(
|
||||
bool isDelete,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after);
|
||||
|
||||
/**
|
||||
* @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&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
finalize(
|
||||
STTx const& tx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j);
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -99,7 +242,13 @@ 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()
|
||||
* 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.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isAuthorized(
|
||||
|
||||
@@ -27,7 +27,7 @@ checkFreeze(
|
||||
}
|
||||
}
|
||||
|
||||
if (auto sle = view.read(keylet::line(src, dst, currency)))
|
||||
if (auto sle = view.read(keylet::trustLine(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::line(prev, cur, currency));
|
||||
auto sleOut = view.read(keylet::line(cur, next, currency));
|
||||
auto sleIn = view.read(keylet::trustLine(prev, cur, currency));
|
||||
auto sleOut = view.read(keylet::trustLine(cur, next, currency));
|
||||
|
||||
if (!sleIn || !sleOut)
|
||||
return terNO_LINE;
|
||||
|
||||
@@ -159,6 +159,11 @@ 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);
|
||||
|
||||
|
||||
59
include/xrpl/tx/transactors/token/ConfidentialMPTClawback.h
Normal file
59
include/xrpl/tx/transactors/token/ConfidentialMPTClawback.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#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
|
||||
61
include/xrpl/tx/transactors/token/ConfidentialMPTConvert.h
Normal file
61
include/xrpl/tx/transactors/token/ConfidentialMPTConvert.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#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
|
||||
@@ -0,0 +1,62 @@
|
||||
#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
|
||||
@@ -0,0 +1,63 @@
|
||||
#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
|
||||
72
include/xrpl/tx/transactors/token/ConfidentialMPTSend.h
Normal file
72
include/xrpl/tx/transactors/token/ConfidentialMPTSend.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#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
|
||||
@@ -6,10 +6,10 @@ This directory contains all files needed to build RPM and Debian packages for `x
|
||||
|
||||
```
|
||||
package/
|
||||
build_pkg.sh Staging and build script (called by CMake targets and CI)
|
||||
build_pkg.sh Staging and build script (called by the CMake `package` target and CI)
|
||||
rpm/
|
||||
xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define)
|
||||
debian/ Debian control files (control, rules, install, links, conffiles, ...)
|
||||
xrpld.spec RPM spec
|
||||
debian/ Debian control files (control, rules, copyright, xrpld.docs, xrpld.links, source/format)
|
||||
shared/
|
||||
xrpld.service systemd unit file (used by both RPM and DEB)
|
||||
xrpld.sysusers sysusers.d config (used by both RPM and DEB)
|
||||
@@ -21,21 +21,19 @@ package/
|
||||
|
||||
Packaging targets and their container images are declared in
|
||||
[`.github/scripts/strategy-matrix/linux.json`](../.github/scripts/strategy-matrix/linux.json)
|
||||
inside `package_configs` configurations. Today only
|
||||
`linux/amd64` is emitted. The package format
|
||||
(deb or rpm) is inferred at build time from the container's package manager
|
||||
(`apt-get` -> deb, `dnf`/`yum` -> rpm). The image tag is composed as
|
||||
`ghcr.io/xrplf/xrpld/packaging-<distro>:sha-<git_sha>` —
|
||||
the same scheme used by `reusable-build-test.yml`. Bump `image_sha` in
|
||||
`linux.json` and both CI and local builds pick up the new image with no
|
||||
workflow edits.
|
||||
under `package_configs`, one entry per distro. Today only `linux/amd64` is
|
||||
emitted. Each entry pins its full container image in an `image` field; to move
|
||||
to a new image, edit that field and both CI and local builds pick it up. The
|
||||
package format (deb or rpm) is inferred at build time from the container's
|
||||
package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm).
|
||||
|
||||
| Package type | Image (derived from `linux.json`) | Tool required |
|
||||
| ------------ | ---------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-<git_sha>` | `rpmbuild` |
|
||||
| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-<git_sha>` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` |
|
||||
| Package type | Image (`package_configs.<distro>[].image` in `linux.json`) | Tools required |
|
||||
| ------------ | ---------------------------------------------------------- | --------------------------------------------------- |
|
||||
| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-<sha>` | `rpmbuild` |
|
||||
| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-<sha>` | `dpkg-buildpackage`, debhelper with compat level 13 |
|
||||
|
||||
To print the exact image tags for the current `linux.json`:
|
||||
To print the full packaging matrix (artifact names and images) for the current
|
||||
`linux.json`:
|
||||
|
||||
```bash
|
||||
./.github/scripts/strategy-matrix/generate.py --packaging
|
||||
@@ -46,12 +44,13 @@ To print the exact image tags for the current `linux.json`:
|
||||
### Via CI
|
||||
|
||||
Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call
|
||||
`reusable-strategy-matrix.yml` with `mode: packaging` to generate the matrix of
|
||||
`{artifact_name, os}` entries, then fan out to
|
||||
`reusable-package.yml` per entry. That workflow downloads the pre-built `xrpld`
|
||||
binary artifact, detects the package format from the container, and calls
|
||||
`build_pkg.sh` directly — no CMake configure or build step is needed inside
|
||||
the packaging job.
|
||||
`reusable-package.yml`. That workflow generates its own packaging matrix from
|
||||
`package_configs` in `linux.json` (via `generate.py --packaging`) and fans out
|
||||
one job per distro. Each job downloads the pre-built `xrpld` binary artifact and
|
||||
runs in that distro's container, so the package format follows from the
|
||||
container's package manager. The packaging script derives the package version
|
||||
from the downloaded binary's `xrpld --version` output; no CMake configure or
|
||||
build step is needed inside the packaging job.
|
||||
|
||||
### Locally (mirrors CI)
|
||||
|
||||
@@ -60,22 +59,19 @@ inside the same container CI uses. The image tag is derived from `linux.json`
|
||||
so you don't need to hardcode a SHA.
|
||||
|
||||
```bash
|
||||
# From the repo root. Pick any image flagged with `"package": true` in
|
||||
# linux.json; the package format is inferred from the container's package
|
||||
# manager. Example for the rpm-producing image:
|
||||
IMAGE=$(jq -r '
|
||||
.os | map(select(.package == true))[0] |
|
||||
"ghcr.io/xrplf/ci/\(.distro_name)-\(.distro_version):\(.compiler_name)-\(.compiler_version)-sha-\(.image_sha)"
|
||||
' .github/scripts/strategy-matrix/linux.json)
|
||||
# From the repo root. Each distro's container image is the `image` field of its
|
||||
# package_configs entry in linux.json; the package format is inferred from the
|
||||
# container's package manager. Example for the rpm-producing image (use
|
||||
# .package_configs.debian[0].image for the deb image):
|
||||
IMAGE=$(jq -r '.package_configs.rhel[0].image' .github/scripts/strategy-matrix/linux.json)
|
||||
|
||||
VERSION=2.4.0-local
|
||||
PKG_RELEASE=1
|
||||
|
||||
docker run --rm \
|
||||
-v "$(pwd):/src" \
|
||||
-w /src \
|
||||
"$IMAGE" \
|
||||
./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE"
|
||||
"${IMAGE}" \
|
||||
./package/build_pkg.sh --pkg-release "${PKG_RELEASE}"
|
||||
|
||||
# Output:
|
||||
# build/debbuild/*.deb (DEB + dbgsym .ddeb)
|
||||
@@ -91,41 +87,73 @@ needed, but the host toolchain replaces the pinned CI image:
|
||||
```bash
|
||||
cmake \
|
||||
-Dxrpld=ON \
|
||||
-Dxrpld_version=2.4.0-local \
|
||||
-Dpkg_release=1 \
|
||||
-Dtests=OFF \
|
||||
..
|
||||
|
||||
cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL
|
||||
```
|
||||
|
||||
The `cmake/XrplPackaging.cmake` module defines the target only if at least one
|
||||
of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then infers the
|
||||
package format from the host's package manager. The packaging script installs
|
||||
to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of
|
||||
The `cmake/XrplPackaging.cmake` module defines the `package` target only if at
|
||||
least one of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then
|
||||
infers the package format from the host's package manager. The packaging script
|
||||
installs to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of
|
||||
`CMAKE_INSTALL_PREFIX`.
|
||||
|
||||
The package version is not a CMake input on this path: `build_pkg.sh` derives it
|
||||
from the just-built `xrpld` binary's `xrpld --version` output. The package
|
||||
release defaults to 1 and is overridable with `-Dpkg_release=N`.
|
||||
|
||||
## How `build_pkg.sh` works
|
||||
|
||||
`build_pkg.sh` accepts long-form flags, each of which can also be set via an
|
||||
environment variable. Flags override env vars; env vars override the built-in
|
||||
defaults. Run `./package/build_pkg.sh --help` for the same table:
|
||||
`build_pkg.sh` derives the `xrpld` software version from
|
||||
`${BUILD_DIR}/xrpld --version` in both package formats.
|
||||
|
||||
| Flag | Env var | Default | Purpose |
|
||||
| -------------------------- | ------------------- | ----------------------------- | ----------------------------------- |
|
||||
| `--src-dir DIR` | `SRC_DIR` | `$PWD` | repo root |
|
||||
| `--build-dir DIR` | `BUILD_DIR` | `$PWD/build` | directory holding pre-built `xrpld` |
|
||||
| `--pkg-version STR` | `PKG_VERSION` | parsed from `xrpld --version` | version string, e.g. `3.2.0-b1` |
|
||||
| `--pkg-release N` | `PKG_RELEASE` | `1` | package release number |
|
||||
| `--source-date-epoch SECS` | `SOURCE_DATE_EPOCH` | latest git commit ctime | reproducibility timestamp |
|
||||
The binary's version is already SemVer-validated by `BuildInfo`.
|
||||
`build_pkg.sh` converts pre-release versions such as `3.2.0-b1` or
|
||||
`3.2.0-rc1` from `-` to `~` for package metadata so pre-releases sort before
|
||||
the final release. If that normalized package version still contains `-`,
|
||||
packaging fails because RPM forbids `-` in `Version`, and Debian uses `-` as
|
||||
the upstream/revision separator.
|
||||
|
||||
`pkg_version` is the normalized package metadata version derived inside
|
||||
`build_pkg.sh` from the binary-reported `xrpld` version (`-` pre-release
|
||||
separator converted to `~`). It is not a separate user input.
|
||||
|
||||
`PKG_RELEASE` is a different value: the package release iteration for that
|
||||
`xrpld` version. RPM receives the normalized `pkg_version` and `PKG_RELEASE` as
|
||||
the `pkg_version` and `pkg_release` macros for its `Version` and `Release`
|
||||
values; DEB writes them as `${pkg_version}-${PKG_RELEASE}` in
|
||||
`debian/changelog`.
|
||||
|
||||
With `PKG_RELEASE=1`, the package metadata becomes:
|
||||
|
||||
| Input version | RPM version/release | Debian version |
|
||||
| ------------------ | ---------------------------- | -------------------- |
|
||||
| `3.2.0` | `3.2.0-1%{?dist}` | `3.2.0-1` |
|
||||
| `3.2.0-b0+abc1234` | `3.2.0~b0+abc1234-1%{?dist}` | `3.2.0~b0+abc1234-1` |
|
||||
| `3.2.0-b1` | `3.2.0~b1-1%{?dist}` | `3.2.0~b1-1` |
|
||||
| `3.2.0-rc1` | `3.2.0~rc1-1%{?dist}` | `3.2.0~rc1-1` |
|
||||
|
||||
The Debian changelog entry carries the repository component: final releases use
|
||||
`stable`, `b0` builds, including `b0+metadata`, use `develop`, and `bN`/`rcN`
|
||||
pre-releases use `unstable`.
|
||||
Build metadata on a final release, such as `3.2.0+abc123`, is rejected.
|
||||
|
||||
The RPM path intentionally uses `~` in `Version`, matching the Debian
|
||||
pre-release ordering convention, so RPM filenames/NVRs begin with forms like
|
||||
`xrpld-3.2.0~b1-...` and `xrpld-3.2.0~rc1-...` instead of encoding
|
||||
pre-releases with an older `0.<release>.<suffix>` RPM `Release` value.
|
||||
|
||||
The package format (`deb` or `rpm`) is inferred from the host's package
|
||||
manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). Hosts without one of those
|
||||
fail early.
|
||||
|
||||
Flags are for explicit invocation; environment variables are intended for
|
||||
CMake/systemd/CI integration. The CI workflow and the CMake `package` target
|
||||
both invoke `build_pkg.sh` with no flags, configuring it entirely via env
|
||||
(see `cmake/XrplPackaging.cmake`).
|
||||
CMake/CI integration. The CI workflow and the CMake `package` target both invoke
|
||||
`build_pkg.sh` with no flags; CMake supplies `SRC_DIR`, `BUILD_DIR`, and
|
||||
`PKG_RELEASE` via env, while CI supplies `BUILD_DIR` and `PKG_RELEASE` via env
|
||||
and lets the script use defaults for the rest.
|
||||
|
||||
It resolves `SRC_DIR` and `BUILD_DIR` to absolute paths, then calls
|
||||
`stage_common()` to copy the binary, config files, and shared support files
|
||||
@@ -134,18 +162,32 @@ into the staging area, and invokes the platform build tool.
|
||||
### RPM
|
||||
|
||||
1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory.
|
||||
2. Copies `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`.
|
||||
3. Runs `rpmbuild -bb --define "xrpld_version ..." --define "pkg_release ..."`. The spec uses manual `install` commands to place files.
|
||||
2. Copies `xrpld.spec` and all shared source files (binary, configs, service files) into `SOURCES/`.
|
||||
3. Runs `rpmbuild -bb`, passing the normalized package metadata version as the
|
||||
`pkg_version` RPM macro and `PKG_RELEASE` as the `pkg_release` RPM macro.
|
||||
The spec uses manual `install` commands to place files, disables `dwz`, and
|
||||
writes uncompressed RPM payloads while generating debuginfo packages.
|
||||
4. Output: `rpmbuild/RPMS/x86_64/xrpld-*.rpm`
|
||||
|
||||
The uncompressed RPM payload setting is intentionally unconditional for
|
||||
generated RPMs. It trades larger RPM artifacts for much shorter package
|
||||
build/validation time, which keeps RPM package validation in the same rough time
|
||||
class as Debian package validation.
|
||||
|
||||
RPM upgrades intentionally do not restart a running `xrpld` service. The spec
|
||||
uses `%systemd_postun`, matching Debian's `dh_installsystemd
|
||||
--no-stop-on-upgrade` behavior; operators pick up the new binary on the next
|
||||
service restart.
|
||||
|
||||
### DEB
|
||||
|
||||
1. Creates a staging source tree at `debbuild/source/` inside the build directory.
|
||||
2. Stages the binary, configs, `README.md`, and `LICENSE.md`.
|
||||
3. Copies `package/debian/` control files into `debbuild/source/debian/`.
|
||||
4. Copies shared service/sysusers/tmpfiles into `debian/` where `dh_installsystemd`, `dh_installsysusers`, and `dh_installtmpfiles` pick them up automatically.
|
||||
5. Generates a minimal `debian/changelog` (pre-release versions use `~` instead of `-`).
|
||||
6. Runs `dpkg-buildpackage -b --no-sign`. `debian/rules` uses manual `install` commands.
|
||||
5. Generates a minimal `debian/changelog` using `${pkg_version}-${PKG_RELEASE}`,
|
||||
where `pkg_version` is derived from the binary-reported `xrpld` version.
|
||||
6. Runs `dpkg-buildpackage -b --no-sign -d` (`-d` skips the build-dependency check, since the binary is already built). `debian/rules` uses manual `install` commands.
|
||||
7. Output: `debbuild/*.deb` and `debbuild/*.ddeb` (dbgsym package)
|
||||
|
||||
## Post-build verification
|
||||
@@ -161,11 +203,14 @@ rpm -qlp rpmbuild/RPMS/x86_64/*.rpm
|
||||
|
||||
## Reproducibility
|
||||
|
||||
The following environment variables improve build reproducibility. They are not
|
||||
set automatically by `build_pkg.sh`; set them manually if needed:
|
||||
`build_pkg.sh` already defaults `SOURCE_DATE_EPOCH` to the latest git commit
|
||||
time, or the current time outside a git tree, and exports it (override with
|
||||
`--source-date-epoch` / `SOURCE_DATE_EPOCH`); the RPM spec clamps file
|
||||
modification times to it via `%build_mtime_policy`. The remaining variables
|
||||
below further improve reproducibility but are _not_ set by the script — export
|
||||
them yourself if needed:
|
||||
|
||||
```bash
|
||||
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
||||
export TZ=UTC
|
||||
export LC_ALL=C.UTF-8
|
||||
export GZIP=-n
|
||||
|
||||
@@ -3,20 +3,18 @@ set -euo pipefail
|
||||
|
||||
# Build an RPM or Debian package from a pre-built xrpld binary.
|
||||
#
|
||||
# Flags override env vars; env vars override defaults. Env vars are intended
|
||||
# for CMake/systemd/CI integration; flags are for explicit invocation.
|
||||
# Flags override env vars; env vars override defaults.
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: build_pkg.sh [options]
|
||||
|
||||
Options (each can also be set via the env var shown):
|
||||
--src-dir DIR repo root [SRC_DIR; default: $PWD]
|
||||
--build-dir DIR directory holding xrpld [BUILD_DIR; default: $PWD/build]
|
||||
--pkg-version STR version, e.g. 3.2.0-b1 [PKG_VERSION; default: parsed from xrpld --version]
|
||||
--pkg-release N package release number [PKG_RELEASE; default: 1]
|
||||
--source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; default: latest git commit ctime]
|
||||
-h, --help show this help and exit
|
||||
--src-dir DIR repo root [SRC_DIR; default: ${PWD}]
|
||||
--build-dir DIR directory holding xrpld [BUILD_DIR; default: ${PWD}/build]
|
||||
--pkg-release N package release iteration [PKG_RELEASE; default: 1]
|
||||
--source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; latest git ctime; fallback: current time]
|
||||
-h, --help show this help and exit
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -30,8 +28,7 @@ need_arg() {
|
||||
# Seed from env. CLI parsing below overrides these directly.
|
||||
SRC_DIR="${SRC_DIR:-}"
|
||||
BUILD_DIR="${BUILD_DIR:-}"
|
||||
PKG_VERSION="${PKG_VERSION:-}"
|
||||
PKG_RELEASE="${PKG_RELEASE:-}"
|
||||
PKG_RELEASE="${PKG_RELEASE:-1}"
|
||||
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -46,11 +43,6 @@ while [[ $# -gt 0 ]]; do
|
||||
BUILD_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--pkg-version)
|
||||
need_arg "$@"
|
||||
PKG_VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--pkg-release)
|
||||
need_arg "$@"
|
||||
PKG_RELEASE="$2"
|
||||
@@ -74,19 +66,61 @@ while [[ $# -gt 0 ]]; do
|
||||
done
|
||||
|
||||
SRC_DIR="$(cd "${SRC_DIR:-${PWD}}" && pwd)"
|
||||
BUILD_DIR="$(cd "${BUILD_DIR:-${PWD}/build}" && pwd)"
|
||||
PKG_RELEASE="${PKG_RELEASE:-1}"
|
||||
|
||||
if [[ -z "${PKG_VERSION}" ]]; then
|
||||
PKG_VERSION="$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3; exit}')"
|
||||
BUILD_DIR="${BUILD_DIR:-${PWD}/build}"
|
||||
if [[ ! -d "${BUILD_DIR}" ]]; then
|
||||
echo "build_pkg.sh: build directory not found: ${BUILD_DIR}" >&2
|
||||
echo "Build xrpld before packaging, or set BUILD_DIR to the directory containing xrpld." >&2
|
||||
exit 1
|
||||
fi
|
||||
BUILD_DIR="$(cd "${BUILD_DIR}" && pwd)"
|
||||
|
||||
if [[ -z "${PKG_VERSION}" ]]; then
|
||||
echo "PKG_VERSION is empty (not provided and could not be derived)." >&2
|
||||
xrpld_binary="${BUILD_DIR}/xrpld"
|
||||
if [[ ! -x "${xrpld_binary}" ]]; then
|
||||
echo "build_pkg.sh: expected executable xrpld binary at ${xrpld_binary}." >&2
|
||||
echo "Build xrpld before packaging, or set BUILD_DIR to the directory containing xrpld." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="${PKG_VERSION}"
|
||||
xrpld_version="$("${xrpld_binary}" --version | awk 'NR == 1 { print $3 }')"
|
||||
|
||||
if [[ -z "${xrpld_version}" ]]; then
|
||||
echo "build_pkg.sh: unable to derive xrpld version from ${xrpld_binary} --version." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The version as the package formats consume it: identical to xrpld_version
|
||||
# except a pre-release uses '~' (3.2.0-b1 -> 3.2.0~b1), which also sorts before
|
||||
# the final 3.2.0; a no-op for a final release. Lowercase = derived internally,
|
||||
# not an input (cf. pkg_type).
|
||||
pkg_version="${xrpld_version}"
|
||||
pre_release=""
|
||||
if [[ "${xrpld_version}" == *-* ]]; then
|
||||
pre_release="${xrpld_version#*-}"
|
||||
pkg_version="${xrpld_version%%-*}~${pre_release}"
|
||||
fi
|
||||
|
||||
# BuildInfo already SemVer-validates the binary's version. Packaging adds one
|
||||
# narrower constraint: after pre-release normalization, the package version must
|
||||
# not contain '-' because RPM forbids it in Version and Debian uses it as the
|
||||
# upstream/revision separator.
|
||||
if [[ "${pkg_version}" == *-* ]]; then
|
||||
echo "build_pkg.sh: unsupported xrpld version '${xrpld_version}'." >&2
|
||||
echo "Package version '${pkg_version}' cannot contain '-'." >&2
|
||||
echo "Use a single-token pre-release like 3.2.0-b1 or 3.2.0-rc2." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${pre_release}" && "${xrpld_version}" == *+* ]]; then
|
||||
echo "build_pkg.sh: unsupported xrpld version '${xrpld_version}'." >&2
|
||||
echo "Build metadata is only supported on bN/rcN pre-releases." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${pre_release}" && ! "${pre_release}" =~ ^(b0|b[1-9][0-9]*|rc[0-9]+)(\+.*)?$ ]]; then
|
||||
echo "build_pkg.sh: unsupported xrpld pre-release '${pre_release}'." >&2
|
||||
echo "Use bN or rcN, e.g. 3.2.0-b1 or 3.2.0-rc2." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
pkg_type=deb
|
||||
@@ -98,32 +132,15 @@ else
|
||||
fi
|
||||
|
||||
if [[ -z "${SOURCE_DATE_EPOCH}" ]]; then
|
||||
if git -C "$SRC_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
SOURCE_DATE_EPOCH="$(git -C "$SRC_DIR" log -1 --format=%ct)"
|
||||
if git -C "${SRC_DIR}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
SOURCE_DATE_EPOCH="$(git -C "${SRC_DIR}" log -1 --format=%ct)"
|
||||
else
|
||||
SOURCE_DATE_EPOCH="$(date +%s)"
|
||||
fi
|
||||
fi
|
||||
|
||||
export SOURCE_DATE_EPOCH
|
||||
CHANGELOG_DATE="$(date -u -R -d "@$SOURCE_DATE_EPOCH")"
|
||||
|
||||
# Split VERSION at the first '-' into base and optional pre-release suffix.
|
||||
# Examples: "3.2.0" -> ("3.2.0", ""); "3.2.0-b1" -> ("3.2.0", "b1").
|
||||
VER_BASE="${VERSION%%-*}"
|
||||
VER_SUFFIX="${VERSION#*-}"
|
||||
[[ "${VER_SUFFIX}" == "${VERSION}" ]] && VER_SUFFIX=""
|
||||
|
||||
# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). Neither an
|
||||
# RPM Version nor a Debian upstream version may contain '-' (it's the NVR /
|
||||
# version-revision separator), and the convention here is single-token
|
||||
# suffixes like b1 or rc2. Fail early with a clear message rather than letting
|
||||
# the package tooling blow up or silently mangle dashes.
|
||||
if [[ "${VER_SUFFIX}" == *-* ]]; then
|
||||
echo "build_pkg.sh: multi-segment pre-release in VERSION='${VERSION}' (suffix '${VER_SUFFIX}')." >&2
|
||||
echo "Use single-token suffixes like 3.2.0-b1 or 3.2.0-rc2." >&2
|
||||
exit 1
|
||||
fi
|
||||
CHANGELOG_DATE="$(date -u -R -d "@${SOURCE_DATE_EPOCH}")"
|
||||
|
||||
SHARED="${SRC_DIR}/package/shared"
|
||||
DEBIAN_DIR="${SRC_DIR}/package/debian"
|
||||
@@ -143,7 +160,6 @@ stage_common() {
|
||||
cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers"
|
||||
cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles"
|
||||
cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate"
|
||||
cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset"
|
||||
}
|
||||
|
||||
build_rpm() {
|
||||
@@ -154,18 +170,11 @@ build_rpm() {
|
||||
cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec"
|
||||
stage_common "${topdir}/SOURCES"
|
||||
|
||||
# Pre-releases use the modern rpm '~' convention (rpm >= 4.10): the suffix
|
||||
# goes in Version (e.g. 3.2.0~b1), which rpmvercmp sorts *before* the final
|
||||
# 3.2.0 — identical semantics to Debian's '~'. Release is just the package
|
||||
# release number. This replaces the older "0.<release>.<suffix>" Release
|
||||
# hack and keeps the RPM and DEB version strings symmetric.
|
||||
local rpm_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}"
|
||||
|
||||
set -x
|
||||
rpmbuild -bb \
|
||||
--define "_topdir ${topdir}" \
|
||||
--define "xrpld_version ${rpm_version}" \
|
||||
--define "xrpld_release ${PKG_RELEASE}" \
|
||||
--define "pkg_version ${pkg_version}" \
|
||||
--define "pkg_release ${PKG_RELEASE}" \
|
||||
"${topdir}/SPECS/xrpld.spec"
|
||||
}
|
||||
|
||||
@@ -182,23 +191,26 @@ build_deb() {
|
||||
cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles"
|
||||
cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate"
|
||||
|
||||
# Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0.
|
||||
local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}"
|
||||
|
||||
# Derive release channel from the version suffix:
|
||||
# (none) -> stable (tagged release)
|
||||
# b0 -> develop (develop-branch build)
|
||||
# b<N>, rc<N> -> unstable (pre-release)
|
||||
local deb_distribution
|
||||
case "${VER_SUFFIX}" in
|
||||
"") deb_distribution="stable" ;;
|
||||
b0) deb_distribution="develop" ;;
|
||||
*) deb_distribution="unstable" ;;
|
||||
esac
|
||||
# Choose the Debian repository component for this package.
|
||||
# 3.2.0 -> stable, *-b0[+metadata] -> develop,
|
||||
# bN/rcN pre-releases -> unstable.
|
||||
local deb_component
|
||||
if [[ -z "${pre_release}" ]]; then
|
||||
deb_component="stable"
|
||||
elif [[ "${pre_release}" =~ ^b0(\+.*)?$ ]]; then
|
||||
deb_component="develop"
|
||||
elif [[ "${pre_release}" =~ ^(b[1-9][0-9]*|rc[0-9]+)(\+.*)?$ ]]; then
|
||||
deb_component="unstable"
|
||||
else
|
||||
echo "build_pkg.sh: unsupported xrpld pre-release '${pre_release}'." >&2
|
||||
echo "Use bN or rcN, e.g. 3.2.0-b1 or 3.2.0-rc2." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Debian version is <upstream>[~<pre>]-<pkg release>.
|
||||
cat >"${staging}/debian/changelog" <<EOF
|
||||
xrpld (${deb_full_version}) ${deb_distribution}; urgency=medium
|
||||
* Release ${VERSION}.
|
||||
xrpld (${pkg_version}-${PKG_RELEASE}) ${deb_component}; urgency=medium
|
||||
* Release ${xrpld_version}.
|
||||
|
||||
-- XRPL Foundation <contact@xrplf.org> ${CHANGELOG_DATE}
|
||||
EOF
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
%if "%{?pkg_version}" == ""
|
||||
%{error:pkg_version must be defined}
|
||||
%endif
|
||||
|
||||
%if "%{?pkg_release}" == ""
|
||||
%{error:pkg_release must be defined}
|
||||
%endif
|
||||
|
||||
Name: xrpld
|
||||
Version: %{xrpld_version}
|
||||
Release: %{xrpld_release}%{?dist}
|
||||
Version: %{pkg_version}
|
||||
Release: %{pkg_release}%{?dist}
|
||||
Summary: XRP Ledger daemon
|
||||
|
||||
License: ISC
|
||||
@@ -11,6 +19,9 @@ BuildRequires: systemd-rpm-macros
|
||||
|
||||
%undefine _debugsource_packages
|
||||
%debug_package
|
||||
# Intentionally trade larger RPM artifacts for faster package validation.
|
||||
%global _binary_payload w.ufdio
|
||||
%global _find_debuginfo_dwz_opts %{nil}
|
||||
|
||||
%build_mtime_policy clamp_to_source_date_epoch
|
||||
|
||||
@@ -37,7 +48,10 @@ install -Dm0644 %{_sourcedir}/validators.txt %{buildroot}%{_sysconfdir}/%{
|
||||
install -Dm0644 %{_sourcedir}/xrpld.service %{buildroot}%{_unitdir}/xrpld.service
|
||||
install -Dm0644 %{_sourcedir}/xrpld.sysusers %{buildroot}%{_sysusersdir}/xrpld.conf
|
||||
install -Dm0644 %{_sourcedir}/xrpld.tmpfiles %{buildroot}%{_tmpfilesdir}/xrpld.conf
|
||||
install -Dm0644 %{_sourcedir}/50-xrpld.preset %{buildroot}%{_presetdir}/50-xrpld.preset
|
||||
install -Dm0644 /dev/null %{buildroot}%{_presetdir}/50-xrpld.preset
|
||||
cat >%{buildroot}%{_presetdir}/50-xrpld.preset <<'EOF'
|
||||
enable xrpld.service
|
||||
EOF
|
||||
|
||||
# Logrotate config
|
||||
install -Dm0644 %{_sourcedir}/xrpld.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
|
||||
@@ -62,7 +76,7 @@ systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
|
||||
%systemd_preun xrpld.service
|
||||
|
||||
%postun
|
||||
%systemd_postun_with_restart xrpld.service
|
||||
%systemd_postun xrpld.service
|
||||
|
||||
%files
|
||||
%license %{_docdir}/%{name}/LICENSE.md
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# /usr/lib/systemd/system-preset/50-xrpld.preset
|
||||
enable xrpld.service
|
||||
@@ -42,7 +42,8 @@ CanonicalTXSet::accountKey(AccountID const& account)
|
||||
void
|
||||
CanonicalTXSet::insert(std::shared_ptr<STTx const> txn)
|
||||
{
|
||||
Key key(accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID());
|
||||
Key const key(
|
||||
accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID());
|
||||
map_.emplace(key, std::move(txn));
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ Ledger::Ledger(
|
||||
}
|
||||
|
||||
{
|
||||
auto sle = std::make_shared<SLE>(keylet::fees());
|
||||
auto sle = std::make_shared<SLE>(keylet::feeSettings());
|
||||
// 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::fees()))
|
||||
if (auto const sle = read(keylet::feeSettings()))
|
||||
{
|
||||
bool oldFees = false;
|
||||
bool newFees = false;
|
||||
|
||||
@@ -70,7 +70,7 @@ isVaultPseudoAccountFrozen(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const mptIssuance = view.read(keylet::mptIssuance(mptShare.getMptID()));
|
||||
auto const mptIssuance = view.read(keylet::mptokenIssuance(mptShare.getMptID()));
|
||||
if (mptIssuance == nullptr)
|
||||
return false; // zero MPToken won't block deletion of MPTokenIssuance
|
||||
|
||||
|
||||
@@ -555,7 +555,7 @@ ammLPHolds(
|
||||
auto const currency = ammLPTCurrency(asset1, asset2);
|
||||
STAmount amount;
|
||||
|
||||
auto const sle = view.read(keylet::line(lpAccount, ammAccount, currency));
|
||||
auto const sle = view.read(keylet::trustLine(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::line(ammAccountID, issue.account, issue.currency));
|
||||
view.read(keylet::trustLine(ammAccountID, issue.account, issue.currency));
|
||||
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
|
||||
{
|
||||
STAmount amount = (*sle)[sfBalance];
|
||||
|
||||
@@ -346,9 +346,9 @@ verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, b
|
||||
}
|
||||
|
||||
TER
|
||||
verifyDepositPreauth(
|
||||
checkDepositPreauth(
|
||||
STTx const& tx,
|
||||
ApplyView& view,
|
||||
ReadView const& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
SLE::const_ref sleDst,
|
||||
@@ -360,9 +360,27 @@ verifyDepositPreauth(
|
||||
// 2. If src is deposit preauthorized by dst (either by account or by
|
||||
// credentials).
|
||||
|
||||
bool const credentialsPresent = tx.isFieldPresent(sfCredentialIDs);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (credentialsPresent)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
cleanupExpiredCredentials(STTx const& tx, ApplyView& view, beast::Journal j)
|
||||
{
|
||||
if (tx.isFieldPresent(sfCredentialIDs))
|
||||
{
|
||||
auto const foundExpired =
|
||||
credentials::removeExpired(view, tx.getFieldV256(sfCredentialIDs), j);
|
||||
@@ -372,20 +390,22 @@ verifyDepositPreauth(
|
||||
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
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace xrpl {
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
|
||||
{
|
||||
if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())))
|
||||
if (auto const sle = view.read(keylet::mptokenIssuance(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::mptIssuance(issuanceID));
|
||||
if (auto const sle = view.read(keylet::mptokenIssuance(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::mptIssuance(mptID));
|
||||
auto issuance = view.read(keylet::mptokenIssuance(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::mptIssuance(mptID));
|
||||
auto const mpt = view.peek(keylet::mptokenIssuance(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::mptIssuance(mptIssuanceID));
|
||||
auto const mpt = view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
if (!mpt || mpt->getAccountID(sfIssuer) == account)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -230,7 +230,7 @@ authorizeMPToken(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
|
||||
auto const sleMptIssuance = view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
if (!sleMptIssuance)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -290,6 +290,15 @@ 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
|
||||
@@ -308,7 +317,19 @@ requireAuth(
|
||||
AuthType authType,
|
||||
std::uint8_t depth)
|
||||
{
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
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 sleIssuance = view.read(mptID);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
@@ -319,7 +340,9 @@ requireAuth(
|
||||
if (mptIssuer == account) // Issuer won't have MPToken
|
||||
return tesSUCCESS;
|
||||
|
||||
bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault);
|
||||
// Post-fix330: exempt before the recursive underlying-asset auth check.
|
||||
if (fix330Enabled && isPseudoAccountExempt())
|
||||
return tesSUCCESS;
|
||||
|
||||
if (featureSAVEnabled)
|
||||
{
|
||||
@@ -382,13 +405,9 @@ requireAuth(
|
||||
// belong to someone who is explicitly authorized e.g. a vault owner.
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// Pre-fix330: exempt after domain/sleToken checks, preserving prior behavior.
|
||||
if (!fix330Enabled && isPseudoAccountExempt())
|
||||
return tesSUCCESS;
|
||||
|
||||
// mptoken must be authorized if issuance enabled requireAuth
|
||||
if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
|
||||
@@ -406,7 +425,7 @@ enforceMPTokenAuthorization(
|
||||
XRPAmount const& priorBalance, // for MPToken authorization
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
|
||||
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
if (!sleIssuance)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -530,7 +549,7 @@ canTransfer(
|
||||
WaiveMPTCanTransfer waive,
|
||||
std::uint8_t depth)
|
||||
{
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto const sleIssuance = view.read(mptID);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
@@ -584,7 +603,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::mptIssuance(mptIssue.getMptID()));
|
||||
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
if (!sleIssuance->isFlag(lsfMPTCanTrade))
|
||||
@@ -638,7 +657,7 @@ TER
|
||||
lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount, beast::Journal j)
|
||||
{
|
||||
auto const mptIssue = amount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
{ // LCOV_EXCL_START
|
||||
@@ -743,7 +762,7 @@ unlockEscrowMPT(
|
||||
|
||||
auto const& issuer = netAmount.getIssuer();
|
||||
auto const& mptIssue = netAmount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
{ // LCOV_EXCL_START
|
||||
@@ -927,7 +946,7 @@ checkCreateMPT(
|
||||
if (mptIssue.getIssuer() == holder)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const mptIssuanceID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto const mptIssuanceID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto const mptokenID = keylet::mptoken(mptIssuanceID.key, holder);
|
||||
if (!view.exists(mptokenID))
|
||||
{
|
||||
@@ -963,7 +982,7 @@ availableMPTAmount(SLE const& sleIssuance)
|
||||
std::int64_t
|
||||
availableMPTAmount(ReadView const& view, MPTID const& mptID)
|
||||
{
|
||||
auto const sle = view.read(keylet::mptIssuance(mptID));
|
||||
auto const sle = view.read(keylet::mptokenIssuance(mptID));
|
||||
if (!sle)
|
||||
Throw<std::runtime_error>(transHuman(tecINTERNAL));
|
||||
return availableMPTAmount(*sle);
|
||||
@@ -987,7 +1006,7 @@ issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue)
|
||||
{
|
||||
STAmount amount{issue};
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(issue));
|
||||
auto const sle = view.read(keylet::mptokenIssuance(issue));
|
||||
if (!sle)
|
||||
return amount;
|
||||
auto const available = availableMPTAmount(*sle);
|
||||
|
||||
@@ -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::nftpage(keylet::nftpageMin(owner), id);
|
||||
auto const last = keylet::nftpageMax(owner);
|
||||
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
|
||||
auto const last = keylet::nftokenPageMax(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::nftpage(keylet::nftpageMin(owner), id);
|
||||
auto const last = keylet::nftpageMax(owner);
|
||||
auto const first = keylet::nftokenPage(keylet::nftokenPageMin(owner), id);
|
||||
auto const last = keylet::nftokenPageMax(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::nftpageMin(owner);
|
||||
auto const first = keylet::nftpage(base, id);
|
||||
auto const last = keylet::nftpageMax(owner);
|
||||
auto const base = keylet::nftokenPageMin(owner);
|
||||
auto const first = keylet::nftokenPage(base, id);
|
||||
auto const last = keylet::nftokenPageMax(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::nftpage(base, tokenIDForNewPage));
|
||||
auto np = std::make_shared<SLE>(keylet::nftokenPage(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::nftoffer(offerIndexes[i])))
|
||||
if (auto const offer = view.peek(keylet::nftokenOffer(offerIndexes[i])))
|
||||
{
|
||||
if (deleteTokenOffer(view, offer))
|
||||
{
|
||||
@@ -651,11 +651,11 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner)
|
||||
{
|
||||
bool didRepair = false;
|
||||
|
||||
auto const last = keylet::nftpageMax(owner);
|
||||
auto const last = keylet::nftokenPageMax(owner);
|
||||
|
||||
SLE::pointer page = view.peek(Keylet(
|
||||
ltNFTOKEN_PAGE,
|
||||
view.succ(keylet::nftpageMin(owner).key, last.key.next()).value_or(last.key)));
|
||||
view.succ(keylet::nftokenPageMin(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::line(nftIssuer, amount.get<Issue>())))
|
||||
!view.read(keylet::trustLine(nftIssuer, amount.get<Issue>())))
|
||||
return tecNO_LINE;
|
||||
}
|
||||
else if (!view.exists(keylet::line(nftIssuer, amount.get<Issue>())))
|
||||
else if (!view.exists(keylet::trustLine(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::nftoffer(acctID, seqProxy.value());
|
||||
auto const offerID = keylet::nftokenOffer(acctID, seqProxy.value());
|
||||
|
||||
// Create the offer:
|
||||
{
|
||||
@@ -1020,7 +1020,7 @@ checkTrustlineAuthorized(
|
||||
|
||||
if (issuerAccount->isFlag(lsfRequireAuth))
|
||||
{
|
||||
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
|
||||
auto const trustLine = view.read(keylet::trustLine(id, issue.account, issue.currency));
|
||||
|
||||
if (!trustLine)
|
||||
{
|
||||
@@ -1070,7 +1070,7 @@ checkTrustlineDeepFrozen(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
|
||||
auto const trustLine = view.read(keylet::trustLine(id, issue.account, issue.currency));
|
||||
|
||||
if (!trustLine)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ creditLimit(
|
||||
{
|
||||
STAmount result(Issue{currency, account});
|
||||
|
||||
auto sleRippleState = view.read(keylet::line(account, issuer, currency));
|
||||
auto sleRippleState = view.read(keylet::trustLine(account, issuer, currency));
|
||||
|
||||
if (sleRippleState)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ creditBalance(
|
||||
{
|
||||
STAmount result(Issue{currency, account});
|
||||
|
||||
auto sleRippleState = view.read(keylet::line(account, issuer, currency));
|
||||
auto sleRippleState = view.read(keylet::trustLine(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::line(account, issuer, currency));
|
||||
auto const sle = view.read(keylet::trustLine(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::line(account, issuer, currency));
|
||||
sle = view.read(keylet::trustLine(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::line(account, issuer, currency));
|
||||
auto const sle = view.read(keylet::trustLine(account, issuer, currency));
|
||||
if (!sle)
|
||||
{
|
||||
return false;
|
||||
@@ -403,7 +403,7 @@ issueIOU(
|
||||
|
||||
bool const bSenderHigh = issue.account > account;
|
||||
|
||||
auto const index = keylet::line(issue.account, account, issue.currency);
|
||||
auto const index = keylet::trustLine(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::line(account, issue.account, issue.currency)))
|
||||
if (auto state = view.peek(keylet::trustLine(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::line(account, issue.account, issue.currency));
|
||||
auto const trustLine = view.read(keylet::trustLine(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::line(account, issue));
|
||||
auto const line = view.read(keylet::trustLine(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::line(srcId, dstId, currency);
|
||||
auto const index = keylet::trustLine(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::line(accountID, issue));
|
||||
auto const line = view.peek(keylet::trustLine(accountID, issue));
|
||||
if (!line)
|
||||
return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
|
||||
if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::kZero)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#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>
|
||||
@@ -34,14 +35,6 @@
|
||||
|
||||
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)
|
||||
@@ -164,6 +157,90 @@ 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
|
||||
@@ -179,7 +256,7 @@ getLineIfUsable(
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto sle = view.read(keylet::line(account, issuer, currency));
|
||||
auto sle = view.read(keylet::trustLine(account, issuer, currency));
|
||||
|
||||
if (!sle)
|
||||
{
|
||||
@@ -320,7 +397,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::mptIssuance(mptIssue.getMptID()));
|
||||
auto const issuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
|
||||
if (!issuance)
|
||||
{
|
||||
@@ -349,7 +426,8 @@ 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(featureSingleAssetVault) ||
|
||||
view.rules().enabled(featureConfidentialTransfer)))
|
||||
{
|
||||
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth);
|
||||
!isTesSuccess(err))
|
||||
@@ -357,7 +435,7 @@ accountHolds(
|
||||
}
|
||||
else if (zeroIfUnauthorized == AuthHandling::ZeroIfUnauthorized)
|
||||
{
|
||||
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
|
||||
// if auth is enabled on the issuance and mpt is not authorized,
|
||||
// clear amount
|
||||
@@ -568,7 +646,7 @@ directSendNoFeeIOU(
|
||||
XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::directSendNoFeeIOU : sender is not receiver");
|
||||
|
||||
bool const bSenderHigh = uSenderID > uReceiverID;
|
||||
auto const index = keylet::line(uSenderID, uReceiverID, currency);
|
||||
auto const index = keylet::trustLine(uSenderID, uReceiverID, currency);
|
||||
|
||||
XRPL_ASSERT(
|
||||
!isXRP(uSenderID) && uSenderID != noAccount(),
|
||||
@@ -776,7 +854,8 @@ 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))
|
||||
if (auto const ter = directSendNoFeeIOU(view, senderID, receiverID, amount, false, j);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because directSendNoFeeIOU took
|
||||
@@ -1066,7 +1145,7 @@ directSendNoFeeMPT(
|
||||
beast::Journal j)
|
||||
{
|
||||
// Do not check MPT authorization here - it must have been checked earlier
|
||||
auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
|
||||
auto const mptID = keylet::mptokenIssuance(saAmount.get<MPTIssue>().getMptID());
|
||||
auto const& issuer = saAmount.getIssuer();
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
@@ -1158,7 +1237,7 @@ directSendNoLimitMPT(
|
||||
// Safe to get MPT since directSendNoLimitMPT is only called by accountSendMPT
|
||||
auto const& issuer = saAmount.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
|
||||
auto const sle = view.read(keylet::mptokenIssuance(saAmount.get<MPTIssue>().getMptID()));
|
||||
if (!sle)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -1215,7 +1294,7 @@ directSendNoLimitMultiMPT(
|
||||
{
|
||||
auto const& issuer = mptIssue.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||
auto const sle = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
if (!sle)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
487
src/libxrpl/protocol/ConfidentialTransfer.cpp
Normal file
487
src/libxrpl/protocol/ConfidentialTransfer.cpp
Normal file
@@ -0,0 +1,487 @@
|
||||
#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
|
||||
@@ -218,7 +218,7 @@ amendments() noexcept
|
||||
}
|
||||
|
||||
Keylet const&
|
||||
fees() noexcept
|
||||
feeSettings() noexcept
|
||||
{
|
||||
static Keylet const kRet{ltFEE_SETTINGS, indexHash(LedgerNameSpace::FeeSettings)};
|
||||
return kRet;
|
||||
@@ -232,18 +232,18 @@ negativeUNL() noexcept
|
||||
}
|
||||
|
||||
Keylet
|
||||
BookT::operator()(Book const& b) const
|
||||
book(Book const& b)
|
||||
{
|
||||
return {ltDIR_NODE, getBookBase(b)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
line(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept
|
||||
trustLine(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::line : accounts must be
|
||||
// XRPL_ASSERT(id0 != id1, "xrpl::keylet::trustLine : 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
|
||||
NextT::operator()(Keylet const& k) const
|
||||
next(Keylet const& k)
|
||||
{
|
||||
XRPL_ASSERT(k.type == ltDIR_NODE, "xrpl::keylet::NextT::operator() : valid input type");
|
||||
XRPL_ASSERT(k.type == ltDIR_NODE, "xrpl::keylet::next : valid input type");
|
||||
return {ltDIR_NODE, getQualityNext(k.key)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
TicketT::operator()(AccountID const& id, std::uint32_t ticketSeq) const
|
||||
ticket(AccountID const& id, std::uint32_t ticketSeq)
|
||||
{
|
||||
return {ltTICKET, getTicketIndex(id, ticketSeq)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
TicketT::operator()(AccountID const& id, SeqProxy ticketSeq) const
|
||||
ticket(AccountID const& id, SeqProxy ticketSeq)
|
||||
{
|
||||
return {ltTICKET, getTicketIndex(id, ticketSeq)};
|
||||
}
|
||||
@@ -307,15 +307,15 @@ TicketT::operator()(AccountID const& id, SeqProxy ticketSeq) const
|
||||
// else. If we ever support multiple pages of signer lists, this would be the
|
||||
// keylet used to locate them.
|
||||
static Keylet
|
||||
signers(AccountID const& account, std::uint32_t page) noexcept
|
||||
signerList(AccountID const& account, std::uint32_t page) noexcept
|
||||
{
|
||||
return {ltSIGNER_LIST, indexHash(LedgerNameSpace::SignerList, account, page)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
signers(AccountID const& account) noexcept
|
||||
signerList(AccountID const& account) noexcept
|
||||
{
|
||||
return signers(account, 0);
|
||||
return signerList(account, 0);
|
||||
}
|
||||
|
||||
Keylet
|
||||
@@ -375,13 +375,13 @@ escrow(AccountID const& src, std::uint32_t seq) noexcept
|
||||
}
|
||||
|
||||
Keylet
|
||||
payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept
|
||||
payChannel(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept
|
||||
{
|
||||
return {ltPAYCHAN, indexHash(LedgerNameSpace::XRPPaymentChannel, src, dst, seq)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
nftpageMin(AccountID const& owner)
|
||||
nftokenPageMin(AccountID const& owner)
|
||||
{
|
||||
std::array<std::uint8_t, 32> buf{};
|
||||
std::memcpy(buf.data(), owner.data(), owner.size());
|
||||
@@ -389,7 +389,7 @@ nftpageMin(AccountID const& owner)
|
||||
}
|
||||
|
||||
Keylet
|
||||
nftpageMax(AccountID const& owner)
|
||||
nftokenPageMax(AccountID const& owner)
|
||||
{
|
||||
uint256 id = nft::kPageMask;
|
||||
std::memcpy(id.data(), owner.data(), owner.size());
|
||||
@@ -397,14 +397,14 @@ nftpageMax(AccountID const& owner)
|
||||
}
|
||||
|
||||
Keylet
|
||||
nftpage(Keylet const& k, uint256 const& token)
|
||||
nftokenPage(Keylet const& k, uint256 const& token)
|
||||
{
|
||||
XRPL_ASSERT(k.type == ltNFTOKEN_PAGE, "xrpl::keylet::nftpage : valid input type");
|
||||
XRPL_ASSERT(k.type == ltNFTOKEN_PAGE, "xrpl::keylet::nftokenPage : valid input type");
|
||||
return {ltNFTOKEN_PAGE, (k.key & ~nft::kPageMask) + (token & nft::kPageMask)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
nftoffer(AccountID const& owner, std::uint32_t seq)
|
||||
nftokenOffer(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
|
||||
mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept
|
||||
mptokenIssuance(std::uint32_t seq, AccountID const& issuer) noexcept
|
||||
{
|
||||
return mptIssuance(makeMptID(seq, issuer));
|
||||
return mptokenIssuance(makeMptID(seq, issuer));
|
||||
}
|
||||
|
||||
Keylet
|
||||
mptIssuance(MPTID const& issuanceID) noexcept
|
||||
mptokenIssuance(MPTID const& issuanceID) noexcept
|
||||
{
|
||||
return {ltMPTOKEN_ISSUANCE, indexHash(LedgerNameSpace::MPTokenIssuance, issuanceID)};
|
||||
}
|
||||
@@ -532,7 +532,7 @@ mptIssuance(MPTID const& issuanceID) noexcept
|
||||
Keylet
|
||||
mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept
|
||||
{
|
||||
return mptoken(mptIssuance(issuanceID).key, holder);
|
||||
return mptoken(mptokenIssuance(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
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -152,9 +153,11 @@ Permission::getPermissionName(std::uint32_t value) const
|
||||
return granular;
|
||||
|
||||
// not a granular permission, check if it maps to a transaction type
|
||||
auto const txType = permissionToTxType(value);
|
||||
if (auto const* item = TxFormats::getInstance().findByType(txType); item != nullptr)
|
||||
return item->getName();
|
||||
if (auto const txType = permissionToTxType(value))
|
||||
{
|
||||
if (auto const* item = TxFormats::getInstance().findByType(*txType); item != nullptr)
|
||||
return item->getName();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -231,7 +234,10 @@ Permission::isDelegable(std::uint32_t permissionValue, Rules const& rules) const
|
||||
}
|
||||
|
||||
auto const txType = permissionToTxType(permissionValue);
|
||||
auto const txIt = txDelegationMap_.find(txType);
|
||||
if (!txType)
|
||||
return false;
|
||||
|
||||
auto const txIt = txDelegationMap_.find(*txType);
|
||||
|
||||
// Tx-level permissions require the transaction type itself to be delegable, and
|
||||
// the corresponding amendment enabled.
|
||||
@@ -245,10 +251,14 @@ Permission::txToPermissionType(TxType const type)
|
||||
return static_cast<uint32_t>(type) + 1;
|
||||
}
|
||||
|
||||
TxType
|
||||
std::optional<TxType>
|
||||
Permission::permissionToTxType(uint32_t value)
|
||||
{
|
||||
XRPL_ASSERT(value > 0, "xrpl::Permission::permissionToTxType : value is greater than 0");
|
||||
// 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;
|
||||
|
||||
return static_cast<TxType>(value - 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#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>
|
||||
@@ -211,7 +212,7 @@ publicKeyType(Slice const& slice)
|
||||
if (slice[0] == 0xED)
|
||||
return KeyType::Ed25519;
|
||||
|
||||
if (slice[0] == 0x02 || slice[0] == 0x03)
|
||||
if (slice[0] == kEcCompressedPrefixEvenY || slice[0] == kEcCompressedPrefixOddY)
|
||||
return KeyType::Secp256k1;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ 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."),
|
||||
@@ -199,6 +200,7 @@ 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."),
|
||||
|
||||
@@ -356,6 +356,15 @@ 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)
|
||||
@@ -528,7 +537,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::kTicket(id, tSeqProx)))
|
||||
if (!view.exists(keylet::ticket(id, tSeqProx)))
|
||||
{
|
||||
JLOG(j.trace()) << "applyTransaction: ticket already used or never created "
|
||||
<< "a_seq=" << aSeq << " t_seq=" << tSeqProx;
|
||||
@@ -593,7 +602,7 @@ Transactor::ticketDelete(
|
||||
{
|
||||
// Delete the Ticket, adjust the account root ticket count, and
|
||||
// reduce the owner count.
|
||||
SLE::pointer const sleTicket = view.peek(keylet::kTicket(ticketIndex));
|
||||
SLE::pointer const sleTicket = view.peek(keylet::ticket(ticketIndex));
|
||||
if (!sleTicket)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -851,7 +860,7 @@ Transactor::checkMultiSign(
|
||||
beast::Journal const j)
|
||||
{
|
||||
// Get id's SignerList and Quorum.
|
||||
STLedgerEntry::const_pointer const sleAccountSigners = view.read(keylet::signers(id));
|
||||
STLedgerEntry::const_pointer const sleAccountSigners = view.read(keylet::signerList(id));
|
||||
// If the signer list doesn't exist the account is not multi-signing.
|
||||
if (!sleAccountSigners)
|
||||
{
|
||||
@@ -1031,7 +1040,7 @@ removeExpiredNFTokenOffers(
|
||||
|
||||
for (auto const& index : offers)
|
||||
{
|
||||
if (auto const offer = view.peek(keylet::nftoffer(index)))
|
||||
if (auto const offer = view.peek(keylet::nftokenOffer(index)))
|
||||
{
|
||||
nft::deleteTokenOffer(view, offer);
|
||||
if (++removed == kExpiredOfferRemoveLimit)
|
||||
|
||||
@@ -518,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::nftpageMin(accountID);
|
||||
Keylet const last = keylet::nftpageMax(accountID);
|
||||
Keylet const first = keylet::nftokenPageMin(accountID);
|
||||
Keylet const last = keylet::nftokenPageMax(accountID);
|
||||
|
||||
std::optional<uint256> key = view.succ(first.key, last.key.next());
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#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>
|
||||
@@ -21,11 +25,46 @@
|
||||
#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)
|
||||
{
|
||||
@@ -436,6 +475,16 @@ 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_)
|
||||
{
|
||||
@@ -466,6 +515,261 @@ 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,
|
||||
@@ -509,6 +813,15 @@ 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())
|
||||
@@ -524,6 +837,8 @@ ValidMPTTransfer::finalize(
|
||||
ReadView const& view,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
auto const fix330Enabled = view.rules().enabled(fixCleanup3_3_0);
|
||||
|
||||
if (hasPrivilege(tx, OverrideFreeze))
|
||||
return true;
|
||||
|
||||
@@ -551,7 +866,7 @@ ValidMPTTransfer::finalize(
|
||||
std::uint16_t senders = 0;
|
||||
std::uint16_t receivers = 0;
|
||||
bool invalidTransfer = false;
|
||||
auto const sleIssuance = view.read(keylet::mptIssuance(mptID));
|
||||
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptID));
|
||||
if (!sleIssuance)
|
||||
{
|
||||
continue;
|
||||
@@ -584,12 +899,29 @@ 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.
|
||||
// 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();
|
||||
if (!invalidTransfer &&
|
||||
(isFrozen(view, account, MPTIssue{mptID}) ||
|
||||
!isAuthorized(view, mptID, account, reqAuth)))
|
||||
(accountFrozen || !isAuthorized(view, mptID, account, reqAuth)))
|
||||
{
|
||||
invalidTransfer = true;
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ ValidVault::deltaAssets(AccountID const& id) const
|
||||
{
|
||||
if (isXRP(issue))
|
||||
return lookup(keylet::account(id).key);
|
||||
auto result = lookup(keylet::line(id, issue).key);
|
||||
auto result = lookup(keylet::trustLine(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::mptIssuance(afterVault.shareMPTID).key);
|
||||
return deltas_.find(keylet::mptokenIssuance(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::mptIssuance(afterVault.shareMPTID));
|
||||
auto const sleShares = view.read(keylet::mptokenIssuance(afterVault.shareMPTID));
|
||||
|
||||
return sleShares ? std::optional<Shares>(Shares::make(*sleShares)) : std::nullopt;
|
||||
}();
|
||||
|
||||
@@ -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::line(*prev, cur, issue.currency));
|
||||
auto sle = view.read(keylet::trustLine(*prev, cur, issue.currency));
|
||||
if (!sle)
|
||||
return terNO_LINE;
|
||||
if (sle->isFlag((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
|
||||
|
||||
@@ -344,7 +344,7 @@ DirectIPaymentStep::quality(ReadView const& sb, QualityDirection qDir) const
|
||||
if (src_ == dst_)
|
||||
return QUALITY_ONE;
|
||||
|
||||
auto const sle = sb.read(keylet::line(dst_, src_, currency_));
|
||||
auto const sle = sb.read(keylet::trustLine(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::line(src_, dst_, currency_));
|
||||
auto const sleLine = ctx.view.read(keylet::trustLine(src_, dst_, currency_));
|
||||
if (!sleLine)
|
||||
{
|
||||
JLOG(j_.trace()) << "DirectStepI: No credit line. " << *this;
|
||||
|
||||
@@ -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::mptIssuance(mptIssue_)))
|
||||
if (auto const sle = sb.read(keylet::mptokenIssuance(mptIssue_)))
|
||||
{
|
||||
// If issuer is the source account, and it is direct payment then
|
||||
// MPTEndpointStep is the only step. Provide available maxFlow.
|
||||
|
||||
@@ -203,7 +203,7 @@ private:
|
||||
{
|
||||
return ctx.strandDeliver.visit(
|
||||
[&](Issue const& issue) {
|
||||
if (!ctx.view.exists(keylet::line(acc, issue)))
|
||||
if (!ctx.view.exists(keylet::trustLine(acc, issue)))
|
||||
return -1;
|
||||
return 0;
|
||||
},
|
||||
|
||||
@@ -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::nftpageMin(account);
|
||||
Keylet const last = keylet::nftpageMax(account);
|
||||
Keylet const first = keylet::nftokenPageMin(account);
|
||||
Keylet const last = keylet::nftokenPageMax(account);
|
||||
|
||||
auto const cp = ctx.view.read(
|
||||
Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key)));
|
||||
|
||||
@@ -194,30 +194,27 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
//
|
||||
// Clawback
|
||||
//
|
||||
if (ctx.view.rules().enabled(featureClawback))
|
||||
if (uSetFlag == asfAllowTrustLineClawback)
|
||||
{
|
||||
if (uSetFlag == asfAllowTrustLineClawback)
|
||||
if (sle->isFlag(lsfNoFreeze))
|
||||
{
|
||||
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;
|
||||
}
|
||||
JLOG(ctx.j.trace()) << "Can't set Clawback if NoFreeze is set";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
else if (uSetFlag == asfNoFreeze)
|
||||
|
||||
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
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))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Can't set NoFreeze if clawback is enabled";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +312,7 @@ AccountSet::doApply()
|
||||
return tecNEED_MASTER_KEY;
|
||||
}
|
||||
|
||||
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signers(accountID_))))
|
||||
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signerList(accountID_))))
|
||||
{
|
||||
// Account has no regular key or multi-signer signer list.
|
||||
return tecNO_ALTERNATIVE_KEY;
|
||||
@@ -576,7 +573,7 @@ AccountSet::doApply()
|
||||
}
|
||||
|
||||
// Set flag for clawback
|
||||
if (ctx_.view().rules().enabled(featureClawback) && uSetFlag == asfAllowTrustLineClawback)
|
||||
if (uSetFlag == asfAllowTrustLineClawback)
|
||||
{
|
||||
JLOG(j_.trace()) << "set allow clawback";
|
||||
uFlagsOut |= lsfAllowTrustLineClawback;
|
||||
|
||||
@@ -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::signers(accountID_)))
|
||||
if (sle->isFlag(lsfDisableMaster) && !view().peek(keylet::signerList(accountID_)))
|
||||
return tecNO_ALTERNATIVE_KEY;
|
||||
|
||||
sle->makeFieldAbsent(sfRegularKey);
|
||||
|
||||
@@ -232,7 +232,7 @@ SignerListSet::removeFromLedger(
|
||||
{
|
||||
auto const accountKeylet = keylet::account(account);
|
||||
auto const ownerDirKeylet = keylet::ownerDir(account);
|
||||
auto const signerListKeylet = keylet::signers(account);
|
||||
auto const signerListKeylet = keylet::signerList(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::signers(accountID_);
|
||||
auto const signerListKeylet = keylet::signerList(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::signers(accountID_);
|
||||
auto const signerListKeylet = keylet::signerList(accountID_);
|
||||
return removeSignersFromLedger(
|
||||
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
|
||||
}
|
||||
|
||||
@@ -759,7 +759,7 @@ getSignersListAndQuorum(ReadView const& view, SLE const& sleBridge, beast::Journ
|
||||
return {r, q, tecINTERNAL};
|
||||
}
|
||||
|
||||
auto const sleS = view.read(keylet::signers(sleBridge[sfAccount]));
|
||||
auto const sleS = view.read(keylet::signerList(sleBridge[sfAccount]));
|
||||
if (!sleS)
|
||||
{
|
||||
return {r, q, tecXCHAIN_NO_SIGNERS_LIST};
|
||||
|
||||
@@ -192,7 +192,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
[&](Issue const& issue) -> TER {
|
||||
Currency const currency{issue.currency};
|
||||
auto const sleTrustLine =
|
||||
ctx.view.read(keylet::line(dstId, issuerId, currency));
|
||||
ctx.view.read(keylet::trustLine(dstId, issuerId, currency));
|
||||
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
|
||||
if (!sleIssuer)
|
||||
@@ -411,7 +411,7 @@ CheckCash::doApply()
|
||||
// If a trust line does not exist yet create one.
|
||||
Issue const& trustLineIssue = issue;
|
||||
AccountID const truster = deliverIssuer == accountID_ ? srcId : accountID_;
|
||||
trustLineKey = keylet::line(truster, trustLineIssue);
|
||||
trustLineKey = keylet::trustLine(truster, trustLineIssue);
|
||||
destLow = deliverIssuer > accountID_;
|
||||
|
||||
if (!psb.exists(*trustLineKey))
|
||||
|
||||
@@ -127,7 +127,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
auto const sleTrust =
|
||||
ctx.view.read(keylet::line(srcId, issuerId, issue.currency));
|
||||
ctx.view.read(keylet::trustLine(srcId, issuerId, issue.currency));
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag((issuerId > srcId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
@@ -139,7 +139,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Check if dst froze the line.
|
||||
auto const sleTrust =
|
||||
ctx.view.read(keylet::line(issuerId, dstId, issue.currency));
|
||||
ctx.view.read(keylet::trustLine(issuerId, dstId, issue.currency));
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag((dstId > issuerId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
|
||||
@@ -136,7 +136,7 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
|
||||
!sleIssuer->isFlag(lsfNoFreeze);
|
||||
},
|
||||
[&](MPTIssue const& issue) {
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(issue.getMptID()));
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(issue.getMptID()));
|
||||
|
||||
return sleIssuance && sleIssuance->isFlag(lsfMPTCanClawback) &&
|
||||
sleIssuance->getAccountID(sfIssuer) == ctx.tx[sfAccount];
|
||||
|
||||
@@ -212,7 +212,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
auto clawbackDisabled = [&](Asset const& asset) -> TER {
|
||||
return asset.visit(
|
||||
[&](MPTIssue const& issue) -> TER {
|
||||
auto const sle = ctx.view.read(keylet::mptIssuance(issue.getMptID()));
|
||||
auto const sle = ctx.view.read(keylet::mptokenIssuance(issue.getMptID()));
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
if (sle->isFlag(lsfMPTCanClawback))
|
||||
@@ -260,7 +260,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
|
||||
|
||||
// LP Token already exists. (should not happen)
|
||||
auto const lptIss = ammLPTIssue(amount.asset(), amount2.asset(), accountId);
|
||||
if (sb.read(keylet::line(accountId, lptIss)))
|
||||
if (sb.read(keylet::trustLine(accountId, lptIss)))
|
||||
{
|
||||
JLOG(j.error()) << "AMM Instance: LP Token already exists.";
|
||||
return {tecDUPLICATE, false};
|
||||
@@ -330,7 +330,8 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
|
||||
// Set AMM flag on AMM trustline
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
SLE::pointer const sleRippleState = sb.peek(keylet::line(accountId, issue));
|
||||
SLE::pointer const sleRippleState =
|
||||
sb.peek(keylet::trustLine(accountId, issue));
|
||||
if (!sleRippleState)
|
||||
{
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -364,7 +365,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
|
||||
<< lpTokens << " " << amount << " " << amount2;
|
||||
auto addOrderBook = [&](Asset const& assetIn, Asset const& assetOut, std::uint64_t uRate) {
|
||||
Book const book{assetIn, assetOut, std::nullopt};
|
||||
auto const dir = keylet::quality(keylet::kBook(book), uRate);
|
||||
auto const dir = keylet::quality(keylet::book(book), uRate);
|
||||
if (auto const bookExisted = static_cast<bool>(sb.read(dir)); !bookExisted)
|
||||
ctx.registry.get().getOrderBookDB().addOrderBook(book);
|
||||
};
|
||||
|
||||
@@ -233,7 +233,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
auto const lpIssue = (*ammSle)[sfLPTokenBalance].get<Issue>();
|
||||
// Adjust the reserve if LP doesn't have LPToken trustline
|
||||
auto const sle =
|
||||
ctx.view.read(keylet::line(accountID, lpIssue.account, lpIssue.currency));
|
||||
ctx.view.read(keylet::trustLine(accountID, lpIssue.account, lpIssue.currency));
|
||||
if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
|
||||
return TER(tesSUCCESS);
|
||||
if (sle)
|
||||
@@ -251,7 +251,36 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
: tecUNFUNDED_AMM;
|
||||
};
|
||||
|
||||
if (ctx.view.rules().enabled(featureAMMClawback))
|
||||
auto const amount = ctx.tx[~sfAmount];
|
||||
auto const amount2 = ctx.tx[~sfAmount2];
|
||||
auto const ammAccountID = ammSle->getAccountID(sfAccount);
|
||||
|
||||
if (ctx.view.rules().enabled(fixCleanup3_3_0))
|
||||
{
|
||||
// Unified deposit freeze check for both pool assets.
|
||||
// AMMDeposit is not allowed if either asset is frozen.
|
||||
auto checkAsset = [&](Asset const& asset) -> TER {
|
||||
if (auto const ter = requireAuth(ctx.view, asset, accountID, AuthType::WeakAuth))
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Deposit: account is not authorized, " << asset;
|
||||
return ter;
|
||||
}
|
||||
if (auto const ter = checkDepositFreeze(ctx.view, accountID, ammAccountID, asset))
|
||||
{
|
||||
JLOG(ctx.j.debug())
|
||||
<< "AMM Deposit: frozen, " << to_string(accountID) << " " << to_string(asset);
|
||||
return ter;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
};
|
||||
|
||||
if (auto const ter = checkAsset(ctx.tx[sfAsset]))
|
||||
return ter;
|
||||
|
||||
if (auto const ter = checkAsset(ctx.tx[sfAsset2]))
|
||||
return ter;
|
||||
}
|
||||
else if (ctx.view.rules().enabled(featureAMMClawback))
|
||||
{
|
||||
// Check if either of the assets is frozen, AMMDeposit is not allowed
|
||||
// if either asset is frozen
|
||||
@@ -283,10 +312,6 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
return ter;
|
||||
}
|
||||
|
||||
auto const amount = ctx.tx[~sfAmount];
|
||||
auto const amount2 = ctx.tx[~sfAmount2];
|
||||
auto const ammAccountID = ammSle->getAccountID(sfAccount);
|
||||
|
||||
auto checkAmount = [&](std::optional<STAmount> const& amount, bool checkBalance) -> TER {
|
||||
if (amount)
|
||||
{
|
||||
@@ -301,21 +326,26 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
return ter;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
// AMM account or currency frozen
|
||||
if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
if (!ctx.view.rules().enabled(fixCleanup3_3_0))
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Deposit: AMM account or currency is frozen or locked, "
|
||||
<< to_string(accountID);
|
||||
return ter;
|
||||
}
|
||||
// Account frozen
|
||||
if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen or locked, "
|
||||
<< to_string(accountID) << " " << to_string(amount->asset());
|
||||
return ter;
|
||||
// AMM account or currency frozen
|
||||
if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.debug())
|
||||
<< "AMM Deposit: AMM account or currency is frozen or locked, "
|
||||
<< to_string(accountID);
|
||||
return ter;
|
||||
}
|
||||
// Account frozen
|
||||
if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.debug())
|
||||
<< "AMM Deposit: account is frozen or locked, " << to_string(accountID)
|
||||
<< " " << to_string(amount->asset());
|
||||
return ter;
|
||||
}
|
||||
}
|
||||
if (checkBalance)
|
||||
{
|
||||
@@ -532,7 +562,8 @@ AMMDeposit::deposit(
|
||||
{
|
||||
auto const& lpIssue = lpTokensDeposit.get<Issue>();
|
||||
// Adjust the reserve if LP doesn't have LPToken trustline
|
||||
auto const sle = view.read(keylet::line(accountID_, lpIssue.account, lpIssue.currency));
|
||||
auto const sle =
|
||||
view.read(keylet::trustLine(accountID_, lpIssue.account, lpIssue.currency));
|
||||
if (xrpLiquid(view, accountID_, !sle, j_) >= depositAmount)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -238,21 +238,36 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
<< "AMM Withdraw: account is not authorized, " << amount->asset();
|
||||
return ter;
|
||||
}
|
||||
// AMM account or currency frozen
|
||||
if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
if (ctx.view.rules().enabled(fixCleanup3_3_0))
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Withdraw: AMM account or currency is frozen or locked, "
|
||||
<< to_string(accountID);
|
||||
return ter;
|
||||
if (auto const ret = checkWithdrawFreeze(
|
||||
ctx.view, ammAccountID, accountID, accountID, amount->asset()))
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Withdraw: frozen, " << to_string(accountID) << " "
|
||||
<< to_string(amount->asset());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// Account frozen
|
||||
if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
else
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen or locked, "
|
||||
<< to_string(accountID) << " " << to_string(amount->asset());
|
||||
return ter;
|
||||
// AMM account or currency frozen
|
||||
if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.debug())
|
||||
<< "AMM Withdraw: AMM account or currency is frozen or locked, "
|
||||
<< to_string(accountID);
|
||||
return ter;
|
||||
}
|
||||
// Account frozen
|
||||
if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.debug())
|
||||
<< "AMM Withdraw: account is frozen or locked, " << to_string(accountID)
|
||||
<< " " << to_string(amount->asset());
|
||||
return ter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tesSUCCESS;
|
||||
@@ -302,6 +317,25 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
FreezeHandling
|
||||
AMMWithdraw::issuerFreezeHandling() const
|
||||
{
|
||||
// When the withdrawer is the issuer of a pool asset, the issuer can
|
||||
// always receive their own token — even when the pool is frozen.
|
||||
// Use IgnoreFreeze so ammHolds returns real balances instead of zero.
|
||||
if (!ctx_.view().rules().enabled(fixCleanup3_3_0))
|
||||
return FreezeHandling::ZeroIfFrozen;
|
||||
|
||||
auto const asset1 = Asset{ctx_.tx[sfAsset]};
|
||||
auto const asset2 = Asset{ctx_.tx[sfAsset2]};
|
||||
if (!asset1.native() && accountID_ == asset1.getIssuer())
|
||||
return FreezeHandling::IgnoreFreeze;
|
||||
if (!asset2.native() && accountID_ == asset2.getIssuer())
|
||||
return FreezeHandling::IgnoreFreeze;
|
||||
|
||||
return FreezeHandling::ZeroIfFrozen;
|
||||
}
|
||||
|
||||
std::pair<TER, bool>
|
||||
AMMWithdraw::applyGuts(Sandbox& sb)
|
||||
{
|
||||
@@ -329,18 +363,19 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
||||
|
||||
auto const tfee = getTradingFee(ctx_.view(), *ammSle, accountID_);
|
||||
|
||||
auto const freezeHandling = issuerFreezeHandling();
|
||||
|
||||
auto const expected = ammHolds(
|
||||
sb,
|
||||
*ammSle,
|
||||
amount ? amount->asset() : std::optional<Asset>{},
|
||||
amount2 ? amount2->asset() : std::optional<Asset>{},
|
||||
FreezeHandling::ZeroIfFrozen,
|
||||
freezeHandling,
|
||||
AuthHandling::ZeroIfUnauthorized,
|
||||
ctx_.journal);
|
||||
if (!expected)
|
||||
return {expected.error(), false};
|
||||
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
|
||||
|
||||
auto const subTxType = ctx_.tx.getFlags() & tfWithdrawSubTx;
|
||||
|
||||
auto const [result, newLPTokenBalance] = [&,
|
||||
@@ -469,7 +504,7 @@ AMMWithdraw::withdraw(
|
||||
lpTokensAMMBalance,
|
||||
lpTokensWithdraw,
|
||||
tfee,
|
||||
FreezeHandling::ZeroIfFrozen,
|
||||
issuerFreezeHandling(),
|
||||
AuthHandling::ZeroIfUnauthorized,
|
||||
isWithdrawAll(ctx_.tx),
|
||||
preFeeBalance_,
|
||||
@@ -615,8 +650,8 @@ AMMWithdraw::withdraw(
|
||||
bool const isIssue = asset.holds<Issue>();
|
||||
bool const assetNotExists = [&] {
|
||||
if (isIssue)
|
||||
return !view.exists(keylet::line(account, asset.get<Issue>()));
|
||||
auto const issuanceKey = keylet::mptIssuance(asset.get<MPTIssue>());
|
||||
return !view.exists(keylet::trustLine(account, asset.get<Issue>()));
|
||||
auto const issuanceKey = keylet::mptokenIssuance(asset.get<MPTIssue>());
|
||||
mptokenKey = keylet::mptoken(issuanceKey.key, account);
|
||||
if (!view.exists(*mptokenKey))
|
||||
return true;
|
||||
@@ -756,7 +791,7 @@ AMMWithdraw::equalWithdrawTokens(
|
||||
lpTokens,
|
||||
lpTokensWithdraw,
|
||||
tfee,
|
||||
FreezeHandling::ZeroIfFrozen,
|
||||
issuerFreezeHandling(),
|
||||
AuthHandling::ZeroIfUnauthorized,
|
||||
isWithdrawAll(ctx_.tx),
|
||||
preFeeBalance_,
|
||||
|
||||
@@ -284,7 +284,7 @@ OfferCreate::checkAcceptAsset(
|
||||
auto const& issuer = issue.getIssuer();
|
||||
if (issuerAccount->isFlag(lsfRequireAuth))
|
||||
{
|
||||
auto const trustLine = view.read(keylet::line(id, issuer, issue.currency));
|
||||
auto const trustLine = view.read(keylet::trustLine(id, issuer, issue.currency));
|
||||
|
||||
if (!trustLine)
|
||||
{
|
||||
@@ -308,7 +308,7 @@ OfferCreate::checkAcceptAsset(
|
||||
}
|
||||
}
|
||||
|
||||
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
|
||||
auto const trustLine = view.read(keylet::trustLine(id, issue.account, issue.currency));
|
||||
|
||||
if (!trustLine)
|
||||
{
|
||||
@@ -575,7 +575,7 @@ OfferCreate::applyHybrid(
|
||||
// if offer is hybrid, need to also place into open offer dir
|
||||
Book const book{saTakerPays.asset(), saTakerGets.asset(), std::nullopt};
|
||||
|
||||
auto dir = keylet::quality(keylet::kBook(book), openRate);
|
||||
auto dir = keylet::quality(keylet::book(book), openRate);
|
||||
bool const bookExists = sb.exists(dir);
|
||||
|
||||
auto const bookNode = sb.dirAppend(dir, offerKey, [&](SLE::ref sle) {
|
||||
@@ -887,7 +887,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
// Hybrid domain offer - BookDirectory points to domain directory,
|
||||
// and AdditionalBooks field stores one entry that points to the open
|
||||
// directory
|
||||
auto dir = keylet::quality(keylet::kBook(book), uRate);
|
||||
auto dir = keylet::quality(keylet::book(book), uRate);
|
||||
bool const bookExisted = static_cast<bool>(sb.peek(dir));
|
||||
|
||||
auto setBookDir = [&](SLE::ref sle, std::optional<uint256> const& maybeDomain) {
|
||||
|
||||
@@ -72,7 +72,7 @@ escrowCancelPreclaimHelper<MPTIssue>(
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -208,7 +208,7 @@ escrowCreatePreclaimHelper<Issue>(
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = ctx.view.read(keylet::line(account, issuer, issue.currency));
|
||||
auto const sleRippleState = ctx.view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
@@ -271,7 +271,7 @@ escrowCreatePreclaimHelper<MPTIssue>(
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -172,7 +172,7 @@ escrowFinishPreclaimHelper<MPTIssue>(
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -218,7 +218,7 @@ preclaimHelper<MPTIssue>(
|
||||
SLE const& sleIssuer,
|
||||
STAmount const& clawAmount)
|
||||
{
|
||||
auto const issuanceKey = keylet::mptIssuance(clawAmount.get<MPTIssue>().getMptID());
|
||||
auto const issuanceKey = keylet::mptokenIssuance(clawAmount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
@@ -245,7 +245,7 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
|
||||
auto const brokerID = *findBrokerID;
|
||||
auto const amount = tx[~sfAmount];
|
||||
|
||||
auto const sleBroker = ctx.view.read(keylet::loanbroker(brokerID));
|
||||
auto const sleBroker = ctx.view.read(keylet::loanBroker(brokerID));
|
||||
if (!sleBroker)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
|
||||
@@ -344,7 +344,7 @@ LoanBrokerCoverClawback::doApply()
|
||||
auto const brokerID = *findBrokerID;
|
||||
auto const amount = tx[~sfAmount];
|
||||
|
||||
auto sleBroker = view().peek(keylet::loanbroker(brokerID));
|
||||
auto sleBroker = view().peek(keylet::loanBroker(brokerID));
|
||||
if (!sleBroker)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -43,13 +43,15 @@ LoanBrokerCoverDeposit::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0);
|
||||
auto const fix330Enabled = ctx.view.rules().enabled(fixCleanup3_3_0);
|
||||
auto const& tx = ctx.tx;
|
||||
|
||||
auto const account = tx[sfAccount];
|
||||
auto const brokerID = tx[sfLoanBrokerID];
|
||||
auto const amount = tx[sfAmount];
|
||||
|
||||
auto const sleBroker = ctx.view.read(keylet::loanbroker(brokerID));
|
||||
auto const sleBroker = ctx.view.read(keylet::loanBroker(brokerID));
|
||||
if (!sleBroker)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
|
||||
@@ -77,12 +79,21 @@ LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// Cannot transfer a non-transferable Asset
|
||||
if (auto const ret = canTransfer(ctx.view, vaultAsset, account, pseudoAccountID))
|
||||
return ret;
|
||||
// Cannot transfer a frozen Asset
|
||||
if (auto const ret = checkFrozen(ctx.view, account, vaultAsset))
|
||||
return ret;
|
||||
// Pseudo-account cannot receive if asset is deep frozen
|
||||
if (auto const ret = checkDeepFrozen(ctx.view, pseudoAccountID, vaultAsset))
|
||||
return ret;
|
||||
|
||||
if (fix330Enabled)
|
||||
{
|
||||
if (auto const ret = checkDepositFreeze(ctx.view, account, pseudoAccountID, vaultAsset))
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto const ret = checkFrozen(ctx.view, account, vaultAsset))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = checkDeepFrozen(ctx.view, pseudoAccountID, vaultAsset))
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Cannot transfer unauthorized asset
|
||||
if (auto const ret = requireAuth(ctx.view, vaultAsset, account, AuthType::StrongAuth))
|
||||
return ret;
|
||||
@@ -92,7 +103,6 @@ LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// `sfCoverAvailable +=` could credit the broker more than the depositor paid Computing it
|
||||
// here in preclaim lets us reject sub-cover-scale dust early with tecPRECISION_LOSS instead of
|
||||
// failing only in doApply.
|
||||
bool const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0);
|
||||
auto const roundedAmount = [&]() -> STAmount {
|
||||
if (!fix320Enabled)
|
||||
return tx[sfAmount];
|
||||
@@ -129,7 +139,7 @@ LoanBrokerCoverDeposit::doApply()
|
||||
auto const& tx = ctx_.tx;
|
||||
|
||||
auto const brokerID = tx[sfLoanBrokerID];
|
||||
auto broker = view().peek(keylet::loanbroker(brokerID));
|
||||
auto broker = view().peek(keylet::loanBroker(brokerID));
|
||||
if (!broker)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ LoanBrokerCoverWithdraw::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0);
|
||||
auto const fix330Enabled = ctx.view.rules().enabled(fixCleanup3_3_0);
|
||||
auto const& tx = ctx.tx;
|
||||
|
||||
auto const account = tx[sfAccount];
|
||||
@@ -68,7 +70,7 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
JLOG(ctx.j.warn()) << "Trying to withdraw into a pseudo-account.";
|
||||
return tecPSEUDO_ACCOUNT;
|
||||
}
|
||||
auto const sleBroker = ctx.view.read(keylet::loanbroker(brokerID));
|
||||
auto const sleBroker = ctx.view.read(keylet::loanBroker(brokerID));
|
||||
if (!sleBroker)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
|
||||
@@ -103,8 +105,7 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
// the lsfMPTCanTransfer flag check, so an issuer cannot trap a broker's
|
||||
// first-loss capital. Other transferability checks (IOU NoRipple, freeze,
|
||||
// requireAuth) still apply.
|
||||
auto const waive = ctx.view.rules().enabled(fixCleanup3_2_0) ? WaiveMPTCanTransfer::Yes
|
||||
: WaiveMPTCanTransfer::No;
|
||||
auto const waive = fix320Enabled ? WaiveMPTCanTransfer::Yes : WaiveMPTCanTransfer::No;
|
||||
if (auto const ret = canTransfer(ctx.view, vaultAsset, pseudoAccountID, dstAcct, waive))
|
||||
return ret;
|
||||
|
||||
@@ -125,22 +126,30 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
if (auto const ter = requireAuth(ctx.view, vaultAsset, dstAcct, authType))
|
||||
return ter;
|
||||
|
||||
// Check for freezes, unless sending directly to the issuer
|
||||
if (dstAcct != vaultAsset.getIssuer())
|
||||
if (fix330Enabled)
|
||||
{
|
||||
// Cannot send a frozen Asset
|
||||
if (auto const ret = checkFrozen(ctx.view, pseudoAccountID, vaultAsset))
|
||||
return ret;
|
||||
// Destination account cannot receive if asset is deep frozen
|
||||
if (auto const ret = checkDeepFrozen(ctx.view, dstAcct, vaultAsset))
|
||||
if (auto const ret =
|
||||
checkWithdrawFreeze(ctx.view, pseudoAccountID, account, dstAcct, vaultAsset))
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{ // Check for freezes, unless sending directly to the issuer
|
||||
if (dstAcct != vaultAsset.getIssuer())
|
||||
{
|
||||
// Cannot send a frozen Asset
|
||||
if (auto const ret = checkFrozen(ctx.view, pseudoAccountID, vaultAsset))
|
||||
return ret;
|
||||
// Destination account cannot receive if asset is deep frozen
|
||||
if (auto const ret = checkDeepFrozen(ctx.view, dstAcct, vaultAsset))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
auto const coverAvail = sleBroker->at(sfCoverAvailable);
|
||||
// Cover Rate is in 1/10 bips units
|
||||
auto const currentDebtTotal = sleBroker->at(sfDebtTotal);
|
||||
auto const minimumCover = [&]() {
|
||||
if (ctx.view.rules().enabled(fixCleanup3_2_0))
|
||||
if (fix320Enabled)
|
||||
{
|
||||
return minimumBrokerCover(
|
||||
currentDebtTotal, TenthBips32{sleBroker->at(sfCoverRateMinimum)}, vault);
|
||||
@@ -159,11 +168,15 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
if ((coverAvail - amount) < minimumCover)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
auto const freezeHandling = fix330Enabled && dstAcct == vaultAsset.getIssuer()
|
||||
? FreezeHandling::IgnoreFreeze
|
||||
: FreezeHandling::ZeroIfFrozen;
|
||||
|
||||
if (accountHolds(
|
||||
ctx.view,
|
||||
pseudoAccountID,
|
||||
vaultAsset,
|
||||
FreezeHandling::ZeroIfFrozen,
|
||||
freezeHandling,
|
||||
AuthHandling::ZeroIfUnauthorized,
|
||||
ctx.j) < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
@@ -180,7 +193,7 @@ LoanBrokerCoverWithdraw::doApply()
|
||||
auto const amount = tx[sfAmount];
|
||||
auto const dstAcct = tx[~sfDestination].value_or(accountID_);
|
||||
|
||||
auto broker = view().peek(keylet::loanbroker(brokerID));
|
||||
auto broker = view().peek(keylet::loanBroker(brokerID));
|
||||
if (!broker)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ LoanBrokerDelete::preclaim(PreclaimContext const& ctx)
|
||||
auto const account = tx[sfAccount];
|
||||
auto const brokerID = tx[sfLoanBrokerID];
|
||||
|
||||
auto const sleBroker = ctx.view.read(keylet::loanbroker(brokerID));
|
||||
auto const sleBroker = ctx.view.read(keylet::loanBroker(brokerID));
|
||||
if (!sleBroker)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
|
||||
@@ -129,7 +129,7 @@ LoanBrokerDelete::doApply()
|
||||
auto const brokerID = tx[sfLoanBrokerID];
|
||||
|
||||
// Delete the loan broker
|
||||
auto broker = view().peek(keylet::loanbroker(brokerID));
|
||||
auto broker = view().peek(keylet::loanBroker(brokerID));
|
||||
if (!broker)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultID = broker->at(sfVaultID);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user