mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-31 09:02:34 +00:00
Compare commits
32 Commits
ximinez/fi
...
mvadari/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bc460951c | ||
|
|
d1dae53097 | ||
|
|
733dd51720 | ||
|
|
9d67db1843 | ||
|
|
29bb59698f | ||
|
|
3b2dcefb81 | ||
|
|
4e92ec2daf | ||
|
|
6afcf51dee | ||
|
|
7e62ecb63b | ||
|
|
ad2c359f21 | ||
|
|
96d4a69a37 | ||
|
|
14fbdd5a1c | ||
|
|
3c5e87888e | ||
|
|
8e218a6285 | ||
|
|
8a1adc4ec9 | ||
|
|
c9db124015 | ||
|
|
7056b43c5e | ||
|
|
ee370fefee | ||
|
|
e68d74cda4 | ||
|
|
89a3a6c379 | ||
|
|
f3a6d17b5b | ||
|
|
45531b1eaf | ||
|
|
fa8de2b47f | ||
|
|
6fcda8802e | ||
|
|
0cf42b150c | ||
|
|
74b010a905 | ||
|
|
265deccabe | ||
|
|
ea1146d413 | ||
|
|
5fa9bb53a8 | ||
|
|
d9bf263543 | ||
|
|
1f73da466c | ||
|
|
e9eb5e53c9 |
110
.clang-tidy
110
.clang-tidy
@@ -1,6 +1,4 @@
|
||||
---
|
||||
# This entire group of checks was applied to all cpp files but not all header files.
|
||||
# ---
|
||||
Checks: "-*,
|
||||
bugprone-argument-comment,
|
||||
bugprone-assert-side-effect,
|
||||
@@ -10,26 +8,26 @@ Checks: "-*,
|
||||
bugprone-chained-comparison,
|
||||
bugprone-compare-pointer-to-member-virtual-function,
|
||||
bugprone-copy-constructor-init,
|
||||
# bugprone-crtp-constructor-accessibility, # has issues
|
||||
bugprone-crtp-constructor-accessibility,
|
||||
bugprone-dangling-handle,
|
||||
bugprone-dynamic-static-initializers,
|
||||
# bugprone-empty-catch, # has issues
|
||||
bugprone-empty-catch,
|
||||
bugprone-fold-init-type,
|
||||
# bugprone-forward-declaration-namespace, # has issues
|
||||
# bugprone-inaccurate-erase,
|
||||
# bugprone-inc-dec-in-conditions,
|
||||
# bugprone-incorrect-enable-if,
|
||||
# bugprone-incorrect-roundings,
|
||||
# bugprone-infinite-loop,
|
||||
# bugprone-integer-division,
|
||||
bugprone-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-lambda-function-name,
|
||||
# bugprone-macro-parentheses, # has issues
|
||||
bugprone-macro-parentheses,
|
||||
bugprone-macro-repeated-side-effects,
|
||||
bugprone-misplaced-operator-in-strlen-in-alloc,
|
||||
bugprone-misplaced-pointer-arithmetic-in-alloc,
|
||||
bugprone-misplaced-widening-cast,
|
||||
bugprone-move-forwarding-reference,
|
||||
# bugprone-multi-level-implicit-pointer-conversion, # has issues
|
||||
bugprone-multi-level-implicit-pointer-conversion,
|
||||
bugprone-multiple-new-in-one-expression,
|
||||
bugprone-multiple-statement-macro,
|
||||
bugprone-no-escape,
|
||||
@@ -39,13 +37,13 @@ Checks: "-*,
|
||||
bugprone-pointer-arithmetic-on-polymorphic-object,
|
||||
bugprone-posix-return,
|
||||
bugprone-redundant-branch-condition,
|
||||
# bugprone-reserved-identifier, # has issues
|
||||
# bugprone-return-const-ref-from-parameter, # has issues
|
||||
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, # has issues
|
||||
bugprone-sizeof-expression,
|
||||
bugprone-spuriously-wake-up-functions,
|
||||
bugprone-standalone-empty,
|
||||
bugprone-string-constructor,
|
||||
@@ -62,7 +60,7 @@ Checks: "-*,
|
||||
bugprone-suspicious-string-compare,
|
||||
bugprone-suspicious-stringview-data-usage,
|
||||
bugprone-swapped-arguments,
|
||||
# bugprone-switch-missing-default-case, # has issues
|
||||
bugprone-switch-missing-default-case,
|
||||
bugprone-terminating-continue,
|
||||
bugprone-throw-keyword-missing,
|
||||
bugprone-too-small-loop-variable,
|
||||
@@ -73,75 +71,70 @@ Checks: "-*,
|
||||
bugprone-unhandled-self-assignment,
|
||||
bugprone-unique-ptr-array-mismatch,
|
||||
bugprone-unsafe-functions,
|
||||
# bugprone-use-after-move, # has issues
|
||||
bugprone-use-after-move,
|
||||
bugprone-unused-raii,
|
||||
bugprone-unused-return-value,
|
||||
bugprone-unused-local-non-trivial-variable,
|
||||
bugprone-virtual-near-miss,
|
||||
# cppcoreguidelines-init-variables, # has issues
|
||||
# cppcoreguidelines-misleading-capture-default-by-value, # has issues
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-misleading-capture-default-by-value,
|
||||
cppcoreguidelines-no-suspend-with-lock,
|
||||
# cppcoreguidelines-pro-type-member-init, # has issues
|
||||
cppcoreguidelines-pro-type-member-init,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
# cppcoreguidelines-rvalue-reference-param-not-moved, # has issues
|
||||
# cppcoreguidelines-use-default-member-init, # has issues
|
||||
# cppcoreguidelines-virtual-class-destructor, # has issues
|
||||
cppcoreguidelines-rvalue-reference-param-not-moved,
|
||||
cppcoreguidelines-use-default-member-init,
|
||||
cppcoreguidelines-virtual-class-destructor,
|
||||
hicpp-ignored-remove-result,
|
||||
# misc-definitions-in-headers, # has issues
|
||||
misc-definitions-in-headers,
|
||||
misc-header-include-cycle,
|
||||
misc-misplaced-const,
|
||||
misc-static-assert,
|
||||
# misc-throw-by-value-catch-by-reference, # has issues
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-unused-alias-decls,
|
||||
misc-unused-using-decls,
|
||||
modernize-deprecated-headers,
|
||||
modernize-make-shared,
|
||||
modernize-make-unique,
|
||||
performance-faster-string-find,
|
||||
performance-for-range-copy,
|
||||
performance-implicit-conversion-in-loop,
|
||||
performance-inefficient-vector-operation,
|
||||
performance-move-const-arg,
|
||||
performance-move-constructor-init,
|
||||
performance-no-automatic-move,
|
||||
performance-trivially-destructible,
|
||||
# readability-avoid-nested-conditional-operator, # has issues
|
||||
# readability-avoid-return-with-void-value, # has issues
|
||||
# readability-braces-around-statements, # has issues
|
||||
# readability-const-return-type, # has issues
|
||||
# readability-container-contains, # has issues
|
||||
# readability-container-size-empty, # has issues
|
||||
# readability-convert-member-functions-to-static, # has issues
|
||||
readability-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-duplicate-include,
|
||||
# readability-else-after-return, # has issues
|
||||
# readability-enum-initial-value, # has issues
|
||||
# readability-implicit-bool-conversion, # has issues
|
||||
# readability-make-member-function-const, # has issues
|
||||
# readability-math-missing-parentheses, # has issues
|
||||
readability-else-after-return,
|
||||
readability-enum-initial-value,
|
||||
readability-make-member-function-const,
|
||||
readability-misleading-indentation,
|
||||
readability-non-const-parameter,
|
||||
# readability-redundant-casting, # has issues
|
||||
# readability-redundant-declaration, # has issues
|
||||
# readability-redundant-inline-specifier, # has issues
|
||||
# readability-redundant-member-init, # has issues
|
||||
readability-redundant-casting,
|
||||
readability-redundant-declaration,
|
||||
readability-redundant-inline-specifier,
|
||||
readability-redundant-member-init,
|
||||
readability-redundant-string-init,
|
||||
readability-reference-to-constructed-temporary,
|
||||
# readability-simplify-boolean-expr, # has issues
|
||||
# readability-static-definition-in-anonymous-namespace, # has issues
|
||||
# readability-suspicious-call-argument, # has issues
|
||||
readability-static-definition-in-anonymous-namespace,
|
||||
readability-use-std-min-max
|
||||
"
|
||||
# ---
|
||||
# other checks that have issues that need to be resolved:
|
||||
# checks that have some issues that need to be resolved:
|
||||
#
|
||||
# llvm-namespace-comment,
|
||||
# misc-const-correctness,
|
||||
# misc-include-cleaner,
|
||||
# misc-redundant-expression,
|
||||
#
|
||||
# readability-inconsistent-declaration-parameter-name, # in this codebase this check will break a lot of arg names
|
||||
# readability-static-accessed-through-instance, # this check is probably unnecessary. it makes the code less readable
|
||||
# readability-identifier-naming, # https://github.com/XRPLF/rippled/pull/6571
|
||||
# readability-convert-member-functions-to-static,
|
||||
# readability-implicit-bool-conversion,
|
||||
# readability-inconsistent-declaration-parameter-name,
|
||||
# readability-identifier-naming,
|
||||
# readability-math-missing-parentheses,
|
||||
# readability-simplify-boolean-expr,
|
||||
# readability-suspicious-call-argument,
|
||||
# readability-static-accessed-through-instance,
|
||||
#
|
||||
# modernize-concat-nested-namespaces,
|
||||
# modernize-pass-by-value,
|
||||
@@ -155,6 +148,12 @@ Checks: "-*,
|
||||
# modernize-use-starts-ends-with,
|
||||
# modernize-use-std-numbers,
|
||||
# modernize-use-using,
|
||||
#
|
||||
# performance-faster-string-find,
|
||||
# performance-for-range-copy,
|
||||
# performance-inefficient-vector-operation,
|
||||
# performance-move-const-arg,
|
||||
# performance-no-automatic-move,
|
||||
# ---
|
||||
#
|
||||
CheckOptions:
|
||||
@@ -196,6 +195,5 @@ CheckOptions:
|
||||
bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
|
||||
# misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp'
|
||||
#
|
||||
HeaderFilterRegex: '^.*/(test|xrpl|xrpld)/.*\.(h|hpp)$'
|
||||
ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$'
|
||||
# HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$'
|
||||
WarningsAsErrors: "*"
|
||||
|
||||
@@ -21,7 +21,6 @@ libxrpl.protocol > xrpl.json
|
||||
libxrpl.protocol > xrpl.protocol
|
||||
libxrpl.protocol_autogen > xrpl.protocol_autogen
|
||||
libxrpl.rdb > xrpl.basics
|
||||
libxrpl.rdb > xrpl.core
|
||||
libxrpl.rdb > xrpl.rdb
|
||||
libxrpl.resource > xrpl.basics
|
||||
libxrpl.resource > xrpl.json
|
||||
@@ -91,7 +90,6 @@ test.core > xrpl.server
|
||||
test.csf > xrpl.basics
|
||||
test.csf > xrpld.consensus
|
||||
test.csf > xrpl.json
|
||||
test.csf > xrpl.ledger
|
||||
test.csf > xrpl.protocol
|
||||
test.json > test.jtx
|
||||
test.json > xrpl.json
|
||||
@@ -110,6 +108,7 @@ test.jtx > xrpl.tx
|
||||
test.ledger > test.jtx
|
||||
test.ledger > test.toplevel
|
||||
test.ledger > xrpl.basics
|
||||
test.ledger > xrpld.app
|
||||
test.ledger > xrpld.core
|
||||
test.ledger > xrpl.ledger
|
||||
test.ledger > xrpl.protocol
|
||||
@@ -126,7 +125,6 @@ test.overlay > xrpl.basics
|
||||
test.overlay > xrpld.app
|
||||
test.overlay > xrpld.overlay
|
||||
test.overlay > xrpld.peerfinder
|
||||
test.overlay > xrpl.ledger
|
||||
test.overlay > xrpl.nodestore
|
||||
test.overlay > xrpl.protocol
|
||||
test.overlay > xrpl.shamap
|
||||
@@ -185,6 +183,7 @@ xrpl.conditions > xrpl.basics
|
||||
xrpl.conditions > xrpl.protocol
|
||||
xrpl.core > xrpl.basics
|
||||
xrpl.core > xrpl.json
|
||||
xrpl.core > xrpl.ledger
|
||||
xrpl.core > xrpl.protocol
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.ledger > xrpl.basics
|
||||
@@ -235,7 +234,6 @@ xrpld.app > xrpl.shamap
|
||||
xrpld.app > xrpl.tx
|
||||
xrpld.consensus > xrpl.basics
|
||||
xrpld.consensus > xrpl.json
|
||||
xrpld.consensus > xrpl.ledger
|
||||
xrpld.consensus > xrpl.protocol
|
||||
xrpld.core > xrpl.basics
|
||||
xrpld.core > xrpl.core
|
||||
|
||||
25
.github/workflows/conflicting-pr.yml
vendored
25
.github/workflows/conflicting-pr.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Label PRs with merge conflicts
|
||||
|
||||
on:
|
||||
# So that PRs touching the same files as the push are updated.
|
||||
push:
|
||||
# So that the `dirtyLabel` is removed if conflicts are resolved.
|
||||
# We recommend `pull_request_target` so that github secrets are available.
|
||||
# In `pull_request` we wouldn't be able to change labels of fork PRs.
|
||||
pull_request_target:
|
||||
types: [synchronize]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if PRs are dirty
|
||||
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
|
||||
with:
|
||||
dirtyLabel: "PR: has conflicts"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "This PR has conflicts, please resolve them in order for the PR to be reviewed."
|
||||
commentOnClean: "All conflicts have been resolved. Assigned reviewers can now start or resume their review."
|
||||
7
.github/workflows/publish-docs.yml
vendored
7
.github/workflows/publish-docs.yml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
- "release*"
|
||||
paths:
|
||||
- ".github/workflows/publish-docs.yml"
|
||||
- "*.md"
|
||||
@@ -81,13 +82,13 @@ jobs:
|
||||
cmake --build . --target docs --parallel ${BUILD_NPROC}
|
||||
|
||||
- name: Create documentation artifact
|
||||
if: ${{ github.event.repository.visibility == 'public' && github.event_name == 'push' }}
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
|
||||
with:
|
||||
path: ${{ env.BUILD_DIR }}/docs/html
|
||||
|
||||
deploy:
|
||||
if: ${{ github.event.repository.visibility == 'public' && github.event_name == 'push' }}
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
@@ -99,4 +100,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deploy
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
|
||||
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
|
||||
|
||||
@@ -199,7 +199,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload the binary (Linux)
|
||||
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: xrpld-${{ inputs.config_name }}
|
||||
@@ -298,7 +298,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
|
||||
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" ${TARGETS} 2>&1 | tee clang-tidy-output.txt
|
||||
|
||||
- name: Upload clang-tidy output
|
||||
if: ${{ github.event.repository.visibility == 'public' && steps.run_clang_tidy.outcome != 'success' }}
|
||||
if: steps.run_clang_tidy.outcome != 'success'
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: clang-tidy-results
|
||||
|
||||
2
.github/workflows/reusable-clang-tidy.yml
vendored
2
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -51,5 +51,5 @@ jobs:
|
||||
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.any_cpp_changed == 'true' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }}
|
||||
uses: ./.github/workflows/reusable-clang-tidy-files.yml
|
||||
with:
|
||||
files: ${{ (needs.determine-files.outputs.clang_tidy_config_changed != 'true' && inputs.check_only_changed) && needs.determine-files.outputs.all_changed_files || '' }}
|
||||
files: ${{ needs.determine-files.outputs.clang_tidy_config_changed == 'true' && '' || (inputs.check_only_changed && needs.determine-files.outputs.all_changed_files || '') }}
|
||||
create_issue_on_failure: ${{ inputs.create_issue_on_failure }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -71,8 +71,6 @@ DerivedData
|
||||
/.zed/
|
||||
|
||||
# AI tools.
|
||||
/.agent
|
||||
/.agents
|
||||
/.augment
|
||||
/.claude
|
||||
/CLAUDE.md
|
||||
|
||||
77
BUILD.md
77
BUILD.md
@@ -125,9 +125,9 @@ default profile.
|
||||
|
||||
### Patched recipes
|
||||
|
||||
Occasionally, we need patched recipes or recipes not present in Conan Center.
|
||||
We maintain a fork of the Conan Center Index
|
||||
[here](https://github.com/XRPLF/conan-center-index/) containing the modified and newly added recipes.
|
||||
The recipes in Conan Center occasionally need to be patched for compatibility
|
||||
with the latest version of `xrpld`. We maintain a fork of the Conan Center
|
||||
[here](https://github.com/XRPLF/conan-center-index/) containing the patches.
|
||||
|
||||
To ensure our patched recipes are used, you must add our Conan remote at a
|
||||
higher index than the default Conan Center remote, so it is consulted first. You
|
||||
@@ -137,11 +137,19 @@ can do this by running:
|
||||
conan remote add --index 0 xrplf https://conan.ripplex.io
|
||||
```
|
||||
|
||||
Alternatively, you can pull our recipes from the repository and export them locally:
|
||||
Alternatively, you can pull the patched recipes into the repository and use them
|
||||
locally:
|
||||
|
||||
```bash
|
||||
# Extract the version number from the lockfile.
|
||||
function extract_version {
|
||||
version=$(cat conan.lock | sed -nE "s@.+${1}/(.+)#.+@\1@p" | head -n1)
|
||||
echo ${version}
|
||||
}
|
||||
|
||||
# Define which recipes to export.
|
||||
recipes=('abseil' 'ed25519' 'grpc' 'm4' 'mpt-crypto' 'nudb' 'openssl' 'secp256k1' 'snappy' 'soci' 'wasm-xrplf' 'wasmi')
|
||||
recipes=('ed25519' 'grpc' 'nudb' 'openssl' 'secp256k1' 'snappy' 'soci')
|
||||
folders=('all' 'all' 'all' '3.x.x' 'all' 'all' 'all')
|
||||
|
||||
# Selectively check out the recipes from our CCI fork.
|
||||
cd external
|
||||
@@ -150,19 +158,29 @@ cd conan-center-index
|
||||
git init
|
||||
git remote add origin git@github.com:XRPLF/conan-center-index.git
|
||||
git sparse-checkout init
|
||||
for recipe in "${recipes[@]}"; do
|
||||
echo "Checking out recipe '${recipe}'..."
|
||||
git sparse-checkout add recipes/${recipe}
|
||||
for ((index = 1; index <= ${#recipes[@]}; index++)); do
|
||||
recipe=${recipes[index]}
|
||||
folder=${folders[index]}
|
||||
echo "Checking out recipe '${recipe}' from folder '${folder}'..."
|
||||
git sparse-checkout add recipes/${recipe}/${folder}
|
||||
done
|
||||
git fetch origin master
|
||||
git checkout master
|
||||
cd ../..
|
||||
|
||||
./export_all.sh
|
||||
cd ../../
|
||||
# Export the recipes into the local cache.
|
||||
for ((index = 1; index <= ${#recipes[@]}; index++)); do
|
||||
recipe=${recipes[index]}
|
||||
folder=${folders[index]}
|
||||
version=$(extract_version ${recipe})
|
||||
echo "Exporting '${recipe}/${version}' from '${recipe}/${folder}'..."
|
||||
conan export --version $(extract_version ${recipe}) \
|
||||
external/conan-center-index/recipes/${recipe}/${folder}
|
||||
done
|
||||
```
|
||||
|
||||
In the case we switch to a newer version of a dependency that still requires a
|
||||
patch or add a new dependency, it will be necessary for you to pull in the changes and re-export the
|
||||
patch, it will be necessary for you to pull in the changes and re-export the
|
||||
updated dependencies with the newer version. However, if we switch to a newer
|
||||
version that no longer requires a patch, no action is required on your part, as
|
||||
the new recipe will be automatically pulled from the official Conan Center.
|
||||
@@ -171,8 +189,6 @@ the new recipe will be automatically pulled from the official Conan Center.
|
||||
> You might need to add `--lockfile=""` to your `conan install` command
|
||||
> to avoid automatic use of the existing `conan.lock` file when you run
|
||||
> `conan export` manually on your machine
|
||||
>
|
||||
> This is not recommended though, as you might end up using different revisions of recipes.
|
||||
|
||||
### Conan profile tweaks
|
||||
|
||||
@@ -188,14 +204,39 @@ Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1',
|
||||
Read "http://docs.conan.io/2/knowledge/faq.html#error-invalid-setting"
|
||||
```
|
||||
|
||||
you need to add your compiler to the list of compiler versions in
|
||||
`$(conan config home)/settings_user.yml`, by adding the required version number(s)
|
||||
you need to amend the list of compiler versions in
|
||||
`$(conan config home)/settings.yml`, by appending the required version number(s)
|
||||
to the `version` array specific for your compiler. For example:
|
||||
|
||||
```yaml
|
||||
compiler:
|
||||
apple-clang:
|
||||
version: ["17.0"]
|
||||
apple-clang:
|
||||
version:
|
||||
[
|
||||
"5.0",
|
||||
"5.1",
|
||||
"6.0",
|
||||
"6.1",
|
||||
"7.0",
|
||||
"7.3",
|
||||
"8.0",
|
||||
"8.1",
|
||||
"9.0",
|
||||
"9.1",
|
||||
"10.0",
|
||||
"11.0",
|
||||
"12.0",
|
||||
"13",
|
||||
"13.0",
|
||||
"13.1",
|
||||
"14",
|
||||
"14.0",
|
||||
"15",
|
||||
"15.0",
|
||||
"16",
|
||||
"16.0",
|
||||
"17",
|
||||
"17.0",
|
||||
]
|
||||
```
|
||||
|
||||
#### Multiple compilers
|
||||
|
||||
@@ -259,10 +259,6 @@ There is a Continuous Integration job that runs clang-tidy on pull requests. The
|
||||
|
||||
This ensures that configuration changes don't introduce new warnings across the codebase.
|
||||
|
||||
### Installing clang-tidy
|
||||
|
||||
See the [environment setup guide](./docs/build/environment.md#clang-tidy) for platform-specific installation instructions.
|
||||
|
||||
### Running clang-tidy locally
|
||||
|
||||
Before running clang-tidy, you must build the project to generate required files (particularly protobuf headers). Refer to [`BUILD.md`](./BUILD.md) for build instructions.
|
||||
@@ -270,15 +266,10 @@ Before running clang-tidy, you must build the project to generate required files
|
||||
Then run clang-tidy on your local changes:
|
||||
|
||||
```
|
||||
run-clang-tidy -p build src include tests
|
||||
run-clang-tidy -p build src tests
|
||||
```
|
||||
|
||||
This will check all source files in the `src`, `include` and `tests` directories using the compile commands from your `build` directory.
|
||||
If you wish to automatically fix whatever clang-tidy finds _and_ is capable of fixing, add `-fix` to the above command:
|
||||
|
||||
```
|
||||
run-clang-tidy -p build -fix src include tests
|
||||
```
|
||||
This will check all source files in the `src` and `tests` directories using the compile commands from your `build` directory.
|
||||
|
||||
## Contracts and instrumentation
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2011, Arthur Britto, David Schwartz, Jed McCaleb, Vinnie Falco, Bob Way, Eric Lombrozo, Nikolaos D. Bougalis, Howard Hinnant.
|
||||
Copyright (c) 2012-present, the XRP Ledger developers.
|
||||
Copyright (c) 2012-2025, the XRP Ledger developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
@@ -140,28 +140,6 @@ function(setup_protocol_autogen)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Check pip index URL configuration
|
||||
execute_process(
|
||||
COMMAND ${VENV_PIP} config get global.index-url
|
||||
OUTPUT_VARIABLE PIP_INDEX_URL
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Default PyPI URL
|
||||
set(DEFAULT_PIP_INDEX "https://pypi.org/simple")
|
||||
|
||||
# Show warning if using non-default index
|
||||
if(PIP_INDEX_URL AND NOT PIP_INDEX_URL STREQUAL "")
|
||||
if(NOT PIP_INDEX_URL STREQUAL DEFAULT_PIP_INDEX)
|
||||
message(
|
||||
WARNING
|
||||
"Private pip index URL detected: ${PIP_INDEX_URL}\n"
|
||||
"You may need to connect to VPN to access this URL."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Installing Python dependencies...")
|
||||
execute_process(
|
||||
COMMAND ${VENV_PIP} install --upgrade pip
|
||||
|
||||
45
conan.lock
45
conan.lock
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"version": "0.5",
|
||||
"requires": [
|
||||
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1774439233.809",
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
|
||||
"sqlite3/3.51.0#66aa11eabd0e34954c5c1c061ad44abe%1763899256.358",
|
||||
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
"secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
|
||||
"secp256k1/0.7.1#3a61e95e220062ef32c48d019e9c81f7%1770306721.686",
|
||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
|
||||
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
|
||||
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1773224203.27",
|
||||
"openssl/3.6.1#e6399de266349245a4542fc5f6c71552%1774458290.139",
|
||||
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
"openssl/3.5.5#05a4ac5b7323f7a329b2db1391d9941f%1769599205.414",
|
||||
"nudb/2.0.9#0432758a24204da08fee953ec9ea03cb%1769436073.32",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
|
||||
@@ -18,26 +18,27 @@
|
||||
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1765850144.736",
|
||||
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
|
||||
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
|
||||
"grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1772623605.068",
|
||||
"grpc/1.72.0#f244a57bff01e708c55a1100b12e1589%1765850193.734",
|
||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
|
||||
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
|
||||
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1765850144.336",
|
||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
|
||||
"boost/1.90.0#d5e8defe7355494953be18524a7f135b%1769454080.269",
|
||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
|
||||
"abseil/20250127.0#99262a368bd01c0ccca8790dfced9719%1766517936.993"
|
||||
],
|
||||
"build_requires": [
|
||||
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1774439233.809",
|
||||
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
|
||||
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1773224203.27",
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
|
||||
"strawberryperl/5.32.1.1#707032463aa0620fa17ec0d887f5fe41%1765850165.196",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
|
||||
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
|
||||
"m4/1.4.19#5d7a4994e5875d76faf7acf3ed056036%1774365463.87",
|
||||
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
|
||||
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
|
||||
"msys2/cci.latest#eea83308ad7e9023f7318c60d5a9e6cb%1770199879.083",
|
||||
"m4/1.4.19#70dc8bbb33e981d119d2acc0175cf381%1763158052.846",
|
||||
"cmake/4.2.0#ae0a44f44a1ef9ab68fd4b3e9a1f8671%1765850153.937",
|
||||
"cmake/3.31.10#313d16a1aa16bbdb2ca0792467214b76%1765850153.479",
|
||||
"b2/5.3.3#107c15377719889654eb9a162a673975%1765850144.355",
|
||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
|
||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
|
||||
"abseil/20250127.0#99262a368bd01c0ccca8790dfced9719%1766517936.993"
|
||||
],
|
||||
"python_requires": [],
|
||||
"overrides": {
|
||||
@@ -45,14 +46,14 @@
|
||||
null,
|
||||
"boost/1.90.0"
|
||||
],
|
||||
"protobuf/[>=5.27.0 <7]": [
|
||||
"protobuf/6.33.5"
|
||||
"protobuf/5.27.0": [
|
||||
"protobuf/6.32.1"
|
||||
],
|
||||
"lz4/1.9.4": [
|
||||
"lz4/1.10.0"
|
||||
],
|
||||
"sqlite3/[>=3.44 <4]": [
|
||||
"sqlite3/3.51.0"
|
||||
"sqlite3/3.44.2": [
|
||||
"sqlite3/3.49.1"
|
||||
],
|
||||
"boost/1.83.0": [
|
||||
"boost/1.90.0"
|
||||
|
||||
23
conanfile.py
23
conanfile.py
@@ -1,9 +1,10 @@
|
||||
import os
|
||||
import re
|
||||
import os
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
from conan import ConanFile
|
||||
from conan import __version__ as conan_version
|
||||
|
||||
|
||||
class Xrpl(ConanFile):
|
||||
@@ -29,10 +30,10 @@ class Xrpl(ConanFile):
|
||||
|
||||
requires = [
|
||||
"ed25519/2015.03",
|
||||
"grpc/1.78.1",
|
||||
"grpc/1.72.0",
|
||||
"libarchive/3.8.1",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.1",
|
||||
"openssl/3.5.5",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"zlib/1.3.1",
|
||||
@@ -43,7 +44,7 @@ class Xrpl(ConanFile):
|
||||
]
|
||||
|
||||
tool_requires = [
|
||||
"protobuf/6.33.5",
|
||||
"protobuf/6.32.1",
|
||||
]
|
||||
|
||||
default_options = {
|
||||
@@ -136,16 +137,20 @@ class Xrpl(ConanFile):
|
||||
self.default_options["fPIC"] = False
|
||||
|
||||
def requirements(self):
|
||||
self.requires("boost/1.90.0", force=True, transitive_headers=True)
|
||||
self.requires("date/3.0.4", transitive_headers=True)
|
||||
# Conan 2 requires transitive headers to be specified
|
||||
transitive_headers_opt = (
|
||||
{"transitive_headers": True} if conan_version.split(".")[0] == "2" else {}
|
||||
)
|
||||
self.requires("boost/1.90.0", force=True, **transitive_headers_opt)
|
||||
self.requires("date/3.0.4", **transitive_headers_opt)
|
||||
self.requires("lz4/1.10.0", force=True)
|
||||
self.requires("protobuf/6.33.5", force=True)
|
||||
self.requires("sqlite3/3.51.0", force=True)
|
||||
self.requires("protobuf/6.32.1", force=True)
|
||||
self.requires("sqlite3/3.49.1", force=True)
|
||||
if self.options.jemalloc:
|
||||
self.requires("jemalloc/5.3.0")
|
||||
if self.options.rocksdb:
|
||||
self.requires("rocksdb/10.5.1")
|
||||
self.requires("xxhash/0.8.3", transitive_headers=True)
|
||||
self.requires("xxhash/0.8.3", **transitive_headers_opt)
|
||||
|
||||
exports_sources = (
|
||||
"CMakeLists.txt",
|
||||
|
||||
@@ -297,7 +297,6 @@ words:
|
||||
- venv
|
||||
- vfalco
|
||||
- vinnie
|
||||
- wasmi
|
||||
- wextra
|
||||
- wptr
|
||||
- writeme
|
||||
|
||||
29
docs/build/environment.md
vendored
29
docs/build/environment.md
vendored
@@ -109,32 +109,3 @@ Install CMake with Homebrew too:
|
||||
```
|
||||
brew install cmake
|
||||
```
|
||||
|
||||
## Clang-tidy
|
||||
|
||||
Clang-tidy is required to run static analysis checks locally (see [CONTRIBUTING.md](../../CONTRIBUTING.md)).
|
||||
It is not required to build the project. Currently this project uses clang-tidy version 21.
|
||||
|
||||
### Linux
|
||||
|
||||
LLVM 21 is not available in the default Debian 12 (Bookworm) repositories.
|
||||
Install it using the official LLVM apt installer:
|
||||
|
||||
```
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 21
|
||||
sudo apt install --yes clang-tidy-21
|
||||
```
|
||||
|
||||
Then use `run-clang-tidy-21` when running clang-tidy locally.
|
||||
|
||||
### macOS
|
||||
|
||||
Install LLVM 21 via Homebrew:
|
||||
|
||||
```
|
||||
brew install llvm@21
|
||||
```
|
||||
|
||||
Then use `run-clang-tidy` from the LLVM 21 Homebrew prefix when running clang-tidy locally.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#define ALWAYS_OR_UNREACHABLE(cond, message) assert((message) && (cond))
|
||||
#define SOMETIMES(cond, message, ...)
|
||||
#define REACHABLE(message, ...)
|
||||
#define UNREACHABLE(message, ...) assert((message) && false) // NOLINT(misc-static-assert)
|
||||
#define UNREACHABLE(message, ...) assert((message) && false)
|
||||
#endif
|
||||
|
||||
#define XRPL_ASSERT ALWAYS_OR_UNREACHABLE
|
||||
|
||||
@@ -70,24 +70,14 @@ JobQueue::Coro::resume()
|
||||
running_ = true;
|
||||
}
|
||||
{
|
||||
std::lock_guard lk(jq_.m_mutex);
|
||||
std::lock_guard lock(jq_.m_mutex);
|
||||
--jq_.nSuspend_;
|
||||
}
|
||||
auto saved = detail::getLocalValues().release();
|
||||
detail::getLocalValues().reset(&lvs_);
|
||||
std::lock_guard lock(mutex_);
|
||||
// A late resume() can arrive after the coroutine has already completed.
|
||||
// This is an expected (if rare) outcome of the race condition documented
|
||||
// in JobQueue.h:354-377 where post() schedules a resume job before the
|
||||
// coroutine yields — the mutex serializes access, but by the time this
|
||||
// resume() acquires the lock the coroutine may have already run to
|
||||
// completion. Calling operator() on a completed boost::coroutine2 is
|
||||
// undefined behavior, so we must check and skip invoking the coroutine
|
||||
// body if it has already completed.
|
||||
if (coro_)
|
||||
{
|
||||
coro_();
|
||||
}
|
||||
XRPL_ASSERT(static_cast<bool>(coro_), "xrpl::JobQueue::Coro::resume : is runnable");
|
||||
coro_();
|
||||
detail::getLocalValues().release();
|
||||
detail::getLocalValues().reset(saved);
|
||||
std::lock_guard lk(mutex_run_);
|
||||
|
||||
@@ -99,8 +99,8 @@ public:
|
||||
Effects:
|
||||
The coroutine continues execution from where it last left off
|
||||
using this same thread.
|
||||
If the coroutine has already completed, returns immediately
|
||||
(handles the documented post-before-yield race condition).
|
||||
Undefined behavior if called after the coroutine has completed
|
||||
with a return (as opposed to a yield()).
|
||||
Undefined behavior if resume() or post() called consecutively
|
||||
without a corresponding yield.
|
||||
*/
|
||||
@@ -316,7 +316,7 @@ private:
|
||||
// Returns the limit of running jobs for the given job type.
|
||||
// For jobs with no limit, we return the largest int. Hopefully that
|
||||
// will be enough.
|
||||
static int
|
||||
int
|
||||
getJobLimit(JobType type);
|
||||
};
|
||||
|
||||
@@ -357,10 +357,8 @@ private:
|
||||
If the post() job were to be executed before yield(), undefined behavior
|
||||
would occur. The lock ensures that coro_ is not called again until we exit
|
||||
the coroutine. At which point a scheduled resume() job waiting on the lock
|
||||
would gain entry. resume() checks if the coroutine has already completed
|
||||
(coro_ converts to false) and, if so, skips invoking operator() since
|
||||
calling operator() on a completed boost::coroutine2 pull_type is undefined
|
||||
behavior.
|
||||
would gain entry, harmlessly call coro_ and immediately return as we have
|
||||
already completed the coroutine.
|
||||
|
||||
The race condition occurs as follows:
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/ledger/CachedSLEs.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
@@ -22,20 +23,6 @@ class PerfLog;
|
||||
// This is temporary until we migrate all code to use ServiceRegistry.
|
||||
class Application;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
bool IsKeyCache,
|
||||
class SharedWeakUnionPointer,
|
||||
class SharedPointerType,
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Mutex>
|
||||
class TaggedCache;
|
||||
class STLedgerEntry;
|
||||
using SLE = STLedgerEntry;
|
||||
using CachedSLEs = TaggedCache<uint256, SLE const>;
|
||||
|
||||
// Forward declarations
|
||||
class AcceptedLedger;
|
||||
class AmendmentTable;
|
||||
@@ -58,7 +45,7 @@ class NetworkIDService;
|
||||
class OpenLedger;
|
||||
class OrderBookDB;
|
||||
class Overlay;
|
||||
class PathRequestManager;
|
||||
class PathRequests;
|
||||
class PeerReservationTable;
|
||||
class PendingSaves;
|
||||
class RelationalDatabase;
|
||||
@@ -102,7 +89,7 @@ public:
|
||||
getNodeFamily() = 0;
|
||||
|
||||
virtual TimeKeeper&
|
||||
getTimeKeeper() = 0;
|
||||
timeKeeper() = 0;
|
||||
|
||||
virtual JobQueue&
|
||||
getJobQueue() = 0;
|
||||
@@ -111,7 +98,7 @@ public:
|
||||
getTempNodeCache() = 0;
|
||||
|
||||
virtual CachedSLEs&
|
||||
getCachedSLEs() = 0;
|
||||
cachedSLEs() = 0;
|
||||
|
||||
virtual NetworkIDService&
|
||||
getNetworkIDService() = 0;
|
||||
@@ -133,26 +120,26 @@ public:
|
||||
getValidations() = 0;
|
||||
|
||||
virtual ValidatorList&
|
||||
getValidators() = 0;
|
||||
validators() = 0;
|
||||
|
||||
virtual ValidatorSite&
|
||||
getValidatorSites() = 0;
|
||||
validatorSites() = 0;
|
||||
|
||||
virtual ManifestCache&
|
||||
getValidatorManifests() = 0;
|
||||
validatorManifests() = 0;
|
||||
|
||||
virtual ManifestCache&
|
||||
getPublisherManifests() = 0;
|
||||
publisherManifests() = 0;
|
||||
|
||||
// Network services
|
||||
virtual Overlay&
|
||||
getOverlay() = 0;
|
||||
overlay() = 0;
|
||||
|
||||
virtual Cluster&
|
||||
getCluster() = 0;
|
||||
cluster() = 0;
|
||||
|
||||
virtual PeerReservationTable&
|
||||
getPeerReservations() = 0;
|
||||
peerReservations() = 0;
|
||||
|
||||
virtual Resource::Manager&
|
||||
getResourceManager() = 0;
|
||||
@@ -187,13 +174,13 @@ public:
|
||||
getLedgerReplayer() = 0;
|
||||
|
||||
virtual PendingSaves&
|
||||
getPendingSaves() = 0;
|
||||
pendingSaves() = 0;
|
||||
|
||||
virtual OpenLedger&
|
||||
getOpenLedger() = 0;
|
||||
openLedger() = 0;
|
||||
|
||||
virtual OpenLedger const&
|
||||
getOpenLedger() const = 0;
|
||||
openLedger() const = 0;
|
||||
|
||||
// Transaction and operation services
|
||||
virtual NetworkOPs&
|
||||
@@ -208,8 +195,8 @@ public:
|
||||
virtual TxQ&
|
||||
getTxQ() = 0;
|
||||
|
||||
virtual PathRequestManager&
|
||||
getPathRequestManager() = 0;
|
||||
virtual PathRequests&
|
||||
getPathRequests() = 0;
|
||||
|
||||
// Server services
|
||||
virtual ServerHandler&
|
||||
@@ -223,16 +210,16 @@ public:
|
||||
isStopping() const = 0;
|
||||
|
||||
virtual beast::Journal
|
||||
getJournal(std::string const& name) = 0;
|
||||
journal(std::string const& name) = 0;
|
||||
|
||||
virtual boost::asio::io_context&
|
||||
getIOContext() = 0;
|
||||
|
||||
virtual Logs&
|
||||
getLogs() = 0;
|
||||
logs() = 0;
|
||||
|
||||
virtual std::optional<uint256> const&
|
||||
getTrapTxID() const = 0;
|
||||
trapTxID() const = 0;
|
||||
|
||||
/** Retrieve the "wallet database" */
|
||||
virtual DatabaseCon&
|
||||
@@ -241,7 +228,7 @@ public:
|
||||
// Temporary: Get the underlying Application for functions that haven't
|
||||
// been migrated yet. This should be removed once all code is migrated.
|
||||
virtual Application&
|
||||
getApp() = 0;
|
||||
app() = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class StartUpType { Fresh, Normal, Load, LoadFile, Replay, Network };
|
||||
enum class StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK };
|
||||
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, StartUpType const& type)
|
||||
|
||||
@@ -76,33 +76,16 @@ public:
|
||||
@return true if a book from this issue to XRP exists
|
||||
*/
|
||||
virtual bool
|
||||
isBookToXRP(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
isBookToXRP(Issue const& issue, std::optional<Domain> domain = std::nullopt) = 0;
|
||||
|
||||
/**
|
||||
* Process a transaction for order book tracking.
|
||||
* @param ledger The ledger the transaction was applied to
|
||||
* @param alTx The transaction to process
|
||||
* @param jvObj The JSON object of the transaction
|
||||
*/
|
||||
virtual void
|
||||
processTxn(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
AcceptedLedgerTx const& alTx,
|
||||
MultiApiJson const& jvObj) = 0;
|
||||
|
||||
/**
|
||||
* Get the book listeners for a book.
|
||||
* @param book The book to get the listeners for
|
||||
* @return The book listeners for the book
|
||||
*/
|
||||
virtual BookListeners::pointer
|
||||
getBookListeners(Book const&) = 0;
|
||||
|
||||
/**
|
||||
* Create a new book listeners for a book.
|
||||
* @param book The book to create the listeners for
|
||||
* @return The new book listeners for the book
|
||||
*/
|
||||
virtual BookListeners::pointer
|
||||
makeBookListeners(Book const&) = 0;
|
||||
};
|
||||
|
||||
@@ -2,19 +2,28 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/OfferHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Asset.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/STTx.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -157,7 +166,7 @@ canWithdraw(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
SLE::const_ref toSle,
|
||||
AccountRoot const& toWrapped,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag);
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -15,40 +17,128 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Check if the issuer has the global freeze flag set.
|
||||
@param issuer The account to check
|
||||
@return true if the account has global freeze set
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(ReadView const& view, AccountID const& issuer);
|
||||
|
||||
// Calculate liquid XRP balance for an account.
|
||||
// This function may be used to calculate the amount of XRP that
|
||||
// the holder is able to freely spend. It subtracts reserve requirements.
|
||||
//
|
||||
// ownerCountAdj adjusts the owner count in case the caller calculates
|
||||
// before ledger entries are added or removed. Positive to add, negative
|
||||
// to subtract.
|
||||
//
|
||||
// @param ownerCountAdj positive to add to count, negative to reduce count.
|
||||
[[nodiscard]] XRPAmount
|
||||
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
|
||||
|
||||
/** Adjust the owner count up or down. */
|
||||
void
|
||||
adjustOwnerCount(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> const& sle,
|
||||
std::int32_t amount,
|
||||
beast::Journal j);
|
||||
|
||||
/** Returns IOU issuer transfer fee as Rate. Rate specifies
|
||||
* the fee as fractions of 1 billion. For example, 1% transfer rate
|
||||
* is represented as 1,010,000,000.
|
||||
* @param issuer The IOU issuer
|
||||
/**
|
||||
* Read-only wrapper for AccountRoot ledger entries.
|
||||
*
|
||||
* Provides read-only access to account data.
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer);
|
||||
class AccountRoot : public ReadOnlySLE
|
||||
{
|
||||
protected:
|
||||
AccountID const id_;
|
||||
|
||||
public:
|
||||
AccountRoot(AccountID const& id, ReadView const& view)
|
||||
: ReadOnlySLE(view.read(keylet::account(id)), view), id_(id)
|
||||
{
|
||||
}
|
||||
|
||||
AccountID const&
|
||||
id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
/** Check if the issuer has the global freeze flag set.
|
||||
@return true if the account has global freeze set
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen() const;
|
||||
|
||||
/** Returns IOU issuer transfer fee as Rate. Rate specifies
|
||||
* the fee as fractions of 1 billion. For example, 1% transfer rate
|
||||
* is represented as 1,010,000,000.
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate() const;
|
||||
|
||||
// Calculate liquid XRP balance for an account.
|
||||
// This function may be used to calculate the amount of XRP that
|
||||
// the holder is able to freely spend. It subtracts reserve requirements.
|
||||
//
|
||||
// ownerCountAdj adjusts the owner count in case the caller calculates
|
||||
// before ledger entries are added or removed. Positive to add, negative
|
||||
// to subtract.
|
||||
//
|
||||
// @param ownerCountAdj positive to add to count, negative to reduce count.
|
||||
[[nodiscard]] XRPAmount
|
||||
xrpLiquid(std::int32_t ownerCountAdj, beast::Journal j) const;
|
||||
|
||||
/** Checks the destination and tag.
|
||||
|
||||
- Checks that the SLE is not null.
|
||||
- If the SLE requires a destination tag, checks that there is a tag.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkDestinationAndTag(bool hasDestinationTag) const;
|
||||
|
||||
/** Returns true if and only if sleAcct is a pseudo-account or specific
|
||||
pseudo-accounts in pseudoFieldFilter.
|
||||
|
||||
Returns false if sleAcct is:
|
||||
- NOT a pseudo-account OR
|
||||
- NOT a ltACCOUNT_ROOT OR
|
||||
- null pointer
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(std::set<SField const*> const& pseudoFieldFilter = {}) const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
operator==(AccountRoot const& other) const
|
||||
{
|
||||
return id_ == other.id_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
operator==(AccountID const& other) const
|
||||
{
|
||||
return id_ == other;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Writable wrapper for AccountRoot ledger entries.
|
||||
*
|
||||
* Provides read-write access to account data.
|
||||
* Inherits from AccountRoot to reuse read-only methods,
|
||||
* and adds write capabilities.
|
||||
*/
|
||||
class WritableAccountRoot : public AccountRoot, public WritableSLE
|
||||
{
|
||||
public:
|
||||
WritableAccountRoot(AccountID const& id, ApplyView& view)
|
||||
: AccountRoot(id, view), WritableSLE(keylet::account(id), view)
|
||||
{
|
||||
}
|
||||
|
||||
/** Create a WritableAccountRoot backed by a brand-new SLE
|
||||
* (not yet inserted into the view).
|
||||
*/
|
||||
[[nodiscard]] static WritableAccountRoot
|
||||
makeNew(AccountID const& id, ApplyView& view)
|
||||
{
|
||||
return WritableAccountRoot(id, view, std::make_shared<SLE>(keylet::account(id)));
|
||||
}
|
||||
|
||||
private:
|
||||
// This is a private constructor only used by `makeNew`
|
||||
WritableAccountRoot(AccountID const& id, ApplyView& view, std::shared_ptr<SLE> sle)
|
||||
: AccountRoot(id, view), WritableSLE(std::move(sle), view)
|
||||
{
|
||||
insert();
|
||||
}
|
||||
|
||||
public:
|
||||
// Resolve ambiguity: use writable operator-> for non-const, read-only for const
|
||||
using WritableSLE::operator->;
|
||||
using AccountRoot::operator->;
|
||||
using WritableSLE::operator*;
|
||||
using AccountRoot::operator*;
|
||||
|
||||
/** Adjust the owner count up or down. */
|
||||
void
|
||||
adjustOwnerCount(std::int32_t amount, beast::Journal j);
|
||||
};
|
||||
|
||||
/** Generate a pseudo-account address from a pseudo owner key.
|
||||
@param pseudoOwnerKey The key to generate the address from
|
||||
@@ -87,7 +177,10 @@ isPseudoAccount(
|
||||
AccountID const& accountId,
|
||||
std::set<SField const*> const& pseudoFieldFilter = {})
|
||||
{
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter);
|
||||
AccountRoot const acct(accountId, view);
|
||||
if (!acct)
|
||||
return false;
|
||||
return acct.isPseudoAccount(pseudoFieldFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,12 +194,4 @@ isPseudoAccount(
|
||||
[[nodiscard]] Expected<std::shared_ptr<SLE>, TER>
|
||||
createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField);
|
||||
|
||||
/** Checks the destination and tag.
|
||||
|
||||
- Checks that the SLE is not null.
|
||||
- If the SLE requires a destination tag, checks that there is a tag.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STArray.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -73,8 +74,7 @@ verifyDepositPreauth(
|
||||
STTx const& tx,
|
||||
ApplyView& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
std::shared_ptr<SLE const> const& sleDst,
|
||||
AccountRoot const& dst,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -14,126 +14,238 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Freeze checking (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
class MPTokenIssuance : public virtual TokenBase
|
||||
{
|
||||
public:
|
||||
MPTokenIssuance(ReadView const& view, MPTIssue const& mptIssue)
|
||||
: ReadOnlySLE(view.read(keylet::mptIssuance(mptIssue.getMptID())), view)
|
||||
, TokenBase(view, view.read(keylet::mptIssuance(mptIssue.getMptID())))
|
||||
, mptID_(mptIssue.getMptID())
|
||||
, mptIssue_(mptIssue)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
|
||||
MPTokenIssuance(ReadView const& view, MPTID const& mptID)
|
||||
: ReadOnlySLE(view.read(keylet::mptIssuance(mptID)), view)
|
||||
, TokenBase(view, view.read(keylet::mptIssuance(mptID)))
|
||||
, mptID_(mptID)
|
||||
, mptIssue_(MPTIssue(mptID_))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
|
||||
MPTID const&
|
||||
getMptID() const
|
||||
{
|
||||
return mptID_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth = 0);
|
||||
MPTIssue const&
|
||||
getMptIssue() const
|
||||
{
|
||||
return mptIssue_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isAnyFrozen(
|
||||
ReadView const& view,
|
||||
std::initializer_list<AccountID> const& accounts,
|
||||
MPTIssue const& mptIssue,
|
||||
int depth = 0);
|
||||
AccountID const&
|
||||
getIssuer() const
|
||||
{
|
||||
return mptIssue_.getIssuer();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Transfer rate (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Freeze checking (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Returns MPT transfer fee as Rate. Rate specifies
|
||||
* the fee as fractions of 1 billion. For example, 1% transfer rate
|
||||
* is represented as 1,010,000,000.
|
||||
* @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, MPTID const& issuanceID);
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen() const override;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Holding checks (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
[[nodiscard]] bool
|
||||
isIndividualFrozen(AccountID const& account) const override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, MPTIssue const& mptIssue);
|
||||
[[nodiscard]] bool
|
||||
isFrozen(AccountID const& account, int depth = 0) const override;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Authorization (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
[[nodiscard]] TER
|
||||
checkFrozen(AccountID const& account) const override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
authorizeMPToken(
|
||||
ApplyView& view,
|
||||
XRPAmount const& priorBalance,
|
||||
MPTID const& mptIssuanceID,
|
||||
AccountID const& account,
|
||||
beast::Journal journal,
|
||||
std::uint32_t flags = 0,
|
||||
std::optional<AccountID> holderID = std::nullopt);
|
||||
[[nodiscard]] bool
|
||||
isAnyFrozen(std::initializer_list<AccountID> const& accounts, int depth = 0) const override;
|
||||
|
||||
/** Check if the account lacks required authorization for MPT.
|
||||
*
|
||||
* requireAuth check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
requireAuth(
|
||||
ReadView const& view,
|
||||
MPTIssue const& mptIssue,
|
||||
AccountID const& account,
|
||||
AuthType authType = AuthType::Legacy,
|
||||
int depth = 0);
|
||||
[[nodiscard]] bool
|
||||
isDeepFrozen(AccountID const& account, int depth = 0) const override;
|
||||
|
||||
/** Enforce account has MPToken to match its authorization.
|
||||
*
|
||||
* Called from doApply - it will check for expired (and delete if found any)
|
||||
* credentials matching DomainID set in MPTokenIssuance. Must be called if
|
||||
* requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
enforceMPTokenAuthorization(
|
||||
ApplyView& view,
|
||||
MPTID const& mptIssuanceID,
|
||||
AccountID const& account,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal j);
|
||||
[[nodiscard]] TER
|
||||
checkDeepFrozen(AccountID const& account) const override;
|
||||
|
||||
/** Check if the destination account is allowed
|
||||
* to receive MPT. Return tecNO_AUTH if it doesn't
|
||||
* and tesSUCCESS otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTransfer(
|
||||
ReadView const& view,
|
||||
MPTIssue const& mptIssue,
|
||||
AccountID const& from,
|
||||
AccountID const& to);
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Transfer rate (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Empty holding operations (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
/** Returns MPT transfer fee as Rate. Rate specifies
|
||||
* the fee as fractions of 1 billion. For example, 1% transfer rate
|
||||
* is represented as 1,010,000,000.
|
||||
* @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate() const override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(
|
||||
ApplyView& view,
|
||||
AccountID const& accountID,
|
||||
XRPAmount priorBalance,
|
||||
MPTIssue const& mptIssue,
|
||||
beast::Journal journal);
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Holding checks (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] TER
|
||||
removeEmptyHolding(
|
||||
ApplyView& view,
|
||||
AccountID const& accountID,
|
||||
MPTIssue const& mptIssue,
|
||||
beast::Journal journal);
|
||||
[[nodiscard]] TER
|
||||
canAddHolding() const override;
|
||||
|
||||
/** Check if the account lacks required authorization for MPT.
|
||||
*
|
||||
* requireAuth check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
requireAuth(AccountID const& account, AuthType authType = AuthType::Legacy, int depth = 0)
|
||||
const override;
|
||||
|
||||
/** Check if the destination account is allowed
|
||||
* to receive MPT. Return tecNO_AUTH if it doesn't
|
||||
* and tesSUCCESS otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTransfer(AccountID const& from, AccountID const& to) const override;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Token capability checks (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] bool
|
||||
canClawback() const override;
|
||||
|
||||
[[nodiscard]] bool
|
||||
requiresAuth() const override;
|
||||
|
||||
STAmount
|
||||
accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const override;
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const override;
|
||||
|
||||
protected:
|
||||
MPTID const mptID_;
|
||||
MPTIssue const mptIssue_;
|
||||
};
|
||||
|
||||
class WritableMPTokenIssuance : public virtual WritableTokenBase, public virtual MPTokenIssuance
|
||||
{
|
||||
public:
|
||||
WritableMPTokenIssuance(ApplyView& view, MPTIssue const& mptIssue)
|
||||
: ReadOnlySLE(view.peek(keylet::mptIssuance(mptIssue.getMptID())), view)
|
||||
, TokenBase(view, view.peek(keylet::mptIssuance(mptIssue.getMptID())))
|
||||
, WritableSLE(view.peek(keylet::mptIssuance(mptIssue.getMptID())), view)
|
||||
, WritableTokenBase(view, view.peek(keylet::mptIssuance(mptIssue.getMptID())))
|
||||
, MPTokenIssuance(view, mptIssue)
|
||||
{
|
||||
}
|
||||
|
||||
WritableMPTokenIssuance(ApplyView& view, MPTID const& mptID)
|
||||
: ReadOnlySLE(view.peek(keylet::mptIssuance(mptID)), view)
|
||||
, TokenBase(view, view.peek(keylet::mptIssuance(mptID)))
|
||||
, WritableSLE(view.peek(keylet::mptIssuance(mptID)), view)
|
||||
, WritableTokenBase(view, view.peek(keylet::mptIssuance(mptID)))
|
||||
, MPTokenIssuance(view, mptID)
|
||||
{
|
||||
}
|
||||
|
||||
// Resolve ambiguity: use writable operator-> for non-const, read-only for const
|
||||
using WritableSLE::operator->;
|
||||
using MPTokenIssuance::operator->;
|
||||
using WritableSLE::operator*;
|
||||
using MPTokenIssuance::operator*;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Authorization (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] TER
|
||||
authorizeMPToken(
|
||||
XRPAmount const& priorBalance,
|
||||
AccountID const& account,
|
||||
beast::Journal journal,
|
||||
std::uint32_t flags = 0,
|
||||
std::optional<AccountID> holderID = std::nullopt);
|
||||
|
||||
/** Enforce account has MPToken to match its authorization.
|
||||
*
|
||||
* Called from doApply - it will check for expired (and delete if found any)
|
||||
* credentials matching DomainID set in MPTokenIssuance. Must be called if
|
||||
* requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
enforceMPTokenAuthorization(
|
||||
AccountID const& account,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal j);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Empty holding operations (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(AccountID const& accountID, XRPAmount priorBalance, beast::Journal journal)
|
||||
override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
removeEmptyHolding(AccountID const& accountID, beast::Journal journal) override;
|
||||
|
||||
/** Create a WritableMPTokenIssuance backed by a brand-new SLE
|
||||
* (not yet inserted into the view).
|
||||
*/
|
||||
[[nodiscard]] static WritableMPTokenIssuance
|
||||
makeNew(MPTID const& mptID, ApplyView& view)
|
||||
{
|
||||
return WritableMPTokenIssuance(
|
||||
mptID, std::make_shared<SLE>(keylet::mptIssuance(mptID)), view);
|
||||
}
|
||||
|
||||
[[nodiscard]] static WritableMPTokenIssuance
|
||||
makeNew(std::uint32_t const seq, AccountID const& issuer, ApplyView& view)
|
||||
{
|
||||
auto const mptID = makeMptID(seq, issuer);
|
||||
return WritableMPTokenIssuance(
|
||||
mptID, std::make_shared<SLE>(keylet::mptIssuance(mptID)), view);
|
||||
}
|
||||
|
||||
private:
|
||||
// This is a private constructor only used by `makeNew`
|
||||
WritableMPTokenIssuance(MPTID const& mptID, std::shared_ptr<SLE> sle, ApplyView& view)
|
||||
: ReadOnlySLE(sle, view)
|
||||
, TokenBase(view, sle)
|
||||
, WritableSLE(sle, view)
|
||||
, WritableTokenBase(view, sle)
|
||||
, MPTokenIssuance(view, mptID)
|
||||
{
|
||||
insert();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
39
include/xrpl/ledger/helpers/README.md
Normal file
39
include/xrpl/ledger/helpers/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Ledger Entry Helpers (`entries/`)
|
||||
|
||||
## Overview
|
||||
|
||||
This folder contains helper classes and free functions for working with **Serialized Ledger Entries (SLEs)**. Its centerpiece is `SLEBase.h`, which defines two base classes — `ReadOnlySLE` and `WritableSLE` — that provide a type-safe, context-aware wrapper around the raw `std::shared_ptr<SLE>` used throughout the rest of the codebase.
|
||||
|
||||
## The Problem: Untyped SLE Access
|
||||
|
||||
Historically, ledger entries are passed around as bare `std::shared_ptr<SLE>` (or `std::shared_ptr<SLE const>`). This has several drawbacks:
|
||||
|
||||
1. **No compile-time entry-type safety.** Any code holding an `std::shared_ptr<SLE>` can read or write _any_ field on _any_ ledger entry type. Nothing prevents you from calling `sle->getFieldU32(sfOwnerCount)` on an Offer SLE, even though Offers don't have that field.
|
||||
|
||||
2. **No read/write distinction.** A function that only needs to _read_ an entry still receives a mutable `std::shared_ptr<SLE>`, making it easy to accidentally mutate state. Conversely, a function that _must_ write has no way to express that requirement in its signature.
|
||||
|
||||
3. **No association with the view.** The SLE and the `ReadView` / `ApplyView` it came from travel as separate arguments, so callers must manually keep them in sync and remember to call `view.update(sle)` after mutations.
|
||||
|
||||
## The Solution: `SLEBase.h`
|
||||
|
||||
`SLEBase.h` introduces two base classes that pair an SLE with its view context and enforce read/write semantics at compile time.
|
||||
|
||||
**`ReadOnlySLE`** bundles a `std::shared_ptr<SLE const>` with the `ReadView` it was read from. It provides existence checks and const-only access to the underlying entry. Derived classes add domain-specific read-only accessors (e.g. `AccountRoot::isGlobalFrozen()`).
|
||||
|
||||
**`WritableSLE`** bundles a mutable `std::shared_ptr<SLE>` with the `ApplyView` used for writes. It provides helpers to insert, update, and erase the entry in the view, keeping the SLE and its view in sync automatically.
|
||||
|
||||
### Dual-Inheritance Pattern
|
||||
|
||||
Concrete writable wrappers inherit from _both_ the read-only wrapper and `WritableSLE`:
|
||||
|
||||
```
|
||||
WritableAccountRoot
|
||||
├── AccountRoot (extends ReadOnlySLE) — read-only domain methods
|
||||
└── WritableSLE — write capabilities
|
||||
```
|
||||
|
||||
This lets a writable wrapper reuse all read-only domain logic from its parent while gaining mutation and persistence operations from `WritableSLE`.
|
||||
|
||||
## Migration Status
|
||||
|
||||
This migration is still in progress and is still in the early stages. New code should prefer the wrapper style where possible; existing free functions will be migrated incrementally.
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -18,6 +19,191 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class IOUToken : public virtual TokenBase
|
||||
{
|
||||
public:
|
||||
IOUToken(ReadView const& view, Issue const& issue)
|
||||
: ReadOnlySLE(view.read(keylet::account(issue.getIssuer())), view)
|
||||
, TokenBase(view, view.read(keylet::account(issue.getIssuer())))
|
||||
, issue_(issue)
|
||||
, issuer_(issue.getIssuer())
|
||||
, issuerAccount_(issuer_, view)
|
||||
, currency_(issue.currency)
|
||||
{
|
||||
}
|
||||
|
||||
IOUToken(ReadView const& view, AccountID const& issuer, Currency const& currency)
|
||||
: IOUToken(view, Issue{currency, issuer})
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] AccountID const&
|
||||
getIssuer() const
|
||||
{
|
||||
return issuer_;
|
||||
}
|
||||
|
||||
[[nodiscard]] Currency const&
|
||||
getCurrency() const
|
||||
{
|
||||
return currency_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen() const override
|
||||
{
|
||||
return issuerAccount_.isGlobalFrozen();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isIndividualFrozen(AccountID const& account) const override;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isFrozen(AccountID const& account, int depth = 0) const override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkFrozen(AccountID const& account) const override;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isAnyFrozen(std::initializer_list<AccountID> const& accounts, int depth = 0) const override;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isDeepFrozen(AccountID const& account, int depth = 0) const override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkDeepFrozen(AccountID const& account) const override;
|
||||
|
||||
[[nodiscard]] Rate
|
||||
transferRate() const override;
|
||||
|
||||
STAmount
|
||||
accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const override;
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const override;
|
||||
|
||||
/** Returns the funds available for account to pay for an amount.
|
||||
*
|
||||
* If the account is the issuer of the currency, it can always
|
||||
* afford to pay (returns saDefault as-is). Otherwise, returns
|
||||
* the result of accountHolds.
|
||||
*/
|
||||
[[nodiscard]] STAmount
|
||||
accountFunds(
|
||||
AccountID const& id,
|
||||
STAmount const& saDefault,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal j) const;
|
||||
|
||||
[[nodiscard]] TER
|
||||
canAddHolding() const override;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Authorization and transfer checks (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] TER
|
||||
requireAuth(AccountID const& account, AuthType authType = AuthType::Legacy, int depth = 0)
|
||||
const override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
canTransfer(AccountID const& from, AccountID const& to) const override;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Token capability checks (IOU-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] bool
|
||||
canClawback() const override;
|
||||
|
||||
[[nodiscard]] bool
|
||||
requiresAuth() const override;
|
||||
|
||||
protected:
|
||||
Issue const issue_;
|
||||
AccountID const issuer_;
|
||||
AccountRoot const issuerAccount_;
|
||||
Currency const currency_;
|
||||
};
|
||||
|
||||
class WritableIOUToken : public virtual WritableTokenBase, public virtual IOUToken
|
||||
{
|
||||
public:
|
||||
WritableIOUToken(ApplyView& view, Issue const& issue)
|
||||
: ReadOnlySLE(view.peek(keylet::account(issue.getIssuer())), view)
|
||||
, TokenBase(view, view.peek(keylet::account(issue.getIssuer())))
|
||||
, WritableSLE(view.peek(keylet::account(issue.getIssuer())), view)
|
||||
, WritableTokenBase(view, view.peek(keylet::account(issue.getIssuer())))
|
||||
, IOUToken(view, issue)
|
||||
{
|
||||
}
|
||||
|
||||
WritableIOUToken(ApplyView& view, AccountID const& issuer, Currency const& currency)
|
||||
: WritableIOUToken(view, Issue{currency, issuer})
|
||||
{
|
||||
}
|
||||
|
||||
// Resolve ambiguity: use writable operator-> for non-const, read-only for const
|
||||
using WritableSLE::operator->;
|
||||
using IOUToken::operator->;
|
||||
using WritableSLE::operator*;
|
||||
using IOUToken::operator*;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Holding management (WritableTokenBase interface)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(AccountID const& accountID, XRPAmount priorBalance, beast::Journal journal)
|
||||
override;
|
||||
|
||||
[[nodiscard]] TER
|
||||
removeEmptyHolding(AccountID const& accountID, beast::Journal journal) override;
|
||||
|
||||
/** Create a WritableIOUToken backed by a brand-new SLE
|
||||
* (not yet inserted into the view).
|
||||
*/
|
||||
[[nodiscard]] static WritableIOUToken
|
||||
makeNew(AccountID const& id, Currency const& currency, ApplyView& view)
|
||||
{
|
||||
return makeNew(Issue{currency, id}, view);
|
||||
}
|
||||
|
||||
[[nodiscard]] static WritableIOUToken
|
||||
makeNew(Issue const& issue, ApplyView& view)
|
||||
{
|
||||
return WritableIOUToken(
|
||||
issue, view, std::make_shared<SLE>(keylet::account(issue.getIssuer())));
|
||||
}
|
||||
|
||||
private:
|
||||
// This is a private constructor only used by `makeNew`
|
||||
WritableIOUToken(Issue const& issue, ApplyView& view, std::shared_ptr<SLE> sle)
|
||||
: ReadOnlySLE(sle, view)
|
||||
, TokenBase(view, sle)
|
||||
, WritableSLE(sle, view)
|
||||
, WritableTokenBase(view, sle)
|
||||
, IOUToken(view, issue)
|
||||
{
|
||||
insert();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Credit functions (from Credit.h)
|
||||
@@ -58,69 +244,6 @@ creditBalance(
|
||||
Currency const& currency);
|
||||
/** @} */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Freeze checking (IOU-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] bool
|
||||
isIndividualFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer);
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
|
||||
{
|
||||
return isIndividualFrozen(view, account, issue.currency, issue.account);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer);
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
isFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
|
||||
{
|
||||
return isFrozen(view, account, issue.currency, issue.account);
|
||||
}
|
||||
|
||||
// Overload with depth parameter for uniformity with MPTIssue version.
|
||||
// The depth parameter is ignored for IOUs since they don't have vault recursion.
|
||||
[[nodiscard]] inline bool
|
||||
isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, int /*depth*/)
|
||||
{
|
||||
return isFrozen(view, account, issue);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isDeepFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer);
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
isDeepFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& issue,
|
||||
int = 0 /*ignored*/)
|
||||
{
|
||||
return isDeepFrozen(view, account, issue.currency, issue.account);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline TER
|
||||
checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
|
||||
{
|
||||
return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Trust line operations
|
||||
@@ -137,16 +260,16 @@ trustCreate(
|
||||
bool const bSrcHigh,
|
||||
AccountID const& uSrcAccountID,
|
||||
AccountID const& uDstAccountID,
|
||||
uint256 const& uIndex, // --> ripple state entry
|
||||
SLE::ref sleAccount, // --> the account being set.
|
||||
bool const bAuth, // --> authorize account.
|
||||
bool const bNoRipple, // --> others cannot ripple through
|
||||
bool const bFreeze, // --> funds cannot leave
|
||||
bool bDeepFreeze, // --> can neither receive nor send funds
|
||||
STAmount const& saBalance, // --> balance of account being set.
|
||||
// Issuer should be noAccount()
|
||||
STAmount const& saLimit, // --> limit for account being set.
|
||||
// Issuer should be the account being set.
|
||||
uint256 const& uIndex, // ripple state entry
|
||||
WritableAccountRoot& wrappedAcct, // the account being set.
|
||||
bool const bAuth, // authorize account.
|
||||
bool const bNoRipple, // others cannot ripple through
|
||||
bool const bFreeze, // funds cannot leave
|
||||
bool bDeepFreeze, // can neither receive nor send funds
|
||||
STAmount const& saBalance, // balance of account being set.
|
||||
// Issuer should be noAccount()
|
||||
STAmount const& saLimit, // limit for account being set.
|
||||
// Issuer should be the account being set.
|
||||
std::uint32_t uQualityIn,
|
||||
std::uint32_t uQualityOut,
|
||||
beast::Journal j);
|
||||
@@ -181,43 +304,6 @@ redeemIOU(
|
||||
Issue const& issue,
|
||||
beast::Journal j);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Authorization and transfer checks (IOU-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Check if the account lacks required authorization.
|
||||
*
|
||||
* Return tecNO_AUTH or tecNO_LINE if it does
|
||||
* and tesSUCCESS otherwise.
|
||||
*
|
||||
* If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return
|
||||
* tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the
|
||||
* RippleState does exist, and the RippleState is not authorized.
|
||||
*
|
||||
* If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the
|
||||
* RippleState exists, and is not authorized. Return tecNO_LINE if
|
||||
* lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if
|
||||
* WeakAuth and lsfRequireAuth is *not* set, this function will return
|
||||
* tesSUCCESS even if RippleState does *not* exist.
|
||||
*
|
||||
* The default "Legacy" auth type is equivalent to WeakAuth.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
requireAuth(
|
||||
ReadView const& view,
|
||||
Issue const& issue,
|
||||
AccountID const& account,
|
||||
AuthType authType = AuthType::Legacy);
|
||||
|
||||
/** Check if the destination account is allowed
|
||||
* to receive IOU. Return terNO_RIPPLE if rippling is
|
||||
* disabled on both sides and tesSUCCESS otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Empty holding operations (IOU-specific)
|
||||
|
||||
200
include/xrpl/ledger/helpers/SLEBase.h
Normal file
200
include/xrpl/ledger/helpers/SLEBase.h
Normal file
@@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Read-only base class for all ledger entry view classes.
|
||||
*
|
||||
* Provides common functionality for existence checking and raw SLE read access.
|
||||
* Supports read-only (ReadView) contexts.
|
||||
*
|
||||
* Derived classes should provide domain-specific accessors that hide
|
||||
* implementation details of the underlying ledger entry format.
|
||||
*/
|
||||
class ReadOnlySLE
|
||||
{
|
||||
public:
|
||||
virtual ~ReadOnlySLE() = default;
|
||||
|
||||
// Copy/move constructors are fine (reference can be initialized from another)
|
||||
ReadOnlySLE(ReadOnlySLE const&) = default;
|
||||
ReadOnlySLE(ReadOnlySLE&&) = default;
|
||||
// Assignment operators are deleted (cannot rebind reference members)
|
||||
ReadOnlySLE&
|
||||
operator=(ReadOnlySLE const&) = delete;
|
||||
ReadOnlySLE&
|
||||
operator=(ReadOnlySLE&&) = delete;
|
||||
|
||||
/** Returns true if the ledger entry exists */
|
||||
bool
|
||||
exists() const
|
||||
{
|
||||
return sle_ != nullptr;
|
||||
}
|
||||
|
||||
/** Explicit conversion to bool for convenient existence checking */
|
||||
explicit
|
||||
operator bool() const
|
||||
{
|
||||
return exists();
|
||||
}
|
||||
|
||||
/** Returns the underlying SLE for read access (always available) */
|
||||
std::shared_ptr<SLE const> const&
|
||||
sle() const
|
||||
{
|
||||
return sle_;
|
||||
}
|
||||
|
||||
/** Returns the read view (always available) */
|
||||
ReadView const&
|
||||
readView() const
|
||||
{
|
||||
return readView_;
|
||||
}
|
||||
|
||||
STLedgerEntry const*
|
||||
operator->() const
|
||||
{
|
||||
XRPL_ASSERT(exists(), "xrpl::ReadOnlySLE::operator-> : exists");
|
||||
return sle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry const&
|
||||
operator*() const
|
||||
{
|
||||
XRPL_ASSERT(exists(), "xrpl::ReadOnlySLE::operator* : exists");
|
||||
return *sle_;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Default constructor is deleted (cannot leave reference uninitialized)
|
||||
ReadOnlySLE() = delete;
|
||||
|
||||
/** Constructor for read-only context (ReadView) */
|
||||
explicit ReadOnlySLE(std::shared_ptr<SLE const> sle, ReadView const& view)
|
||||
: sle_(std::move(sle)), readView_(view)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<SLE const> sle_; // Always valid (const view)
|
||||
ReadView const& readView_; // Always valid
|
||||
};
|
||||
|
||||
/**
|
||||
* Writable base class for all ledger entry view classes.
|
||||
*
|
||||
* Extends ReadOnlySLE with write access capabilities.
|
||||
* Supports read-write (ApplyView) contexts.
|
||||
*
|
||||
* Derived classes should provide domain-specific accessors that hide
|
||||
* implementation details of the underlying ledger entry format.
|
||||
*/
|
||||
class WritableSLE
|
||||
{
|
||||
public:
|
||||
virtual ~WritableSLE() = default;
|
||||
|
||||
// Copy/move constructors are fine (reference can be initialized from another)
|
||||
WritableSLE(WritableSLE const&) = default;
|
||||
WritableSLE(WritableSLE&&) = default;
|
||||
// Assignment operators are deleted (cannot rebind reference members)
|
||||
WritableSLE&
|
||||
operator=(WritableSLE const&) = delete;
|
||||
WritableSLE&
|
||||
operator=(WritableSLE&&) = delete;
|
||||
|
||||
/** Returns a mutable SLE for write operations */
|
||||
std::shared_ptr<SLE> const&
|
||||
mutableSle() const
|
||||
{
|
||||
return mutableSle_;
|
||||
}
|
||||
|
||||
/** Returns true if this wrapper supports write operations */
|
||||
bool
|
||||
canModify() const
|
||||
{
|
||||
return mutableSle_ != nullptr;
|
||||
}
|
||||
|
||||
/** Returns the apply view for write operations */
|
||||
ApplyView&
|
||||
applyView() const
|
||||
{
|
||||
return applyView_;
|
||||
}
|
||||
|
||||
STLedgerEntry*
|
||||
operator->()
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::WritableSLE::operator-> : can modify");
|
||||
return mutableSle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry&
|
||||
operator*()
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::WritableSLE::operator* : can modify");
|
||||
return *mutableSle_;
|
||||
}
|
||||
|
||||
void
|
||||
insert()
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::WritableSLE::insert : can modify");
|
||||
applyView_.insert(mutableSle_);
|
||||
}
|
||||
|
||||
void
|
||||
erase()
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::WritableSLE::erase : can modify");
|
||||
applyView_.erase(mutableSle_);
|
||||
}
|
||||
|
||||
void
|
||||
update()
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::WritableSLE::update : can modify");
|
||||
applyView_.update(mutableSle_);
|
||||
}
|
||||
|
||||
void
|
||||
newSLE()
|
||||
{
|
||||
XRPL_ASSERT(!canModify(), "xrpl::WritableSLE::newSLE : mutableSle_ is not null");
|
||||
mutableSle_ = std::make_shared<SLE>(key_);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Default constructor is deleted (cannot leave reference uninitialized)
|
||||
WritableSLE() = delete;
|
||||
|
||||
/** Constructor for read-write context (ApplyView) */
|
||||
explicit WritableSLE(std::shared_ptr<SLE> sle, ApplyView& view)
|
||||
: applyView_(view)
|
||||
, key_(sle ? Keylet(sle->getType(), sle->key()) : Keylet(ltANY, uint256{}))
|
||||
, mutableSle_(std::move(sle))
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor for read-write context (ApplyView) */
|
||||
explicit WritableSLE(Keylet const& key, ApplyView& view)
|
||||
: applyView_(view), key_(key), mutableSle_(applyView_.peek(key))
|
||||
{
|
||||
}
|
||||
|
||||
ApplyView& applyView_; // ApplyView for write contexts (first for init order)
|
||||
Keylet const key_;
|
||||
std::shared_ptr<SLE> mutableSle_; // Mutable SLE for write contexts
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -48,82 +50,156 @@ enum class AuthType { StrongAuth, WeakAuth, Legacy };
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(ReadView const& view, Asset const& asset);
|
||||
class TokenBase : public virtual ReadOnlySLE
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] virtual bool
|
||||
isGlobalFrozen() const = 0;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
|
||||
[[nodiscard]] virtual bool
|
||||
isIndividualFrozen(AccountID const& account) const = 0;
|
||||
|
||||
/**
|
||||
* isFrozen check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0);
|
||||
/**
|
||||
* isFrozen check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
isFrozen(AccountID const& account, int depth = 0) const;
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue);
|
||||
[[nodiscard]] virtual TER
|
||||
checkFrozen(AccountID const& account) const = 0;
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
|
||||
[[nodiscard]] virtual bool
|
||||
isAnyFrozen(std::initializer_list<AccountID> const& accounts, int depth = 0) const = 0;
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
|
||||
/**
|
||||
* isFrozen check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
isDeepFrozen(AccountID const& account, int depth = 0) const = 0;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isAnyFrozen(
|
||||
ReadView const& view,
|
||||
std::initializer_list<AccountID> const& accounts,
|
||||
Issue const& issue);
|
||||
[[nodiscard]] virtual TER
|
||||
checkDeepFrozen(AccountID const& account) const = 0;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isAnyFrozen(
|
||||
ReadView const& view,
|
||||
std::initializer_list<AccountID> const& accounts,
|
||||
Asset const& asset,
|
||||
int depth = 0);
|
||||
/** Returns the transfer fee as Rate based on the type of token
|
||||
* @param view The ledger view
|
||||
* @param amount The amount to transfer
|
||||
*/
|
||||
[[nodiscard]] virtual Rate
|
||||
transferRate() const = 0;
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Account balance functions (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] bool
|
||||
isDeepFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTIssue const& mptIssue,
|
||||
int depth = 0);
|
||||
// Returns the amount an account can spend.
|
||||
//
|
||||
// If shSIMPLE_BALANCE is specified, this is the amount the account can spend
|
||||
// without going into debt.
|
||||
//
|
||||
// If shFULL_BALANCE is specified, this is the amount the account can spend
|
||||
// total. Specifically:
|
||||
// * The account can go into debt if using a trust line, and the other side has
|
||||
// a non-zero limit.
|
||||
// * If the account is the asset issuer the limit is defined by the asset /
|
||||
// issuance.
|
||||
//
|
||||
// <-- saAmount: amount of currency held by account. May be negative.
|
||||
virtual STAmount
|
||||
accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const = 0;
|
||||
|
||||
/**
|
||||
* isFrozen check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0);
|
||||
[[nodiscard]] virtual STAmount
|
||||
accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const = 0;
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
|
||||
[[nodiscard]] virtual TER
|
||||
canAddHolding() const = 0;
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Authorization and transfer checks (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Account balance functions (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
[[nodiscard]] virtual TER
|
||||
requireAuth(AccountID const& account, AuthType authType = AuthType::Legacy, int depth = 0)
|
||||
const = 0;
|
||||
|
||||
// Returns the amount an account can spend.
|
||||
//
|
||||
// If shSIMPLE_BALANCE is specified, this is the amount the account can spend
|
||||
// without going into debt.
|
||||
//
|
||||
// If shFULL_BALANCE is specified, this is the amount the account can spend
|
||||
// total. Specifically:
|
||||
// * The account can go into debt if using a trust line, and the other side has
|
||||
// a non-zero limit.
|
||||
// * If the account is the asset issuer the limit is defined by the asset /
|
||||
// issuance.
|
||||
//
|
||||
// <-- saAmount: amount of currency held by account. May be negative.
|
||||
[[nodiscard]] virtual TER
|
||||
canTransfer(AccountID const& from, AccountID const& to) const = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Token capability checks (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Check if the token issuer has enabled clawback capability.
|
||||
* For IOUs, checks lsfAllowTrustLineClawback on issuer's AccountRoot.
|
||||
* For MPTs, checks lsfMPTCanClawback on the issuance.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
canClawback() const = 0;
|
||||
|
||||
/** Check if the token requires authorization for holders.
|
||||
* For IOUs, checks lsfRequireAuth on issuer's AccountRoot.
|
||||
* For MPTs, checks lsfMPTRequireAuth on the issuance.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
requiresAuth() const = 0;
|
||||
|
||||
protected:
|
||||
TokenBase(ReadView const& view, std::shared_ptr<SLE const> sle) : ReadOnlySLE(sle, view)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class WritableTokenBase : public virtual TokenBase, public virtual WritableSLE
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Holding operations (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] virtual TER
|
||||
addEmptyHolding(AccountID const& accountID, XRPAmount priorBalance, beast::Journal journal) = 0;
|
||||
|
||||
[[nodiscard]] virtual TER
|
||||
removeEmptyHolding(AccountID const& accountID, beast::Journal journal) = 0;
|
||||
|
||||
protected:
|
||||
WritableTokenBase(ApplyView& view, std::shared_ptr<SLE> sle)
|
||||
: TokenBase(view, sle), WritableSLE(sle, view)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<TokenBase>
|
||||
makeTokenBase(ReadView const& view, Asset const& asset);
|
||||
|
||||
std::unique_ptr<WritableTokenBase>
|
||||
makeWritableTokenBase(ApplyView& view, Asset const& asset);
|
||||
|
||||
// Helper function to get transfer rate from an STAmount
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, STAmount const& amount);
|
||||
|
||||
// Returns the amount the specified account can spend.
|
||||
// Supports both IOU and MPT via Currency/AccountID parameters.
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
@@ -134,25 +210,8 @@ accountHolds(
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& issue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTIssue const& mptIssue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
// Returns the amount the specified account can spend for a given Asset.
|
||||
// Dispatches to appropriate token wrapper based on Asset type.
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
@@ -163,66 +222,6 @@ accountHolds(
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
|
||||
|
||||
// Returns the amount an account can spend of the currency type saDefault, or
|
||||
// returns saDefault if this account is the issuer of the currency in
|
||||
// question. Should be used in favor of accountHolds when questioning how much
|
||||
// an account can spend while also allowing currency issuers to spend
|
||||
// unlimited amounts of their own currency (since they can always issue more).
|
||||
[[nodiscard]] STAmount
|
||||
accountFunds(
|
||||
ReadView const& view,
|
||||
AccountID const& id,
|
||||
STAmount const& saDefault,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal j);
|
||||
|
||||
/** Returns the transfer fee as Rate based on the type of token
|
||||
* @param view The ledger view
|
||||
* @param amount The amount to transfer
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, STAmount const& amount);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Holding operations (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, Asset const& asset);
|
||||
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(
|
||||
ApplyView& view,
|
||||
AccountID const& accountID,
|
||||
XRPAmount priorBalance,
|
||||
Asset const& asset,
|
||||
beast::Journal journal);
|
||||
|
||||
[[nodiscard]] TER
|
||||
removeEmptyHolding(
|
||||
ApplyView& view,
|
||||
AccountID const& accountID,
|
||||
Asset const& asset,
|
||||
beast::Journal journal);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Authorization and transfer checks (Asset-based dispatchers)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] TER
|
||||
requireAuth(
|
||||
ReadView const& view,
|
||||
Asset const& asset,
|
||||
AccountID const& account,
|
||||
AuthType authType = AuthType::Legacy);
|
||||
|
||||
[[nodiscard]] TER
|
||||
canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Money Transfers (Asset-based dispatchers)
|
||||
@@ -232,7 +231,7 @@ canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, Acc
|
||||
// Direct send w/o fees:
|
||||
// - Redeeming IOUs and/or sending sender's own IOUs.
|
||||
// - Create trust line of needed.
|
||||
// --> bCheckIssuer : normally require issuer to be involved.
|
||||
// bCheckIssuer : normally require issuer to be involved.
|
||||
// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles.
|
||||
|
||||
/** Calls static rippleCreditIOU if saAmount represents Issue.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
@@ -21,7 +22,7 @@ namespace xrpl {
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& assets);
|
||||
|
||||
/** From the perspective of a vault, return the number of assets to take from
|
||||
@@ -37,7 +38,7 @@ assetsToSharesDeposit(
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& shares);
|
||||
|
||||
/** Controls whether to truncate shares instead of rounding. */
|
||||
@@ -58,7 +59,7 @@ enum class TruncateShares : bool { no = false, yes = true };
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate = TruncateShares::no);
|
||||
|
||||
@@ -75,7 +76,7 @@ assetsToSharesWithdraw(
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& shares);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Deprecated constant for backwards compatibility with pre-XRPFees amendment.
|
||||
// This was the reference fee units used in the old fee calculation.
|
||||
inline constexpr std::uint32_t FEE_UNITS_DEPRECATED = 10;
|
||||
|
||||
/** Reflects the fee settings for a particular ledger.
|
||||
|
||||
The fees are always the same for any transactions applied
|
||||
@@ -15,25 +11,15 @@ inline constexpr std::uint32_t FEE_UNITS_DEPRECATED = 10;
|
||||
*/
|
||||
struct Fees
|
||||
{
|
||||
/** @brief Cost of a reference transaction in drops. */
|
||||
XRPAmount base{0};
|
||||
|
||||
/** @brief Minimum XRP an account must hold to exist on the ledger. */
|
||||
XRPAmount reserve{0};
|
||||
|
||||
/** @brief Additional XRP reserve required per owned ledger object. */
|
||||
XRPAmount increment{0};
|
||||
XRPAmount base{0}; // Reference tx cost (drops)
|
||||
XRPAmount reserve{0}; // Reserve base (drops)
|
||||
XRPAmount increment{0}; // Reserve increment (drops)
|
||||
|
||||
explicit Fees() = default;
|
||||
Fees(Fees const&) = default;
|
||||
Fees&
|
||||
operator=(Fees const&) = default;
|
||||
|
||||
Fees(XRPAmount base_, XRPAmount reserve_, XRPAmount increment_)
|
||||
: base(base_), reserve(reserve_), increment(increment_)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the account reserve given the owner count, in drops.
|
||||
|
||||
The reserve is calculated as the reserve base plus
|
||||
|
||||
@@ -287,7 +287,7 @@ credential(uint256 const& key) noexcept
|
||||
}
|
||||
|
||||
Keylet
|
||||
mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept;
|
||||
mptIssuance(std::uint32_t const seq, AccountID const& issuer) noexcept;
|
||||
|
||||
Keylet
|
||||
mptIssuance(MPTID const& issuanceID) noexcept;
|
||||
|
||||
@@ -72,8 +72,4 @@ deserializeHeader(Slice data, bool hasHash = false);
|
||||
LedgerHeader
|
||||
deserializePrefixedHeader(Slice data, bool hasHash = false);
|
||||
|
||||
/** Calculate the hash of a ledger header. */
|
||||
uint256
|
||||
calculateLedgerHash(LedgerHeader const& info);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -72,12 +72,12 @@ public:
|
||||
isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const;
|
||||
|
||||
// for tx level permission, permission value is equal to tx type plus one
|
||||
static uint32_t
|
||||
txToPermissionType(TxType const& type);
|
||||
uint32_t
|
||||
txToPermissionType(TxType const& type) const;
|
||||
|
||||
// tx type value is permission value minus one
|
||||
static TxType
|
||||
permissionToTxType(uint32_t const& value);
|
||||
TxType
|
||||
permissionToTxType(uint32_t const& value) const;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -69,6 +69,9 @@ public:
|
||||
std::vector<uint256>::iterator
|
||||
insert(std::vector<uint256>::const_iterator pos, uint256 const& value);
|
||||
|
||||
std::vector<uint256>::iterator
|
||||
insert(std::vector<uint256>::const_iterator pos, uint256&& value);
|
||||
|
||||
void
|
||||
push_back(uint256 const& v);
|
||||
|
||||
@@ -181,6 +184,12 @@ STVector256::insert(std::vector<uint256>::const_iterator pos, uint256 const& val
|
||||
return mValue.insert(pos, value);
|
||||
}
|
||||
|
||||
inline std::vector<uint256>::iterator
|
||||
STVector256::insert(std::vector<uint256>::const_iterator pos, uint256&& value)
|
||||
{
|
||||
return mValue.insert(pos, std::move(value));
|
||||
}
|
||||
|
||||
inline void
|
||||
STVector256::push_back(uint256 const& v)
|
||||
{
|
||||
|
||||
@@ -336,7 +336,7 @@ public:
|
||||
static_assert(N > 0, "");
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
std::size_t
|
||||
empty() const noexcept
|
||||
{
|
||||
return remain_ == 0;
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class TxSearched { All, Some, Unknown };
|
||||
enum class TxSearched { all, some, unknown };
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/core/PerfLog.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/core/StartUpType.h>
|
||||
#include <xrpl/rdb/DBInit.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
@@ -70,7 +69,7 @@ public:
|
||||
{
|
||||
explicit Setup() = default;
|
||||
|
||||
StartUpType startUp = StartUpType::Normal;
|
||||
StartUpType startUp = StartUpType::NORMAL;
|
||||
bool standAlone = false;
|
||||
boost::filesystem::path dataDir;
|
||||
// Indicates whether or not to return the `globalPragma`
|
||||
@@ -95,7 +94,7 @@ public:
|
||||
struct CheckpointerSetup
|
||||
{
|
||||
JobQueue* jobQueue;
|
||||
std::reference_wrapper<ServiceRegistry> registry;
|
||||
Logs* logs;
|
||||
};
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
@@ -107,8 +106,9 @@ public:
|
||||
beast::Journal journal)
|
||||
// Use temporary files or regular DB files?
|
||||
: DatabaseCon(
|
||||
setup.standAlone && setup.startUp != StartUpType::Load &&
|
||||
setup.startUp != StartUpType::LoadFile && setup.startUp != StartUpType::Replay
|
||||
setup.standAlone && setup.startUp != StartUpType::LOAD &&
|
||||
setup.startUp != StartUpType::LOAD_FILE &&
|
||||
setup.startUp != StartUpType::REPLAY
|
||||
? ""
|
||||
: (setup.dataDir / dbName),
|
||||
setup.commonPragma(),
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
beast::Journal journal)
|
||||
: DatabaseCon(setup, dbName, pragma, initSQL, journal)
|
||||
{
|
||||
setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get());
|
||||
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
|
||||
}
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
@@ -154,7 +154,7 @@ public:
|
||||
beast::Journal journal)
|
||||
: DatabaseCon(dataDir, dbName, pragma, initSQL, journal)
|
||||
{
|
||||
setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get());
|
||||
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
|
||||
}
|
||||
|
||||
~DatabaseCon();
|
||||
@@ -177,7 +177,7 @@ public:
|
||||
|
||||
private:
|
||||
void
|
||||
setupCheckpointing(JobQueue*, ServiceRegistry&);
|
||||
setupCheckpointing(JobQueue*, Logs&);
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
|
||||
@@ -49,9 +49,8 @@ public:
|
||||
struct AccountTxOptions
|
||||
{
|
||||
AccountID const& account;
|
||||
/// Ledger sequence range to search. A value of 0 for min or max
|
||||
/// means unbounded in that direction (no constraint applied).
|
||||
LedgerRange ledgerRange;
|
||||
std::uint32_t minLedger;
|
||||
std::uint32_t maxLedger;
|
||||
std::uint32_t offset;
|
||||
std::uint32_t limit;
|
||||
bool bUnlimited;
|
||||
@@ -60,7 +59,8 @@ public:
|
||||
struct AccountTxPageOptions
|
||||
{
|
||||
AccountID const& account;
|
||||
LedgerRange ledgerRange;
|
||||
std::uint32_t minLedger;
|
||||
std::uint32_t maxLedger;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::uint32_t limit;
|
||||
bool bAdmin;
|
||||
@@ -247,7 +247,7 @@ public:
|
||||
* @return Struct CountMinMax which contains the minimum sequence,
|
||||
* maximum sequence and number of ledgers.
|
||||
*/
|
||||
virtual CountMinMax
|
||||
virtual struct CountMinMax
|
||||
getLedgerCountMinMax() = 0;
|
||||
|
||||
/**
|
||||
@@ -405,10 +405,10 @@ public:
|
||||
* @param id Hash of the transaction.
|
||||
* @param range Range of ledgers to check, if present.
|
||||
* @param ec Default error code value.
|
||||
* @return Transaction and its metadata if found, otherwise TxSearched::All
|
||||
* @return Transaction and its metadata if found, otherwise TxSearched::all
|
||||
* if a range is provided and all ledgers from the range are present
|
||||
* in the database, TxSearched::Some if a range is provided and not
|
||||
* all ledgers are present, TxSearched::Unknown if the range is not
|
||||
* in the database, TxSearched::some if a range is provided and not
|
||||
* all ledgers are present, TxSearched::unknown if the range is not
|
||||
* provided or a deserializing error occurred. In the last case the
|
||||
* error code is returned via the ec parameter, in other cases the
|
||||
* default error code is not changed.
|
||||
@@ -455,10 +455,9 @@ public:
|
||||
closeTransactionDB() = 0;
|
||||
};
|
||||
|
||||
template <typename T, typename C>
|
||||
template <class T, class C>
|
||||
T
|
||||
rangeCheckedCast(C c)
|
||||
requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<C> && std::convertible_to<C, T>)
|
||||
{
|
||||
if ((c > std::numeric_limits<T>::max()) || (!std::numeric_limits<T>::is_signed && c < 0) ||
|
||||
(std::numeric_limits<T>::is_signed && std::numeric_limits<C>::is_signed &&
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/core/JobQueue.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
|
||||
#define SOCI_USE_BOOST
|
||||
#include <soci/soci.h>
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
and so must outlive them both.
|
||||
*/
|
||||
std::shared_ptr<Checkpointer>
|
||||
makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, ServiceRegistry&);
|
||||
makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, Logs&);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
|
||||
@@ -520,7 +520,7 @@ private:
|
||||
// getMissingNodes helper functions
|
||||
void
|
||||
gmn_ProcessNodes(MissingNodes&, MissingNodes::StackEntry& node);
|
||||
static void
|
||||
void
|
||||
gmn_ProcessDeferredReads(MissingNodes&);
|
||||
|
||||
// fetch from DB helper function
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
XRPL_ASSERT((flags & tapBATCH) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
std::reference_wrapper<ServiceRegistry> registry;
|
||||
ServiceRegistry& registry;
|
||||
STTx const& tx;
|
||||
TER const preclaimResult;
|
||||
XRPAmount const baseFee;
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
checkInvariants(TER const result, XRPAmount const fee);
|
||||
|
||||
private:
|
||||
static TER
|
||||
TER
|
||||
failInvariantCheck(TER const result);
|
||||
|
||||
template <std::size_t... Is>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/WrappedSink.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
@@ -13,7 +14,7 @@ namespace xrpl {
|
||||
struct PreflightContext
|
||||
{
|
||||
public:
|
||||
std::reference_wrapper<ServiceRegistry> registry;
|
||||
ServiceRegistry& registry;
|
||||
STTx const& tx;
|
||||
Rules const rules;
|
||||
ApplyFlags flags;
|
||||
@@ -56,7 +57,7 @@ public:
|
||||
struct PreclaimContext
|
||||
{
|
||||
public:
|
||||
std::reference_wrapper<ServiceRegistry> registry;
|
||||
ServiceRegistry& registry;
|
||||
ReadView const& view;
|
||||
TER preflightResult;
|
||||
ApplyFlags flags;
|
||||
@@ -113,7 +114,8 @@ protected:
|
||||
beast::WrappedSink sink_;
|
||||
beast::Journal const j_;
|
||||
|
||||
AccountID const account_;
|
||||
AccountID const accountID_;
|
||||
WritableAccountRoot account_;
|
||||
XRPAmount preFeeBalance_{}; // Balance before fees.
|
||||
|
||||
virtual ~Transactor() = default;
|
||||
|
||||
@@ -48,7 +48,7 @@ private:
|
||||
bool
|
||||
isValidEntry(std::shared_ptr<SLE const> const& before, std::shared_ptr<SLE const> const& after);
|
||||
|
||||
static STAmount
|
||||
STAmount
|
||||
calculateBalanceChange(
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after,
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
std::shared_ptr<SLE const>
|
||||
findIssuer(AccountID const& issuerID, ReadView const& view);
|
||||
|
||||
static bool
|
||||
bool
|
||||
validateIssuerChanges(
|
||||
std::shared_ptr<SLE const> const& issuer,
|
||||
IssuerChanges const& changes,
|
||||
@@ -71,7 +71,7 @@ private:
|
||||
beast::Journal const& j,
|
||||
bool enforce);
|
||||
|
||||
static bool
|
||||
bool
|
||||
validateFrozenState(
|
||||
BalanceChange const& change,
|
||||
bool high,
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/tx/invariants/AMMInvariant.h>
|
||||
#include <xrpl/tx/invariants/FreezeInvariant.h>
|
||||
#include <xrpl/tx/invariants/LoanBrokerInvariant.h>
|
||||
#include <xrpl/tx/invariants/LoanInvariant.h>
|
||||
#include <xrpl/tx/invariants/MPTInvariant.h>
|
||||
#include <xrpl/tx/invariants/NFTInvariant.h>
|
||||
@@ -112,7 +111,7 @@ public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
static bool
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Invariants: Loan brokers are internally consistent
|
||||
*
|
||||
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
|
||||
* node (the root), which will only hold entries for `RippleState` or
|
||||
* `MPToken` objects.
|
||||
*
|
||||
*/
|
||||
class ValidLoanBroker
|
||||
{
|
||||
// Not all of these elements will necessarily be populated. Remaining items
|
||||
// will be looked up as needed.
|
||||
struct BrokerInfo
|
||||
{
|
||||
SLE::const_pointer brokerBefore = nullptr;
|
||||
// After is used for most of the checks, except
|
||||
// those that check changed values.
|
||||
SLE::const_pointer brokerAfter = nullptr;
|
||||
};
|
||||
// Collect all the LoanBrokers found directly or indirectly through
|
||||
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
|
||||
// LoanBroker object if brokerBefore and brokerAfter are nullptr
|
||||
std::map<uint256, BrokerInfo> brokers_;
|
||||
// Collect all the modified trust lines. Their high and low accounts will be
|
||||
// loaded to look for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> lines_;
|
||||
// Collect all the modified MPTokens. Their accounts will be loaded to look
|
||||
// for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> mpts_;
|
||||
|
||||
static bool
|
||||
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j);
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,14 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Invariants: Loan brokers are internally consistent
|
||||
*
|
||||
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
|
||||
* node (the root), which will only hold entries for `RippleState` or
|
||||
* `MPToken` objects.
|
||||
*
|
||||
*/
|
||||
class ValidLoanBroker
|
||||
{
|
||||
// Not all of these elements will necessarily be populated. Remaining items
|
||||
// will be looked up as needed.
|
||||
struct BrokerInfo
|
||||
{
|
||||
SLE::const_pointer brokerBefore = nullptr;
|
||||
// After is used for most of the checks, except
|
||||
// those that check changed values.
|
||||
SLE::const_pointer brokerAfter = nullptr;
|
||||
};
|
||||
// Collect all the LoanBrokers found directly or indirectly through
|
||||
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
|
||||
// LoanBroker object if brokerBefore and brokerAfter are nullptr
|
||||
std::map<uint256, BrokerInfo> brokers_;
|
||||
// Collect all the modified trust lines. Their high and low accounts will be
|
||||
// loaded to look for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> lines_;
|
||||
// Collect all the modified MPTokens. Their accounts will be loaded to look
|
||||
// for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> mpts_;
|
||||
|
||||
bool
|
||||
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j) const;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Loans are internally consistent
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
STPathSet const& spsPaths,
|
||||
|
||||
std::optional<uint256> const& domainID,
|
||||
ServiceRegistry& registry,
|
||||
Logs& l,
|
||||
Input const* const pInputs = nullptr);
|
||||
|
||||
// The view we are currently working on
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/OfferHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
|
||||
@@ -203,7 +203,7 @@ getAMMOfferStartWithTakerGets(
|
||||
|
||||
// Try to reduce the offer size to improve the quality.
|
||||
// The quality might still not match the targetQuality for a tiny offer.
|
||||
if (auto amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
|
||||
if (auto const amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
|
||||
return getAmounts(detail::reduceOffer(amounts.out));
|
||||
else
|
||||
return amounts;
|
||||
@@ -270,7 +270,7 @@ getAMMOfferStartWithTakerPays(
|
||||
|
||||
// Try to reduce the offer size to improve the quality.
|
||||
// The quality might still not match the targetQuality for a tiny offer.
|
||||
if (auto amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
|
||||
if (auto const amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
|
||||
return getAmounts(detail::reduceOffer(amounts.in));
|
||||
else
|
||||
return amounts;
|
||||
@@ -335,7 +335,8 @@ changeSpotPriceQuality(
|
||||
}
|
||||
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPays, Number::upward);
|
||||
// should not fail
|
||||
if (auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
if (auto const amounts =
|
||||
TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
Quality{amounts} < quality &&
|
||||
!withinRelativeDistance(Quality{amounts}, quality, Number(1, -7)))
|
||||
{
|
||||
@@ -361,7 +362,7 @@ changeSpotPriceQuality(
|
||||
|
||||
// Generate the offer starting with XRP side. Return seated offer amounts
|
||||
// if the offer can be generated, otherwise nullopt.
|
||||
auto amounts = [&]() {
|
||||
auto const amounts = [&]() {
|
||||
if (isXRP(getIssue(pool.out)))
|
||||
return getAMMOfferStartWithTakerGets(pool, quality, tfee);
|
||||
return getAMMOfferStartWithTakerPays(pool, quality, tfee);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -475,4 +475,15 @@ loanMakePayment(
|
||||
LoanPaymentType const paymentType,
|
||||
beast::Journal j);
|
||||
|
||||
// LoanBroker-specific `adjustOwnerCount` function (temporary, while the Wrapped classes are WIP)
|
||||
// Assert will check the type, so that we ensure it's not used by anything else
|
||||
// Order of parameters is different from the old `adjustOwnerCount` function to avoid anything
|
||||
// accidentally calling this with the wrong type.
|
||||
void
|
||||
adjustOwnerCount(
|
||||
std::shared_ptr<SLE> const& sle,
|
||||
ApplyView& view,
|
||||
std::int32_t amount,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -26,7 +27,7 @@ private:
|
||||
Expected<std::pair<STAmount, STAmount>, TER>
|
||||
assetsToClawback(
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
std::shared_ptr<SLE const> const& sleShareIssuance,
|
||||
MPTokenIssuance const& shareIssuance,
|
||||
AccountID const& holder,
|
||||
STAmount const& clawbackAmount);
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ getFileContents(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result{
|
||||
std::string const result{
|
||||
std::istreambuf_iterator<char>{fileStream}, std::istreambuf_iterator<char>{}};
|
||||
|
||||
if (fileStream.bad())
|
||||
|
||||
@@ -981,7 +981,7 @@ root(Number f, unsigned d)
|
||||
auto ex = [e = e, di = di]() // Euclidean remainder of e/d
|
||||
{
|
||||
int k = (e >= 0 ? e : e - (di - 1)) / di;
|
||||
int k2 = e - (k * di);
|
||||
int k2 = e - k * di;
|
||||
if (k2 == 0)
|
||||
return 0;
|
||||
return di - k2;
|
||||
@@ -998,7 +998,7 @@ root(Number f, unsigned d)
|
||||
}
|
||||
|
||||
// Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
|
||||
auto const D = (((6 * di + 11) * di + 6) * di) + 1;
|
||||
auto const D = ((6 * di + 11) * di + 6) * di + 1;
|
||||
auto const a0 = 3 * di * ((2 * di - 3) * di + 1);
|
||||
auto const a1 = 24 * di * (2 * di - 1);
|
||||
auto const a2 = -30 * (di - 1) * di;
|
||||
|
||||
@@ -169,7 +169,7 @@ public:
|
||||
XRPL_ASSERT(m_stopped == true, "xrpl::ResolverAsioImpl::start : stopped");
|
||||
XRPL_ASSERT(m_stop_called == false, "xrpl::ResolverAsioImpl::start : not stopping");
|
||||
|
||||
if (m_stopped.exchange(false))
|
||||
if (m_stopped.exchange(false) == true)
|
||||
{
|
||||
{
|
||||
std::lock_guard lk{m_mut};
|
||||
@@ -182,7 +182,7 @@ public:
|
||||
void
|
||||
stop_async() override
|
||||
{
|
||||
if (!m_stop_called.exchange(true))
|
||||
if (m_stop_called.exchange(true) == false)
|
||||
{
|
||||
boost::asio::dispatch(
|
||||
m_io_context,
|
||||
@@ -229,7 +229,7 @@ public:
|
||||
{
|
||||
XRPL_ASSERT(m_stop_called == true, "xrpl::ResolverAsioImpl::do_stop : stopping");
|
||||
|
||||
if (!m_stopped.exchange(true))
|
||||
if (m_stopped.exchange(true) == false)
|
||||
{
|
||||
m_work.clear();
|
||||
m_resolver.cancel();
|
||||
@@ -271,7 +271,7 @@ public:
|
||||
m_strand, std::bind(&ResolverAsioImpl::do_work, this, CompletionCounter(this))));
|
||||
}
|
||||
|
||||
static HostAndPort
|
||||
HostAndPort
|
||||
parseName(std::string const& str)
|
||||
{
|
||||
// first attempt to parse as an endpoint (IP addr + port).
|
||||
@@ -319,7 +319,7 @@ public:
|
||||
void
|
||||
do_work(CompletionCounter)
|
||||
{
|
||||
if (m_stop_called)
|
||||
if (m_stop_called == true)
|
||||
return;
|
||||
|
||||
// We don't have any work to do at this time
|
||||
@@ -367,7 +367,7 @@ public:
|
||||
{
|
||||
XRPL_ASSERT(!names.empty(), "xrpl::ResolverAsioImpl::do_resolve : names non-empty");
|
||||
|
||||
if (!m_stop_called)
|
||||
if (m_stop_called == false)
|
||||
{
|
||||
m_work.emplace_back(names, handler);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ sqlBlobLiteral(Blob const& blob)
|
||||
{
|
||||
std::string j;
|
||||
|
||||
j.reserve((blob.size() * 2) + 3);
|
||||
j.reserve(blob.size() * 2 + 3);
|
||||
j.push_back('X');
|
||||
j.push_back('\'');
|
||||
boost::algorithm::hex(blob.begin(), blob.end(), std::back_inserter(j));
|
||||
|
||||
@@ -107,7 +107,7 @@ encode(void* dest, void const* src, std::size_t len)
|
||||
char const* in = static_cast<char const*>(src);
|
||||
auto const tab = base64::get_alphabet();
|
||||
|
||||
for (auto n = len / 3; n != 0u; --n)
|
||||
for (auto n = len / 3; n--;)
|
||||
{
|
||||
*out++ = tab[(in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
|
||||
@@ -162,7 +162,7 @@ decode(void* dest, char const* src, std::size_t len)
|
||||
|
||||
auto const inverse = base64::get_inverse();
|
||||
|
||||
while (((len--) != 0u) && *in != '=')
|
||||
while (len-- && *in != '=')
|
||||
{
|
||||
auto const v = inverse[*in];
|
||||
if (v == -1)
|
||||
@@ -181,7 +181,7 @@ decode(void* dest, char const* src, std::size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
if (i != 0)
|
||||
if (i)
|
||||
{
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
|
||||
@@ -253,7 +253,7 @@ initAuthenticated(
|
||||
// VFALCO Replace fopen() with RAII
|
||||
FILE* f = fopen(chain_file.c_str(), "r");
|
||||
|
||||
if (f == nullptr)
|
||||
if (!f)
|
||||
{
|
||||
LogicError(
|
||||
"Problem opening SSL chain file" +
|
||||
|
||||
@@ -352,7 +352,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
log(std::vector<boost::asio::const_buffer> const& buffers)
|
||||
{
|
||||
(void)buffers;
|
||||
|
||||
@@ -10,7 +10,7 @@ bool
|
||||
is_private(AddressV6 const& addr)
|
||||
{
|
||||
return (
|
||||
((addr.to_bytes()[0] & 0xfd) != 0) || // TODO fc00::/8 too ?
|
||||
(addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ?
|
||||
(addr.is_v4_mapped() &&
|
||||
is_private(boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, addr))));
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ Endpoint::to_string() const
|
||||
if (port() != 0 && address().is_v6())
|
||||
s += '[';
|
||||
s += address().to_string();
|
||||
if (port() != 0u)
|
||||
if (port())
|
||||
{
|
||||
if (address().is_v6())
|
||||
s += ']';
|
||||
@@ -111,7 +111,7 @@ operator>>(std::istream& is, Endpoint& endpoint)
|
||||
// so we continue to honor that here by assuming we are at the end
|
||||
// of the address portion if we hit a space (or the separator
|
||||
// we were expecting to see)
|
||||
if ((isspace(static_cast<unsigned char>(i)) != 0) || ((readTo != 0) && i == readTo))
|
||||
if (isspace(static_cast<unsigned char>(i)) || (readTo && i == readTo))
|
||||
break;
|
||||
|
||||
if ((i == '.') || (i >= '0' && i <= ':') || (i >= 'a' && i <= 'f') ||
|
||||
@@ -121,13 +121,13 @@ operator>>(std::istream& is, Endpoint& endpoint)
|
||||
|
||||
// don't exceed a reasonable length...
|
||||
if (addrStr.size() == INET6_ADDRSTRLEN ||
|
||||
((readTo != 0) && readTo == ':' && addrStr.size() > 15))
|
||||
(readTo && readTo == ':' && addrStr.size() > 15))
|
||||
{
|
||||
is.setstate(std::ios_base::failbit);
|
||||
return is;
|
||||
}
|
||||
|
||||
if ((readTo == 0) && (i == '.' || i == ':'))
|
||||
if (!readTo && (i == '.' || i == ':'))
|
||||
{
|
||||
// if we see a dot first, must be IPv4
|
||||
// otherwise must be non-bracketed IPv6
|
||||
@@ -145,7 +145,7 @@ operator>>(std::istream& is, Endpoint& endpoint)
|
||||
if (readTo == ']' && is.rdbuf()->in_avail() > 0)
|
||||
{
|
||||
is.get(i);
|
||||
if ((isspace(static_cast<unsigned char>(i)) == 0) && i != ':')
|
||||
if (!(isspace(static_cast<unsigned char>(i)) || i == ':'))
|
||||
{
|
||||
is.unget();
|
||||
is.setstate(std::ios_base::failbit);
|
||||
|
||||
@@ -84,7 +84,8 @@ JobQueue::addRefCountedJob(JobType type, std::string const& name, JobFunction co
|
||||
|
||||
JobType const type(job.getType());
|
||||
XRPL_ASSERT(type != jtINVALID, "xrpl::JobQueue::addRefCountedJob : has valid job type");
|
||||
XRPL_ASSERT(m_jobSet.contains(job), "xrpl::JobQueue::addRefCountedJob : job found");
|
||||
XRPL_ASSERT(
|
||||
m_jobSet.find(job) != m_jobSet.end(), "xrpl::JobQueue::addRefCountedJob : job found");
|
||||
perfLog_.jobQueue(type);
|
||||
|
||||
JobTypeData& data(getJobTypeData(type));
|
||||
|
||||
@@ -141,7 +141,7 @@ LoadMonitor::isOver()
|
||||
update();
|
||||
|
||||
if (mLatencyEvents == 0)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
return isOverTarget(
|
||||
mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4));
|
||||
|
||||
@@ -47,7 +47,7 @@ Workers::setNumberOfThreads(int numberOfThreads)
|
||||
if (m_numberOfThreads == numberOfThreads)
|
||||
return;
|
||||
|
||||
if (perfLog_ != nullptr)
|
||||
if (perfLog_)
|
||||
perfLog_->resizeJobs(numberOfThreads);
|
||||
|
||||
if (numberOfThreads > m_numberOfThreads)
|
||||
|
||||
@@ -210,8 +210,8 @@ RFC1751::extract(char const* s, int start, int length)
|
||||
|
||||
int const shiftR = 24 - (length + (start % 8));
|
||||
cl = s[start / 8]; // get components
|
||||
cc = (shiftR < 16) ? s[(start / 8) + 1] : 0;
|
||||
cr = (shiftR < 8) ? s[(start / 8) + 2] : 0;
|
||||
cc = (shiftR < 16) ? s[start / 8 + 1] : 0;
|
||||
cr = (shiftR < 8) ? s[start / 8 + 2] : 0;
|
||||
|
||||
x = ((long)(cl << 8 | cc) << 8 | cr); // Put bits together
|
||||
x = x >> shiftR; // Right justify number
|
||||
@@ -265,13 +265,13 @@ RFC1751::insert(char* s, int x, int start, int length)
|
||||
if (shift + length > 16)
|
||||
{
|
||||
s[start / 8] |= cl;
|
||||
s[(start / 8) + 1] |= cc;
|
||||
s[(start / 8) + 2] |= cr;
|
||||
s[start / 8 + 1] |= cc;
|
||||
s[start / 8 + 2] |= cr;
|
||||
}
|
||||
else if (shift + length > 8)
|
||||
{
|
||||
s[start / 8] |= cc;
|
||||
s[(start / 8) + 1] |= cr;
|
||||
s[start / 8 + 1] |= cr;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -284,7 +284,7 @@ RFC1751::standard(std::string& strWord)
|
||||
{
|
||||
for (auto& letter : strWord)
|
||||
{
|
||||
if (islower(static_cast<unsigned char>(letter)) != 0)
|
||||
if (islower(static_cast<unsigned char>(letter)))
|
||||
{
|
||||
letter = toupper(static_cast<unsigned char>(letter));
|
||||
}
|
||||
@@ -312,10 +312,10 @@ RFC1751::wsrch(std::string const& strWord, int iMin, int iMax)
|
||||
while (iResult < 0 && iMin != iMax)
|
||||
{
|
||||
// Have a range to search.
|
||||
int iMid = iMin + ((iMax - iMin) / 2);
|
||||
int iMid = iMin + (iMax - iMin) / 2;
|
||||
int iDir = strWord.compare(s_dictionary[iMid]);
|
||||
|
||||
if (iDir == 0)
|
||||
if (!iDir)
|
||||
{
|
||||
iResult = iMid; // Found it.
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public:
|
||||
#ifndef NDEBUG
|
||||
// Make sure we haven't already seen this tag.
|
||||
auto& tags = stack_.top().tags;
|
||||
check(!tags.contains(tag), "Already seen tag " + tag);
|
||||
check(tags.find(tag) == tags.end(), "Already seen tag " + tag);
|
||||
tags.insert(tag);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@ Reader::match(Location pattern, int patternLength)
|
||||
|
||||
int index = patternLength;
|
||||
|
||||
while ((index--) != 0)
|
||||
while (index--)
|
||||
{
|
||||
if (current_[index] != pattern[index])
|
||||
return false;
|
||||
@@ -362,7 +362,7 @@ Reader::readNumber()
|
||||
|
||||
while (current_ != end_)
|
||||
{
|
||||
if (std::isdigit(static_cast<unsigned char>(*current_)) == 0)
|
||||
if (!std::isdigit(static_cast<unsigned char>(*current_)))
|
||||
{
|
||||
auto ret =
|
||||
std::find(std::begin(extended_tokens), std::end(extended_tokens), *current_);
|
||||
@@ -913,7 +913,7 @@ Reader::getFormattedErrorMessages() const
|
||||
formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
|
||||
formattedMessage += " " + error.message_ + "\n";
|
||||
|
||||
if (error.extra_ != nullptr)
|
||||
if (error.extra_)
|
||||
formattedMessage += "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@ public:
|
||||
// return 0;
|
||||
|
||||
if (length == unknown)
|
||||
length = (value != nullptr) ? (unsigned int)strlen(value) : 0;
|
||||
length = value ? (unsigned int)strlen(value) : 0;
|
||||
|
||||
char* newString = static_cast<char*>(malloc(length + 1));
|
||||
if (value != nullptr)
|
||||
if (value)
|
||||
memcpy(newString, value, length);
|
||||
newString[length] = 0;
|
||||
return newString;
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
void
|
||||
releaseStringValue(char* value) override
|
||||
{
|
||||
if (value != nullptr)
|
||||
if (value)
|
||||
free(value);
|
||||
}
|
||||
};
|
||||
@@ -108,14 +108,14 @@ Value::CZString::CZString(CZString const& other)
|
||||
|
||||
Value::CZString::~CZString()
|
||||
{
|
||||
if ((cstr_ != nullptr) && index_ == duplicate)
|
||||
if (cstr_ && index_ == duplicate)
|
||||
valueAllocator()->releaseMemberName(const_cast<char*>(cstr_));
|
||||
}
|
||||
|
||||
bool
|
||||
Value::CZString::operator<(CZString const& other) const
|
||||
{
|
||||
if ((cstr_ != nullptr) && (other.cstr_ != nullptr))
|
||||
if (cstr_ && other.cstr_)
|
||||
return strcmp(cstr_, other.cstr_) < 0;
|
||||
|
||||
return index_ < other.index_;
|
||||
@@ -124,7 +124,7 @@ Value::CZString::operator<(CZString const& other) const
|
||||
bool
|
||||
Value::CZString::operator==(CZString const& other) const
|
||||
{
|
||||
if ((cstr_ != nullptr) && (other.cstr_ != nullptr))
|
||||
if (cstr_ && other.cstr_)
|
||||
return strcmp(cstr_, other.cstr_) == 0;
|
||||
|
||||
return index_ == other.index_;
|
||||
@@ -251,7 +251,7 @@ Value::Value(Value const& other) : type_(other.type_)
|
||||
break;
|
||||
|
||||
case stringValue:
|
||||
if (other.value_.string_ != nullptr)
|
||||
if (other.value_.string_)
|
||||
{
|
||||
value_.string_ = valueAllocator()->duplicateStringValue(other.value_.string_);
|
||||
allocated_ = true;
|
||||
@@ -294,7 +294,7 @@ Value::~Value()
|
||||
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_ != nullptr)
|
||||
if (value_.map_)
|
||||
delete value_.map_;
|
||||
break;
|
||||
|
||||
@@ -392,11 +392,11 @@ operator<(Value const& x, Value const& y)
|
||||
return x.value_.real_ < y.value_.real_;
|
||||
|
||||
case booleanValue:
|
||||
return static_cast<int>(x.value_.bool_) < static_cast<int>(y.value_.bool_);
|
||||
return x.value_.bool_ < y.value_.bool_;
|
||||
|
||||
case stringValue:
|
||||
return (x.value_.string_ == 0 && (y.value_.string_ != nullptr)) ||
|
||||
((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) &&
|
||||
return (x.value_.string_ == 0 && y.value_.string_) ||
|
||||
(y.value_.string_ && x.value_.string_ &&
|
||||
strcmp(x.value_.string_, y.value_.string_) < 0);
|
||||
|
||||
case arrayValue:
|
||||
@@ -413,7 +413,7 @@ operator<(Value const& x, Value const& y)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return false; // unreachable
|
||||
return 0; // unreachable
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -422,9 +422,9 @@ operator==(Value const& x, Value const& y)
|
||||
if (x.type_ != y.type_)
|
||||
{
|
||||
if (x.type_ == intValue && y.type_ == uintValue)
|
||||
return integerCmp(x.value_.int_, y.value_.uint_) == 0;
|
||||
return !integerCmp(x.value_.int_, y.value_.uint_);
|
||||
if (x.type_ == uintValue && y.type_ == intValue)
|
||||
return integerCmp(y.value_.int_, x.value_.uint_) == 0;
|
||||
return !integerCmp(y.value_.int_, x.value_.uint_);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -447,8 +447,8 @@ operator==(Value const& x, Value const& y)
|
||||
|
||||
case stringValue:
|
||||
return x.value_.string_ == y.value_.string_ ||
|
||||
((y.value_.string_ != nullptr) && (x.value_.string_ != nullptr) &&
|
||||
(strcmp(x.value_.string_, y.value_.string_) == 0));
|
||||
(y.value_.string_ && x.value_.string_ &&
|
||||
!strcmp(x.value_.string_, y.value_.string_));
|
||||
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
@@ -461,7 +461,7 @@ operator==(Value const& x, Value const& y)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return false; // unreachable
|
||||
return 0; // unreachable
|
||||
}
|
||||
|
||||
char const*
|
||||
@@ -480,7 +480,7 @@ Value::asString() const
|
||||
return "";
|
||||
|
||||
case stringValue:
|
||||
return (value_.string_ != nullptr) ? value_.string_ : "";
|
||||
return value_.string_ ? value_.string_ : "";
|
||||
|
||||
case booleanValue:
|
||||
return value_.bool_ ? "true" : "false";
|
||||
@@ -525,7 +525,7 @@ Value::asInt() const
|
||||
|
||||
case realValue:
|
||||
JSON_ASSERT_MESSAGE(
|
||||
(value_.real_ >= minInt && value_.real_ <= maxInt),
|
||||
value_.real_ >= minInt && value_.real_ <= maxInt,
|
||||
"Real out of signed integer range");
|
||||
return Int(value_.real_);
|
||||
|
||||
@@ -533,7 +533,7 @@ Value::asInt() const
|
||||
return value_.bool_ ? 1 : 0;
|
||||
|
||||
case stringValue: {
|
||||
char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
|
||||
char const* const str{value_.string_ ? value_.string_ : ""};
|
||||
return beast::lexicalCastThrow<int>(str);
|
||||
}
|
||||
|
||||
@@ -584,7 +584,7 @@ Value::asAbsUInt() const
|
||||
return value_.bool_ ? 1 : 0;
|
||||
|
||||
case stringValue: {
|
||||
char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
|
||||
char const* const str{value_.string_ ? value_.string_ : ""};
|
||||
auto const temp = beast::lexicalCastThrow<std::int64_t>(str);
|
||||
if (temp < 0)
|
||||
{
|
||||
@@ -626,15 +626,14 @@ Value::asUInt() const
|
||||
|
||||
case realValue:
|
||||
JSON_ASSERT_MESSAGE(
|
||||
(value_.real_ >= 0 && value_.real_ <= maxUInt),
|
||||
"Real out of unsigned integer range");
|
||||
value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range");
|
||||
return UInt(value_.real_);
|
||||
|
||||
case booleanValue:
|
||||
return value_.bool_ ? 1 : 0;
|
||||
|
||||
case stringValue: {
|
||||
char const* const str{(value_.string_ != nullptr) ? value_.string_ : ""};
|
||||
char const* const str{value_.string_ ? value_.string_ : ""};
|
||||
return beast::lexicalCastThrow<unsigned int>(str);
|
||||
}
|
||||
|
||||
@@ -704,7 +703,7 @@ Value::asBool() const
|
||||
return value_.bool_;
|
||||
|
||||
case stringValue:
|
||||
return (value_.string_ != nullptr) && value_.string_[0] != 0;
|
||||
return value_.string_ && value_.string_[0] != 0;
|
||||
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
@@ -746,13 +745,13 @@ Value::isConvertibleTo(ValueType other) const
|
||||
other == realValue || other == stringValue || other == booleanValue;
|
||||
|
||||
case booleanValue:
|
||||
return (other == nullValue && !value_.bool_) || other == intValue ||
|
||||
return (other == nullValue && value_.bool_ == false) || other == intValue ||
|
||||
other == uintValue || other == realValue || other == stringValue ||
|
||||
other == booleanValue;
|
||||
|
||||
case stringValue:
|
||||
return other == stringValue ||
|
||||
(other == nullValue && ((value_.string_ == nullptr) || value_.string_[0] == 0));
|
||||
(other == nullValue && (!value_.string_ || value_.string_[0] == 0));
|
||||
|
||||
case arrayValue:
|
||||
return other == arrayValue || (other == nullValue && value_.map_->empty());
|
||||
@@ -814,10 +813,10 @@ operator bool() const
|
||||
if (isString())
|
||||
{
|
||||
auto s = asCString();
|
||||
return (s != nullptr) && (s[0] != 0);
|
||||
return s && s[0];
|
||||
}
|
||||
|
||||
return !(isArray() || isObject()) || (size() != 0u);
|
||||
return !(isArray() || isObject()) || size();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1140,7 +1139,7 @@ Value::begin() const
|
||||
{
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_ != nullptr)
|
||||
if (value_.map_)
|
||||
return const_iterator(value_.map_->begin());
|
||||
|
||||
break;
|
||||
@@ -1158,7 +1157,7 @@ Value::end() const
|
||||
{
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_ != nullptr)
|
||||
if (value_.map_)
|
||||
return const_iterator(value_.map_->end());
|
||||
|
||||
break;
|
||||
@@ -1176,7 +1175,7 @@ Value::begin()
|
||||
{
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_ != nullptr)
|
||||
if (value_.map_)
|
||||
return iterator(value_.map_->begin());
|
||||
break;
|
||||
default:
|
||||
@@ -1193,7 +1192,7 @@ Value::end()
|
||||
{
|
||||
case arrayValue:
|
||||
case objectValue:
|
||||
if (value_.map_ != nullptr)
|
||||
if (value_.map_)
|
||||
return iterator(value_.map_->end());
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -89,7 +89,7 @@ ValueIteratorBase::key() const
|
||||
{
|
||||
Value::CZString const czString = (*current_).first;
|
||||
|
||||
if (czString.c_str() != nullptr)
|
||||
if (czString.c_str())
|
||||
{
|
||||
if (czString.isStaticString())
|
||||
return Value(StaticString(czString.c_str()));
|
||||
@@ -105,7 +105,7 @@ ValueIteratorBase::index() const
|
||||
{
|
||||
Value::CZString const czString = (*current_).first;
|
||||
|
||||
if (czString.c_str() == nullptr)
|
||||
if (!czString.c_str())
|
||||
return czString.index();
|
||||
|
||||
return Value::UInt(-1);
|
||||
@@ -115,7 +115,7 @@ char const*
|
||||
ValueIteratorBase::memberName() const
|
||||
{
|
||||
char const* name = (*current_).first.c_str();
|
||||
return (name != nullptr) ? name : "";
|
||||
return name ? name : "";
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -23,7 +23,7 @@ isControlCharacter(char ch)
|
||||
static bool
|
||||
containsControlCharacter(char const* str)
|
||||
{
|
||||
while (*str != 0)
|
||||
while (*str)
|
||||
{
|
||||
if (isControlCharacter(*(str++)))
|
||||
return true;
|
||||
@@ -106,7 +106,7 @@ valueToQuotedString(char const* value)
|
||||
// We have to walk value and escape any special characters.
|
||||
// Appending to std::string is not efficient, but this should be rare.
|
||||
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
||||
unsigned maxsize = (strlen(value) * 2) + 3; // all-escaped+quotes+NULL
|
||||
unsigned maxsize = strlen(value) * 2 + 3; // all-escaped+quotes+NULL
|
||||
std::string result;
|
||||
result.reserve(maxsize); // to avoid lots of mallocs
|
||||
result += "\"";
|
||||
@@ -416,7 +416,7 @@ StyledWriter::isMultilineArray(Value const& value)
|
||||
{
|
||||
childValues_.reserve(size);
|
||||
addChildValues_ = true;
|
||||
int lineLength = 4 + ((size - 1) * 2); // '[ ' + ', '*n + ' ]'
|
||||
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||
|
||||
for (int index = 0; index < size; ++index)
|
||||
{
|
||||
@@ -651,7 +651,7 @@ StyledStreamWriter::isMultilineArray(Value const& value)
|
||||
{
|
||||
childValues_.reserve(size);
|
||||
addChildValues_ = true;
|
||||
int lineLength = 4 + ((size - 1) * 2); // '[ ' + ', '*n + ' ]'
|
||||
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
||||
|
||||
for (int index = 0; index < size; ++index)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/ledger/AcceptedLedgerTx.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
@@ -45,12 +45,10 @@ AcceptedLedgerTx::AcceptedLedgerTx(
|
||||
// If the offer create is not self funded then add the owner balance
|
||||
if (account != amount.issue().account)
|
||||
{
|
||||
auto const ownerFunds = accountFunds(
|
||||
*ledger,
|
||||
account,
|
||||
amount,
|
||||
fhIGNORE_FREEZE,
|
||||
beast::Journal{beast::Journal::getNullSink()});
|
||||
auto const ownerFunds =
|
||||
makeTokenBase(*ledger, amount.asset())
|
||||
->accountHolds(
|
||||
account, fhIGNORE_FREEZE, beast::Journal{beast::Journal::getNullSink()});
|
||||
mJson[jss::transaction][jss::owner_funds] = ownerFunds.getText();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +275,9 @@ ApplyStateTable::exists(ReadView const& base, Keylet const& k) const
|
||||
case Action::modify:
|
||||
break;
|
||||
}
|
||||
return k.check(*sle);
|
||||
if (!k.check(*sle))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto
|
||||
|
||||
@@ -36,7 +36,7 @@ findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start)
|
||||
|
||||
auto node = start;
|
||||
|
||||
if (page != 0u)
|
||||
if (page)
|
||||
{
|
||||
node = view.peek(keylet::page(directory, page));
|
||||
if (!node)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <xrpl/ledger/BookDirs.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -164,7 +164,7 @@ PaymentSandbox::balanceHook(
|
||||
auto delta = amount.zeroed();
|
||||
auto lastBal = amount;
|
||||
auto minBal = amount;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
for (auto curSB = this; curSB; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.adjustments(account, issuer, currency))
|
||||
{
|
||||
@@ -198,7 +198,7 @@ std::uint32_t
|
||||
PaymentSandbox::ownerCountHook(AccountID const& account, std::uint32_t count) const
|
||||
{
|
||||
std::uint32_t result = count;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
for (auto curSB = this; curSB; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.ownerCount(account))
|
||||
result = std::max(result, *adj);
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
#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/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -52,13 +50,13 @@ isVaultPseudoAccountFrozen(
|
||||
if (depth >= maxAssetCheckDepth)
|
||||
return true; // LCOV_EXCL_LINE
|
||||
|
||||
auto const mptIssuance = view.read(keylet::mptIssuance(mptShare.getMptID()));
|
||||
if (mptIssuance == nullptr)
|
||||
auto mptIssuance = MPTokenIssuance(view, mptShare);
|
||||
if (!mptIssuance.exists())
|
||||
return false; // zero MPToken won't block deletion of MPTokenIssuance
|
||||
|
||||
auto const issuer = mptIssuance->getAccountID(sfIssuer);
|
||||
auto const mptIssuer = view.read(keylet::account(issuer));
|
||||
if (mptIssuer == nullptr)
|
||||
auto const mptIssuer = AccountRoot(issuer, view);
|
||||
if (!mptIssuer.exists())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
|
||||
@@ -77,7 +75,9 @@ isVaultPseudoAccountFrozen(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
|
||||
auto const vaultAsset = vault->at(sfAsset);
|
||||
auto const token = makeTokenBase(view, vaultAsset);
|
||||
return token->isAnyFrozen({issuer, account}, depth + 1);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -87,8 +87,9 @@ isLPTokenFrozen(
|
||||
Issue const& asset,
|
||||
Issue const& asset2)
|
||||
{
|
||||
return isFrozen(view, account, asset.currency, asset.account) ||
|
||||
isFrozen(view, account, asset2.currency, asset2.account);
|
||||
auto assetToken = IOUToken(view, asset);
|
||||
auto asset2Token = IOUToken(view, asset2);
|
||||
return assetToken.isFrozen(account) || asset2Token.isFrozen(account);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -357,17 +358,17 @@ canWithdraw(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
SLE::const_ref toSle,
|
||||
AccountRoot const& toWrapped,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag)
|
||||
{
|
||||
if (auto const ret = checkDestinationAndTag(toSle, hasDestinationTag))
|
||||
if (auto const ret = toWrapped.checkDestinationAndTag(hasDestinationTag))
|
||||
return ret;
|
||||
|
||||
if (from == to)
|
||||
return tesSUCCESS;
|
||||
|
||||
if (toSle->isFlag(lsfDepositAuth))
|
||||
if (toWrapped->isFlag(lsfDepositAuth))
|
||||
{
|
||||
if (!view.exists(keylet::depositPreauth(to, from)))
|
||||
return tecNO_PERMISSION;
|
||||
@@ -384,9 +385,9 @@ canWithdraw(
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag)
|
||||
{
|
||||
auto const toSle = view.read(keylet::account(to));
|
||||
auto const toWrapped = AccountRoot(to, view);
|
||||
|
||||
return canWithdraw(view, from, to, toSle, amount, hasDestinationTag);
|
||||
return canWithdraw(view, from, to, toWrapped, amount, hasDestinationTag);
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
@@ -409,31 +410,28 @@ doWithdraw(
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const asset = makeWritableTokenBase(view, amount.asset());
|
||||
// Create trust line or MPToken for the receiving account
|
||||
if (dstAcct == senderAcct)
|
||||
{
|
||||
if (auto const ter = addEmptyHolding(view, senderAcct, priorBalance, amount.asset(), j);
|
||||
if (auto const ter = asset->addEmptyHolding(senderAcct, priorBalance, j);
|
||||
!isTesSuccess(ter) && ter != tecDUPLICATE)
|
||||
return ter;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dstSle = view.read(keylet::account(dstAcct));
|
||||
if (auto err = verifyDepositPreauth(tx, view, senderAcct, dstAcct, dstSle, j))
|
||||
auto dst = AccountRoot(dstAcct, view);
|
||||
if (auto err = verifyDepositPreauth(tx, view, senderAcct, dst, j))
|
||||
return err;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (accountHolds(
|
||||
view,
|
||||
sourceAcct,
|
||||
amount.asset(),
|
||||
FreezeHandling::fhIGNORE_FREEZE,
|
||||
AuthHandling::ahIGNORE_AUTH,
|
||||
j) < amount)
|
||||
if (asset->accountHolds(
|
||||
sourceAcct, FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j) < amount)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "doWithdraw: negative balance of broker cover assets.";
|
||||
JLOG(j.error()) << "LoanBrokerCoverWithdraw: negative balance of "
|
||||
"broker cover assets.";
|
||||
return tefINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
@@ -12,13 +12,11 @@
|
||||
namespace xrpl {
|
||||
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, AccountID const& issuer)
|
||||
AccountRoot::isGlobalFrozen() const
|
||||
{
|
||||
if (isXRP(issuer))
|
||||
if (!exists())
|
||||
return false;
|
||||
if (auto const sle = view.read(keylet::account(issuer)))
|
||||
return sle->isFlag(lsfGlobalFreeze);
|
||||
return false;
|
||||
return sle_->isFlag(lsfGlobalFreeze);
|
||||
}
|
||||
|
||||
// An owner count cannot be negative. If adjustment would cause a negative
|
||||
@@ -64,27 +62,26 @@ confineOwnerCount(
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
|
||||
AccountRoot::xrpLiquid(std::int32_t ownerCountAdj, beast::Journal j) const
|
||||
{
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
if (sle == nullptr)
|
||||
if (!exists())
|
||||
return beast::zero;
|
||||
|
||||
// Return balance minus reserve
|
||||
std::uint32_t const ownerCount =
|
||||
confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
|
||||
std::uint32_t const ownerCount = confineOwnerCount(
|
||||
readView_.ownerCountHook(id_, sle_->getFieldU32(sfOwnerCount)), ownerCountAdj);
|
||||
|
||||
// Pseudo-accounts have no reserve requirement
|
||||
auto const reserve =
|
||||
isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount);
|
||||
isPseudoAccount() ? XRPAmount{0} : readView_.fees().accountReserve(ownerCount);
|
||||
|
||||
auto const fullBalance = sle->getFieldAmount(sfBalance);
|
||||
auto const fullBalance = sle_->getFieldAmount(sfBalance);
|
||||
|
||||
auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
|
||||
auto const balance = readView_.balanceHook(id_, xrpAccount(), fullBalance);
|
||||
|
||||
STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve;
|
||||
|
||||
JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id)
|
||||
JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id_)
|
||||
<< " amount=" << amount.getFullText()
|
||||
<< " fullBalance=" << fullBalance.getFullText()
|
||||
<< " balance=" << balance.getFullText() << " reserve=" << reserve
|
||||
@@ -94,32 +91,25 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
|
||||
}
|
||||
|
||||
Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer)
|
||||
AccountRoot::transferRate() const
|
||||
{
|
||||
auto const sle = view.read(keylet::account(issuer));
|
||||
|
||||
if (sle && sle->isFieldPresent(sfTransferRate))
|
||||
return Rate{sle->getFieldU32(sfTransferRate)};
|
||||
if (sle_ && sle_->isFieldPresent(sfTransferRate))
|
||||
return Rate{sle_->getFieldU32(sfTransferRate)};
|
||||
|
||||
return parityRate;
|
||||
}
|
||||
|
||||
void
|
||||
adjustOwnerCount(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> const& sle,
|
||||
std::int32_t amount,
|
||||
beast::Journal j)
|
||||
WritableAccountRoot::adjustOwnerCount(std::int32_t amount, beast::Journal j)
|
||||
{
|
||||
if (!sle)
|
||||
return;
|
||||
XRPL_ASSERT(canModify(), "xrpl::adjustOwnerCount : can modify");
|
||||
XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input");
|
||||
std::uint32_t const current{sle->getFieldU32(sfOwnerCount)};
|
||||
AccountID const id = (*sle)[sfAccount];
|
||||
std::uint32_t const current{mutableSle_->getFieldU32(sfOwnerCount)};
|
||||
AccountID const id = (*mutableSle_)[sfAccount];
|
||||
std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
|
||||
view.adjustOwnerCountHook(id, current, adjusted);
|
||||
sle->at(sfOwnerCount) = adjusted;
|
||||
view.update(sle);
|
||||
applyView_.adjustOwnerCountHook(id_, current, adjusted);
|
||||
mutableSle_->at(sfOwnerCount) = adjusted;
|
||||
update();
|
||||
}
|
||||
|
||||
AccountID
|
||||
@@ -171,6 +161,21 @@ getPseudoAccountFields()
|
||||
return pseudoFields;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
AccountRoot::isPseudoAccount(std::set<SField const*> const& pseudoFieldFilter) const
|
||||
{
|
||||
auto const& fields = getPseudoAccountFields();
|
||||
|
||||
// Intentionally use defensive coding here because it's cheap and makes the
|
||||
// semantics of true return value clean.
|
||||
return sle_ && sle_->getType() == ltACCOUNT_ROOT &&
|
||||
std::count_if(
|
||||
fields.begin(), fields.end(), [this, &pseudoFieldFilter](SField const* sf) -> bool {
|
||||
return sle_->isFieldPresent(*sf) &&
|
||||
(pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf));
|
||||
}) > 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(
|
||||
std::shared_ptr<SLE const> sleAcct,
|
||||
@@ -231,14 +236,14 @@ createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
|
||||
AccountRoot::checkDestinationAndTag(bool hasDestinationTag) const
|
||||
{
|
||||
if (toSle == nullptr)
|
||||
if (sle_ == nullptr)
|
||||
return tecNO_DST;
|
||||
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag)
|
||||
if (sle_->isFlag(lsfRequireDestTag) && !hasDestinationTag)
|
||||
return tecDST_TAG_NEEDED; // Cannot send without a tag
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -51,8 +51,8 @@ deleteSLE(ApplyView& view, std::shared_ptr<SLE> const& sleCredential, beast::Jou
|
||||
|
||||
auto delSLE = [&view, &sleCredential, j](
|
||||
AccountID const& account, SField const& node, bool isOwner) -> TER {
|
||||
auto const sleAccount = view.peek(keylet::account(account));
|
||||
if (!sleAccount)
|
||||
WritableAccountRoot wrappedAccount(account, view);
|
||||
if (!wrappedAccount)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Internal error: can't retrieve Owner account.";
|
||||
@@ -71,14 +71,14 @@ deleteSLE(ApplyView& view, std::shared_ptr<SLE> const& sleCredential, beast::Jou
|
||||
}
|
||||
|
||||
if (isOwner)
|
||||
adjustOwnerCount(view, sleAccount, -1, j);
|
||||
wrappedAccount.adjustOwnerCount(-1, j);
|
||||
|
||||
return tesSUCCESS;
|
||||
};
|
||||
|
||||
auto const issuer = sleCredential->getAccountID(sfIssuer);
|
||||
auto const subject = sleCredential->getAccountID(sfSubject);
|
||||
bool const accepted = (sleCredential->getFlags() & lsfAccepted) != 0u;
|
||||
bool const accepted = sleCredential->getFlags() & lsfAccepted;
|
||||
|
||||
auto err = delSLE(issuer, sfIssuerNode, !accepted || (subject == issuer));
|
||||
if (!isTesSuccess(err))
|
||||
@@ -147,7 +147,7 @@ valid(STTx const& tx, ReadView const& view, AccountID const& src, beast::Journal
|
||||
return tecBAD_CREDENTIALS;
|
||||
}
|
||||
|
||||
if ((sleCred->getFlags() & lsfAccepted) == 0u)
|
||||
if (!(sleCred->getFlags() & lsfAccepted))
|
||||
{
|
||||
JLOG(j.trace()) << "Credential isn't accepted. Cred: " << h;
|
||||
return tecBAD_CREDENTIALS;
|
||||
@@ -188,7 +188,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject)
|
||||
foundExpired = true;
|
||||
continue;
|
||||
}
|
||||
if ((sleCredential->getFlags() & lsfAccepted) != 0u)
|
||||
if (sleCredential->getFlags() & lsfAccepted)
|
||||
{
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -309,7 +309,7 @@ verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, b
|
||||
if (!sleCredential)
|
||||
continue; // expired, i.e. deleted in credentials::removeExpired
|
||||
|
||||
if ((sleCredential->getFlags() & lsfAccepted) != 0u)
|
||||
if (sleCredential->getFlags() & lsfAccepted)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -321,8 +321,7 @@ verifyDepositPreauth(
|
||||
STTx const& tx,
|
||||
ApplyView& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
std::shared_ptr<SLE const> const& sleDst,
|
||||
AccountRoot const& dst,
|
||||
beast::Journal j)
|
||||
{
|
||||
// If depositPreauth is enabled, then an account that requires
|
||||
@@ -336,15 +335,15 @@ verifyDepositPreauth(
|
||||
if (credentialsPresent && credentials::removeExpired(view, tx.getFieldV256(sfCredentialIDs), j))
|
||||
return tecEXPIRED;
|
||||
|
||||
if (sleDst && ((sleDst->getFlags() & lsfDepositAuth) != 0u))
|
||||
if (dst.exists() && (dst->getFlags() & lsfDepositAuth))
|
||||
{
|
||||
if (src != dst)
|
||||
{
|
||||
if (!view.exists(keylet::depositPreauth(dst, src)))
|
||||
if (!view.exists(keylet::depositPreauth(dst.id(), src)))
|
||||
{
|
||||
return !credentialsPresent ? tecNO_PERMISSION
|
||||
: credentials::authorizedDepositPreauth(
|
||||
view, tx.getFieldV256(sfCredentialIDs), dst);
|
||||
view, tx.getFieldV256(sfCredentialIDs), dst.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ forEachItem(
|
||||
for (auto const& key : sle->getFieldV256(sfIndexes))
|
||||
f(view.read(keylet::child(key)));
|
||||
auto const next = sle->getFieldU64(sfIndexNext);
|
||||
if (next == 0u)
|
||||
if (!next)
|
||||
return;
|
||||
pos = keylet::page(root, next);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
//
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -13,77 +12,103 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations for functions that remain in View.h/cpp
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
|
||||
isVaultPseudoAccountFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTIssue const& mptShare,
|
||||
int depth);
|
||||
|
||||
[[nodiscard]] TER
|
||||
dirLink(
|
||||
ApplyView& view,
|
||||
AccountID const& owner,
|
||||
std::shared_ptr<SLE>& object,
|
||||
SF_UINT64 const& node = sfOwnerNode);
|
||||
|
||||
bool
|
||||
MPTokenIssuance::isGlobalFrozen() const
|
||||
{
|
||||
if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())))
|
||||
if (sle_)
|
||||
return sle_->isFlag(lsfMPTLocked);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MPTokenIssuance::isIndividualFrozen(AccountID const& account) const
|
||||
{
|
||||
if (auto const sle = readView_.read(keylet::mptoken(mptID_, account)))
|
||||
return sle->isFlag(lsfMPTLocked);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
|
||||
MPTokenIssuance::isFrozen(AccountID const& account, int depth) const
|
||||
{
|
||||
if (auto const sle = view.read(keylet::mptoken(mptIssue.getMptID(), account)))
|
||||
return sle->isFlag(lsfMPTLocked);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth)
|
||||
{
|
||||
return isGlobalFrozen(view, mptIssue) || isIndividualFrozen(view, account, mptIssue) ||
|
||||
isVaultPseudoAccountFrozen(view, account, mptIssue, depth);
|
||||
return isGlobalFrozen() || isIndividualFrozen(account) ||
|
||||
isVaultPseudoAccountFrozen(readView_, account, mptIssue_, depth);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isAnyFrozen(
|
||||
ReadView const& view,
|
||||
std::initializer_list<AccountID> const& accounts,
|
||||
MPTIssue const& mptIssue,
|
||||
int depth)
|
||||
MPTokenIssuance::isAnyFrozen(std::initializer_list<AccountID> const& accounts, int depth) const
|
||||
{
|
||||
if (isGlobalFrozen(view, mptIssue))
|
||||
if (isGlobalFrozen())
|
||||
return true;
|
||||
|
||||
for (auto const& account : accounts)
|
||||
{
|
||||
if (isIndividualFrozen(view, account, mptIssue))
|
||||
if (isIndividualFrozen(account))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto const& account : accounts)
|
||||
{
|
||||
if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth))
|
||||
if (isVaultPseudoAccountFrozen(readView_, account, mptIssue_, depth))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TER
|
||||
MPTokenIssuance::checkFrozen(AccountID const& account) const
|
||||
{
|
||||
return isFrozen(account) ? TER{tecLOCKED} : TER{tesSUCCESS};
|
||||
}
|
||||
|
||||
bool
|
||||
MPTokenIssuance::isDeepFrozen(AccountID const& account, int depth) const
|
||||
{
|
||||
return isFrozen(account, depth);
|
||||
}
|
||||
|
||||
TER
|
||||
MPTokenIssuance::checkDeepFrozen(AccountID const& account) const
|
||||
{
|
||||
return isDeepFrozen(account) ? TER{tecLOCKED} : TER{tesSUCCESS};
|
||||
}
|
||||
|
||||
Rate
|
||||
transferRate(ReadView const& view, MPTID const& issuanceID)
|
||||
MPTokenIssuance::transferRate() const
|
||||
{
|
||||
// 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));
|
||||
sle && sle->isFieldPresent(sfTransferFee))
|
||||
return Rate{1'000'000'000u + (10'000 * sle->getFieldU16(sfTransferFee))};
|
||||
if (sle_ && sle_->isFieldPresent(sfTransferFee))
|
||||
return Rate{1'000'000'000u + 10'000 * sle_->getFieldU16(sfTransferFee)};
|
||||
|
||||
return parityRate;
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
|
||||
MPTokenIssuance::canAddHolding() const
|
||||
{
|
||||
auto mptID = mptIssue.getMptID();
|
||||
auto issuance = view.read(keylet::mptIssuance(mptID));
|
||||
if (!issuance)
|
||||
if (!sle_)
|
||||
{
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
}
|
||||
if (!issuance->isFlag(lsfMPTCanTransfer))
|
||||
if (!sle_->isFlag(lsfMPTCanTransfer))
|
||||
{
|
||||
return tecNO_AUTH;
|
||||
}
|
||||
@@ -92,39 +117,33 @@ canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(
|
||||
ApplyView& view,
|
||||
WritableMPTokenIssuance::addEmptyHolding(
|
||||
AccountID const& accountID,
|
||||
XRPAmount priorBalance,
|
||||
MPTIssue const& mptIssue,
|
||||
beast::Journal journal)
|
||||
{
|
||||
auto const& mptID = mptIssue.getMptID();
|
||||
auto const mpt = view.peek(keylet::mptIssuance(mptID));
|
||||
if (!mpt)
|
||||
if (!mutableSle_)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
if (mpt->isFlag(lsfMPTLocked))
|
||||
if (mutableSle_->isFlag(lsfMPTLocked))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
if (view.peek(keylet::mptoken(mptID, accountID)))
|
||||
if (applyView_.peek(keylet::mptoken(mptID_, accountID)))
|
||||
return tecDUPLICATE;
|
||||
if (accountID == mptIssue.getIssuer())
|
||||
if (accountID == mptIssue_.getIssuer())
|
||||
return tesSUCCESS;
|
||||
|
||||
return authorizeMPToken(view, priorBalance, mptID, accountID, journal);
|
||||
return authorizeMPToken(priorBalance, accountID, journal);
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
authorizeMPToken(
|
||||
ApplyView& view,
|
||||
WritableMPTokenIssuance::authorizeMPToken(
|
||||
XRPAmount const& priorBalance,
|
||||
MPTID const& mptIssuanceID,
|
||||
AccountID const& account,
|
||||
beast::Journal journal,
|
||||
std::uint32_t flags,
|
||||
std::optional<AccountID> holderID)
|
||||
{
|
||||
auto const sleAcct = view.peek(keylet::account(account));
|
||||
if (!sleAcct)
|
||||
WritableAccountRoot wrappedAcct(account, applyView_);
|
||||
if (!wrappedAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account that submitted the tx is a holder
|
||||
@@ -135,20 +154,20 @@ authorizeMPToken(
|
||||
// When a holder wants to unauthorize/delete a MPT, the ledger must
|
||||
// - delete mptokenKey from owner directory
|
||||
// - delete the MPToken
|
||||
if ((flags & tfMPTUnauthorize) != 0)
|
||||
if (flags & tfMPTUnauthorize)
|
||||
{
|
||||
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||
auto const sleMpt = view.peek(mptokenKey);
|
||||
auto const mptokenKey = keylet::mptoken(mptID_, account);
|
||||
auto const sleMpt = applyView_.peek(mptokenKey);
|
||||
if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (!view.dirRemove(
|
||||
if (!applyView_.dirRemove(
|
||||
keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleAcct, -1, journal);
|
||||
wrappedAcct.adjustOwnerCount(-1, journal);
|
||||
|
||||
view.erase(sleMpt);
|
||||
applyView_.erase(sleMpt);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -161,52 +180,50 @@ authorizeMPToken(
|
||||
// an account owns, in the case of MPTokens we only
|
||||
// *enforce* a reserve if the user owns more than two
|
||||
// items. This is similar to the reserve requirements of trust lines.
|
||||
std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
|
||||
std::uint32_t const uOwnerCount = wrappedAcct->getFieldU32(sfOwnerCount);
|
||||
XRPAmount const reserveCreate(
|
||||
(uOwnerCount < 2) ? XRPAmount(beast::zero)
|
||||
: view.fees().accountReserve(uOwnerCount + 1));
|
||||
: applyView_.fees().accountReserve(uOwnerCount + 1));
|
||||
|
||||
if (priorBalance < reserveCreate)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
// Defensive check before we attempt to create MPToken for the issuer
|
||||
auto const mpt = view.read(keylet::mptIssuance(mptIssuanceID));
|
||||
if (!mpt || mpt->getAccountID(sfIssuer) == account)
|
||||
if (!mutableSle_ || mutableSle_->getAccountID(sfIssuer) == account)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::authorizeMPToken : invalid issuance or issuers token");
|
||||
if (view.rules().enabled(featureLendingProtocol))
|
||||
if (applyView_.rules().enabled(featureLendingProtocol))
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||
auto const mptokenKey = keylet::mptoken(mptID_, account);
|
||||
auto mptoken = std::make_shared<SLE>(mptokenKey);
|
||||
if (auto ter = dirLink(view, account, mptoken))
|
||||
if (auto ter = dirLink(applyView_, account, mptoken))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
(*mptoken)[sfAccount] = account;
|
||||
(*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
|
||||
(*mptoken)[sfMPTokenIssuanceID] = mptID_;
|
||||
(*mptoken)[sfFlags] = 0;
|
||||
view.insert(mptoken);
|
||||
applyView_.insert(mptoken);
|
||||
|
||||
// Update owner count.
|
||||
adjustOwnerCount(view, sleAcct, 1, journal);
|
||||
wrappedAcct.adjustOwnerCount(1, journal);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
|
||||
if (!sleMptIssuance)
|
||||
if (!mutableSle_)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account that submitted this tx is the issuer of the MPT
|
||||
// Note: `account_` is issuer's account
|
||||
// `holderID` is holder's account
|
||||
if (account != (*sleMptIssuance)[sfIssuer])
|
||||
if (account != (*mutableSle_)[sfIssuer])
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID));
|
||||
auto const sleMpt = applyView_.peek(keylet::mptoken(mptID_, *holderID));
|
||||
if (!sleMpt)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -215,7 +232,7 @@ authorizeMPToken(
|
||||
|
||||
// Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
|
||||
// their MPToken
|
||||
if ((flags & tfMPTUnauthorize) != 0)
|
||||
if (flags & tfMPTUnauthorize)
|
||||
{
|
||||
flagsOut &= ~lsfMPTAuthorized;
|
||||
}
|
||||
@@ -229,23 +246,18 @@ authorizeMPToken(
|
||||
if (flagsIn != flagsOut)
|
||||
sleMpt->setFieldU32(sfFlags, flagsOut);
|
||||
|
||||
view.update(sleMpt);
|
||||
applyView_.update(sleMpt);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
removeEmptyHolding(
|
||||
ApplyView& view,
|
||||
AccountID const& accountID,
|
||||
MPTIssue const& mptIssue,
|
||||
beast::Journal journal)
|
||||
WritableMPTokenIssuance::removeEmptyHolding(AccountID const& accountID, beast::Journal journal)
|
||||
{
|
||||
// If the account is the issuer, then no token should exist. MPTs do not
|
||||
// have the legacy ability to create such a situation, but check anyway. If
|
||||
// a token does exist, it will get deleted. If not, return success.
|
||||
bool const accountIsIssuer = accountID == mptIssue.getIssuer();
|
||||
auto const& mptID = mptIssue.getMptID();
|
||||
auto const mptoken = view.peek(keylet::mptoken(mptID, accountID));
|
||||
bool const accountIsIssuer = accountID == mptIssue_.getIssuer();
|
||||
auto const mptoken = applyView_.peek(keylet::mptoken(mptID_, accountID));
|
||||
if (!mptoken)
|
||||
return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
|
||||
// Unlike a trust line, if the account is the issuer, and the token has a
|
||||
@@ -256,9 +268,7 @@ removeEmptyHolding(
|
||||
return tecHAS_OBLIGATIONS;
|
||||
|
||||
return authorizeMPToken(
|
||||
view,
|
||||
{}, // priorBalance
|
||||
mptID,
|
||||
accountID,
|
||||
journal,
|
||||
tfMPTUnauthorize // flags
|
||||
@@ -266,25 +276,18 @@ removeEmptyHolding(
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
requireAuth(
|
||||
ReadView const& view,
|
||||
MPTIssue const& mptIssue,
|
||||
AccountID const& account,
|
||||
AuthType authType,
|
||||
int depth)
|
||||
MPTokenIssuance::requireAuth(AccountID const& account, AuthType authType, int depth) const
|
||||
{
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto const sleIssuance = view.read(mptID);
|
||||
if (!sleIssuance)
|
||||
if (!sle_)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
|
||||
auto const mptIssuer = AccountRoot(sle_->getAccountID(sfIssuer), readView_);
|
||||
|
||||
// issuer is always "authorized"
|
||||
if (mptIssuer == account) // Issuer won't have MPToken
|
||||
return tesSUCCESS;
|
||||
|
||||
bool const featureSAVEnabled = view.rules().enabled(featureSingleAssetVault);
|
||||
bool const featureSAVEnabled = readView_.rules().enabled(featureSingleAssetVault);
|
||||
|
||||
if (featureSAVEnabled)
|
||||
{
|
||||
@@ -292,36 +295,24 @@ requireAuth(
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// requireAuth is recursive if the issuer is a vault pseudo-account
|
||||
auto const sleIssuer = view.read(keylet::account(mptIssuer));
|
||||
if (!sleIssuer)
|
||||
if (!mptIssuer.exists())
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (sleIssuer->isFieldPresent(sfVaultID))
|
||||
if (mptIssuer->isFieldPresent(sfVaultID))
|
||||
{
|
||||
auto const sleVault = view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
|
||||
auto const sleVault = readView_.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
|
||||
if (!sleVault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const asset = sleVault->at(sfAsset);
|
||||
if (auto const err = std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return requireAuth(view, issue, account, authType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return requireAuth(view, issue, account, authType, depth + 1);
|
||||
}
|
||||
},
|
||||
asset.value());
|
||||
if (auto const err =
|
||||
makeTokenBase(readView_, asset)->requireAuth(account, authType, depth + 1);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, account);
|
||||
auto const sleToken = view.read(mptokenID);
|
||||
auto const sleToken = readView_.read(keylet::mptoken(mptID_, account));
|
||||
|
||||
// if account has no MPToken, fail
|
||||
if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy))
|
||||
@@ -329,14 +320,14 @@ requireAuth(
|
||||
|
||||
// Note, this check is not amendment-gated because DomainID will be always
|
||||
// empty **unless** writing to it has been enabled by an amendment
|
||||
auto const maybeDomainID = sleIssuance->at(~sfDomainID);
|
||||
auto const maybeDomainID = sle_->at(~sfDomainID);
|
||||
if (maybeDomainID)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth,
|
||||
sle_->getFieldU32(sfFlags) & lsfMPTRequireAuth,
|
||||
"xrpl::requireAuth : issuance requires authorization");
|
||||
// ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED
|
||||
auto const ter = credentials::validDomain(view, *maybeDomainID, account);
|
||||
auto const ter = credentials::validDomain(readView_, *maybeDomainID, account);
|
||||
if (isTesSuccess(ter))
|
||||
{
|
||||
return ter; // Note: sleToken might be null
|
||||
@@ -352,47 +343,43 @@ requireAuth(
|
||||
if (featureSAVEnabled)
|
||||
{
|
||||
// Implicitly authorize Vault and LoanBroker pseudo-accounts
|
||||
if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID}))
|
||||
if (isPseudoAccount(readView_, account, {&sfVaultID, &sfLoanBrokerID}))
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// mptoken must be authorized if issuance enabled requireAuth
|
||||
if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
|
||||
(!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
|
||||
if (sle_->isFlag(lsfMPTRequireAuth) && (!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
|
||||
return tecNO_AUTH;
|
||||
|
||||
return tesSUCCESS; // Note: sleToken might be null
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
enforceMPTokenAuthorization(
|
||||
ApplyView& view,
|
||||
MPTID const& mptIssuanceID,
|
||||
WritableMPTokenIssuance::enforceMPTokenAuthorization(
|
||||
AccountID const& account,
|
||||
XRPAmount const& priorBalance, // for MPToken authorization
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
|
||||
if (!sleIssuance)
|
||||
if (!mutableSle_)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(
|
||||
sleIssuance->isFlag(lsfMPTRequireAuth),
|
||||
mutableSle_->isFlag(lsfMPTRequireAuth),
|
||||
"xrpl::enforceMPTokenAuthorization : authorization required");
|
||||
|
||||
if (account == sleIssuance->at(sfIssuer))
|
||||
if (account == mutableSle_->at(sfIssuer))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const keylet = keylet::mptoken(mptIssuanceID, account);
|
||||
auto const sleToken = view.read(keylet); // NOTE: might be null
|
||||
auto const maybeDomainID = sleIssuance->at(~sfDomainID);
|
||||
auto const keylet = keylet::mptoken(mptID_, account);
|
||||
auto const sleToken = readView_.read(keylet); // NOTE: might be null
|
||||
auto const maybeDomainID = mutableSle_->at(~sfDomainID);
|
||||
bool expired = false;
|
||||
bool const authorizedByDomain = [&]() -> bool {
|
||||
// NOTE: defensive here, should be checked in preclaim
|
||||
if (!maybeDomainID.has_value())
|
||||
if (!maybeDomainID)
|
||||
return false; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter = verifyValidDomain(view, account, *maybeDomainID, j);
|
||||
auto const ter = verifyValidDomain(applyView(), account, *maybeDomainID, j);
|
||||
if (isTesSuccess(ter))
|
||||
return true;
|
||||
if (ter == tecEXPIRED)
|
||||
@@ -411,7 +398,7 @@ enforceMPTokenAuthorization(
|
||||
// Either way, return tecNO_AUTH and there is nothing else to do
|
||||
return expired ? tecEXPIRED : tecNO_AUTH;
|
||||
}
|
||||
if (!authorizedByDomain && maybeDomainID.has_value())
|
||||
if (!authorizedByDomain && maybeDomainID)
|
||||
{
|
||||
// Found an MPToken but the account is not authorized and we expect
|
||||
// it to have been authorized by the domain. This could be because the
|
||||
@@ -423,7 +410,7 @@ enforceMPTokenAuthorization(
|
||||
// We found an MPToken, but sfDomainID is not set, so this is a classic
|
||||
// MPToken which requires authorization by the token issuer.
|
||||
XRPL_ASSERT(
|
||||
sleToken != nullptr && !maybeDomainID.has_value(),
|
||||
sleToken != nullptr && !maybeDomainID,
|
||||
"xrpl::enforceMPTokenAuthorization : found MPToken");
|
||||
if (sleToken->isFlag(lsfMPTAuthorized))
|
||||
return tesSUCCESS;
|
||||
@@ -434,9 +421,7 @@ enforceMPTokenAuthorization(
|
||||
{
|
||||
// Found an MPToken, authorized by the domain. Ignore authorization flag
|
||||
// lsfMPTAuthorized because it is meaningless. Return tesSUCCESS
|
||||
XRPL_ASSERT(
|
||||
maybeDomainID.has_value(),
|
||||
"xrpl::enforceMPTokenAuthorization : found MPToken for domain");
|
||||
XRPL_ASSERT(maybeDomainID, "xrpl::enforceMPTokenAuthorization : found MPToken for domain");
|
||||
return tesSUCCESS;
|
||||
}
|
||||
if (authorizedByDomain)
|
||||
@@ -444,13 +429,11 @@ enforceMPTokenAuthorization(
|
||||
// Could not find MPToken but there should be one because we are
|
||||
// authorized by domain. Proceed to create it, then return tesSUCCESS
|
||||
XRPL_ASSERT(
|
||||
maybeDomainID.has_value() && sleToken == nullptr,
|
||||
maybeDomainID && sleToken == nullptr,
|
||||
"xrpl::enforceMPTokenAuthorization : new MPToken for domain");
|
||||
if (auto const err = authorizeMPToken(
|
||||
view,
|
||||
priorBalance, // priorBalance
|
||||
mptIssuanceID, // mptIssuanceID
|
||||
account, // account
|
||||
priorBalance, // priorBalance
|
||||
account, // account
|
||||
j);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
@@ -465,25 +448,41 @@ enforceMPTokenAuthorization(
|
||||
}
|
||||
|
||||
TER
|
||||
canTransfer(
|
||||
ReadView const& view,
|
||||
MPTIssue const& mptIssue,
|
||||
AccountID const& from,
|
||||
AccountID const& to)
|
||||
MPTokenIssuance::canTransfer(AccountID const& from, AccountID const& to) const
|
||||
{
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto const sleIssuance = view.read(mptID);
|
||||
if (!sleIssuance)
|
||||
if (!sle_)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
if (!sleIssuance->isFlag(lsfMPTCanTransfer))
|
||||
if (!(sle_->getFieldU32(sfFlags) & lsfMPTCanTransfer))
|
||||
{
|
||||
if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
|
||||
if (from != (*sle_)[sfIssuer] && to != (*sle_)[sfIssuer])
|
||||
return TER{tecNO_AUTH};
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Token capability checks (MPT-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
MPTokenIssuance::canClawback() const
|
||||
{
|
||||
if (!sle_)
|
||||
return false;
|
||||
return sle_->isFlag(lsfMPTCanClawback);
|
||||
}
|
||||
|
||||
bool
|
||||
MPTokenIssuance::requiresAuth() const
|
||||
{
|
||||
if (!sle_)
|
||||
return false;
|
||||
return sle_->isFlag(lsfMPTRequireAuth);
|
||||
}
|
||||
|
||||
TER
|
||||
rippleLockEscrowMPT(
|
||||
ApplyView& view,
|
||||
@@ -492,16 +491,15 @@ rippleLockEscrowMPT(
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const mptIssue = amount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
auto mptIssuance = WritableMPTokenIssuance(view, mptIssue);
|
||||
if (!mptIssuance.exists())
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for "
|
||||
<< mptIssue.getMptID();
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
if (amount.getIssuer() == sender)
|
||||
if (mptIssuance.getIssuer() == sender)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs.";
|
||||
return tecINTERNAL;
|
||||
@@ -510,7 +508,7 @@ rippleLockEscrowMPT(
|
||||
// 1. Decrease the MPT Holder MPTAmount
|
||||
// 2. Increase the MPT Holder EscrowedAmount
|
||||
{
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, sender);
|
||||
auto const mptokenID = keylet::mptoken(mptIssuance.getMptID(), sender);
|
||||
auto sle = view.peek(mptokenID);
|
||||
if (!sle)
|
||||
{ // LCOV_EXCL_START
|
||||
@@ -556,7 +554,7 @@ rippleLockEscrowMPT(
|
||||
// 1. Increase the Issuance EscrowedAmount
|
||||
// 2. DO NOT change the Issuance OutstandingAmount
|
||||
{
|
||||
uint64_t const issuanceEscrowed = (*sleIssuance)[~sfLockedAmount].value_or(0);
|
||||
uint64_t const issuanceEscrowed = (*mptIssuance)[~sfLockedAmount].value_or(0);
|
||||
auto const pay = amount.mpt().value();
|
||||
|
||||
// Overflow check for addition
|
||||
@@ -568,16 +566,16 @@ rippleLockEscrowMPT(
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
if (sleIssuance->isFieldPresent(sfLockedAmount))
|
||||
if (mptIssuance->isFieldPresent(sfLockedAmount))
|
||||
{
|
||||
(*sleIssuance)[sfLockedAmount] += pay;
|
||||
(*mptIssuance)[sfLockedAmount] += pay;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleIssuance->setFieldU64(sfLockedAmount, pay);
|
||||
mptIssuance->setFieldU64(sfLockedAmount, pay);
|
||||
}
|
||||
|
||||
view.update(sleIssuance);
|
||||
mptIssuance.update();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -599,9 +597,8 @@ rippleUnlockEscrowMPT(
|
||||
|
||||
auto const& issuer = netAmount.getIssuer();
|
||||
auto const& mptIssue = netAmount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
auto mptIssuance = WritableMPTokenIssuance(view, mptIssue);
|
||||
if (!mptIssuance)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for "
|
||||
<< mptIssue.getMptID();
|
||||
@@ -610,14 +607,14 @@ rippleUnlockEscrowMPT(
|
||||
|
||||
// Decrease the Issuance EscrowedAmount
|
||||
{
|
||||
if (!sleIssuance->isFieldPresent(sfLockedAmount))
|
||||
if (!mptIssuance->isFieldPresent(sfLockedAmount))
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleUnlockEscrowMPT: no locked amount in issuance for "
|
||||
<< mptIssue.getMptID();
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
|
||||
auto const locked = mptIssuance->getFieldU64(sfLockedAmount);
|
||||
auto const redeem = grossAmount.mpt().value();
|
||||
|
||||
// Underflow check for subtraction
|
||||
@@ -631,19 +628,19 @@ rippleUnlockEscrowMPT(
|
||||
auto const newLocked = locked - redeem;
|
||||
if (newLocked == 0)
|
||||
{
|
||||
sleIssuance->makeFieldAbsent(sfLockedAmount);
|
||||
mptIssuance->makeFieldAbsent(sfLockedAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
sleIssuance->setFieldU64(sfLockedAmount, newLocked);
|
||||
mptIssuance->setFieldU64(sfLockedAmount, newLocked);
|
||||
}
|
||||
view.update(sleIssuance);
|
||||
mptIssuance.update();
|
||||
}
|
||||
|
||||
if (issuer != receiver)
|
||||
{
|
||||
// Increase the MPT Holder MPTAmount
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, receiver);
|
||||
auto const mptokenID = keylet::mptoken(mptIssue.getMptID(), receiver);
|
||||
auto sle = view.peek(mptokenID);
|
||||
if (!sle)
|
||||
{ // LCOV_EXCL_START
|
||||
@@ -668,7 +665,7 @@ rippleUnlockEscrowMPT(
|
||||
else
|
||||
{
|
||||
// Decrease the Issuance OutstandingAmount
|
||||
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const outstanding = mptIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const redeem = netAmount.mpt().value();
|
||||
|
||||
// Underflow check for subtraction
|
||||
@@ -679,8 +676,8 @@ rippleUnlockEscrowMPT(
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
view.update(sleIssuance);
|
||||
mptIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
mptIssuance.update();
|
||||
}
|
||||
|
||||
if (issuer == sender)
|
||||
@@ -690,7 +687,7 @@ rippleUnlockEscrowMPT(
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
// Decrease the MPT Holder EscrowedAmount
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, sender);
|
||||
auto const mptokenID = keylet::mptoken(mptIssue.getMptID(), sender);
|
||||
auto sle = view.peek(mptokenID);
|
||||
if (!sle)
|
||||
{ // LCOV_EXCL_START
|
||||
@@ -734,7 +731,7 @@ rippleUnlockEscrowMPT(
|
||||
auto const diff = grossAmount.mpt().value() - netAmount.mpt().value();
|
||||
if (diff != 0)
|
||||
{
|
||||
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const outstanding = mptIssuance->getFieldU64(sfOutstandingAmount);
|
||||
// Underflow check for subtraction
|
||||
if (!canSubtract(STAmount(mptIssue, outstanding), STAmount(mptIssue, diff)))
|
||||
{ // LCOV_EXCL_START
|
||||
@@ -743,10 +740,80 @@ rippleUnlockEscrowMPT(
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
|
||||
view.update(sleIssuance);
|
||||
mptIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
|
||||
mptIssuance.update();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
STAmount
|
||||
MPTokenIssuance::accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance) const
|
||||
{
|
||||
return accountHolds(account, zeroIfFrozen, ahIGNORE_AUTH, j, includeFullBalance);
|
||||
}
|
||||
|
||||
STAmount
|
||||
MPTokenIssuance::accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance) const
|
||||
{
|
||||
bool const returnSpendable = (includeFullBalance == shFULL_BALANCE);
|
||||
|
||||
if (returnSpendable && account == mptIssue_.getIssuer())
|
||||
{
|
||||
// if the account is the issuer, and the issuance exists, their limit is
|
||||
// the issuance limit minus the outstanding value
|
||||
|
||||
if (!sle_)
|
||||
{
|
||||
return STAmount{mptIssue_};
|
||||
}
|
||||
return STAmount{
|
||||
mptIssue_,
|
||||
sle_->at(~sfMaximumAmount).value_or(maxMPTokenAmount) - sle_->at(sfOutstandingAmount)};
|
||||
}
|
||||
|
||||
STAmount amount;
|
||||
|
||||
auto const sleMpt = readView_.read(keylet::mptoken(mptID_, account));
|
||||
|
||||
if (!sleMpt)
|
||||
{
|
||||
amount.clear(mptIssue_);
|
||||
}
|
||||
else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(account))
|
||||
{
|
||||
amount.clear(mptIssue_);
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = STAmount{mptIssue_, sleMpt->getFieldU64(sfMPTAmount)};
|
||||
|
||||
// Only if auth check is needed, as it needs to do an additional read
|
||||
// operation. Note featureSingleAssetVault will affect error codes.
|
||||
if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
|
||||
readView_.rules().enabled(featureSingleAssetVault))
|
||||
{
|
||||
if (auto const err = requireAuth(account, AuthType::StrongAuth); !isTesSuccess(err))
|
||||
amount.clear(mptIssue_);
|
||||
}
|
||||
else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED)
|
||||
{
|
||||
// if auth is enabled on the issuance and mpt is not authorized,
|
||||
// clear amount
|
||||
if (sle_ && sle_->isFlag(lsfMPTRequireAuth) && !sleMpt->isFlag(lsfMPTAuthorized))
|
||||
amount.clear(mptIssue_);
|
||||
}
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -48,7 +48,8 @@ offerDelete(ApplyView& view, std::shared_ptr<SLE> const& sle, beast::Journal j)
|
||||
}
|
||||
}
|
||||
|
||||
adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
|
||||
WritableAccountRoot wrappedOwner(owner, view);
|
||||
wrappedOwner.adjustOwnerCount(-1, j);
|
||||
|
||||
view.erase(sle);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.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/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AmountConversions.h>
|
||||
@@ -21,14 +22,14 @@ namespace xrpl {
|
||||
|
||||
STAmount
|
||||
creditLimit(
|
||||
ReadView const& view,
|
||||
ReadView const& readView_,
|
||||
AccountID const& account,
|
||||
AccountID const& issuer,
|
||||
Currency const& currency)
|
||||
{
|
||||
STAmount result(Issue{currency, account});
|
||||
|
||||
auto sleRippleState = view.read(keylet::line(account, issuer, currency));
|
||||
auto sleRippleState = readView_.read(keylet::line(account, issuer, currency));
|
||||
|
||||
if (sleRippleState)
|
||||
{
|
||||
@@ -49,14 +50,14 @@ creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Curr
|
||||
|
||||
STAmount
|
||||
creditBalance(
|
||||
ReadView const& view,
|
||||
ReadView const& readView_,
|
||||
AccountID const& account,
|
||||
AccountID const& issuer,
|
||||
Currency const& currency)
|
||||
{
|
||||
STAmount result(Issue{currency, account});
|
||||
|
||||
auto sleRippleState = view.read(keylet::line(account, issuer, currency));
|
||||
auto sleRippleState = readView_.read(keylet::line(account, issuer, currency));
|
||||
|
||||
if (sleRippleState)
|
||||
{
|
||||
@@ -78,19 +79,15 @@ creditBalance(
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
isIndividualFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer)
|
||||
IOUToken::isIndividualFrozen(AccountID const& account) const
|
||||
{
|
||||
if (isXRP(currency))
|
||||
if (isXRP(currency_))
|
||||
return false;
|
||||
if (issuer != account)
|
||||
if (issuer_ != account)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
auto const sle = view.read(keylet::line(account, issuer, currency));
|
||||
if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
auto const sle = readView_.read(keylet::line(account, issuer_, currency_));
|
||||
if (sle && sle->isFlag((issuer_ > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -99,45 +96,38 @@ isIndividualFrozen(
|
||||
// Can the specified account spend the specified currency issued by
|
||||
// the specified issuer or does the freeze flag prohibit it?
|
||||
bool
|
||||
isFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer)
|
||||
IOUToken::isFrozen(AccountID const& account, int depth) const
|
||||
{
|
||||
if (isXRP(currency))
|
||||
// NOTE: depth is ignored here because it's only relevant for MPTs
|
||||
if (isXRP(currency_))
|
||||
return false;
|
||||
auto sle = view.read(keylet::account(issuer));
|
||||
if (sle && sle->isFlag(lsfGlobalFreeze))
|
||||
if (issuerAccount_.exists() && issuerAccount_->isFlag(lsfGlobalFreeze))
|
||||
return true;
|
||||
if (issuer != account)
|
||||
if (issuer_ != account)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
sle = view.read(keylet::line(account, issuer, currency));
|
||||
if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
auto const sleLine = readView_.read(keylet::line(account, issuer_, currency_));
|
||||
if (sleLine && sleLine->isFlag((issuer_ > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
isDeepFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
AccountID const& issuer)
|
||||
IOUToken::isDeepFrozen(AccountID const& account, int depth) const
|
||||
{
|
||||
if (isXRP(currency))
|
||||
// NOTE: depth is ignored here because it's only relevant for MPTs
|
||||
if (isXRP(currency_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (issuer == account)
|
||||
if (issuer_ == account)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const sle = view.read(keylet::line(account, issuer, currency));
|
||||
auto const sle = readView_.read(keylet::line(account, issuer_, currency_));
|
||||
if (!sle)
|
||||
{
|
||||
return false;
|
||||
@@ -146,6 +136,166 @@ isDeepFrozen(
|
||||
return sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze);
|
||||
}
|
||||
|
||||
TER
|
||||
IOUToken::checkFrozen(AccountID const& account) const
|
||||
{
|
||||
return isFrozen(account) ? TER{tecFROZEN} : TER{tesSUCCESS};
|
||||
}
|
||||
|
||||
TER
|
||||
IOUToken::checkDeepFrozen(AccountID const& account) const
|
||||
{
|
||||
return isDeepFrozen(account) ? TER{tecFROZEN} : TER{tesSUCCESS};
|
||||
}
|
||||
|
||||
bool
|
||||
IOUToken::isAnyFrozen(std::initializer_list<AccountID> const& accounts, int depth) const
|
||||
{
|
||||
// NOTE: depth is ignored here because it's only relevant for MPTs
|
||||
if (isGlobalFrozen())
|
||||
return true;
|
||||
|
||||
for (auto const& account : accounts)
|
||||
{
|
||||
if (isFrozen(account, depth))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
STAmount
|
||||
IOUToken::accountFunds(
|
||||
AccountID const& id,
|
||||
STAmount const& saDefault,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal j) const
|
||||
{
|
||||
if (!saDefault.native() && saDefault.getIssuer() == id)
|
||||
return saDefault;
|
||||
|
||||
return accountHolds(id, freezeHandling, j);
|
||||
}
|
||||
|
||||
STAmount
|
||||
IOUToken::accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance) const
|
||||
{
|
||||
return accountHolds(account, zeroIfFrozen, ahIGNORE_AUTH, j, includeFullBalance);
|
||||
}
|
||||
|
||||
STAmount
|
||||
IOUToken::accountHolds(
|
||||
AccountID const& account,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance) const
|
||||
{
|
||||
if (isXRP(currency_))
|
||||
{
|
||||
AccountRoot accountRoot(account, readView_);
|
||||
return {accountRoot.xrpLiquid(0, j)};
|
||||
}
|
||||
|
||||
bool const returnSpendable = (includeFullBalance == shFULL_BALANCE);
|
||||
if (returnSpendable && account == issuer_)
|
||||
{
|
||||
// If the account is the issuer, then their limit is effectively
|
||||
// infinite
|
||||
return STAmount{issue_, STAmount::cMaxValue, STAmount::cMaxOffset};
|
||||
}
|
||||
|
||||
// IOU: Return balance on trust line modulo freeze
|
||||
// Check if line exists and is usable (mirrors old getLineIfUsable)
|
||||
SLE::const_pointer sle = readView_.read(keylet::line(account, issuer_, currency_));
|
||||
|
||||
if (sle && zeroIfFrozen == fhZERO_IF_FROZEN)
|
||||
{
|
||||
if (isFrozen(account) || isDeepFrozen(account))
|
||||
{
|
||||
sle = nullptr;
|
||||
}
|
||||
|
||||
// when fixFrozenLPTokenTransfer is enabled, if currency is lptoken,
|
||||
// we need to check if the associated assets have been frozen
|
||||
if (sle && readView_.rules().enabled(fixFrozenLPTokenTransfer))
|
||||
{
|
||||
auto const sleIssuer = readView_.read(keylet::account(issuer_));
|
||||
if (!sleIssuer)
|
||||
{
|
||||
sle = nullptr; // LCOV_EXCL_LINE
|
||||
}
|
||||
else if (sleIssuer->isFieldPresent(sfAMMID))
|
||||
{
|
||||
auto const sleAmm = readView_.read(keylet::amm((*sleIssuer)[sfAMMID]));
|
||||
|
||||
if (!sleAmm ||
|
||||
isLPTokenFrozen(
|
||||
readView_,
|
||||
account,
|
||||
(*sleAmm)[sfAsset].get<Issue>(),
|
||||
(*sleAmm)[sfAsset2].get<Issue>()))
|
||||
{
|
||||
sle = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract balance (mirrors old getTrustLineBalance)
|
||||
STAmount amount;
|
||||
if (sle)
|
||||
{
|
||||
amount = sle->getFieldAmount(sfBalance);
|
||||
bool const accountHigh = account > issuer_;
|
||||
auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit;
|
||||
if (accountHigh)
|
||||
{
|
||||
// Put balance in account terms.
|
||||
amount.negate();
|
||||
}
|
||||
if (returnSpendable)
|
||||
{
|
||||
amount += sle->getFieldAmount(oppositeField);
|
||||
}
|
||||
amount.setIssuer(issuer_);
|
||||
}
|
||||
else
|
||||
{
|
||||
amount.clear(Issue{currency_, issuer_});
|
||||
}
|
||||
|
||||
JLOG(j.trace()) << "IOUToken::accountHolds:" << " account=" << to_string(account)
|
||||
<< " amount=" << amount.getFullText();
|
||||
|
||||
return readView_.balanceHook(account, issuer_, amount);
|
||||
}
|
||||
|
||||
TER
|
||||
IOUToken::canAddHolding() const
|
||||
{
|
||||
if (isXRP(issue_))
|
||||
return tesSUCCESS;
|
||||
|
||||
if (!issuerAccount_.exists())
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
if (!issuerAccount_->isFlag(lsfDefaultRipple))
|
||||
return terNO_RIPPLE;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
Rate
|
||||
IOUToken::transferRate() const
|
||||
{
|
||||
return issuerAccount_.transferRate();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Trust line operations
|
||||
@@ -158,16 +308,16 @@ trustCreate(
|
||||
bool const bSrcHigh,
|
||||
AccountID const& uSrcAccountID,
|
||||
AccountID const& uDstAccountID,
|
||||
uint256 const& uIndex, // --> ripple state entry
|
||||
SLE::ref sleAccount, // --> the account being set.
|
||||
bool const bAuth, // --> authorize account.
|
||||
bool const bNoRipple, // --> others cannot ripple through
|
||||
bool const bFreeze, // --> funds cannot leave
|
||||
bool bDeepFreeze, // --> can neither receive nor send funds
|
||||
STAmount const& saBalance, // --> balance of account being set.
|
||||
// Issuer should be noAccount()
|
||||
STAmount const& saLimit, // --> limit for account being set.
|
||||
// Issuer should be the account being set.
|
||||
uint256 const& uIndex, // ripple state entry
|
||||
WritableAccountRoot& wrappedAcct, // the account being set.
|
||||
bool const bAuth, // authorize account.
|
||||
bool const bNoRipple, // others cannot ripple through
|
||||
bool const bFreeze, // funds cannot leave
|
||||
bool bDeepFreeze, // can neither receive nor send funds
|
||||
STAmount const& saBalance, // balance of account being set.
|
||||
// Issuer should be noAccount()
|
||||
STAmount const& saLimit, // limit for account being set.
|
||||
// Issuer should be the account being set.
|
||||
std::uint32_t uQualityIn,
|
||||
std::uint32_t uQualityOut,
|
||||
beast::Journal j)
|
||||
@@ -204,15 +354,15 @@ trustCreate(
|
||||
bool const bSetDst = saLimit.getIssuer() == uDstAccountID;
|
||||
bool const bSetHigh = bSrcHigh ^ bSetDst;
|
||||
|
||||
XRPL_ASSERT(sleAccount, "xrpl::trustCreate : non-null SLE");
|
||||
if (!sleAccount)
|
||||
XRPL_ASSERT(wrappedAcct, "xrpl::trustCreate : non-null SLE");
|
||||
if (!wrappedAcct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(
|
||||
sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
|
||||
wrappedAcct->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
|
||||
"xrpl::trustCreate : matching account ID");
|
||||
auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
|
||||
if (!slePeer)
|
||||
auto const peer = AccountRoot(bSetHigh ? uLowAccountID : uHighAccountID, view);
|
||||
if (!peer.exists())
|
||||
return tecNO_TARGET;
|
||||
|
||||
// Remember deletion hints.
|
||||
@@ -224,10 +374,10 @@ trustCreate(
|
||||
bSetHigh ? sfLowLimit : sfHighLimit,
|
||||
STAmount(Issue{saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
|
||||
|
||||
if (uQualityIn != 0u)
|
||||
if (uQualityIn)
|
||||
sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
|
||||
|
||||
if (uQualityOut != 0u)
|
||||
if (uQualityOut)
|
||||
sleRippleState->setFieldU32(bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
|
||||
|
||||
std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
|
||||
@@ -249,14 +399,14 @@ trustCreate(
|
||||
uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze);
|
||||
}
|
||||
|
||||
if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
|
||||
if ((peer->getFlags() & lsfDefaultRipple) == 0)
|
||||
{
|
||||
// The other side's default is no rippling
|
||||
uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
|
||||
}
|
||||
|
||||
sleRippleState->setFieldU32(sfFlags, uFlags);
|
||||
adjustOwnerCount(view, sleAccount, 1, j);
|
||||
wrappedAcct.adjustOwnerCount(1, j);
|
||||
|
||||
// ONLY: Create ripple balance.
|
||||
sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance);
|
||||
@@ -318,8 +468,8 @@ updateTrustLine(
|
||||
return false;
|
||||
std::uint32_t const flags(state->getFieldU32(sfFlags));
|
||||
|
||||
auto sle = view.peek(keylet::account(sender));
|
||||
if (!sle)
|
||||
WritableAccountRoot wrappedAcct(sender, view);
|
||||
if (!wrappedAcct)
|
||||
return false;
|
||||
|
||||
// YYY Could skip this if rippling in reverse.
|
||||
@@ -327,28 +477,28 @@ updateTrustLine(
|
||||
// Sender balance was positive.
|
||||
&& after <= beast::zero
|
||||
// Sender is zero or negative.
|
||||
&& ((flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u)
|
||||
&& (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
|
||||
// Sender reserve is set.
|
||||
&& static_cast<bool>(flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
|
||||
static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
|
||||
((flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) &&
|
||||
static_cast<bool>(wrappedAcct->getFlags() & lsfDefaultRipple) &&
|
||||
!(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
|
||||
!state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
|
||||
// Sender trust limit is 0.
|
||||
&& (state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) == 0u)
|
||||
&& !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
|
||||
// Sender quality in is 0.
|
||||
&& (state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut) == 0u))
|
||||
&& !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
|
||||
// Sender quality out is 0.
|
||||
{
|
||||
// VFALCO Where is the line being deleted?
|
||||
// Clear the reserve of the sender, possibly delete the line!
|
||||
adjustOwnerCount(view, sle, -1, j);
|
||||
wrappedAcct.adjustOwnerCount(-1, j);
|
||||
|
||||
// Clear reserve flag.
|
||||
state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
|
||||
|
||||
// Balance is zero, receiver reserve is clear.
|
||||
if (!after // Balance is zero.
|
||||
&& ((flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)) == 0u))
|
||||
&& !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -424,7 +574,7 @@ issueIOU(
|
||||
|
||||
final_balance.setIssuer(noAccount());
|
||||
|
||||
auto const receiverAccount = view.peek(keylet::account(account));
|
||||
WritableAccountRoot receiverAccount(account, view);
|
||||
if (!receiverAccount)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -450,7 +600,7 @@ issueIOU(
|
||||
|
||||
TER
|
||||
redeemIOU(
|
||||
ApplyView& view,
|
||||
ApplyView& applyView,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
Issue const& issue,
|
||||
@@ -470,7 +620,7 @@ redeemIOU(
|
||||
|
||||
bool bSenderHigh = account > issue.account;
|
||||
|
||||
if (auto state = view.peek(keylet::line(account, issue.account, issue.currency)))
|
||||
if (auto state = applyView.peek(keylet::line(account, issue.account, issue.currency)))
|
||||
{
|
||||
STAmount final_balance = state->getFieldAmount(sfBalance);
|
||||
|
||||
@@ -481,10 +631,10 @@ redeemIOU(
|
||||
|
||||
final_balance -= amount;
|
||||
|
||||
auto const must_delete =
|
||||
updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j);
|
||||
auto const must_delete = updateTrustLine(
|
||||
applyView, state, bSenderHigh, account, start_balance, final_balance, j);
|
||||
|
||||
view.creditHook(account, issue.account, amount, start_balance);
|
||||
applyView.creditHook(account, issue.account, amount, start_balance);
|
||||
|
||||
if (bSenderHigh)
|
||||
final_balance.negate();
|
||||
@@ -497,14 +647,14 @@ redeemIOU(
|
||||
if (must_delete)
|
||||
{
|
||||
return trustDelete(
|
||||
view,
|
||||
applyView,
|
||||
state,
|
||||
bSenderHigh ? issue.account : account,
|
||||
bSenderHigh ? account : issue.account,
|
||||
j);
|
||||
}
|
||||
|
||||
view.update(state);
|
||||
applyView.update(state);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -526,25 +676,24 @@ redeemIOU(
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TER
|
||||
requireAuth(ReadView const& view, Issue const& issue, AccountID const& account, AuthType authType)
|
||||
IOUToken::requireAuth(AccountID const& account, AuthType authType, int depth) const
|
||||
{
|
||||
if (isXRP(issue) || issue.account == account)
|
||||
// NOTE: depth is ignored here because it's only relevant for MPTs
|
||||
if (isXRP(issue_) || issuer_ == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const trustLine = view.read(keylet::line(account, issue.account, issue.currency));
|
||||
auto const trustLine = readView_.read(keylet::line(account, issuer_, issue_.currency));
|
||||
// If account has no line, and this is a strong check, fail
|
||||
if (!trustLine && authType == AuthType::StrongAuth)
|
||||
return tecNO_LINE;
|
||||
|
||||
// If this is a weak or legacy check, or if the account has a line, fail if
|
||||
// auth is required and not set on the line
|
||||
if (auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
issuerAccount && (((*issuerAccount)[sfFlags] & lsfRequireAuth) != 0u))
|
||||
if (issuerAccount_.exists() && (*issuerAccount_)[sfFlags] & lsfRequireAuth)
|
||||
{
|
||||
if (trustLine)
|
||||
{
|
||||
return (((*trustLine)[sfFlags] &
|
||||
((account > issue.account) ? lsfLowAuth : lsfHighAuth)) != 0u)
|
||||
return ((*trustLine)[sfFlags] & ((account > issuer_) ? lsfLowAuth : lsfHighAuth))
|
||||
? tesSUCCESS
|
||||
: TER{tecNO_AUTH};
|
||||
}
|
||||
@@ -555,28 +704,26 @@ requireAuth(ReadView const& view, Issue const& issue, AccountID const& account,
|
||||
}
|
||||
|
||||
TER
|
||||
canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to)
|
||||
IOUToken::canTransfer(AccountID const& from, AccountID const& to) const
|
||||
{
|
||||
if (issue.native())
|
||||
if (issue_.native())
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issuerId = issue.getIssuer();
|
||||
if (issuerId == from || issuerId == to)
|
||||
if (issuer_ == from || issuer_ == to)
|
||||
return tesSUCCESS;
|
||||
auto const sleIssuer = view.read(keylet::account(issuerId));
|
||||
if (sleIssuer == nullptr)
|
||||
if (!issuerAccount_.exists())
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
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 = readView_.read(keylet::line(account, issue_));
|
||||
if (line)
|
||||
{
|
||||
bool const issuerHigh = issuerId > account;
|
||||
bool const issuerHigh = issuer_ > account;
|
||||
return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple);
|
||||
}
|
||||
return !sleIssuer->isFlag(lsfDefaultRipple);
|
||||
return issuerAccount_->isFlag(lsfDefaultRipple) == false;
|
||||
};
|
||||
|
||||
// Fail if rippling disabled on both trust lines
|
||||
@@ -586,6 +733,29 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Token capability checks (IOU-specific)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
IOUToken::canClawback() const
|
||||
{
|
||||
if (!issuerAccount_.exists())
|
||||
return false;
|
||||
return issuerAccount_->isFlag(lsfAllowTrustLineClawback) &&
|
||||
!issuerAccount_->isFlag(lsfNoFreeze);
|
||||
}
|
||||
|
||||
bool
|
||||
IOUToken::requiresAuth() const
|
||||
{
|
||||
if (!issuerAccount_.exists())
|
||||
return false;
|
||||
return issuerAccount_->isFlag(lsfRequireAuth);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Empty holding operations (IOU-specific)
|
||||
@@ -593,73 +763,65 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TER
|
||||
addEmptyHolding(
|
||||
ApplyView& view,
|
||||
WritableIOUToken::addEmptyHolding(
|
||||
AccountID const& accountID,
|
||||
XRPAmount priorBalance,
|
||||
Issue const& issue,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Every account can hold XRP. An issuer can issue directly.
|
||||
if (issue.native() || accountID == issue.getIssuer())
|
||||
if (issue_.native() || accountID == issuer_)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issuerId = issue.getIssuer();
|
||||
auto const& currency = issue.currency;
|
||||
if (isGlobalFrozen(view, issuerId))
|
||||
if (issuerAccount_.isGlobalFrozen())
|
||||
return tecFROZEN; // LCOV_EXCL_LINE
|
||||
|
||||
auto const& srcId = issuerId;
|
||||
auto const& srcId = issuer_;
|
||||
auto const& dstId = accountID;
|
||||
auto const high = srcId > dstId;
|
||||
auto const index = keylet::line(srcId, dstId, currency);
|
||||
auto const sleSrc = view.peek(keylet::account(srcId));
|
||||
auto const sleDst = view.peek(keylet::account(dstId));
|
||||
if (!sleDst || !sleSrc)
|
||||
auto const index = keylet::line(srcId, dstId, currency_);
|
||||
WritableAccountRoot wrappedSrc(srcId, applyView_);
|
||||
WritableAccountRoot wrappedDst(dstId, applyView_);
|
||||
if (!wrappedDst || !wrappedSrc)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
if (!sleSrc->isFlag(lsfDefaultRipple))
|
||||
if (!wrappedSrc->isFlag(lsfDefaultRipple))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
// If the line already exists, don't create it again.
|
||||
if (view.read(index))
|
||||
if (applyView_.read(index))
|
||||
return tecDUPLICATE;
|
||||
|
||||
// Can the account cover the trust line reserve ?
|
||||
std::uint32_t const ownerCount = sleDst->at(sfOwnerCount);
|
||||
if (priorBalance < view.fees().accountReserve(ownerCount + 1))
|
||||
std::uint32_t const ownerCount = wrappedDst->at(sfOwnerCount);
|
||||
if (priorBalance < readView_.fees().accountReserve(ownerCount + 1))
|
||||
return tecNO_LINE_INSUF_RESERVE;
|
||||
|
||||
return trustCreate(
|
||||
view,
|
||||
applyView_,
|
||||
high,
|
||||
srcId,
|
||||
dstId,
|
||||
index.key,
|
||||
sleDst,
|
||||
wrappedDst,
|
||||
/*bAuth=*/false,
|
||||
/*bNoRipple=*/true,
|
||||
/*bFreeze=*/false,
|
||||
/*deepFreeze*/ false,
|
||||
/*saBalance=*/STAmount{Issue{currency, noAccount()}},
|
||||
/*saLimit=*/STAmount{Issue{currency, dstId}},
|
||||
/*saBalance=*/STAmount{Issue{currency_, noAccount()}},
|
||||
/*saLimit=*/STAmount{Issue{currency_, dstId}},
|
||||
/*uQualityIn=*/0,
|
||||
/*uQualityOut=*/0,
|
||||
journal);
|
||||
}
|
||||
|
||||
TER
|
||||
removeEmptyHolding(
|
||||
ApplyView& view,
|
||||
AccountID const& accountID,
|
||||
Issue const& issue,
|
||||
beast::Journal journal)
|
||||
WritableIOUToken::removeEmptyHolding(AccountID const& accountID, beast::Journal journal)
|
||||
{
|
||||
if (issue.native())
|
||||
if (issue_.native())
|
||||
{
|
||||
auto const sle = view.read(keylet::account(accountID));
|
||||
if (!sle)
|
||||
auto const account = AccountRoot(accountID, applyView_);
|
||||
if (!account.exists())
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const balance = sle->getFieldAmount(sfBalance);
|
||||
auto const balance = account->getFieldAmount(sfBalance);
|
||||
if (balance.xrp() != 0)
|
||||
return tecHAS_OBLIGATIONS;
|
||||
|
||||
@@ -669,8 +831,8 @@ removeEmptyHolding(
|
||||
// `asset` is an IOU.
|
||||
// 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));
|
||||
bool const accountIsIssuer = accountID == issue_.account;
|
||||
auto const line = applyView_.peek(keylet::line(accountID, issue_));
|
||||
if (!line)
|
||||
return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
|
||||
if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::zero)
|
||||
@@ -680,11 +842,11 @@ removeEmptyHolding(
|
||||
if (line->isFlag(lsfLowReserve))
|
||||
{
|
||||
// Clear reserve for low account.
|
||||
auto sleLowAccount = view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
|
||||
if (!sleLowAccount)
|
||||
WritableAccountRoot wrappedLow(line->at(sfLowLimit)->getIssuer(), applyView_);
|
||||
if (!wrappedLow)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleLowAccount, -1, journal);
|
||||
wrappedLow.adjustOwnerCount(-1, journal);
|
||||
// It's not really necessary to clear the reserve flag, since the line
|
||||
// is about to be deleted, but this will make the metadata reflect an
|
||||
// accurate state at the time of deletion.
|
||||
@@ -694,11 +856,11 @@ removeEmptyHolding(
|
||||
if (line->isFlag(lsfHighReserve))
|
||||
{
|
||||
// Clear reserve for high account.
|
||||
auto sleHighAccount = view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
|
||||
if (!sleHighAccount)
|
||||
WritableAccountRoot wrappedHigh(line->at(sfHighLimit)->getIssuer(), applyView_);
|
||||
if (!wrappedHigh)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleHighAccount, -1, journal);
|
||||
wrappedHigh.adjustOwnerCount(-1, journal);
|
||||
// It's not really necessary to clear the reserve flag, since the line
|
||||
// is about to be deleted, but this will make the metadata reflect an
|
||||
// accurate state at the time of deletion.
|
||||
@@ -706,7 +868,11 @@ removeEmptyHolding(
|
||||
}
|
||||
|
||||
return trustDelete(
|
||||
view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal);
|
||||
applyView_,
|
||||
line,
|
||||
line->at(sfLowLimit)->getIssuer(),
|
||||
line->at(sfHighLimit)->getIssuer(),
|
||||
journal);
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -722,13 +888,13 @@ deleteAMMTrustLine(
|
||||
auto const& [low, high] = std::minmax(
|
||||
sleState->getFieldAmount(sfLowLimit).getIssuer(),
|
||||
sleState->getFieldAmount(sfHighLimit).getIssuer());
|
||||
auto sleLow = view.peek(keylet::account(low));
|
||||
auto sleHigh = view.peek(keylet::account(high));
|
||||
if (!sleLow || !sleHigh)
|
||||
WritableAccountRoot wrappedLow(low, view);
|
||||
WritableAccountRoot wrappedHigh(high, view);
|
||||
if (!wrappedLow || !wrappedHigh)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
bool const ammLow = sleLow->isFieldPresent(sfAMMID);
|
||||
bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
|
||||
bool const ammLow = wrappedLow->isFieldPresent(sfAMMID);
|
||||
bool const ammHigh = wrappedHigh->isFieldPresent(sfAMMID);
|
||||
|
||||
// can't both be AMM
|
||||
if (ammLow && ammHigh)
|
||||
@@ -749,10 +915,11 @@ deleteAMMTrustLine(
|
||||
}
|
||||
|
||||
auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
|
||||
if ((sleState->getFlags() & uFlags) == 0u)
|
||||
if (!(sleState->getFlags() & uFlags))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
|
||||
WritableAccountRoot wrappedHolder = !ammLow ? wrappedLow : wrappedHigh;
|
||||
wrappedHolder.adjustOwnerCount(-1, j);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -26,124 +26,62 @@ isLPTokenFrozen(
|
||||
Issue const& asset,
|
||||
Issue const& asset2);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Freeze checking (Asset-based)
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
class IOUToken;
|
||||
class MPTokenIssuance;
|
||||
class WritableIOUToken;
|
||||
class WritableMPTokenIssuance;
|
||||
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, Asset const& asset)
|
||||
std::unique_ptr<TokenBase>
|
||||
makeTokenBase(ReadView const& view, Asset const& asset)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
[&]<typename T>(T const& issue) -> std::unique_ptr<TokenBase> {
|
||||
if constexpr (std::is_same_v<T, Issue>)
|
||||
{
|
||||
return isGlobalFrozen(view, issue.getIssuer());
|
||||
return std::make_unique<IOUToken>(view, issue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return isGlobalFrozen(view, issue);
|
||||
return std::make_unique<MPTokenIssuance>(view, issue);
|
||||
}
|
||||
},
|
||||
asset.value());
|
||||
}
|
||||
|
||||
bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
|
||||
std::unique_ptr<WritableTokenBase>
|
||||
makeWritableTokenBase(ApplyView& view, Asset const& asset)
|
||||
{
|
||||
return std::visit(
|
||||
[&](auto const& issue) { return isIndividualFrozen(view, account, issue); }, asset.value());
|
||||
}
|
||||
|
||||
bool
|
||||
isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth)
|
||||
{
|
||||
return std::visit(
|
||||
[&](auto const& issue) { return isFrozen(view, account, issue, depth); }, asset.value());
|
||||
}
|
||||
|
||||
TER
|
||||
checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
|
||||
{
|
||||
return isFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
|
||||
{
|
||||
return isFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
|
||||
{
|
||||
return std::visit(
|
||||
[&](auto const& issue) { return checkFrozen(view, account, issue); }, asset.value());
|
||||
}
|
||||
|
||||
bool
|
||||
isAnyFrozen(
|
||||
ReadView const& view,
|
||||
std::initializer_list<AccountID> const& accounts,
|
||||
Issue const& issue)
|
||||
{
|
||||
for (auto const& account : accounts)
|
||||
{
|
||||
if (isFrozen(view, account, issue.currency, issue.account))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
isAnyFrozen(
|
||||
ReadView const& view,
|
||||
std::initializer_list<AccountID> const& accounts,
|
||||
Asset const& asset,
|
||||
int depth)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
[&]<typename T>(T const& issue) -> std::unique_ptr<WritableTokenBase> {
|
||||
if constexpr (std::is_same_v<T, Issue>)
|
||||
{
|
||||
return isAnyFrozen(view, accounts, issue);
|
||||
return std::make_unique<WritableIOUToken>(view, issue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return isAnyFrozen(view, accounts, issue, depth);
|
||||
return std::make_unique<WritableMPTokenIssuance>(view, issue);
|
||||
}
|
||||
},
|
||||
asset.value());
|
||||
}
|
||||
|
||||
bool
|
||||
isDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth)
|
||||
{
|
||||
// Unlike IOUs, frozen / locked MPTs are not allowed to send or receive
|
||||
// funds, so checking "deep frozen" is the same as checking "frozen".
|
||||
return isFrozen(view, account, mptIssue, depth);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// TokenBase implementation
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth)
|
||||
TokenBase::isFrozen(AccountID const& account, int depth) const
|
||||
{
|
||||
return std::visit(
|
||||
[&](auto const& issue) { return isDeepFrozen(view, account, issue, depth); },
|
||||
asset.value());
|
||||
// Default implementation - subclasses should override
|
||||
return isGlobalFrozen() || isIndividualFrozen(account);
|
||||
}
|
||||
|
||||
TER
|
||||
checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
|
||||
Rate
|
||||
transferRate(ReadView const& view, STAmount const& amount)
|
||||
{
|
||||
return isDeepFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
|
||||
{
|
||||
return std::visit(
|
||||
[&](auto const& issue) { return checkDeepFrozen(view, account, issue); }, asset.value());
|
||||
return makeTokenBase(view, amount.asset())->transferRate();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -161,7 +99,8 @@ getLineIfUsable(
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto sle = view.read(keylet::line(account, issuer, currency));
|
||||
IOUToken token(view, issuer, currency);
|
||||
auto const sle = view.read(keylet::line(account, issuer, currency));
|
||||
|
||||
if (!sle)
|
||||
{
|
||||
@@ -170,8 +109,7 @@ getLineIfUsable(
|
||||
|
||||
if (zeroIfFrozen == fhZERO_IF_FROZEN)
|
||||
{
|
||||
if (isFrozen(view, account, currency, issuer) ||
|
||||
isDeepFrozen(view, account, currency, issuer))
|
||||
if (token.isFrozen(account) || token.isDeepFrozen(account))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -180,14 +118,14 @@ getLineIfUsable(
|
||||
// we need to check if the associated assets have been frozen
|
||||
if (view.rules().enabled(fixFrozenLPTokenTransfer))
|
||||
{
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
auto const issuerRoot = AccountRoot(issuer, view);
|
||||
if (!issuerRoot.exists())
|
||||
{
|
||||
return nullptr; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (sleIssuer->isFieldPresent(sfAMMID))
|
||||
if (issuerRoot->isFieldPresent(sfAMMID))
|
||||
{
|
||||
auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID]));
|
||||
auto const sleAmm = view.read(keylet::amm((*issuerRoot)[sfAMMID]));
|
||||
|
||||
if (!sleAmm ||
|
||||
isLPTokenFrozen(
|
||||
@@ -256,7 +194,8 @@ accountHolds(
|
||||
STAmount amount;
|
||||
if (isXRP(currency))
|
||||
{
|
||||
return {xrpLiquid(view, account, 0, j)};
|
||||
AccountRoot accountRoot(account, view);
|
||||
return {accountRoot.xrpLiquid(0, j)};
|
||||
}
|
||||
|
||||
bool const returnSpendable = (includeFullBalance == shFULL_BALANCE);
|
||||
@@ -275,87 +214,6 @@ accountHolds(
|
||||
}
|
||||
|
||||
STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& issue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance)
|
||||
{
|
||||
return accountHolds(
|
||||
view, account, issue.currency, issue.account, zeroIfFrozen, j, includeFullBalance);
|
||||
}
|
||||
|
||||
STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTIssue const& mptIssue,
|
||||
FreezeHandling zeroIfFrozen,
|
||||
AuthHandling zeroIfUnauthorized,
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance)
|
||||
{
|
||||
bool const returnSpendable = (includeFullBalance == shFULL_BALANCE);
|
||||
|
||||
if (returnSpendable && account == mptIssue.getIssuer())
|
||||
{
|
||||
// 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()));
|
||||
|
||||
if (!issuance)
|
||||
{
|
||||
return STAmount{mptIssue};
|
||||
}
|
||||
return STAmount{
|
||||
mptIssue,
|
||||
issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) -
|
||||
issuance->at(sfOutstandingAmount)};
|
||||
}
|
||||
|
||||
STAmount amount;
|
||||
|
||||
auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account));
|
||||
|
||||
if (!sleMpt)
|
||||
{
|
||||
amount.clear(mptIssue);
|
||||
}
|
||||
else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue))
|
||||
{
|
||||
amount.clear(mptIssue);
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)};
|
||||
|
||||
// Only if auth check is needed, as it needs to do an additional read
|
||||
// operation. Note featureSingleAssetVault will affect error codes.
|
||||
if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
|
||||
view.rules().enabled(featureSingleAssetVault))
|
||||
{
|
||||
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth);
|
||||
!isTesSuccess(err))
|
||||
amount.clear(mptIssue);
|
||||
}
|
||||
else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED)
|
||||
{
|
||||
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||
|
||||
// if auth is enabled on the issuance and mpt is not authorized,
|
||||
// clear amount
|
||||
if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) &&
|
||||
!sleMpt->isFlag(lsfMPTAuthorized))
|
||||
amount.clear(mptIssue);
|
||||
}
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
accountHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
@@ -365,51 +223,8 @@ accountHolds(
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& value) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance);
|
||||
}
|
||||
else if constexpr (std::is_same_v<TIss, MPTIssue>)
|
||||
{
|
||||
return accountHolds(
|
||||
view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
|
||||
}
|
||||
},
|
||||
asset.value());
|
||||
}
|
||||
|
||||
STAmount
|
||||
accountFunds(
|
||||
ReadView const& view,
|
||||
AccountID const& id,
|
||||
STAmount const& saDefault,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (!saDefault.native() && saDefault.getIssuer() == id)
|
||||
return saDefault;
|
||||
|
||||
return accountHolds(
|
||||
view, id, saDefault.getCurrency(), saDefault.getIssuer(), freezeHandling, j);
|
||||
}
|
||||
|
||||
Rate
|
||||
transferRate(ReadView const& view, STAmount const& amount)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return transferRate(view, issue.getIssuer());
|
||||
}
|
||||
else
|
||||
{
|
||||
return transferRate(view, issue.getMptID());
|
||||
}
|
||||
},
|
||||
amount.asset().value());
|
||||
auto token = makeTokenBase(view, asset);
|
||||
return token->accountHolds(account, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -425,12 +240,13 @@ canAddHolding(ReadView const& view, Issue const& issue)
|
||||
{
|
||||
return tesSUCCESS; // No special checks for XRP
|
||||
}
|
||||
auto const issuer = AccountRoot(issue.getIssuer(), view);
|
||||
|
||||
auto const issuer = view.read(keylet::account(issue.getIssuer()));
|
||||
if (!issuer)
|
||||
if (!issuer.exists())
|
||||
{
|
||||
return terNO_ACCOUNT;
|
||||
}
|
||||
|
||||
if (!issuer->isFlag(lsfDefaultRipple))
|
||||
{
|
||||
return terNO_RIPPLE;
|
||||
@@ -442,9 +258,7 @@ canAddHolding(ReadView const& view, Issue const& issue)
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, Asset const& asset)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) -> TER { return canAddHolding(view, issue); },
|
||||
asset.value());
|
||||
return makeTokenBase(view, asset)->canAddHolding();
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -455,11 +269,7 @@ addEmptyHolding(
|
||||
Asset const& asset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
||||
return addEmptyHolding(view, accountID, priorBalance, issue, journal);
|
||||
},
|
||||
asset.value());
|
||||
return makeWritableTokenBase(view, asset)->addEmptyHolding(accountID, priorBalance, journal);
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -469,37 +279,7 @@ removeEmptyHolding(
|
||||
Asset const& asset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
||||
return removeEmptyHolding(view, accountID, issue, journal);
|
||||
},
|
||||
asset.value());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Authorization and transfer checks
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TER
|
||||
requireAuth(ReadView const& view, Asset const& asset, AccountID const& account, AuthType authType)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue_) {
|
||||
return requireAuth(view, issue_, account, authType);
|
||||
},
|
||||
asset.value());
|
||||
}
|
||||
|
||||
TER
|
||||
canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
||||
return canTransfer(view, issue, from, to);
|
||||
},
|
||||
asset.value());
|
||||
return makeWritableTokenBase(view, asset)->removeEmptyHolding(accountID, journal);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -511,7 +291,7 @@ canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, Acc
|
||||
// Direct send w/o fees:
|
||||
// - Redeeming IOUs and/or sending sender's own IOUs.
|
||||
// - Create trust line if needed.
|
||||
// --> bCheckIssuer : normally require issuer to be involved.
|
||||
// bCheckIssuer : normally require issuer to be involved.
|
||||
static TER
|
||||
rippleCreditIOU(
|
||||
ApplyView& view,
|
||||
@@ -570,22 +350,21 @@ rippleCreditIOU(
|
||||
// Sender balance was positive.
|
||||
&& saBalance <= beast::zero
|
||||
// Sender is zero or negative.
|
||||
&& ((uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u)
|
||||
&& (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
|
||||
// Sender reserve is set.
|
||||
&& static_cast<bool>(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
|
||||
static_cast<bool>(
|
||||
view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) &&
|
||||
((uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) &&
|
||||
static_cast<bool>(AccountRoot(uSenderID, view)->getFlags() & lsfDefaultRipple) &&
|
||||
!(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
|
||||
!sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
|
||||
// Sender trust limit is 0.
|
||||
&& (sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) == 0u)
|
||||
&& !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
|
||||
// Sender quality in is 0.
|
||||
&&
|
||||
(sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut) == 0u))
|
||||
&& !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
|
||||
// Sender quality out is 0.
|
||||
{
|
||||
// Clear the reserve of the sender, possibly delete the line!
|
||||
adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), -1, j);
|
||||
WritableAccountRoot wrappedSender(uSenderID, view);
|
||||
wrappedSender.adjustOwnerCount(-1, j);
|
||||
|
||||
// Clear reserve flag.
|
||||
sleRippleState->setFieldU32(
|
||||
@@ -593,7 +372,7 @@ rippleCreditIOU(
|
||||
|
||||
// Balance is zero, receiver reserve is clear.
|
||||
bDelete = !saBalance // Balance is zero.
|
||||
&& ((uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)) == 0u);
|
||||
&& !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
|
||||
// Receiver reserve is clear.
|
||||
}
|
||||
|
||||
@@ -628,11 +407,11 @@ rippleCreditIOU(
|
||||
<< to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : "
|
||||
<< saAmount.getFullText();
|
||||
|
||||
auto const sleAccount = view.peek(keylet::account(uReceiverID));
|
||||
if (!sleAccount)
|
||||
WritableAccountRoot wrappedAccount(uReceiverID, view);
|
||||
if (!wrappedAccount)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
|
||||
bool const noRipple = (wrappedAccount->getFlags() & lsfDefaultRipple) == 0;
|
||||
|
||||
return trustCreate(
|
||||
view,
|
||||
@@ -640,7 +419,7 @@ rippleCreditIOU(
|
||||
uSenderID,
|
||||
uReceiverID,
|
||||
index.key,
|
||||
sleAccount,
|
||||
wrappedAccount,
|
||||
false,
|
||||
noRipple,
|
||||
false,
|
||||
@@ -653,7 +432,7 @@ rippleCreditIOU(
|
||||
}
|
||||
|
||||
// Send regardless of limits.
|
||||
// --> saAmount: Amount/currency/issuer to deliver to receiver.
|
||||
// saAmount: Amount/currency/issuer to deliver to receiver.
|
||||
// <-- saActual: Amount actually cost. Sender pays fees.
|
||||
static TER
|
||||
rippleSendIOU(
|
||||
@@ -686,8 +465,10 @@ rippleSendIOU(
|
||||
|
||||
// Calculate the amount to transfer accounting
|
||||
// for any transfer fees if the fee is not waived:
|
||||
saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount
|
||||
: multiply(saAmount, transferRate(view, issuer));
|
||||
WritableAccountRoot wrappedIssuer(issuer, view);
|
||||
saActual = (waiveFee == WaiveTransferFee::Yes)
|
||||
? saAmount
|
||||
: multiply(saAmount, wrappedIssuer.transferRate());
|
||||
|
||||
JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > "
|
||||
<< to_string(uReceiverID) << " : deliver=" << saAmount.getFullText()
|
||||
@@ -702,7 +483,7 @@ rippleSendIOU(
|
||||
}
|
||||
|
||||
// Send regardless of limits.
|
||||
// --> receivers: Amount/currency/issuer to deliver to receivers.
|
||||
// receivers: Amount/currency/issuer to deliver to receivers.
|
||||
// <-- saActual: Amount actually cost to sender. Sender pays fees.
|
||||
static TER
|
||||
rippleSendMultiIOU(
|
||||
@@ -752,9 +533,10 @@ rippleSendMultiIOU(
|
||||
|
||||
// Calculate the amount to transfer accounting
|
||||
// for any transfer fees if the fee is not waived:
|
||||
WritableAccountRoot wrappedIssuer(issuer, view);
|
||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
|
||||
? amount
|
||||
: multiply(amount, transferRate(view, issuer));
|
||||
: multiply(amount, wrappedIssuer.transferRate());
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
@@ -1030,19 +812,18 @@ rippleCreditMPT(
|
||||
beast::Journal j)
|
||||
{
|
||||
// Do not check MPT authorization here - it must have been checked earlier
|
||||
auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
|
||||
WritableMPTokenIssuance mptIssuance(view, saAmount.get<MPTIssue>().getMptID());
|
||||
auto const& issuer = saAmount.getIssuer();
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
if (!mptIssuance.exists())
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
if (uSenderID == issuer)
|
||||
{
|
||||
(*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
|
||||
view.update(sleIssuance);
|
||||
(*mptIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
|
||||
mptIssuance.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
|
||||
auto const mptokenID = keylet::mptoken(mptIssuance.getMptID(), uSenderID);
|
||||
if (auto sle = view.peek(mptokenID))
|
||||
{
|
||||
auto const amt = sle->getFieldU64(sfMPTAmount);
|
||||
@@ -1060,12 +841,12 @@ rippleCreditMPT(
|
||||
|
||||
if (uReceiverID == issuer)
|
||||
{
|
||||
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const outstanding = mptIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const redeem = saAmount.mpt().value();
|
||||
if (outstanding >= redeem)
|
||||
{
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
view.update(sleIssuance);
|
||||
mptIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
mptIssuance.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1074,7 +855,7 @@ rippleCreditMPT(
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
|
||||
auto const mptokenID = keylet::mptoken(mptIssuance.getMptID(), uReceiverID);
|
||||
if (auto sle = view.peek(mptokenID))
|
||||
{
|
||||
(*sle)[sfMPTAmount] += saAmount.mpt().value();
|
||||
@@ -1103,9 +884,8 @@ rippleSendMPT(
|
||||
|
||||
// Safe to get MPT since rippleSendMPT is only called by accountSendMPT
|
||||
auto const& issuer = saAmount.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
|
||||
if (!sle)
|
||||
WritableMPTokenIssuance mptIssuance(view, saAmount.get<MPTIssue>().getMptID());
|
||||
if (!mptIssuance.exists())
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
if (uSenderID == issuer || uReceiverID == issuer)
|
||||
@@ -1115,9 +895,9 @@ rippleSendMPT(
|
||||
if (uSenderID == issuer)
|
||||
{
|
||||
auto const sendAmount = saAmount.mpt().value();
|
||||
auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
auto const maximumAmount = mptIssuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
if (sendAmount > maximumAmount ||
|
||||
sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
mptIssuance->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
@@ -1130,9 +910,8 @@ rippleSendMPT(
|
||||
}
|
||||
|
||||
// Sending 3rd party MPTs: transit.
|
||||
saActual = (waiveFee == WaiveTransferFee::Yes)
|
||||
? saAmount
|
||||
: multiply(saAmount, transferRate(view, saAmount.get<MPTIssue>().getMptID()));
|
||||
saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount
|
||||
: multiply(saAmount, mptIssuance.transferRate());
|
||||
|
||||
JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > "
|
||||
<< to_string(uReceiverID) << " : deliver=" << saAmount.getFullText()
|
||||
@@ -1158,9 +937,8 @@ rippleSendMultiMPT(
|
||||
// Safe to get MPT since rippleSendMultiMPT is only called by
|
||||
// accountSendMultiMPT
|
||||
auto const& issuer = mptIssue.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||
if (!sle)
|
||||
auto mptIssuance = MPTokenIssuance(view, mptIssue);
|
||||
if (!mptIssuance.exists())
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// These may diverge
|
||||
@@ -1194,9 +972,10 @@ rippleSendMultiMPT(
|
||||
"xrpl::rippleSendMultiMPT",
|
||||
"sender == issuer, takeFromSender == zero");
|
||||
auto const sendAmount = amount.mpt().value();
|
||||
auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
auto const maximumAmount =
|
||||
mptIssuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
if (sendAmount > maximumAmount ||
|
||||
sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
mptIssuance->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
@@ -1213,7 +992,7 @@ rippleSendMultiMPT(
|
||||
// Sending 3rd party MPTs: transit.
|
||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
|
||||
? amount
|
||||
: multiply(amount, transferRate(view, amount.get<MPTIssue>().getMptID()));
|
||||
: multiply(amount, mptIssuance.transferRate());
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
@@ -1362,15 +1141,15 @@ transferXRP(
|
||||
XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver");
|
||||
XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP");
|
||||
|
||||
SLE::pointer const sender = view.peek(keylet::account(from));
|
||||
SLE::pointer const receiver = view.peek(keylet::account(to));
|
||||
if (!sender || !receiver)
|
||||
WritableAccountRoot acctSender(from, view);
|
||||
WritableAccountRoot acctReceiver(to, view);
|
||||
if (!acctSender || !acctReceiver)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to)
|
||||
<< ") : " << amount.getFullText();
|
||||
|
||||
if (sender->getFieldAmount(sfBalance) < amount)
|
||||
if (acctSender->getFieldAmount(sfBalance) < amount)
|
||||
{
|
||||
// VFALCO Its unfortunate we have to keep
|
||||
// mutating these TER everywhere
|
||||
@@ -1381,11 +1160,11 @@ transferXRP(
|
||||
}
|
||||
|
||||
// Decrement XRP balance.
|
||||
sender->setFieldAmount(sfBalance, sender->getFieldAmount(sfBalance) - amount);
|
||||
view.update(sender);
|
||||
acctSender->setFieldAmount(sfBalance, acctSender->getFieldAmount(sfBalance) - amount);
|
||||
acctSender.update();
|
||||
|
||||
receiver->setFieldAmount(sfBalance, receiver->getFieldAmount(sfBalance) + amount);
|
||||
view.update(receiver);
|
||||
acctReceiver->setFieldAmount(sfBalance, acctReceiver->getFieldAmount(sfBalance) + amount);
|
||||
acctReceiver.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace xrpl {
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& assets)
|
||||
{
|
||||
XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets");
|
||||
@@ -36,7 +36,7 @@ assetsToSharesDeposit(
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& shares)
|
||||
{
|
||||
XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares");
|
||||
@@ -62,7 +62,7 @@ sharesToAssetsDeposit(
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate)
|
||||
{
|
||||
@@ -89,10 +89,10 @@ assetsToSharesWithdraw(
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuance const& issuance,
|
||||
STAmount const& shares)
|
||||
{
|
||||
XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsWithdraw : non-negative shares");
|
||||
XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares");
|
||||
XRPL_ASSERT(
|
||||
shares.asset() == vault->at(sfShareMPTID),
|
||||
"xrpl::sharesToAssetsWithdraw : shares and vault match");
|
||||
|
||||
@@ -59,7 +59,6 @@ public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
makeGet(std::string const& strPath, boost::asio::streambuf& sb, std::string const& strHost)
|
||||
{
|
||||
std::ostream osRequest(&sb);
|
||||
|
||||
@@ -55,7 +55,7 @@ ManagerImp::make_Backend(
|
||||
missing_backend();
|
||||
|
||||
auto factory{find(type)};
|
||||
if (factory == nullptr)
|
||||
if (!factory)
|
||||
{
|
||||
missing_backend();
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public:
|
||||
rocksdb::DB* db = nullptr;
|
||||
m_options.create_if_missing = createIfMissing;
|
||||
rocksdb::Status status = rocksdb::DB::Open(m_options, m_name, &db);
|
||||
if (!status.ok() || (db == nullptr))
|
||||
if (!status.ok() || !db)
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
std::string("Unable to open/create RocksDB: ") + status.ToString());
|
||||
|
||||
@@ -113,7 +113,7 @@ encodeSoftwareVersion(std::string_view versionStr)
|
||||
{
|
||||
std::uint8_t x = 0;
|
||||
|
||||
for (auto const& id : v.preReleaseIdentifiers)
|
||||
for (auto id : v.preReleaseIdentifiers)
|
||||
{
|
||||
auto parsePreRelease = [](std::string_view identifier,
|
||||
std::string_view prefix,
|
||||
@@ -140,7 +140,7 @@ encodeSoftwareVersion(std::string_view versionStr)
|
||||
if (x == 0)
|
||||
x = parsePreRelease(id, "b", 0x40, 0, 63);
|
||||
|
||||
if ((x & 0xC0) != 0)
|
||||
if (x & 0xC0)
|
||||
{
|
||||
c |= static_cast<std::uint64_t>(x) << 16;
|
||||
break;
|
||||
|
||||
@@ -205,7 +205,9 @@ make_error(error_code_i code, std::string const& message)
|
||||
bool
|
||||
contains_error(Json::Value const& json)
|
||||
{
|
||||
return json.isObject() && json.isMember(jss::error);
|
||||
if (json.isObject() && json.isMember(jss::error))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -209,7 +209,7 @@ FeatureCollections::getRegisteredFeature(std::string const& name) const
|
||||
XRPL_ASSERT(
|
||||
readOnly.load(), "xrpl::FeatureCollections::getRegisteredFeature : startup completed");
|
||||
Feature const* feature = getByName(name);
|
||||
if (feature != nullptr)
|
||||
if (feature)
|
||||
return feature->feature;
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -229,7 +229,7 @@ FeatureCollections::registerFeature(std::string const& name, Supported support,
|
||||
support == Supported::yes || vote == VoteBehavior::DefaultNo,
|
||||
"Invalid feature parameters. Must be supported to be up-voted.");
|
||||
Feature const* i = getByName(name);
|
||||
if (i == nullptr)
|
||||
if (!i)
|
||||
{
|
||||
check(features.size() < detail::numFeatures, "More features defined than allocated.");
|
||||
|
||||
@@ -283,7 +283,7 @@ FeatureCollections::featureToBitsetIndex(uint256 const& f) const
|
||||
readOnly.load(), "xrpl::FeatureCollections::featureToBitsetIndex : startup completed");
|
||||
|
||||
Feature const* feature = getByFeature(f);
|
||||
if (feature == nullptr)
|
||||
if (!feature)
|
||||
LogicError("Invalid Feature ID");
|
||||
|
||||
return getIndex(*feature);
|
||||
@@ -303,7 +303,7 @@ FeatureCollections::featureToName(uint256 const& f) const
|
||||
{
|
||||
XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::featureToName : startup completed");
|
||||
Feature const* feature = getByFeature(f);
|
||||
return (feature != nullptr) ? feature->name : to_string(f);
|
||||
return feature ? feature->name : to_string(f);
|
||||
}
|
||||
|
||||
FeatureCollections featureCollections;
|
||||
|
||||
@@ -186,7 +186,7 @@ mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundU
|
||||
{
|
||||
using namespace boost::multiprecision;
|
||||
|
||||
if (den == 0u)
|
||||
if (!den)
|
||||
Throw<std::runtime_error>("division by zero");
|
||||
|
||||
// A vector with the value 10^index for indexes from 0 to 29
|
||||
|
||||
@@ -471,7 +471,7 @@ oracle(AccountID const& account, std::uint32_t const& documentID) noexcept
|
||||
}
|
||||
|
||||
Keylet
|
||||
mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept
|
||||
mptIssuance(std::uint32_t const seq, AccountID const& issuer) noexcept
|
||||
{
|
||||
return mptIssuance(makeMptID(seq, issuer));
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ SOTemplate const*
|
||||
InnerObjectFormats::findSOTemplateBySField(SField const& sField) const
|
||||
{
|
||||
auto itemPtr = findByType(sField.getCode());
|
||||
if (itemPtr != nullptr)
|
||||
if (itemPtr)
|
||||
return &(itemPtr->getSOTemplate());
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -53,21 +51,4 @@ deserializePrefixedHeader(Slice data, bool hasHash)
|
||||
return deserializeHeader(data + 4, hasHash);
|
||||
}
|
||||
|
||||
uint256
|
||||
calculateLedgerHash(LedgerHeader const& info)
|
||||
{
|
||||
// VFALCO This has to match addRaw in View.h.
|
||||
return sha512Half(
|
||||
HashPrefix::ledgerMaster,
|
||||
std::uint32_t(info.seq),
|
||||
std::uint64_t(info.drops.drops()),
|
||||
info.parentHash,
|
||||
info.txHash,
|
||||
info.accountHash,
|
||||
std::uint32_t(info.parentCloseTime.time_since_epoch().count()),
|
||||
std::uint32_t(info.closeTime.time_since_epoch().count()),
|
||||
std::uint8_t(info.closeTimeResolution.count()),
|
||||
std::uint8_t(info.closeFlags));
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -24,7 +24,7 @@ canHaveNFTokenOfferID(
|
||||
return false;
|
||||
|
||||
TxType const tt = serializedTx->getTxnType();
|
||||
if ((tt != ttNFTOKEN_MINT || !serializedTx->isFieldPresent(sfAmount)) &&
|
||||
if (!(tt == ttNFTOKEN_MINT && serializedTx->isFieldPresent(sfAmount)) &&
|
||||
tt != ttNFTOKEN_CREATE_OFFER)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -176,13 +176,13 @@ Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Permission::txToPermissionType(TxType const& type)
|
||||
Permission::txToPermissionType(TxType const& type) const
|
||||
{
|
||||
return static_cast<uint32_t>(type) + 1;
|
||||
}
|
||||
|
||||
TxType
|
||||
Permission::permissionToTxType(uint32_t const& value)
|
||||
Permission::permissionToTxType(uint32_t const& value) const
|
||||
{
|
||||
return static_cast<TxType>(value - 1);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user