mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-27 15:12:28 +00:00
Compare commits
23 Commits
develop
...
mvadari/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcdc5e1b52 | ||
|
|
c0895c6e2e | ||
|
|
5b0b1ff1f6 | ||
|
|
e9c4ed6a74 | ||
|
|
deaa23f4e5 | ||
|
|
638477c824 | ||
|
|
9b42e178a2 | ||
|
|
f41c02d486 | ||
|
|
a423813098 | ||
|
|
56c173a097 | ||
|
|
7900fa9ead | ||
|
|
0ffb3e2227 | ||
|
|
add3d7e68d | ||
|
|
c24432f43a | ||
|
|
1ccd84e43a | ||
|
|
1cc7424934 | ||
|
|
b5562cc81e | ||
|
|
e6369c0faa | ||
|
|
43caa7d47a | ||
|
|
e0f487bb2e | ||
|
|
a8987cf271 | ||
|
|
4157e3684c | ||
|
|
659f455335 |
@@ -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."
|
||||
1
.github/workflows/publish-docs.yml
vendored
1
.github/workflows/publish-docs.yml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
- "release*"
|
||||
paths:
|
||||
- ".github/workflows/publish-docs.yml"
|
||||
- "*.md"
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations for SLE wrappers
|
||||
template <typename ViewT>
|
||||
class AccountRoot;
|
||||
using ReadOnlyAccountRoot = AccountRoot<ReadView>;
|
||||
|
||||
enum class SkipEntry : bool { No = false, Yes };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -157,7 +162,7 @@ canWithdraw(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
SLE::const_ref toSle,
|
||||
ReadOnlyAccountRoot 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,145 @@
|
||||
|
||||
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
|
||||
/**
|
||||
* View-parameterized wrapper for AccountRoot ledger entries.
|
||||
*
|
||||
* AccountRoot<ReadView> — read-only access to account data
|
||||
* AccountRoot<ApplyView> — read-write access, with insert/update/erase
|
||||
* and domain-specific write methods
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer);
|
||||
template <typename ViewT>
|
||||
class AccountRoot : public SLEBase<ViewT>
|
||||
{
|
||||
static constexpr bool is_writable = SLEBase<ViewT>::is_writable;
|
||||
|
||||
AccountID const id_;
|
||||
|
||||
public:
|
||||
/** Constructor for read-only context */
|
||||
AccountRoot(AccountID const& id, ReadView const& view)
|
||||
requires(!is_writable)
|
||||
: SLEBase<ViewT>(view.read(keylet::account(id)), view), id_(id)
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor for writable context */
|
||||
AccountRoot(AccountID const& id, ApplyView& view)
|
||||
requires is_writable
|
||||
: SLEBase<ViewT>(keylet::account(id), view), id_(id)
|
||||
{
|
||||
}
|
||||
|
||||
/** Converting constructor: writable → read-only. */
|
||||
template <WritableView OtherViewT>
|
||||
AccountRoot(AccountRoot<OtherViewT> const& other)
|
||||
requires(!is_writable)
|
||||
: SLEBase<ViewT>(other), id_(other.id())
|
||||
{
|
||||
}
|
||||
|
||||
/** Create an AccountRoot backed by a brand-new SLE
|
||||
* (not yet inserted into the view).
|
||||
*/
|
||||
[[nodiscard]] static AccountRoot
|
||||
makeNew(AccountID const& id, ApplyView& view)
|
||||
requires is_writable
|
||||
{
|
||||
return AccountRoot(id, view, std::make_shared<SLE>(keylet::account(id)));
|
||||
}
|
||||
|
||||
AccountID const&
|
||||
id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
// --- Read-only domain methods (available on both specializations) ---
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
// --- Write-only domain methods (compile-time gated) ---
|
||||
|
||||
/** Adjust the owner count up or down. */
|
||||
void
|
||||
adjustOwnerCount(std::int32_t amount, beast::Journal j)
|
||||
requires is_writable;
|
||||
|
||||
private:
|
||||
// Private constructor only used by `makeNew`
|
||||
AccountRoot(AccountID const& id, ApplyView& view, std::shared_ptr<SLE> sle)
|
||||
requires is_writable
|
||||
: SLEBase<ViewT>(std::move(sle), view), id_(id)
|
||||
{
|
||||
this->insert();
|
||||
}
|
||||
};
|
||||
|
||||
// CTAD deduction guide — bare AccountRoot(id, view) always deduces read-only.
|
||||
// For writable access, use WritableAccountRoot(id, applyView) explicitly.
|
||||
AccountRoot(AccountID const&, ReadView const&) -> AccountRoot<ReadView>;
|
||||
|
||||
// Backward-compatible aliases
|
||||
using ReadOnlyAccountRoot = AccountRoot<ReadView>;
|
||||
using WritableAccountRoot = AccountRoot<ApplyView>;
|
||||
|
||||
// Explicit instantiation declarations (definitions in .cpp)
|
||||
extern template class AccountRoot<ReadView>;
|
||||
extern template class AccountRoot<ApplyView>;
|
||||
|
||||
/** Generate a pseudo-account address from a pseudo owner key.
|
||||
@param pseudoOwnerKey The key to generate the address from
|
||||
@@ -87,7 +194,10 @@ isPseudoAccount(
|
||||
AccountID const& accountId,
|
||||
std::set<SField const*> const& pseudoFieldFilter = {})
|
||||
{
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter);
|
||||
AccountRoot<ReadView> const acct(accountId, view);
|
||||
if (!acct)
|
||||
return false;
|
||||
return acct.isPseudoAccount(pseudoFieldFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,12 +211,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,
|
||||
ReadOnlyAccountRoot const& dst,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
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>
|
||||
@@ -137,16 +138,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);
|
||||
|
||||
228
include/xrpl/ledger/helpers/SLEBase.h
Normal file
228
include/xrpl/ledger/helpers/SLEBase.h
Normal file
@@ -0,0 +1,228 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Concept to distinguish read-only vs writable view types
|
||||
template <typename V>
|
||||
concept WritableView = std::derived_from<V, ApplyView>;
|
||||
|
||||
/**
|
||||
* View-parameterized base class for all ledger entry wrappers.
|
||||
*
|
||||
* SLEBase<ReadView> — read-only: holds shared_ptr<SLE const> + ReadView const&
|
||||
* SLEBase<ApplyView> — writable: holds shared_ptr<SLE> + ApplyView& + Keylet,
|
||||
* plus insert/update/erase operations
|
||||
*
|
||||
* Write-only members are gated by `requires` clauses, providing compile-time
|
||||
* guarantees that read-only wrappers cannot mutate state.
|
||||
*
|
||||
* Derived classes should provide domain-specific accessors that hide
|
||||
* implementation details of the underlying ledger entry format.
|
||||
*/
|
||||
template <typename ViewT>
|
||||
class SLEBase
|
||||
{
|
||||
public:
|
||||
static constexpr bool is_writable = WritableView<ViewT>;
|
||||
|
||||
// SLE pointer type: mutable for writable views, const for read-only
|
||||
using sle_ptr_type =
|
||||
std::conditional_t<is_writable, std::shared_ptr<SLE>, std::shared_ptr<SLE const>>;
|
||||
|
||||
// View reference type: ApplyView& for writable, ReadView const& for read-only
|
||||
using view_ref_type = std::conditional_t<is_writable, ApplyView&, ReadView const&>;
|
||||
|
||||
virtual ~SLEBase() = default;
|
||||
|
||||
SLEBase(SLEBase const&) = default;
|
||||
SLEBase(SLEBase&&) = default;
|
||||
SLEBase&
|
||||
operator=(SLEBase const&) = delete;
|
||||
SLEBase&
|
||||
operator=(SLEBase&&) = delete;
|
||||
|
||||
// --- Common interface (always available) ---
|
||||
|
||||
/** 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 */
|
||||
std::shared_ptr<SLE const>
|
||||
sle() const
|
||||
{
|
||||
return sle_;
|
||||
}
|
||||
|
||||
/** Returns the read view (always available; ApplyView inherits ReadView) */
|
||||
ReadView const&
|
||||
readView() const
|
||||
{
|
||||
return view_;
|
||||
}
|
||||
|
||||
/** Const dereference operators (always available) */
|
||||
STLedgerEntry const*
|
||||
operator->() const
|
||||
{
|
||||
XRPL_ASSERT(exists(), "xrpl::SLEBase::operator-> : exists");
|
||||
return sle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry const&
|
||||
operator*() const
|
||||
{
|
||||
XRPL_ASSERT(exists(), "xrpl::SLEBase::operator* : exists");
|
||||
return *sle_;
|
||||
}
|
||||
|
||||
// --- Writable interface (compile-time gated) ---
|
||||
|
||||
/** Returns a mutable SLE for write operations */
|
||||
sle_ptr_type const&
|
||||
mutableSle() const
|
||||
requires is_writable
|
||||
{
|
||||
return sle_;
|
||||
}
|
||||
|
||||
/** Returns true if this wrapper supports write operations */
|
||||
bool
|
||||
canModify() const
|
||||
requires is_writable
|
||||
{
|
||||
return sle_ != nullptr;
|
||||
}
|
||||
|
||||
/** Returns the apply view for write operations */
|
||||
ApplyView&
|
||||
applyView() const
|
||||
requires is_writable
|
||||
{
|
||||
return view_;
|
||||
}
|
||||
|
||||
/** Mutable dereference operators */
|
||||
STLedgerEntry*
|
||||
operator->()
|
||||
requires is_writable
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::SLEBase::operator-> : can modify");
|
||||
return sle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry&
|
||||
operator*()
|
||||
requires is_writable
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::SLEBase::operator* : can modify");
|
||||
return *sle_;
|
||||
}
|
||||
|
||||
void
|
||||
insert()
|
||||
requires is_writable
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::SLEBase::insert : can modify");
|
||||
view_.insert(sle_);
|
||||
}
|
||||
|
||||
void
|
||||
erase()
|
||||
requires is_writable
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::SLEBase::erase : can modify");
|
||||
view_.erase(sle_);
|
||||
}
|
||||
|
||||
void
|
||||
update()
|
||||
requires is_writable
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::SLEBase::update : can modify");
|
||||
view_.update(sle_);
|
||||
}
|
||||
|
||||
void
|
||||
newSLE()
|
||||
requires is_writable
|
||||
{
|
||||
XRPL_ASSERT(!canModify(), "xrpl::SLEBase::newSLE : sle_ is not null");
|
||||
sle_ = std::make_shared<SLE>(key_);
|
||||
}
|
||||
|
||||
protected:
|
||||
SLEBase() = delete;
|
||||
|
||||
/** Constructor for read-only context */
|
||||
explicit SLEBase(std::shared_ptr<SLE const> sle, ReadView const& view)
|
||||
requires(!is_writable)
|
||||
: view_(view), sle_(std::move(sle))
|
||||
{
|
||||
}
|
||||
|
||||
/** Converting constructor: writable → read-only.
|
||||
* Enables implicit conversion from SLEBase<ApplyView> to
|
||||
* SLEBase<ReadView>, so functions taking ReadOnlySLE const& can
|
||||
* accept WritableSLE.
|
||||
*/
|
||||
template <WritableView OtherViewT>
|
||||
SLEBase(SLEBase<OtherViewT> const& other)
|
||||
requires(!is_writable)
|
||||
: view_(other.readView()), sle_(other.sle())
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor for writable context (from existing SLE) */
|
||||
explicit SLEBase(std::shared_ptr<SLE> sle, ApplyView& view)
|
||||
requires is_writable
|
||||
: view_(view)
|
||||
, key_(sle ? Keylet(sle->getType(), sle->key()) : Keylet(ltANY, uint256{}))
|
||||
, sle_(std::move(sle))
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor for writable context (peek from view by keylet) */
|
||||
explicit SLEBase(Keylet const& key, ApplyView& view)
|
||||
requires is_writable
|
||||
: view_(view), key_(key), sle_(view_.peek(key))
|
||||
{
|
||||
}
|
||||
|
||||
view_ref_type view_;
|
||||
|
||||
// Keylet is only meaningful for writable views, but we conditionally
|
||||
// include it to avoid wasting space in read-only wrappers.
|
||||
struct Empty
|
||||
{
|
||||
};
|
||||
[[no_unique_address]]
|
||||
std::conditional_t<is_writable, Keylet, Empty> key_{};
|
||||
|
||||
sle_ptr_type sle_;
|
||||
};
|
||||
|
||||
// Backward-compatible aliases
|
||||
using ReadOnlySLE = SLEBase<ReadView>;
|
||||
using WritableSLE = SLEBase<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -232,7 +232,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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -130,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>
|
||||
@@ -155,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();
|
||||
@@ -178,7 +177,7 @@ public:
|
||||
|
||||
private:
|
||||
void
|
||||
setupCheckpointing(JobQueue*, ServiceRegistry&);
|
||||
setupCheckpointing(JobQueue*, Logs&);
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -57,8 +57,8 @@ isVaultPseudoAccountFrozen(
|
||||
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");
|
||||
@@ -357,17 +357,17 @@ canWithdraw(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
SLE::const_ref toSle,
|
||||
ReadOnlyAccountRoot 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 +384,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
|
||||
@@ -418,8 +418,8 @@ doWithdraw(
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -433,7 +433,8 @@ doWithdraw(
|
||||
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
|
||||
}
|
||||
|
||||
@@ -11,14 +11,13 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, AccountID const& issuer)
|
||||
AccountRoot<ViewT>::isGlobalFrozen() const
|
||||
{
|
||||
if (isXRP(issuer))
|
||||
if (!this->exists())
|
||||
return false;
|
||||
if (auto const sle = view.read(keylet::account(issuer)))
|
||||
return sle->isFlag(lsfGlobalFreeze);
|
||||
return false;
|
||||
return this->sle_->isFlag(lsfGlobalFreeze);
|
||||
}
|
||||
|
||||
// An owner count cannot be negative. If adjustment would cause a negative
|
||||
@@ -63,28 +62,28 @@ confineOwnerCount(
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
XRPAmount
|
||||
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
|
||||
AccountRoot<ViewT>::xrpLiquid(std::int32_t ownerCountAdj, beast::Journal j) const
|
||||
{
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
if (sle == nullptr)
|
||||
if (!this->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(
|
||||
this->readView().ownerCountHook(id_, this->sle_->getFieldU32(sfOwnerCount)), ownerCountAdj);
|
||||
|
||||
// Pseudo-accounts have no reserve requirement
|
||||
auto const reserve =
|
||||
isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount);
|
||||
this->isPseudoAccount() ? XRPAmount{0} : this->readView().fees().accountReserve(ownerCount);
|
||||
|
||||
auto const fullBalance = sle->getFieldAmount(sfBalance);
|
||||
auto const fullBalance = this->sle_->getFieldAmount(sfBalance);
|
||||
|
||||
auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
|
||||
auto const balance = this->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
|
||||
@@ -93,33 +92,29 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
|
||||
return amount.xrp();
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer)
|
||||
AccountRoot<ViewT>::transferRate() const
|
||||
{
|
||||
auto const sle = view.read(keylet::account(issuer));
|
||||
|
||||
if (sle && sle->isFieldPresent(sfTransferRate))
|
||||
return Rate{sle->getFieldU32(sfTransferRate)};
|
||||
if (this->sle_ && this->sle_->isFieldPresent(sfTransferRate))
|
||||
return Rate{this->sle_->getFieldU32(sfTransferRate)};
|
||||
|
||||
return parityRate;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
void
|
||||
adjustOwnerCount(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> const& sle,
|
||||
std::int32_t amount,
|
||||
beast::Journal j)
|
||||
AccountRoot<ViewT>::adjustOwnerCount(std::int32_t amount, beast::Journal j)
|
||||
requires is_writable
|
||||
{
|
||||
if (!sle)
|
||||
return;
|
||||
XRPL_ASSERT(this->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{this->sle_->getFieldU32(sfOwnerCount)};
|
||||
AccountID const id = (*this->sle_)[sfAccount];
|
||||
std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
|
||||
view.adjustOwnerCountHook(id, current, adjusted);
|
||||
sle->at(sfOwnerCount) = adjusted;
|
||||
view.update(sle);
|
||||
this->applyView().adjustOwnerCountHook(id_, current, adjusted);
|
||||
this->sle_->at(sfOwnerCount) = adjusted;
|
||||
this->update();
|
||||
}
|
||||
|
||||
AccountID
|
||||
@@ -171,6 +166,22 @@ getPseudoAccountFields()
|
||||
return pseudoFields;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
[[nodiscard]] bool
|
||||
AccountRoot<ViewT>::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 this->sle_ && this->sle_->getType() == ltACCOUNT_ROOT &&
|
||||
std::count_if(
|
||||
fields.begin(), fields.end(), [this, &pseudoFieldFilter](SField const* sf) -> bool {
|
||||
return this->sle_->isFieldPresent(*sf) &&
|
||||
(pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf));
|
||||
}) > 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(
|
||||
std::shared_ptr<SLE const> sleAcct,
|
||||
@@ -230,18 +241,23 @@ createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const
|
||||
return account;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
[[nodiscard]] TER
|
||||
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
|
||||
AccountRoot<ViewT>::checkDestinationAndTag(bool hasDestinationTag) const
|
||||
{
|
||||
if (toSle == nullptr)
|
||||
if (this->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 (this->sle_->isFlag(lsfRequireDestTag) && !hasDestinationTag)
|
||||
return tecDST_TAG_NEEDED; // Cannot send without a tag
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template class AccountRoot<ReadView>;
|
||||
template class AccountRoot<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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,7 +71,7 @@ deleteSLE(ApplyView& view, std::shared_ptr<SLE> const& sleCredential, beast::Jou
|
||||
}
|
||||
|
||||
if (isOwner)
|
||||
adjustOwnerCount(view, sleAccount, -1, j);
|
||||
wrappedAccount.adjustOwnerCount(-1, j);
|
||||
|
||||
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,
|
||||
ReadOnlyAccountRoot 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) != 0u))
|
||||
{
|
||||
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,6 +12,21 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations for functions that remain in View.h/cpp
|
||||
bool
|
||||
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
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
|
||||
{
|
||||
@@ -123,8 +137,8 @@ authorizeMPToken(
|
||||
std::uint32_t flags,
|
||||
std::optional<AccountID> holderID)
|
||||
{
|
||||
auto const sleAcct = view.peek(keylet::account(account));
|
||||
if (!sleAcct)
|
||||
WritableAccountRoot wrappedAcct(account, view);
|
||||
if (!wrappedAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account that submitted the tx is a holder
|
||||
@@ -135,7 +149,7 @@ 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) != 0u)
|
||||
{
|
||||
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||
auto const sleMpt = view.peek(mptokenKey);
|
||||
@@ -146,7 +160,7 @@ authorizeMPToken(
|
||||
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);
|
||||
return tesSUCCESS;
|
||||
@@ -161,7 +175,7 @@ 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));
|
||||
@@ -191,7 +205,7 @@ authorizeMPToken(
|
||||
view.insert(mptoken);
|
||||
|
||||
// Update owner count.
|
||||
adjustOwnerCount(view, sleAcct, 1, journal);
|
||||
wrappedAcct.adjustOwnerCount(1, journal);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -215,7 +229,7 @@ authorizeMPToken(
|
||||
|
||||
// Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
|
||||
// their MPToken
|
||||
if ((flags & tfMPTUnauthorize) != 0)
|
||||
if ((flags & tfMPTUnauthorize) != 0u)
|
||||
{
|
||||
flagsOut &= ~lsfMPTAuthorized;
|
||||
}
|
||||
@@ -278,7 +292,7 @@ requireAuth(
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
|
||||
auto const mptIssuer = AccountRoot(sleIssuance->getAccountID(sfIssuer), view);
|
||||
|
||||
// issuer is always "authorized"
|
||||
if (mptIssuer == account) // Issuer won't have MPToken
|
||||
@@ -292,13 +306,12 @@ 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 = view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
|
||||
if (!sleVault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -476,7 +489,7 @@ canTransfer(
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
if (!sleIssuance->isFlag(lsfMPTCanTransfer))
|
||||
if ((sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer) == 0u)
|
||||
{
|
||||
if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
|
||||
return TER{tecNO_AUTH};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -107,14 +107,14 @@ isFrozen(
|
||||
{
|
||||
if (isXRP(currency))
|
||||
return false;
|
||||
auto sle = view.read(keylet::account(issuer));
|
||||
if (sle && sle->isFlag(lsfGlobalFreeze))
|
||||
auto const issuerRoot = AccountRoot(issuer, view);
|
||||
if (issuerRoot.exists() && issuerRoot->isFlag(lsfGlobalFreeze))
|
||||
return true;
|
||||
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 = view.read(keylet::line(account, issuer, currency));
|
||||
if (sleLine && sleLine->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -158,16 +158,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 +204,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.
|
||||
@@ -249,14 +249,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 +318,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.
|
||||
@@ -330,7 +330,7 @@ updateTrustLine(
|
||||
&& ((flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u)
|
||||
// Sender reserve is set.
|
||||
&& static_cast<bool>(flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
|
||||
static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
|
||||
static_cast<bool>(wrappedAcct->getFlags() & lsfDefaultRipple) &&
|
||||
((flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) &&
|
||||
!state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
|
||||
// Sender trust limit is 0.
|
||||
@@ -341,7 +341,7 @@ updateTrustLine(
|
||||
{
|
||||
// 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));
|
||||
@@ -424,7 +424,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
|
||||
|
||||
@@ -538,8 +538,8 @@ requireAuth(ReadView const& view, Issue const& issue, AccountID const& account,
|
||||
|
||||
// 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))
|
||||
auto const issuerAccount = AccountRoot(issue.account, view);
|
||||
if (issuerAccount.exists() && (((*issuerAccount)[sfFlags] & lsfRequireAuth) != 0u))
|
||||
{
|
||||
if (trustLine)
|
||||
{
|
||||
@@ -563,8 +563,8 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
|
||||
auto const& issuerId = issue.getIssuer();
|
||||
if (issuerId == from || issuerId == to)
|
||||
return tesSUCCESS;
|
||||
auto const sleIssuer = view.read(keylet::account(issuerId));
|
||||
if (sleIssuer == nullptr)
|
||||
auto const issuer = AccountRoot(issuerId, view);
|
||||
if (!issuer.exists())
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const isRippleDisabled = [&](AccountID account) -> bool {
|
||||
@@ -576,7 +576,7 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
|
||||
bool const issuerHigh = issuerId > account;
|
||||
return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple);
|
||||
}
|
||||
return !sleIssuer->isFlag(lsfDefaultRipple);
|
||||
return !issuer->isFlag(lsfDefaultRipple);
|
||||
};
|
||||
|
||||
// Fail if rippling disabled on both trust lines
|
||||
@@ -606,25 +606,26 @@ addEmptyHolding(
|
||||
|
||||
auto const& issuerId = issue.getIssuer();
|
||||
auto const& currency = issue.currency;
|
||||
if (isGlobalFrozen(view, issuerId))
|
||||
WritableAccountRoot wrappedIssuer(issuerId, view);
|
||||
if (wrappedIssuer.isGlobalFrozen())
|
||||
return tecFROZEN; // LCOV_EXCL_LINE
|
||||
|
||||
auto const& srcId = issuerId;
|
||||
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)
|
||||
WritableAccountRoot wrappedSrc(srcId, view);
|
||||
WritableAccountRoot wrappedDst(dstId, view);
|
||||
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))
|
||||
return tecDUPLICATE;
|
||||
|
||||
// Can the account cover the trust line reserve ?
|
||||
std::uint32_t const ownerCount = sleDst->at(sfOwnerCount);
|
||||
std::uint32_t const ownerCount = wrappedDst->at(sfOwnerCount);
|
||||
if (priorBalance < view.fees().accountReserve(ownerCount + 1))
|
||||
return tecNO_LINE_INSUF_RESERVE;
|
||||
|
||||
@@ -634,7 +635,7 @@ addEmptyHolding(
|
||||
srcId,
|
||||
dstId,
|
||||
index.key,
|
||||
sleDst,
|
||||
wrappedDst,
|
||||
/*bAuth=*/false,
|
||||
/*bNoRipple=*/true,
|
||||
/*bFreeze=*/false,
|
||||
@@ -655,11 +656,11 @@ removeEmptyHolding(
|
||||
{
|
||||
if (issue.native())
|
||||
{
|
||||
auto const sle = view.read(keylet::account(accountID));
|
||||
if (!sle)
|
||||
auto const account = AccountRoot(accountID, view);
|
||||
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;
|
||||
|
||||
@@ -680,11 +681,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(), view);
|
||||
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 +695,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(), view);
|
||||
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.
|
||||
@@ -722,13 +723,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)
|
||||
@@ -752,7 +753,8 @@ deleteAMMTrustLine(
|
||||
if ((sleState->getFlags() & uFlags) == 0u)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
|
||||
WritableAccountRoot wrappedHolder = !ammLow ? wrappedLow : wrappedHigh;
|
||||
wrappedHolder.adjustOwnerCount(-1, j);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ isGlobalFrozen(ReadView const& view, Asset const& asset)
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return isGlobalFrozen(view, issue.getIssuer());
|
||||
AccountRoot issuer(issue.getIssuer(), view);
|
||||
return issuer.isGlobalFrozen();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -180,14 +181,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 +257,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);
|
||||
@@ -402,7 +404,8 @@ transferRate(ReadView const& view, STAmount const& amount)
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return transferRate(view, issue.getIssuer());
|
||||
AccountRoot issuer(issue.getIssuer(), view);
|
||||
return issuer.transferRate();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -425,12 +428,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;
|
||||
@@ -511,7 +515,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,
|
||||
@@ -573,8 +577,7 @@ rippleCreditIOU(
|
||||
&& ((uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u)
|
||||
// Sender reserve is set.
|
||||
&& static_cast<bool>(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
|
||||
static_cast<bool>(
|
||||
view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) &&
|
||||
static_cast<bool>(AccountRoot(uSenderID, view)->getFlags() & lsfDefaultRipple) &&
|
||||
((uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) &&
|
||||
!sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
|
||||
// Sender trust limit is 0.
|
||||
@@ -585,7 +588,8 @@ rippleCreditIOU(
|
||||
// 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(
|
||||
@@ -628,11 +632,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 +644,7 @@ rippleCreditIOU(
|
||||
uSenderID,
|
||||
uReceiverID,
|
||||
index.key,
|
||||
sleAccount,
|
||||
wrappedAccount,
|
||||
false,
|
||||
noRipple,
|
||||
false,
|
||||
@@ -653,7 +657,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 +690,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 +708,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 +758,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;
|
||||
|
||||
@@ -1362,15 +1369,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 +1388,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;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ sharesToAssetsWithdraw(
|
||||
std::shared_ptr<SLE const> 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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
|
||||
@@ -40,14 +40,11 @@ public:
|
||||
}
|
||||
|
||||
std::shared_ptr<Checkpointer>
|
||||
create(
|
||||
std::shared_ptr<soci::session> const& session,
|
||||
JobQueue& jobQueue,
|
||||
ServiceRegistry& registry)
|
||||
create(std::shared_ptr<soci::session> const& session, JobQueue& jobQueue, Logs& logs)
|
||||
{
|
||||
std::lock_guard lock{mutex_};
|
||||
auto const id = nextId_++;
|
||||
auto const r = makeCheckpointer(id, session, jobQueue, registry);
|
||||
auto const r = makeCheckpointer(id, session, jobQueue, logs);
|
||||
checkpointers_[id] = r;
|
||||
return r;
|
||||
}
|
||||
@@ -85,11 +82,11 @@ DatabaseCon::~DatabaseCon()
|
||||
std::unique_ptr<std::vector<std::string> const> DatabaseCon::Setup::globalPragma;
|
||||
|
||||
void
|
||||
DatabaseCon::setupCheckpointing(JobQueue* q, ServiceRegistry& registry)
|
||||
DatabaseCon::setupCheckpointing(JobQueue* q, Logs& l)
|
||||
{
|
||||
if (q == nullptr)
|
||||
Throw<std::logic_error>("No JobQueue");
|
||||
checkpointer_ = checkpointers.create(session_, *q, registry);
|
||||
checkpointer_ = checkpointers.create(session_, *q, l);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -187,11 +187,8 @@ public:
|
||||
std::uintptr_t id,
|
||||
std::weak_ptr<soci::session> session,
|
||||
JobQueue& q,
|
||||
ServiceRegistry& registry)
|
||||
: id_(id)
|
||||
, session_(std::move(session))
|
||||
, jobQueue_(q)
|
||||
, j_(registry.getJournal("WALCheckpointer"))
|
||||
Logs& logs)
|
||||
: id_(id), session_(std::move(session)), jobQueue_(q), j_(logs.journal("WALCheckpointer"))
|
||||
{
|
||||
if (auto [conn, keepAlive] = getConnection(); conn)
|
||||
{
|
||||
@@ -310,9 +307,9 @@ makeCheckpointer(
|
||||
std::uintptr_t id,
|
||||
std::weak_ptr<soci::session> session,
|
||||
JobQueue& queue,
|
||||
ServiceRegistry& registry)
|
||||
Logs& logs)
|
||||
{
|
||||
return std::make_shared<WALCheckpointer>(id, std::move(session), queue, registry);
|
||||
return std::make_shared<WALCheckpointer>(id, std::move(session), queue, logs);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace xrpl {
|
||||
Expected<std::vector<SignerEntries::SignerEntry>, NotTEC>
|
||||
SignerEntries::deserialize(STObject const& obj, beast::Journal journal, std::string_view annotation)
|
||||
{
|
||||
std::pair<std::vector<SignerEntry>, NotTEC> s;
|
||||
|
||||
if (!obj.isFieldPresent(sfSignerEntries))
|
||||
{
|
||||
JLOG(journal.trace()) << "Malformed " << annotation << ": Need signer entry array.";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/core/NetworkIDService.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
@@ -231,7 +232,8 @@ Transactor::Transactor(ApplyContext& ctx)
|
||||
: ctx_(ctx)
|
||||
, sink_(ctx.journal, to_short_string(ctx.tx.getTransactionID()) + " ")
|
||||
, j_(sink_)
|
||||
, account_(ctx.tx.getAccountID(sfAccount))
|
||||
, accountID_(ctx.tx.getAccountID(sfAccount))
|
||||
, account_(accountID_, ctx.view())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -301,7 +303,7 @@ Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx)
|
||||
// need to rethink charging an owner reserve as a transaction fee.
|
||||
// TODO: This function is static, and I don't want to add more parameters.
|
||||
// When it is finally refactored to be in a context that has access to the
|
||||
// Application, include "app().getOverlay().networkID() > 2 ||" in the
|
||||
// Application, include "app().overlay().networkID() > 2 ||" in the
|
||||
// condition.
|
||||
XRPL_ASSERT(
|
||||
view.fees().increment > view.fees().base * 100,
|
||||
@@ -357,11 +359,11 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const id = ctx.tx.getFeePayer();
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
if (!sle)
|
||||
AccountRoot const acctRoot(id, ctx.view);
|
||||
if (!acctRoot)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const balance = (*sle)[sfBalance].xrp();
|
||||
auto const balance = (*acctRoot)[sfBalance].xrp();
|
||||
|
||||
if (balance < feePaid)
|
||||
{
|
||||
@@ -386,17 +388,15 @@ Transactor::payFee()
|
||||
auto const feePaid = ctx_.tx[sfFee].xrp();
|
||||
|
||||
auto const feePayer = ctx_.tx.getFeePayer();
|
||||
auto const sle = view().peek(keylet::account(feePayer));
|
||||
if (!sle)
|
||||
WritableAccountRoot payerAcct(feePayer, view());
|
||||
if (!payerAcct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Deduct the fee, so it's not available during the transaction.
|
||||
// Will only write the account back if the transaction succeeds.
|
||||
sle->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance) - feePaid);
|
||||
if (feePayer != account_)
|
||||
view().update(sle); // done in `apply()` for the account
|
||||
|
||||
// VFALCO Should we call view().rawDestroyXRP() here as well?
|
||||
payerAcct->setFieldAmount(sfBalance, payerAcct->getFieldAmount(sfBalance) - feePaid);
|
||||
if (feePayer != accountID_)
|
||||
payerAcct.update(); // done in `apply()` for the account
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -405,9 +405,9 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j
|
||||
{
|
||||
auto const id = tx.getAccountID(sfAccount);
|
||||
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
AccountRoot const acctRoot(id, view);
|
||||
|
||||
if (!sle)
|
||||
if (!acctRoot)
|
||||
{
|
||||
JLOG(j.trace()) << "applyTransaction: delay: source account does not exist "
|
||||
<< toBase58(id);
|
||||
@@ -415,7 +415,7 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j
|
||||
}
|
||||
|
||||
SeqProxy const t_seqProx = tx.getSeqProxy();
|
||||
SeqProxy const a_seq = SeqProxy::sequence((*sle)[sfSequence]);
|
||||
SeqProxy const a_seq = SeqProxy::sequence((*acctRoot)[sfSequence]);
|
||||
|
||||
if (t_seqProx.isSeq())
|
||||
{
|
||||
@@ -470,9 +470,9 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const id = ctx.tx.getAccountID(sfAccount);
|
||||
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
AccountRoot const acctRoot(id, ctx.view);
|
||||
|
||||
if (!sle)
|
||||
if (!acctRoot)
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "applyTransaction: delay: source account does not exist "
|
||||
<< toBase58(id);
|
||||
@@ -480,7 +480,7 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfAccountTxnID) &&
|
||||
(sle->getFieldH256(sfAccountTxnID) != ctx.tx.getFieldH256(sfAccountTxnID)))
|
||||
(acctRoot->getFieldH256(sfAccountTxnID) != ctx.tx.getFieldH256(sfAccountTxnID)))
|
||||
return tefWRONG_PRIOR;
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfLastLedgerSequence) &&
|
||||
@@ -506,7 +506,7 @@ Transactor::consumeSeqProxy(SLE::pointer const& sleAccount)
|
||||
sleAccount->setFieldU32(sfSequence, seqProx.value() + 1);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
return ticketDelete(view(), account_, getTicketIndex(account_, seqProx), j_);
|
||||
return ticketDelete(view(), accountID_, getTicketIndex(accountID_, seqProx), j_);
|
||||
}
|
||||
|
||||
// Remove a single Ticket from the ledger.
|
||||
@@ -539,8 +539,8 @@ Transactor::ticketDelete(
|
||||
|
||||
// Update the account root's TicketCount. If the ticket count drops to
|
||||
// zero remove the (optional) field.
|
||||
auto sleAccount = view.peek(keylet::account(account));
|
||||
if (!sleAccount)
|
||||
WritableAccountRoot wrappedAcct(account, view);
|
||||
if (!wrappedAcct)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not find Ticket owner account root.";
|
||||
@@ -548,11 +548,11 @@ Transactor::ticketDelete(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (auto ticketCount = (*sleAccount)[~sfTicketCount])
|
||||
if (auto ticketCount = (*wrappedAcct)[~sfTicketCount])
|
||||
{
|
||||
if (*ticketCount == 1)
|
||||
{
|
||||
sleAccount->makeFieldAbsent(sfTicketCount);
|
||||
wrappedAcct->makeFieldAbsent(sfTicketCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -568,7 +568,7 @@ Transactor::ticketDelete(
|
||||
}
|
||||
|
||||
// Update the Ticket owner's reserve.
|
||||
adjustOwnerCount(view, sleAccount, -1, j);
|
||||
wrappedAcct.adjustOwnerCount(-1, j);
|
||||
|
||||
// Remove Ticket from ledger.
|
||||
view.erase(sleTicket);
|
||||
@@ -579,7 +579,7 @@ Transactor::ticketDelete(
|
||||
void
|
||||
Transactor::preCompute()
|
||||
{
|
||||
XRPL_ASSERT(account_ != beast::zero, "xrpl::Transactor::preCompute : nonzero account");
|
||||
XRPL_ASSERT(accountID_ != beast::zero, "xrpl::Transactor::preCompute : nonzero account");
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -589,19 +589,19 @@ Transactor::apply()
|
||||
|
||||
// If the transactor requires a valid account and the transaction doesn't
|
||||
// list one, preflight will have already a flagged a failure.
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
WritableAccountRoot acct(accountID_, view());
|
||||
|
||||
// sle must exist except for transactions
|
||||
// acct must exist except for transactions
|
||||
// that allow zero account.
|
||||
XRPL_ASSERT(
|
||||
sle != nullptr || account_ == beast::zero,
|
||||
acct.exists() || accountID_ == beast::zero,
|
||||
"xrpl::Transactor::apply : non-null SLE or zero account");
|
||||
|
||||
if (sle)
|
||||
if (acct)
|
||||
{
|
||||
preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp();
|
||||
preFeeBalance_ = STAmount{(*acct)[sfBalance]}.xrp();
|
||||
|
||||
TER result = consumeSeqProxy(sle);
|
||||
TER result = consumeSeqProxy(acct.mutableSle());
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
|
||||
@@ -609,10 +609,10 @@ Transactor::apply()
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
|
||||
if (sle->isFieldPresent(sfAccountTxnID))
|
||||
sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
|
||||
if (acct->isFieldPresent(sfAccountTxnID))
|
||||
acct->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
|
||||
|
||||
view().update(sle);
|
||||
acct.update();
|
||||
}
|
||||
|
||||
return doApply();
|
||||
@@ -627,10 +627,9 @@ Transactor::checkSign(
|
||||
STObject const& sigObject,
|
||||
beast::Journal const j)
|
||||
{
|
||||
AccountRoot const acctSign(idAccount, view);
|
||||
{
|
||||
auto const sle = view.read(keylet::account(idAccount));
|
||||
|
||||
if (view.rules().enabled(featureLendingProtocol) && isPseudoAccount(sle))
|
||||
if (view.rules().enabled(featureLendingProtocol) && acctSign.isPseudoAccount())
|
||||
{
|
||||
// Pseudo-accounts can't sign transactions. This check is gated on
|
||||
// the Lending Protocol amendment because that's the project it was
|
||||
@@ -676,12 +675,12 @@ Transactor::checkSign(
|
||||
}
|
||||
|
||||
// Look up the account.
|
||||
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
|
||||
auto const sleAccount = view.read(keylet::account(idAccount));
|
||||
if (!sleAccount)
|
||||
auto const idSigner =
|
||||
pkSigner.empty() ? idAccount : calcAccountID(PublicKey(makeSlice(pkSigner)));
|
||||
if (!acctSign)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
|
||||
return checkSingleSign(view, idSigner, idAccount, acctSign.sle(), j);
|
||||
}
|
||||
|
||||
NotTEC
|
||||
@@ -716,11 +715,11 @@ Transactor::checkBatchSign(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
|
||||
auto const sleAccount = ctx.view.read(keylet::account(idAccount));
|
||||
AccountRoot const acctRoot(idAccount, ctx.view);
|
||||
|
||||
// A batch can include transactions from an un-created account ONLY
|
||||
// when the account master key is the signer
|
||||
if (!sleAccount)
|
||||
if (!acctRoot)
|
||||
{
|
||||
if (idAccount != idSigner)
|
||||
return tefBAD_AUTH;
|
||||
@@ -728,7 +727,7 @@ Transactor::checkBatchSign(PreclaimContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
if (ret = checkSingleSign(ctx.view, idSigner, idAccount, sleAccount, ctx.j);
|
||||
if (ret = checkSingleSign(ctx.view, idSigner, idAccount, acctRoot.sle(), ctx.j);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
@@ -874,15 +873,15 @@ Transactor::checkMultiSign(
|
||||
|
||||
// In any of these cases we need to know whether the account is in
|
||||
// the ledger. Determine that now.
|
||||
auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID));
|
||||
AccountRoot const acctSigner(txSignerAcctID, view);
|
||||
|
||||
if (signingAcctIDFromPubKey == txSignerAcctID)
|
||||
{
|
||||
// Either Phantom or Master. Phantoms automatically pass.
|
||||
if (sleTxSignerRoot)
|
||||
if (acctSigner)
|
||||
{
|
||||
// Master Key. Account may not have asfDisableMaster set.
|
||||
std::uint32_t const signerAccountFlags = sleTxSignerRoot->getFieldU32(sfFlags);
|
||||
std::uint32_t const signerAccountFlags = acctSigner->getFieldU32(sfFlags);
|
||||
|
||||
if ((signerAccountFlags & lsfDisableMaster) != 0u)
|
||||
{
|
||||
@@ -895,19 +894,19 @@ Transactor::checkMultiSign(
|
||||
{
|
||||
// May be a Regular Key. Let's find out.
|
||||
// Public key must hash to the account's regular key.
|
||||
if (!sleTxSignerRoot)
|
||||
if (!acctSigner)
|
||||
{
|
||||
JLOG(j.trace()) << "applyTransaction: Non-phantom signer "
|
||||
"lacks account root.";
|
||||
return tefBAD_SIGNATURE;
|
||||
}
|
||||
|
||||
if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
|
||||
if (!acctSigner->isFieldPresent(sfRegularKey))
|
||||
{
|
||||
JLOG(j.trace()) << "applyTransaction: Account lacks RegularKey.";
|
||||
return tefBAD_SIGNATURE;
|
||||
}
|
||||
if (signingAcctIDFromPubKey != sleTxSignerRoot->getAccountID(sfRegularKey))
|
||||
if (signingAcctIDFromPubKey != acctSigner->getAccountID(sfRegularKey))
|
||||
{
|
||||
JLOG(j.trace()) << "applyTransaction: Account doesn't match RegularKey.";
|
||||
return tefBAD_SIGNATURE;
|
||||
@@ -1009,18 +1008,25 @@ Transactor::reset(XRPAmount fee)
|
||||
{
|
||||
ctx_.discard();
|
||||
|
||||
auto const txnAcct = view().peek(keylet::account(ctx_.tx.getAccountID(sfAccount)));
|
||||
WritableAccountRoot txnAcct(ctx_.tx.getAccountID(sfAccount), view());
|
||||
|
||||
// The account should never be missing from the ledger. But if it
|
||||
// is missing then we can't very well charge it a fee, can we?
|
||||
if (!txnAcct)
|
||||
return {tefINTERNAL, beast::zero};
|
||||
|
||||
auto const payerSle = view().peek(keylet::account(ctx_.tx.getFeePayer()));
|
||||
if (!payerSle)
|
||||
return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE
|
||||
auto const feePayer = ctx_.tx.getFeePayer();
|
||||
bool const hasDelegateAcct = (feePayer != accountID_);
|
||||
std::optional<WritableAccountRoot> delegateAcct;
|
||||
if (hasDelegateAcct)
|
||||
{
|
||||
delegateAcct.emplace(feePayer, view());
|
||||
if (!*delegateAcct)
|
||||
return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
auto const balance = payerSle->getFieldAmount(sfBalance).xrp();
|
||||
auto& payer = hasDelegateAcct ? *delegateAcct : txnAcct;
|
||||
auto const balance = payer->getFieldAmount(sfBalance).xrp();
|
||||
|
||||
// balance should have already been checked in checkFee / preFlight.
|
||||
XRPL_ASSERT(
|
||||
@@ -1039,15 +1045,15 @@ Transactor::reset(XRPAmount fee)
|
||||
// If for some reason we are unable to consume the ticket or sequence
|
||||
// then the ledger is corrupted. Rather than make things worse we
|
||||
// reject the transaction.
|
||||
payerSle->setFieldAmount(sfBalance, balance - fee);
|
||||
TER const ter{consumeSeqProxy(txnAcct)};
|
||||
payer->setFieldAmount(sfBalance, balance - fee);
|
||||
TER const ter{consumeSeqProxy(txnAcct.mutableSle())};
|
||||
XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS");
|
||||
|
||||
if (isTesSuccess(ter))
|
||||
{
|
||||
view().update(txnAcct);
|
||||
if (payerSle != txnAcct)
|
||||
view().update(payerSle);
|
||||
txnAcct.update();
|
||||
if (hasDelegateAcct)
|
||||
delegateAcct->update();
|
||||
}
|
||||
|
||||
return {ter, fee};
|
||||
@@ -1095,7 +1101,7 @@ Transactor::operator()()
|
||||
}
|
||||
#endif
|
||||
|
||||
if (auto const& trap = ctx_.registry.getTrapTxID(); trap && *trap == ctx_.tx.getTransactionID())
|
||||
if (auto const& trap = ctx_.registry.trapTxID(); trap && *trap == ctx_.tx.getTransactionID())
|
||||
{
|
||||
trapTransaction(*trap);
|
||||
}
|
||||
@@ -1197,25 +1203,16 @@ Transactor::operator()()
|
||||
|
||||
// If necessary, remove any offers found unfunded during processing
|
||||
if ((result == tecOVERSIZE) || (result == tecKILLED))
|
||||
{
|
||||
removeUnfundedOffers(view(), removedOffers, ctx_.registry.getJournal("View"));
|
||||
}
|
||||
removeUnfundedOffers(view(), removedOffers, ctx_.registry.journal("View"));
|
||||
|
||||
if (result == tecEXPIRED)
|
||||
{
|
||||
removeExpiredNFTokenOffers(
|
||||
view(), expiredNFTokenOffers, ctx_.registry.getJournal("View"));
|
||||
}
|
||||
removeExpiredNFTokenOffers(view(), expiredNFTokenOffers, ctx_.registry.journal("View"));
|
||||
|
||||
if (result == tecINCOMPLETE)
|
||||
{
|
||||
removeDeletedTrustLines(view(), removedTrustLines, ctx_.registry.getJournal("View"));
|
||||
}
|
||||
removeDeletedTrustLines(view(), removedTrustLines, ctx_.registry.journal("View"));
|
||||
|
||||
if (result == tecEXPIRED)
|
||||
{
|
||||
removeExpiredCredentials(view(), expiredCredentials, ctx_.registry.getJournal("View"));
|
||||
}
|
||||
removeExpiredCredentials(view(), expiredCredentials, ctx_.registry.journal("View"));
|
||||
|
||||
applied = isTecClaim(result);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -98,7 +99,7 @@ ValidLoanBroker::finalize(
|
||||
{
|
||||
for (auto const& field : {&sfLowLimit, &sfHighLimit})
|
||||
{
|
||||
auto const account = view.read(keylet::account(line->at(*field).getIssuer()));
|
||||
AccountRoot const account(line->at(*field).getIssuer(), view);
|
||||
// This Invariant doesn't know about the rules for Trust Lines, so
|
||||
// if the account is missing, don't treat it as an error. This
|
||||
// loop is only concerned with finding Broker pseudo-accounts
|
||||
@@ -112,7 +113,7 @@ ValidLoanBroker::finalize(
|
||||
}
|
||||
for (auto const& mpt : mpts_)
|
||||
{
|
||||
auto const account = view.read(keylet::account(mpt->at(sfAccount)));
|
||||
AccountRoot const account(mpt->at(sfAccount), view);
|
||||
// This Invariant doesn't know about the rules for MPTokens, so
|
||||
// if the account is missing, don't treat is as an error. This
|
||||
// loop is only concerned with finding Broker pseudo-accounts
|
||||
@@ -236,7 +237,8 @@ ValidLoan::finalize(
|
||||
after->at(sfPrincipalOutstanding) == beast::zero &&
|
||||
after->at(sfManagementFeeOutstanding) == beast::zero)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: Fully paid off Loan still has payments remaining";
|
||||
JLOG(j.fatal()) << "Invariant failed: Loan with zero payments "
|
||||
"remaining has not been paid off";
|
||||
return false;
|
||||
}
|
||||
if (before && (before->isFlag(lsfLoanOverpayment) != after->isFlag(lsfLoanOverpayment)))
|
||||
|
||||
@@ -481,16 +481,15 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const sleSharesIssuer =
|
||||
view.read(keylet::account(updatedShares->share.getIssuer()));
|
||||
if (!sleSharesIssuer)
|
||||
AccountRoot const acctSharesIssuer(updatedShares->share.getIssuer(), view);
|
||||
if (!acctSharesIssuer)
|
||||
{
|
||||
JLOG(j.fatal()) //
|
||||
<< "Invariant failed: shares issuer must exist";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isPseudoAccount(sleSharesIssuer))
|
||||
if (!acctSharesIssuer.isPseudoAccount())
|
||||
{
|
||||
JLOG(j.fatal()) //
|
||||
<< "Invariant failed: shares issuer must be a "
|
||||
@@ -498,7 +497,7 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (auto const vaultId = (*sleSharesIssuer)[~sfVaultID];
|
||||
if (auto const vaultId = acctSharesIssuer->at(~sfVaultID);
|
||||
!vaultId || *vaultId != afterVault.key)
|
||||
{
|
||||
JLOG(j.fatal()) //
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/tx/paths/Flow.h>
|
||||
@@ -34,12 +35,12 @@ RippleCalc::rippleCalculate(
|
||||
STPathSet const& spsPaths,
|
||||
|
||||
std::optional<uint256> const& domainID,
|
||||
ServiceRegistry& registry,
|
||||
Logs& l,
|
||||
Input const* const pInputs)
|
||||
{
|
||||
Output flowOut;
|
||||
PaymentSandbox flowSB(&view);
|
||||
auto j = registry.getJournal("Flow");
|
||||
auto j = l.journal("Flow");
|
||||
|
||||
{
|
||||
bool const defaultPaths = (pInputs == nullptr) ? true : pInputs->defaultPathsAllowed;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/instrumentation.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/OfferHelpers.h>
|
||||
@@ -208,12 +209,12 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
AccountID const account{ctx.tx[sfAccount]};
|
||||
AccountID const dst{ctx.tx[sfDestination]};
|
||||
|
||||
auto sleDst = ctx.view.read(keylet::account(dst));
|
||||
AccountRoot const acctDst(dst, ctx.view);
|
||||
|
||||
if (!sleDst)
|
||||
if (!acctDst)
|
||||
return tecNO_DST;
|
||||
|
||||
if ((((*sleDst)[sfFlags] & lsfRequireDestTag) != 0u) && !ctx.tx[~sfDestinationTag])
|
||||
if (((acctDst->getFlags() & lsfRequireDestTag) != 0u) && !ctx.tx[~sfDestinationTag])
|
||||
return tecDST_TAG_NEEDED;
|
||||
|
||||
// If credentials are provided - check them anyway
|
||||
@@ -225,21 +226,21 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
if (!ctx.tx.isFieldPresent(sfCredentialIDs))
|
||||
{
|
||||
// Check whether the destination account requires deposit authorization.
|
||||
if ((sleDst->getFlags() & lsfDepositAuth) != 0u)
|
||||
if ((acctDst->getFlags() & lsfDepositAuth) != 0u)
|
||||
{
|
||||
if (!ctx.view.exists(keylet::depositPreauth(dst, account)))
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
auto sleAccount = ctx.view.read(keylet::account(account));
|
||||
XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account");
|
||||
if (!sleAccount)
|
||||
AccountRoot const acctSrc(account, ctx.view);
|
||||
XRPL_ASSERT(acctSrc, "xrpl::AccountDelete::preclaim : non-null account");
|
||||
if (!acctSrc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
// If an issuer has any issued NFTs resident in the ledger then it
|
||||
// cannot be deleted.
|
||||
if ((*sleAccount)[~sfMintedNFTokens] != (*sleAccount)[~sfBurnedNFTokens])
|
||||
if (acctSrc->at(~sfMintedNFTokens) != acctSrc->at(~sfBurnedNFTokens))
|
||||
return tecHAS_OBLIGATIONS;
|
||||
|
||||
// If the account owns any NFTs it cannot be deleted.
|
||||
@@ -258,7 +259,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
// We look at the account's Sequence rather than the transaction's
|
||||
// Sequence in preparation for Tickets.
|
||||
constexpr std::uint32_t seqDelta{255};
|
||||
if ((*sleAccount)[sfSequence] + seqDelta > ctx.view.seq())
|
||||
if (acctSrc->getFieldU32(sfSequence) + seqDelta > ctx.view.seq())
|
||||
return tecTOO_SOON;
|
||||
|
||||
// We don't allow an account to be deleted if
|
||||
@@ -272,8 +273,8 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
// their account and mints a NFToken, it is possible that the
|
||||
// NFTokenSequence of this NFToken is the same as the one that the
|
||||
// authorized minter minted in a previous ledger.
|
||||
if ((*sleAccount)[~sfFirstNFTokenSequence].value_or(0) +
|
||||
(*sleAccount)[~sfMintedNFTokens].value_or(0) + seqDelta >
|
||||
if (acctSrc->at(~sfFirstNFTokenSequence).value_or(0) +
|
||||
acctSrc->at(~sfMintedNFTokens).value_or(0) + seqDelta >
|
||||
ctx.view.seq())
|
||||
return tecTOO_SOON;
|
||||
|
||||
@@ -326,25 +327,24 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
AccountDelete::doApply()
|
||||
{
|
||||
auto src = view().peek(keylet::account(account_));
|
||||
XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account");
|
||||
WritableAccountRoot src(accountID_, view());
|
||||
XRPL_ASSERT(src.exists(), "xrpl::AccountDelete::doApply : non-null source account");
|
||||
|
||||
auto const dstID = ctx_.tx[sfDestination];
|
||||
auto dst = view().peek(keylet::account(dstID));
|
||||
XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account");
|
||||
WritableAccountRoot dst(dstID, view());
|
||||
XRPL_ASSERT(dst.exists(), "xrpl::AccountDelete::doApply : non-null destination account");
|
||||
|
||||
if (!src || !dst)
|
||||
if (!src.exists() || !dst.exists())
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfCredentialIDs))
|
||||
{
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, dstID, dst, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dst, ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
}
|
||||
|
||||
Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
|
||||
Keylet const ownerDirKeylet{keylet::ownerDir(accountID_)};
|
||||
auto const ter = cleanupOnAccountDelete(
|
||||
view(),
|
||||
ownerDirKeylet,
|
||||
@@ -353,7 +353,7 @@ AccountDelete::doApply()
|
||||
std::shared_ptr<SLE>& sleItem) -> std::pair<TER, SkipEntry> {
|
||||
if (auto deleter = nonObligationDeleter(nodeType))
|
||||
{
|
||||
TER const result{deleter(ctx_.registry, view(), account_, dirEntry, sleItem, j_)};
|
||||
TER const result{deleter(ctx_.registry, view(), accountID_, dirEntry, sleItem, j_)};
|
||||
|
||||
return {result, SkipEntry::No};
|
||||
}
|
||||
@@ -384,7 +384,7 @@ AccountDelete::doApply()
|
||||
// delete it.
|
||||
if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
|
||||
{
|
||||
JLOG(j_.error()) << "AccountDelete cannot delete root dir node of " << toBase58(account_);
|
||||
JLOG(j_.error()) << "AccountDelete cannot delete root dir node of " << toBase58(accountID_);
|
||||
return tecHAS_OBLIGATIONS;
|
||||
}
|
||||
|
||||
@@ -392,8 +392,8 @@ AccountDelete::doApply()
|
||||
if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
|
||||
dst->clearFlag(lsfPasswordSpent);
|
||||
|
||||
view().update(dst);
|
||||
view().erase(src);
|
||||
dst.update();
|
||||
src.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -208,11 +209,11 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
std::uint32_t const uTxFlags = ctx.tx.getFlags();
|
||||
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
if (!sle)
|
||||
AccountRoot const acctRoot(id, ctx.view);
|
||||
if (!acctRoot)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
std::uint32_t const uFlagsIn = sle->getFieldU32(sfFlags);
|
||||
std::uint32_t const uFlagsIn = acctRoot->getFieldU32(sfFlags);
|
||||
|
||||
std::uint32_t const uSetFlag = ctx.tx.getFieldU32(sfSetFlag);
|
||||
|
||||
@@ -267,11 +268,11 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
AccountSet::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
WritableAccountRoot acct(accountID_, view());
|
||||
if (!acct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::uint32_t const uFlagsIn = sle->getFieldU32(sfFlags);
|
||||
std::uint32_t const uFlagsIn = acct->getFieldU32(sfFlags);
|
||||
std::uint32_t uFlagsOut = uFlagsIn;
|
||||
|
||||
STTx const& tx{ctx_.tx};
|
||||
@@ -290,7 +291,7 @@ AccountSet::doApply()
|
||||
bool const bSetDisallowXRP{((uTxFlags & tfDisallowXRP) != 0u) || (uSetFlag == asfDisallowXRP)};
|
||||
bool const bClearDisallowXRP{((uTxFlags & tfAllowXRP) != 0u) || (uClearFlag == asfDisallowXRP)};
|
||||
|
||||
bool const sigWithMaster{[&tx, &acct = account_]() {
|
||||
bool const sigWithMaster{[&tx, &acct = accountID_]() {
|
||||
auto const spk = tx.getSigningPubKey();
|
||||
|
||||
if (publicKeyType(makeSlice(spk)))
|
||||
@@ -359,7 +360,7 @@ AccountSet::doApply()
|
||||
return tecNEED_MASTER_KEY;
|
||||
}
|
||||
|
||||
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signers(account_))))
|
||||
if ((!acct->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signers(accountID_))))
|
||||
{
|
||||
// Account has no regular key or multi-signer signer list.
|
||||
return tecNO_ALTERNATIVE_KEY;
|
||||
@@ -424,16 +425,16 @@ AccountSet::doApply()
|
||||
//
|
||||
// Track transaction IDs signed by this account in its root
|
||||
//
|
||||
if ((uSetFlag == asfAccountTxnID) && !sle->isFieldPresent(sfAccountTxnID))
|
||||
if ((uSetFlag == asfAccountTxnID) && !acct->isFieldPresent(sfAccountTxnID))
|
||||
{
|
||||
JLOG(j_.trace()) << "Set AccountTxnID.";
|
||||
sle->makeFieldPresent(sfAccountTxnID);
|
||||
acct->makeFieldPresent(sfAccountTxnID);
|
||||
}
|
||||
|
||||
if ((uClearFlag == asfAccountTxnID) && sle->isFieldPresent(sfAccountTxnID))
|
||||
if ((uClearFlag == asfAccountTxnID) && acct->isFieldPresent(sfAccountTxnID))
|
||||
{
|
||||
JLOG(j_.trace()) << "Clear AccountTxnID.";
|
||||
sle->makeFieldAbsent(sfAccountTxnID);
|
||||
acct->makeFieldAbsent(sfAccountTxnID);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -460,12 +461,12 @@ AccountSet::doApply()
|
||||
if (!uHash)
|
||||
{
|
||||
JLOG(j_.trace()) << "unset email hash";
|
||||
sle->makeFieldAbsent(sfEmailHash);
|
||||
acct->makeFieldAbsent(sfEmailHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.trace()) << "set email hash";
|
||||
sle->setFieldH128(sfEmailHash, uHash);
|
||||
acct->setFieldH128(sfEmailHash, uHash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,12 +480,12 @@ AccountSet::doApply()
|
||||
if (!uHash)
|
||||
{
|
||||
JLOG(j_.trace()) << "unset wallet locator";
|
||||
sle->makeFieldAbsent(sfWalletLocator);
|
||||
acct->makeFieldAbsent(sfWalletLocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.trace()) << "set wallet locator";
|
||||
sle->setFieldH256(sfWalletLocator, uHash);
|
||||
acct->setFieldH256(sfWalletLocator, uHash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,12 +499,12 @@ AccountSet::doApply()
|
||||
if (messageKey.empty())
|
||||
{
|
||||
JLOG(j_.debug()) << "clear message key";
|
||||
sle->makeFieldAbsent(sfMessageKey);
|
||||
acct->makeFieldAbsent(sfMessageKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.debug()) << "set message key";
|
||||
sle->setFieldVL(sfMessageKey, messageKey);
|
||||
acct->setFieldVL(sfMessageKey, messageKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,12 +518,12 @@ AccountSet::doApply()
|
||||
if (domain.empty())
|
||||
{
|
||||
JLOG(j_.trace()) << "unset domain";
|
||||
sle->makeFieldAbsent(sfDomain);
|
||||
acct->makeFieldAbsent(sfDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.trace()) << "set domain";
|
||||
sle->setFieldVL(sfDomain, domain);
|
||||
acct->setFieldVL(sfDomain, domain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,12 +537,12 @@ AccountSet::doApply()
|
||||
if (uRate == 0 || uRate == QUALITY_ONE)
|
||||
{
|
||||
JLOG(j_.trace()) << "unset transfer rate";
|
||||
sle->makeFieldAbsent(sfTransferRate);
|
||||
acct->makeFieldAbsent(sfTransferRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.trace()) << "set transfer rate";
|
||||
sle->setFieldU32(sfTransferRate, uRate);
|
||||
acct->setFieldU32(sfTransferRate, uRate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,21 +555,21 @@ AccountSet::doApply()
|
||||
if ((uTickSize == 0) || (uTickSize == Quality::maxTickSize))
|
||||
{
|
||||
JLOG(j_.trace()) << "unset tick size";
|
||||
sle->makeFieldAbsent(sfTickSize);
|
||||
acct->makeFieldAbsent(sfTickSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.trace()) << "set tick size";
|
||||
sle->setFieldU8(sfTickSize, uTickSize);
|
||||
acct->setFieldU8(sfTickSize, uTickSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure authorized minting account:
|
||||
if (uSetFlag == asfAuthorizedNFTokenMinter)
|
||||
sle->setAccountID(sfNFTokenMinter, ctx_.tx[sfNFTokenMinter]);
|
||||
acct->setAccountID(sfNFTokenMinter, ctx_.tx[sfNFTokenMinter]);
|
||||
|
||||
if (uClearFlag == asfAuthorizedNFTokenMinter && sle->isFieldPresent(sfNFTokenMinter))
|
||||
sle->makeFieldAbsent(sfNFTokenMinter);
|
||||
if (uClearFlag == asfAuthorizedNFTokenMinter && acct->isFieldPresent(sfNFTokenMinter))
|
||||
acct->makeFieldAbsent(sfNFTokenMinter);
|
||||
|
||||
if (uSetFlag == asfDisallowIncomingNFTokenOffer)
|
||||
{
|
||||
@@ -627,9 +628,9 @@ AccountSet::doApply()
|
||||
}
|
||||
|
||||
if (uFlagsIn != uFlagsOut)
|
||||
sle->setFieldU32(sfFlags, uFlagsOut);
|
||||
acct->setFieldU32(sfFlags, uFlagsOut);
|
||||
|
||||
ctx_.view().update(sle);
|
||||
acct.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/account/SetRegularKey.h>
|
||||
@@ -15,9 +16,9 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
if (calcAccountID(PublicKey(makeSlice(spk))) == id)
|
||||
{
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
AccountRoot const acct(id, view);
|
||||
|
||||
if (sle && ((sle->getFlags() & lsfPasswordSpent) == 0u))
|
||||
if (acct && ((acct->getFlags() & lsfPasswordSpent) == 0u))
|
||||
{
|
||||
// flag is armed and they signed with the right account
|
||||
return XRPAmount{0};
|
||||
@@ -43,27 +44,27 @@ SetRegularKey::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
SetRegularKey::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
WritableAccountRoot acct(accountID_, view());
|
||||
if (!acct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (!minimumFee(ctx_.registry, ctx_.baseFee, view().fees(), view().flags()))
|
||||
sle->setFlag(lsfPasswordSpent);
|
||||
acct->setFlag(lsfPasswordSpent);
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfRegularKey))
|
||||
{
|
||||
sle->setAccountID(sfRegularKey, ctx_.tx.getAccountID(sfRegularKey));
|
||||
acct->setAccountID(sfRegularKey, ctx_.tx.getAccountID(sfRegularKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Account has disabled master key and no multi-signer signer list.
|
||||
if (sle->isFlag(lsfDisableMaster) && !view().peek(keylet::signers(account_)))
|
||||
if (acct->isFlag(lsfDisableMaster) && !view().peek(keylet::signers(accountID_)))
|
||||
return tecNO_ALTERNATIVE_KEY;
|
||||
|
||||
sle->makeFieldAbsent(sfRegularKey);
|
||||
acct->makeFieldAbsent(sfRegularKey);
|
||||
}
|
||||
|
||||
ctx_.view().update(sle);
|
||||
acct.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -160,11 +161,11 @@ static TER
|
||||
removeSignersFromLedger(
|
||||
ServiceRegistry& registry,
|
||||
ApplyView& view,
|
||||
Keylet const& accountKeylet,
|
||||
Keylet const& ownerDirKeylet,
|
||||
Keylet const& signerListKeylet,
|
||||
AccountID const& account,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const ownerDirKeylet = keylet::ownerDir(account);
|
||||
auto const signerListKeylet = keylet::signers(account);
|
||||
// We have to examine the current SignerList so we know how much to
|
||||
// reduce the OwnerCount.
|
||||
SLE::pointer signers = view.peek(signerListKeylet);
|
||||
@@ -195,8 +196,8 @@ removeSignersFromLedger(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
adjustOwnerCount(
|
||||
view, view.peek(accountKeylet), removeFromOwnerCount, registry.getJournal("View"));
|
||||
WritableAccountRoot wrappedAcct(account, view);
|
||||
wrappedAcct.adjustOwnerCount(removeFromOwnerCount, registry.journal("View"));
|
||||
|
||||
view.erase(signers);
|
||||
|
||||
@@ -210,12 +211,7 @@ SignerListSet::removeFromLedger(
|
||||
AccountID const& account,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const accountKeylet = keylet::account(account);
|
||||
auto const ownerDirKeylet = keylet::ownerDir(account);
|
||||
auto const signerListKeylet = keylet::signers(account);
|
||||
|
||||
return removeSignersFromLedger(
|
||||
registry, view, accountKeylet, ownerDirKeylet, signerListKeylet, j);
|
||||
return removeSignersFromLedger(registry, view, account, j);
|
||||
}
|
||||
|
||||
NotTEC
|
||||
@@ -280,23 +276,18 @@ SignerListSet::validateQuorumAndSignerEntries(
|
||||
TER
|
||||
SignerListSet::replaceSignerList()
|
||||
{
|
||||
auto const accountKeylet = keylet::account(account_);
|
||||
auto const ownerDirKeylet = keylet::ownerDir(account_);
|
||||
auto const signerListKeylet = keylet::signers(account_);
|
||||
|
||||
// This may be either a create or a replace. Preemptively remove any
|
||||
// old signer list. May reduce the reserve, so this is done before
|
||||
// checking the reserve.
|
||||
if (TER const ter = removeSignersFromLedger(
|
||||
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_))
|
||||
if (TER const ter = removeSignersFromLedger(ctx_.registry, view(), accountID_, j_))
|
||||
return ter;
|
||||
|
||||
auto const sle = view().peek(accountKeylet);
|
||||
if (!sle)
|
||||
WritableAccountRoot wrappedAcct(accountID_, view());
|
||||
if (!wrappedAcct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Compute new reserve. Verify the account has funds to meet the reserve.
|
||||
std::uint32_t const oldOwnerCount{(*sle)[sfOwnerCount]};
|
||||
std::uint32_t const oldOwnerCount{(*wrappedAcct)[sfOwnerCount]};
|
||||
|
||||
constexpr int addedOwnerCount = 1;
|
||||
std::uint32_t flags{lsfOneOwnerCount};
|
||||
@@ -310,16 +301,18 @@ SignerListSet::replaceSignerList()
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
// Everything's ducky. Add the ltSIGNER_LIST to the ledger.
|
||||
Keylet const ownerDirKeylet = keylet::ownerDir(accountID_);
|
||||
Keylet const signerListKeylet = keylet::signers(accountID_);
|
||||
auto signerList = std::make_shared<SLE>(signerListKeylet);
|
||||
view().insert(signerList);
|
||||
writeSignersToSLE(signerList, flags);
|
||||
|
||||
auto viewJ = ctx_.registry.getJournal("View");
|
||||
auto viewJ = ctx_.registry.journal("View");
|
||||
// Add the signer list to the account's directory.
|
||||
auto const page =
|
||||
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(account_));
|
||||
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Create signer list for account " << toBase58(account_) << ": "
|
||||
JLOG(j_.trace()) << "Create signer list for account " << toBase58(accountID_) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
@@ -329,27 +322,22 @@ SignerListSet::replaceSignerList()
|
||||
|
||||
// If we succeeded, the new entry counts against the
|
||||
// creator's reserve.
|
||||
adjustOwnerCount(view(), sle, addedOwnerCount, viewJ);
|
||||
wrappedAcct.adjustOwnerCount(addedOwnerCount, viewJ);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
SignerListSet::destroySignerList()
|
||||
{
|
||||
auto const accountKeylet = keylet::account(account_);
|
||||
// Destroying the signer list is only allowed if either the master key
|
||||
// is enabled or there is a regular key.
|
||||
SLE::pointer ledgerEntry = view().peek(accountKeylet);
|
||||
if (!ledgerEntry)
|
||||
if (!account_)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if ((ledgerEntry->isFlag(lsfDisableMaster)) && (!ledgerEntry->isFieldPresent(sfRegularKey)))
|
||||
if ((account_->isFlag(lsfDisableMaster)) && (!account_->isFieldPresent(sfRegularKey)))
|
||||
return tecNO_ALTERNATIVE_KEY;
|
||||
|
||||
auto const ownerDirKeylet = keylet::ownerDir(account_);
|
||||
auto const signerListKeylet = keylet::signers(account_);
|
||||
return removeSignersFromLedger(
|
||||
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
|
||||
return removeSignersFromLedger(ctx_.registry, view(), accountID_, j_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -358,7 +346,7 @@ SignerListSet::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t
|
||||
// Assign the quorum, default SignerListID, and flags.
|
||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||
{
|
||||
ledgerEntry->setAccountID(sfOwner, account_);
|
||||
ledgerEntry->setAccountID(sfOwner, accountID_);
|
||||
}
|
||||
ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
|
||||
ledgerEntry->setFieldU32(sfSignerListID, DEFAULT_SIGNER_LIST_ID);
|
||||
|
||||
@@ -103,13 +103,12 @@ checkAttestationPublicKey(
|
||||
|
||||
AccountID const accountFromPK = calcAccountID(pk);
|
||||
|
||||
if (auto const sleAttestationSigningAccount =
|
||||
view.read(keylet::account(attestationSignerAccount)))
|
||||
if (AccountRoot const acctSigner(attestationSignerAccount, view); acctSigner)
|
||||
{
|
||||
if (accountFromPK == attestationSignerAccount)
|
||||
{
|
||||
// master key
|
||||
if ((sleAttestationSigningAccount->getFieldU32(sfFlags) & lsfDisableMaster) != 0u)
|
||||
if ((acctSigner->getFieldU32(sfFlags) & lsfDisableMaster) != 0u)
|
||||
{
|
||||
JLOG(j.trace()) << "Attempt to add an attestation with "
|
||||
"disabled master key.";
|
||||
@@ -119,8 +118,7 @@ checkAttestationPublicKey(
|
||||
else
|
||||
{
|
||||
// regular key
|
||||
if (std::optional<AccountID> regularKey =
|
||||
(*sleAttestationSigningAccount)[~sfRegularKey];
|
||||
if (std::optional<AccountID> regularKey = acctSigner->at(~sfRegularKey);
|
||||
regularKey != accountFromPK)
|
||||
{
|
||||
if (!regularKey)
|
||||
@@ -381,12 +379,12 @@ transferHelper(
|
||||
if (dst == src)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const dstK = keylet::account(dst);
|
||||
if (auto sleDst = psb.read(dstK))
|
||||
AccountRoot const acctDst(dst, psb);
|
||||
if (acctDst)
|
||||
{
|
||||
// Check dst tag and deposit auth
|
||||
|
||||
if (((sleDst->getFlags() & lsfRequireDestTag) != 0u) && !dstTag)
|
||||
if (((acctDst->getFlags() & lsfRequireDestTag) != 0u) && !dstTag)
|
||||
return tecDST_TAG_NEEDED;
|
||||
|
||||
// If the destination is the claim owner, and this is a claim
|
||||
@@ -395,7 +393,7 @@ transferHelper(
|
||||
bool const canBypassDepositAuth =
|
||||
dst == claimOwner && depositAuthPolicy == DepositAuthPolicy::dstCanBypass;
|
||||
|
||||
if (!canBypassDepositAuth && ((sleDst->getFlags() & lsfDepositAuth) != 0u) &&
|
||||
if (!canBypassDepositAuth && ((acctDst->getFlags() & lsfDepositAuth) != 0u) &&
|
||||
!psb.exists(keylet::depositPreauth(dst, src)))
|
||||
{
|
||||
return tecNO_PERMISSION;
|
||||
@@ -408,17 +406,17 @@ transferHelper(
|
||||
|
||||
if (amt.native())
|
||||
{
|
||||
auto const sleSrc = psb.peek(keylet::account(src));
|
||||
XRPL_ASSERT(sleSrc, "xrpl::transferHelper : non-null source account");
|
||||
if (!sleSrc)
|
||||
WritableAccountRoot acctSrc(src, psb);
|
||||
XRPL_ASSERT(acctSrc, "xrpl::transferHelper : non-null source account");
|
||||
if (!acctSrc)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
|
||||
auto const ownerCount = acctSrc->getFieldU32(sfOwnerCount);
|
||||
auto const reserve = psb.fees().accountReserve(ownerCount);
|
||||
|
||||
auto const availableBalance = [&]() -> STAmount {
|
||||
STAmount const curBal = (*sleSrc)[sfBalance];
|
||||
STAmount const curBal = acctSrc->at(sfBalance);
|
||||
// Checking that account == src and postFeeBalance == curBal is
|
||||
// not strictly necessary, but helps protect against future
|
||||
// changes
|
||||
@@ -434,8 +432,8 @@ transferHelper(
|
||||
}
|
||||
}
|
||||
|
||||
auto sleDst = psb.peek(dstK);
|
||||
if (!sleDst)
|
||||
WritableAccountRoot acctDst(dst, psb);
|
||||
if (!acctDst)
|
||||
{
|
||||
if (canCreate == CanCreateDstPolicy::no)
|
||||
{
|
||||
@@ -449,17 +447,17 @@ transferHelper(
|
||||
}
|
||||
|
||||
// Create the account.
|
||||
sleDst = std::make_shared<SLE>(dstK);
|
||||
sleDst->setAccountID(sfAccount, dst);
|
||||
sleDst->setFieldU32(sfSequence, psb.seq());
|
||||
acctDst.newSLE();
|
||||
acctDst->setAccountID(sfAccount, dst);
|
||||
acctDst->setFieldU32(sfSequence, psb.seq());
|
||||
|
||||
psb.insert(sleDst);
|
||||
acctDst.insert();
|
||||
}
|
||||
|
||||
(*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt;
|
||||
(*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt;
|
||||
psb.update(sleSrc);
|
||||
psb.update(sleDst);
|
||||
acctSrc->at(sfBalance) = acctSrc->at(sfBalance) - amt;
|
||||
acctDst->at(sfBalance) = acctDst->at(sfBalance) + amt;
|
||||
acctSrc.update();
|
||||
acctDst.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -695,7 +693,7 @@ finalizeClaimHelper(
|
||||
auto const cidOwner = (*sleClaimID)[sfAccount];
|
||||
{
|
||||
// Remove the claim id
|
||||
auto const sleOwner = outerSb.peek(keylet::account(cidOwner));
|
||||
WritableAccountRoot wrappedOwner(cidOwner, outerSb);
|
||||
auto const page = (*sleClaimID)[sfOwnerNode];
|
||||
if (!outerSb.dirRemove(keylet::ownerDir(cidOwner), page, sleClaimID->key(), true))
|
||||
{
|
||||
@@ -707,7 +705,7 @@ finalizeClaimHelper(
|
||||
// Remove the claim id from the ledger
|
||||
outerSb.erase(sleClaimID);
|
||||
|
||||
adjustOwnerCount(outerSb, sleOwner, -1, j);
|
||||
wrappedOwner.adjustOwnerCount(-1, j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -730,9 +728,9 @@ getSignersListAndQuorum(ReadView const& view, SLE const& sleBridge, beast::Journ
|
||||
std::uint32_t q = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
AccountID const thisDoor = sleBridge[sfAccount];
|
||||
auto const sleDoor = [&] { return view.read(keylet::account(thisDoor)); }();
|
||||
AccountRoot const acctDoor(thisDoor, view);
|
||||
|
||||
if (!sleDoor)
|
||||
if (!acctDoor)
|
||||
{
|
||||
return {r, q, tecINTERNAL};
|
||||
}
|
||||
@@ -1111,14 +1109,13 @@ applyCreateAccountAttestations(
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*createdSleClaimID)[sfOwnerNode] = *page;
|
||||
|
||||
auto const sleDoor = psb.peek(doorK);
|
||||
if (!sleDoor)
|
||||
WritableAccountRoot wrappedDoor(doorAccount, psb);
|
||||
if (!wrappedDoor)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Reserve was already checked
|
||||
adjustOwnerCount(psb, sleDoor, 1, j);
|
||||
wrappedDoor.adjustOwnerCount(1, j);
|
||||
psb.insert(createdSleClaimID);
|
||||
psb.update(sleDoor);
|
||||
}
|
||||
|
||||
psb.apply(rawView);
|
||||
@@ -1396,25 +1393,25 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (!isXRP(bridgeSpec.issue(chainType)))
|
||||
{
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(bridgeSpec.issue(chainType).account));
|
||||
AccountRoot const acctIssuer(bridgeSpec.issue(chainType).account, ctx.view);
|
||||
|
||||
if (!sleIssuer)
|
||||
if (!acctIssuer)
|
||||
return tecNO_ISSUER;
|
||||
|
||||
// Allowing clawing back funds would break the bridge's invariant that
|
||||
// wrapped funds are always backed by locked funds
|
||||
if ((sleIssuer->getFlags() & lsfAllowTrustLineClawback) != 0u)
|
||||
if ((acctIssuer->getFlags() & lsfAllowTrustLineClawback) != 0u)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
{
|
||||
// Check reserve
|
||||
auto const sleAcc = ctx.view.read(keylet::account(account));
|
||||
if (!sleAcc)
|
||||
AccountRoot const acctSrc(account, ctx.view);
|
||||
if (!acctSrc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const balance = (*sleAcc)[sfBalance];
|
||||
auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1);
|
||||
auto const balance = acctSrc->at(sfBalance);
|
||||
auto const reserve = ctx.view.fees().accountReserve(acctSrc->getFieldU32(sfOwnerCount) + 1);
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -1431,8 +1428,8 @@ XChainCreateBridge::doApply()
|
||||
auto const reward = ctx_.tx[sfSignatureReward];
|
||||
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
|
||||
|
||||
auto const sleAcct = ctx_.view().peek(keylet::account(account));
|
||||
if (!sleAcct)
|
||||
WritableAccountRoot wrappedAcct(account, ctx_.view());
|
||||
if (!wrappedAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
STXChainBridge::ChainType const chainType =
|
||||
@@ -1459,10 +1456,10 @@ XChainCreateBridge::doApply()
|
||||
(*sleBridge)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal);
|
||||
wrappedAcct.adjustOwnerCount(1, ctx_.journal);
|
||||
|
||||
ctx_.view().insert(sleBridge);
|
||||
ctx_.view().update(sleAcct);
|
||||
wrappedAcct.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -1542,8 +1539,7 @@ BridgeModify::doApply()
|
||||
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
|
||||
bool const clearAccountCreate = (ctx_.tx.getFlags() & tfClearAccountCreateAmount) != 0u;
|
||||
|
||||
auto const sleAcct = ctx_.view().peek(keylet::account(account));
|
||||
if (!sleAcct)
|
||||
if (!AccountRoot(account, ctx_.view()))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
STXChainBridge::ChainType const chainType =
|
||||
@@ -1600,7 +1596,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
if (!ctx.view.read(keylet::account(ctx.tx[sfDestination])))
|
||||
if (!AccountRoot(ctx.tx[sfDestination], ctx.view))
|
||||
{
|
||||
return tecNO_DST;
|
||||
}
|
||||
@@ -1705,11 +1701,11 @@ XChainClaim::doApply()
|
||||
// `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
|
||||
// views, it's important that the sle's lifetime doesn't overlap.
|
||||
|
||||
auto const sleAcct = psb.peek(keylet::account(account));
|
||||
AccountRoot const acctRoot(account, psb);
|
||||
auto const sleBridge = peekBridge(psb, bridgeSpec);
|
||||
auto const sleClaimID = psb.peek(claimIDKeylet);
|
||||
|
||||
if (!(sleBridge && sleClaimID && sleAcct))
|
||||
if (!(sleBridge && sleClaimID && acctRoot))
|
||||
return Unexpected(tecINTERNAL);
|
||||
|
||||
AccountID const thisDoor = (*sleBridge)[sfAccount];
|
||||
@@ -1886,8 +1882,8 @@ XChainCommit::doApply()
|
||||
auto const amount = ctx_.tx[sfAmount];
|
||||
auto const bridgeSpec = ctx_.tx[sfXChainBridge];
|
||||
|
||||
auto const sleAccount = psb.read(keylet::account(account));
|
||||
if (!sleAccount)
|
||||
AccountRoot const acctRoot(account, psb);
|
||||
if (!acctRoot)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleBridge = readBridge(psb, bridgeSpec);
|
||||
@@ -1898,7 +1894,7 @@ XChainCommit::doApply()
|
||||
|
||||
// Support dipping into reserves to pay the fee
|
||||
TransferHelperSubmittingAccountInfo submittingAccountInfo{
|
||||
account_, preFeeBalance_, (*sleAccount)[sfBalance]};
|
||||
accountID_, preFeeBalance_, (*acctRoot)[sfBalance]};
|
||||
|
||||
auto const thTer = transferHelper(
|
||||
psb,
|
||||
@@ -1955,12 +1951,12 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
{
|
||||
// Check reserve
|
||||
auto const sleAcc = ctx.view.read(keylet::account(account));
|
||||
if (!sleAcc)
|
||||
AccountRoot const acctSrc(account, ctx.view);
|
||||
if (!acctSrc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const balance = (*sleAcc)[sfBalance];
|
||||
auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1);
|
||||
auto const balance = acctSrc->at(sfBalance);
|
||||
auto const reserve = ctx.view.fees().accountReserve(acctSrc->getFieldU32(sfOwnerCount) + 1);
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -1977,8 +1973,8 @@ XChainCreateClaimID::doApply()
|
||||
auto const reward = ctx_.tx[sfSignatureReward];
|
||||
auto const otherChainSrc = ctx_.tx[sfOtherChainSource];
|
||||
|
||||
auto const sleAcct = ctx_.view().peek(keylet::account(account));
|
||||
if (!sleAcct)
|
||||
WritableAccountRoot wrappedAcct(account, ctx_.view());
|
||||
if (!wrappedAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleBridge = peekBridge(ctx_.view(), bridgeSpec);
|
||||
@@ -2019,11 +2015,10 @@ XChainCreateClaimID::doApply()
|
||||
(*sleClaimID)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal);
|
||||
wrappedAcct.adjustOwnerCount(1, ctx_.journal);
|
||||
|
||||
ctx_.view().insert(sleClaimID);
|
||||
ctx_.view().update(sleBridge);
|
||||
ctx_.view().update(sleAcct);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -2161,8 +2156,8 @@ XChainCreateAccountCommit::doApply()
|
||||
STAmount const reward = ctx_.tx[sfSignatureReward];
|
||||
STXChainBridge const bridge = ctx_.tx[sfXChainBridge];
|
||||
|
||||
auto const sle = psb.peek(keylet::account(account));
|
||||
if (!sle)
|
||||
AccountRoot const acctRoot(account, psb);
|
||||
if (!acctRoot)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleBridge = peekBridge(psb, bridge);
|
||||
@@ -2173,7 +2168,7 @@ XChainCreateAccountCommit::doApply()
|
||||
|
||||
// Support dipping into reserves to pay the fee
|
||||
TransferHelperSubmittingAccountInfo submittingAccountInfo{
|
||||
account_, preFeeBalance_, (*sle)[sfBalance]};
|
||||
accountID_, preFeeBalance_, (*acctRoot)[sfBalance]};
|
||||
STAmount const toTransfer = amount + reward;
|
||||
auto const thTer = transferHelper(
|
||||
psb,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
@@ -57,7 +58,7 @@ CheckCancel::doApply()
|
||||
|
||||
AccountID const srcId{sleCheck->getAccountID(sfAccount)};
|
||||
AccountID const dstId{sleCheck->getAccountID(sfDestination)};
|
||||
auto viewJ = ctx_.registry.getJournal("View");
|
||||
auto viewJ = ctx_.registry.journal("View");
|
||||
|
||||
// If the check is not written to self (and it shouldn't be), remove the
|
||||
// check from the destination account root.
|
||||
@@ -84,8 +85,8 @@ CheckCancel::doApply()
|
||||
}
|
||||
|
||||
// If we succeeded, update the check owner's reserve.
|
||||
auto const sleSrc = view().peek(keylet::account(srcId));
|
||||
adjustOwnerCount(view(), sleSrc, -1, viewJ);
|
||||
WritableAccountRoot wrappedSrc(srcId, view());
|
||||
wrappedSrc.adjustOwnerCount(-1, viewJ);
|
||||
|
||||
// Remove check from ledger.
|
||||
view().erase(sleCheck);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/scope.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
@@ -72,16 +73,16 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
{
|
||||
auto const sleSrc = ctx.view.read(keylet::account(srcId));
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
if (!sleSrc || !sleDst)
|
||||
AccountRoot const acctSrc(srcId, ctx.view);
|
||||
AccountRoot const acctDst(dstId, ctx.view);
|
||||
if (!acctSrc || !acctDst)
|
||||
{
|
||||
// If the check exists this should never occur.
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: source or destination not in ledger";
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
if (((sleDst->getFlags() & lsfRequireDestTag) != 0u) &&
|
||||
if (((acctDst->getFlags() & lsfRequireDestTag) != 0u) &&
|
||||
!sleCheck->isFieldPresent(sfDestinationTag))
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
@@ -147,15 +148,15 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
// An issuer can always accept their own currency.
|
||||
if (!value.native() && (value.getIssuer() != dstId))
|
||||
{
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
|
||||
if (!sleIssuer)
|
||||
AccountRoot const acctIssuer(issuerId, ctx.view);
|
||||
if (!acctIssuer)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Can't receive IOUs from non-existent issuer: " << to_string(issuerId);
|
||||
return tecNO_ISSUER;
|
||||
}
|
||||
|
||||
if ((sleIssuer->at(sfFlags) & lsfRequireAuth) != 0u)
|
||||
if ((acctIssuer->at(sfFlags) & lsfRequireAuth) != 0u)
|
||||
{
|
||||
auto const sleTrustLine = ctx.view.read(keylet::line(dstId, issuerId, currency));
|
||||
|
||||
@@ -214,7 +215,7 @@ CheckCash::doApply()
|
||||
}
|
||||
|
||||
AccountID const srcId{sleCheck->getAccountID(sfAccount)};
|
||||
if (!psb.exists(keylet::account(srcId)) || !psb.exists(keylet::account(account_)))
|
||||
if (!psb.exists(keylet::account(srcId)) || !psb.exists(keylet::account(accountID_)))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(ctx_.journal.fatal()) << "Precheck did not verify source or destination's existence.";
|
||||
@@ -232,10 +233,10 @@ CheckCash::doApply()
|
||||
//
|
||||
// If it is not a check to self (as should be the case), then there's
|
||||
// work to do...
|
||||
auto viewJ = ctx_.registry.getJournal("View");
|
||||
auto viewJ = ctx_.registry.journal("View");
|
||||
auto const optDeliverMin = ctx_.tx[~sfDeliverMin];
|
||||
|
||||
if (srcId != account_)
|
||||
if (srcId != accountID_)
|
||||
{
|
||||
STAmount const sendMax = sleCheck->at(sfSendMax);
|
||||
|
||||
@@ -250,7 +251,8 @@ CheckCash::doApply()
|
||||
// from src's directory, we allow them to send that additional
|
||||
// incremental reserve amount in the transfer. Hence the -1
|
||||
// argument.
|
||||
STAmount const srcLiquid{xrpLiquid(psb, srcId, -1, viewJ)};
|
||||
WritableAccountRoot wrappedSrc(srcId, psb);
|
||||
STAmount const srcLiquid{wrappedSrc.xrpLiquid(-1, viewJ)};
|
||||
|
||||
// Now, how much do they need in order to be successful?
|
||||
STAmount const xrpDeliver{
|
||||
@@ -273,7 +275,7 @@ CheckCash::doApply()
|
||||
}
|
||||
|
||||
// The source account has enough XRP so make the ledger change.
|
||||
if (TER const ter{transferXRP(psb, srcId, account_, xrpDeliver, viewJ)};
|
||||
if (TER const ter{transferXRP(psb, srcId, accountID_, xrpDeliver, viewJ)};
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
// The transfer failed. Return the error code.
|
||||
@@ -296,9 +298,9 @@ CheckCash::doApply()
|
||||
// If a trust line does not exist yet create one.
|
||||
Issue const& trustLineIssue = flowDeliver.issue();
|
||||
AccountID const issuer = flowDeliver.getIssuer();
|
||||
AccountID const truster = issuer == account_ ? srcId : account_;
|
||||
AccountID const truster = issuer == accountID_ ? srcId : accountID_;
|
||||
Keylet const trustLineKey = keylet::line(truster, trustLineIssue);
|
||||
bool const destLow = issuer > account_;
|
||||
bool const destLow = issuer > accountID_;
|
||||
|
||||
if (!psb.exists(trustLineKey))
|
||||
{
|
||||
@@ -309,10 +311,10 @@ CheckCash::doApply()
|
||||
// a. this (destination) account and
|
||||
// b. issuing account (not sending account).
|
||||
|
||||
auto const sleDst = psb.peek(keylet::account(account_));
|
||||
WritableAccountRoot wrappedDst(accountID_, psb);
|
||||
|
||||
// Can the account cover the trust line's reserve?
|
||||
if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)};
|
||||
if (std::uint32_t const ownerCount = {wrappedDst->at(sfOwnerCount)};
|
||||
preFeeBalance_ < psb.fees().accountReserve(ownerCount + 1))
|
||||
{
|
||||
JLOG(j_.trace()) << "Trust line does not exist. "
|
||||
@@ -330,15 +332,15 @@ CheckCash::doApply()
|
||||
psb, // payment sandbox
|
||||
destLow, // is dest low?
|
||||
issuer, // source
|
||||
account_, // destination
|
||||
accountID_, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDst, // Account to add to
|
||||
wrappedDst, // Account to add to
|
||||
false, // authorize account
|
||||
(sleDst->getFlags() & lsfDefaultRipple) == 0,
|
||||
(wrappedDst->getFlags() & lsfDefaultRipple) == 0,
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, account_), // limit of zero
|
||||
Issue(currency, accountID_), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
viewJ); // journal
|
||||
@@ -348,7 +350,7 @@ CheckCash::doApply()
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
psb.update(sleDst);
|
||||
wrappedDst.update();
|
||||
|
||||
// Note that we _don't_ need to be careful about destroying
|
||||
// the trust line if the check cashing fails. The transaction
|
||||
@@ -382,7 +384,7 @@ CheckCash::doApply()
|
||||
psb,
|
||||
flowDeliver,
|
||||
srcId,
|
||||
account_,
|
||||
accountID_,
|
||||
STPathSet{},
|
||||
true, // default path
|
||||
static_cast<bool>(optDeliverMin), // partial payment
|
||||
@@ -419,9 +421,9 @@ CheckCash::doApply()
|
||||
|
||||
// Check was cashed. If not a self send (and it shouldn't be), remove
|
||||
// check link from destination directory.
|
||||
if (srcId != account_ &&
|
||||
if (srcId != accountID_ &&
|
||||
!psb.dirRemove(
|
||||
keylet::ownerDir(account_), sleCheck->at(sfDestinationNode), sleCheck->key(), true))
|
||||
keylet::ownerDir(accountID_), sleCheck->at(sfDestinationNode), sleCheck->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete check from destination.";
|
||||
@@ -439,7 +441,8 @@ CheckCash::doApply()
|
||||
}
|
||||
|
||||
// If we succeeded, update the check owner's reserve.
|
||||
adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), -1, viewJ);
|
||||
WritableAccountRoot wrappedSrc(srcId, psb);
|
||||
wrappedSrc.adjustOwnerCount(-1, viewJ);
|
||||
|
||||
// Remove check from ledger.
|
||||
psb.erase(sleCheck);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -52,14 +53,14 @@ TER
|
||||
CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const dstId{ctx.tx[sfDestination]};
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
if (!sleDst)
|
||||
AccountRoot const acctDst(dstId, ctx.view);
|
||||
if (!acctDst)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Destination account does not exist.";
|
||||
return tecNO_DST;
|
||||
}
|
||||
|
||||
auto const flags = sleDst->getFlags();
|
||||
auto const flags = acctDst->getFlags();
|
||||
|
||||
// Check if the destination has disallowed incoming checks
|
||||
if ((flags & lsfDisallowIncomingCheck) != 0u)
|
||||
@@ -69,7 +70,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
// because all writes to pseudo-account discriminator fields **are**
|
||||
// amendment gated, hence the behaviour of this check will always match the
|
||||
// currently active amendments.
|
||||
if (isPseudoAccount(sleDst))
|
||||
if (acctDst.isPseudoAccount())
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if (((flags & lsfRequireDestTag) != 0u) && !ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
@@ -85,8 +86,9 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (!sendMax.native())
|
||||
{
|
||||
// The currency may not be globally frozen
|
||||
AccountID const& issuerId{sendMax.getIssuer()};
|
||||
if (isGlobalFrozen(ctx.view, issuerId))
|
||||
AccountID const issuerId{sendMax.getIssuer()};
|
||||
AccountRoot wrappedIssuer(issuerId, ctx.view);
|
||||
if (wrappedIssuer.isGlobalFrozen())
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Creating a check for frozen asset";
|
||||
return tecFROZEN;
|
||||
@@ -132,15 +134,15 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
CheckCreate::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
if (!account_)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// A check counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
{
|
||||
STAmount const reserve{view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)};
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(account_->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -149,10 +151,10 @@ CheckCreate::doApply()
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// Check sequence. For more explanation see comments in SeqProxy.h.
|
||||
std::uint32_t const seq = ctx_.tx.getSeqValue();
|
||||
Keylet const checkKeylet = keylet::check(account_, seq);
|
||||
Keylet const checkKeylet = keylet::check(accountID_, seq);
|
||||
auto sleCheck = std::make_shared<SLE>(checkKeylet);
|
||||
|
||||
sleCheck->setAccountID(sfAccount, account_);
|
||||
sleCheck->setAccountID(sfAccount, accountID_);
|
||||
AccountID const dstAccountId = ctx_.tx[sfDestination];
|
||||
sleCheck->setAccountID(sfDestination, dstAccountId);
|
||||
sleCheck->setFieldU32(sfSequence, seq);
|
||||
@@ -168,10 +170,10 @@ CheckCreate::doApply()
|
||||
|
||||
view().insert(sleCheck);
|
||||
|
||||
auto viewJ = ctx_.registry.getJournal("View");
|
||||
auto viewJ = ctx_.registry.journal("View");
|
||||
// If it's not a self-send (and it shouldn't be), add Check to the
|
||||
// destination's owner directory.
|
||||
if (dstAccountId != account_)
|
||||
if (dstAccountId != accountID_)
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(dstAccountId), checkKeylet, describeOwnerDir(dstAccountId));
|
||||
@@ -186,8 +188,8 @@ CheckCreate::doApply()
|
||||
}
|
||||
|
||||
{
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(account_), checkKeylet, describeOwnerDir(account_));
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), checkKeylet, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding Check to owner directory " << to_string(checkKeylet.key) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
@@ -198,7 +200,7 @@ CheckCreate::doApply()
|
||||
sleCheck->setFieldU64(sfOwnerNode, *page);
|
||||
}
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
adjustOwnerCount(view(), sle, 1, viewJ);
|
||||
account_.adjustOwnerCount(1, viewJ);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,21 +76,21 @@ CredentialAccept::doApply()
|
||||
AccountID const issuer{ctx_.tx[sfIssuer]};
|
||||
|
||||
// Both exist as credential object exist itself (checked in preclaim)
|
||||
auto const sleSubject = view().peek(keylet::account(account_));
|
||||
auto const sleIssuer = view().peek(keylet::account(issuer));
|
||||
WritableAccountRoot wrappedSubject(accountID_, view());
|
||||
WritableAccountRoot wrappedIssuer(issuer, view());
|
||||
|
||||
if (!sleSubject || !sleIssuer)
|
||||
if (!wrappedSubject || !wrappedIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleSubject->getFieldU32(sfOwnerCount) + 1)};
|
||||
view().fees().accountReserve(wrappedSubject->getFieldU32(sfOwnerCount) + 1)};
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
Keylet const credentialKey = keylet::credential(account_, issuer, credType);
|
||||
Keylet const credentialKey = keylet::credential(accountID_, issuer, credType);
|
||||
auto const sleCred = view().peek(credentialKey); // Checked in preclaim()
|
||||
|
||||
if (checkExpired(sleCred, view().header().parentCloseTime))
|
||||
@@ -104,8 +104,8 @@ CredentialAccept::doApply()
|
||||
sleCred->setFieldU32(sfFlags, lsfAccepted);
|
||||
view().update(sleCred);
|
||||
|
||||
adjustOwnerCount(view(), sleIssuer, -1, j_);
|
||||
adjustOwnerCount(view(), sleSubject, 1, j_);
|
||||
wrappedIssuer.adjustOwnerCount(-1, j_);
|
||||
wrappedSubject.adjustOwnerCount(1, j_);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ CredentialCreate::doApply()
|
||||
{
|
||||
auto const subject = ctx_.tx[sfSubject];
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
Keylet const credentialKey = keylet::credential(subject, account_, credType);
|
||||
Keylet const credentialKey = keylet::credential(subject, accountID_, credType);
|
||||
|
||||
auto const sleCred = std::make_shared<SLE>(credentialKey);
|
||||
if (!sleCred)
|
||||
@@ -111,37 +111,37 @@ CredentialCreate::doApply()
|
||||
sleCred->setFieldU32(sfExpiration, *optExp);
|
||||
}
|
||||
|
||||
auto const sleIssuer = view().peek(keylet::account(account_));
|
||||
if (!sleIssuer)
|
||||
WritableAccountRoot wrappedIssuer(accountID_, view());
|
||||
if (!wrappedIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleIssuer->getFieldU32(sfOwnerCount) + 1)};
|
||||
view().fees().accountReserve(wrappedIssuer->getFieldU32(sfOwnerCount) + 1)};
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
sleCred->setAccountID(sfSubject, subject);
|
||||
sleCred->setAccountID(sfIssuer, account_);
|
||||
sleCred->setAccountID(sfIssuer, accountID_);
|
||||
sleCred->setFieldVL(sfCredentialType, credType);
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfURI))
|
||||
sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI));
|
||||
|
||||
{
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(account_), credentialKey, describeOwnerDir(account_));
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), credentialKey, describeOwnerDir(accountID_));
|
||||
JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfIssuerNode, *page);
|
||||
|
||||
adjustOwnerCount(view(), sleIssuer, 1, j_);
|
||||
wrappedIssuer.adjustOwnerCount(1, j_);
|
||||
}
|
||||
|
||||
if (subject == account_)
|
||||
if (subject == accountID_)
|
||||
{
|
||||
sleCred->setFieldU32(sfFlags, lsfAccepted);
|
||||
}
|
||||
|
||||
@@ -69,15 +69,15 @@ CredentialDelete::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
CredentialDelete::doApply()
|
||||
{
|
||||
auto const subject = ctx_.tx[~sfSubject].value_or(account_);
|
||||
auto const issuer = ctx_.tx[~sfIssuer].value_or(account_);
|
||||
auto const subject = ctx_.tx[~sfSubject].value_or(accountID_);
|
||||
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
|
||||
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
auto const sleCred = view().peek(keylet::credential(subject, issuer, credType));
|
||||
if (!sleCred)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if ((subject != account_) && (issuer != account_) &&
|
||||
if ((subject != accountID_) && (issuer != accountID_) &&
|
||||
!checkExpired(sleCred, ctx_.view().header().parentCloseTime))
|
||||
{
|
||||
JLOG(j_.trace()) << "Can't delete non-expired credential.";
|
||||
|
||||
@@ -56,12 +56,12 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
DelegateSet::doApply()
|
||||
{
|
||||
auto const sleOwner = ctx_.view().peek(keylet::account(account_));
|
||||
if (!sleOwner)
|
||||
WritableAccountRoot wrappedOwner(accountID_, ctx_.view());
|
||||
if (!wrappedOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const& authAccount = ctx_.tx[sfAuthorize];
|
||||
auto const delegateKey = keylet::delegate(account_, authAccount);
|
||||
auto const delegateKey = keylet::delegate(accountID_, authAccount);
|
||||
|
||||
auto sle = ctx_.view().peek(delegateKey);
|
||||
if (sle)
|
||||
@@ -70,7 +70,7 @@ DelegateSet::doApply()
|
||||
if (permissions.empty())
|
||||
{
|
||||
// if permissions array is empty, delete the ledger object.
|
||||
return deleteDelegate(view(), sle, account_, j_);
|
||||
return deleteDelegate(view(), sle, accountID_, j_);
|
||||
}
|
||||
|
||||
sle->setFieldArray(sfPermissions, permissions);
|
||||
@@ -83,25 +83,25 @@ DelegateSet::doApply()
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
STAmount const reserve{
|
||||
ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
|
||||
ctx_.view().fees().accountReserve(wrappedOwner->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
sle = std::make_shared<SLE>(delegateKey);
|
||||
sle->setAccountID(sfAccount, account_);
|
||||
sle->setAccountID(sfAccount, accountID_);
|
||||
sle->setAccountID(sfAuthorize, authAccount);
|
||||
|
||||
sle->setFieldArray(sfPermissions, permissions);
|
||||
auto const page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(account_), delegateKey, describeOwnerDir(account_));
|
||||
auto const page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(accountID_), delegateKey, describeOwnerDir(accountID_));
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
(*sle)[sfOwnerNode] = *page;
|
||||
ctx_.view().insert(sle);
|
||||
adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal);
|
||||
wrappedOwner.adjustOwnerCount(1, ctx_.journal);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -124,11 +124,11 @@ DelegateSet::deleteDelegate(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(account));
|
||||
if (!sleOwner)
|
||||
WritableAccountRoot wrappedOwner(account, view);
|
||||
if (!wrappedOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleOwner, -1, j);
|
||||
wrappedOwner.adjustOwnerCount(-1, j);
|
||||
|
||||
view.erase(sle);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -90,7 +91,7 @@ AMMBid::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
for (auto const& account : ctx.tx.getFieldArray(sfAuthAccounts))
|
||||
{
|
||||
if (!ctx.view.read(keylet::account(account[sfAccount])))
|
||||
if (AccountRoot const acct(account[sfAccount], ctx.view); !acct)
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Bid: Invalid Account.";
|
||||
return terNO_ACCOUNT;
|
||||
@@ -147,14 +148,14 @@ AMMBid::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
static std::pair<TER, bool>
|
||||
applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Journal j_)
|
||||
applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& accountID_, beast::Journal j_)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto const ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
|
||||
if (!ammSle)
|
||||
return {tecINTERNAL, false};
|
||||
STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance];
|
||||
auto const lpTokens = ammLPHolds(sb, *ammSle, account_, ctx_.journal);
|
||||
auto const lpTokens = ammLPHolds(sb, *ammSle, accountID_, ctx_.journal);
|
||||
auto const& rules = ctx_.view().rules();
|
||||
if (!rules.enabled(fixInnerObjTemplate))
|
||||
{
|
||||
@@ -186,11 +187,11 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour
|
||||
auto validOwner = [&](AccountID const& account) {
|
||||
// Valid range is 0-19 but the tailing slot pays MinSlotPrice
|
||||
// and doesn't refund so the check is < instead of <= to optimize.
|
||||
return timeSlot && *timeSlot < tailingSlot && sb.read(keylet::account(account));
|
||||
return timeSlot && *timeSlot < tailingSlot && AccountRoot(account, sb);
|
||||
};
|
||||
|
||||
auto updateSlot = [&](std::uint32_t fee, Number const& minPrice, Number const& burn) -> TER {
|
||||
auctionSlot.setAccountID(sfAccount, account_);
|
||||
auctionSlot.setAccountID(sfAccount, accountID_);
|
||||
auctionSlot.setFieldU32(sfExpiration, current + TOTAL_TIME_SLOT_SECS);
|
||||
if (fee != 0)
|
||||
{
|
||||
@@ -221,7 +222,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto res = redeemIOU(sb, account_, saBurn, lpTokens.issue(), ctx_.journal);
|
||||
auto res = redeemIOU(sb, accountID_, saBurn, lpTokens.issue(), ctx_.journal);
|
||||
if (!isTesSuccess(res))
|
||||
{
|
||||
JLOG(ctx_.journal.debug()) << "AMM Bid: failed to redeem.";
|
||||
@@ -319,7 +320,7 @@ applyBid(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jour
|
||||
}
|
||||
res = accountSend(
|
||||
sb,
|
||||
account_,
|
||||
accountID_,
|
||||
auctionSlot[sfAccount],
|
||||
toSTAmount(lpTokens.issue(), refund),
|
||||
ctx_.journal);
|
||||
@@ -343,7 +344,7 @@ AMMBid::doApply()
|
||||
// as we go on processing transactions.
|
||||
Sandbox sb(&ctx_.view());
|
||||
|
||||
auto const result = applyBid(ctx_, sb, account_, j_);
|
||||
auto const result = applyBid(ctx_, sb, accountID_, j_);
|
||||
if (result.second)
|
||||
sb.apply(ctx_.rawView());
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -72,11 +73,11 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const asset = ctx.tx[sfAsset].get<Issue>();
|
||||
auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
|
||||
if (!sleIssuer)
|
||||
AccountRoot const acctIssuer(ctx.tx[sfAccount], ctx.view);
|
||||
if (!acctIssuer)
|
||||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||
|
||||
if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
|
||||
if (AccountRoot const acctHolder(ctx.tx[sfHolder], ctx.view); !acctHolder)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
|
||||
@@ -86,7 +87,7 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
|
||||
return terNO_AMM;
|
||||
}
|
||||
|
||||
std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags);
|
||||
std::uint32_t const issuerFlagsIn = acctIssuer->getFieldU32(sfFlags);
|
||||
|
||||
// If AllowTrustLineClawback is not set or NoFreeze is set, return no
|
||||
// permission
|
||||
@@ -123,8 +124,8 @@ AMMClawback::applyGuts(Sandbox& sb)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ammAccount = (*ammSle)[sfAccount];
|
||||
auto const accountSle = sb.read(keylet::account(ammAccount));
|
||||
if (!accountSle)
|
||||
AccountRoot const ammAcct(ammAccount, sb);
|
||||
if (!ammAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (sb.rules().enabled(fixAMMClawbackRounding))
|
||||
|
||||
@@ -97,8 +97,9 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (isXRP(issue))
|
||||
return false;
|
||||
|
||||
if (auto const issuerAccount = view.read(keylet::account(issue.account)))
|
||||
return (issuerAccount->getFlags() & lsfDefaultRipple) == 0;
|
||||
AccountRoot const issuerAcct(issue.account, view);
|
||||
if (issuerAcct)
|
||||
return (issuerAcct->getFlags() & lsfDefaultRipple) == 0;
|
||||
|
||||
return false;
|
||||
};
|
||||
@@ -110,7 +111,8 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
// Check the reserve for LPToken trustline
|
||||
STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
|
||||
AccountRoot wrappedAcct(accountID, ctx.view);
|
||||
STAmount const xrpBalance = wrappedAcct.xrpLiquid(1, ctx.j);
|
||||
// Insufficient reserve
|
||||
if (xrpBalance <= beast::zero)
|
||||
{
|
||||
@@ -134,8 +136,9 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
auto isLPToken = [&](STAmount const& amount) -> bool {
|
||||
if (auto const sle = ctx.view.read(keylet::account(amount.issue().account)))
|
||||
return sle->isFieldPresent(sfAMMID);
|
||||
AccountRoot const acct(amount.issue().account, ctx.view);
|
||||
if (acct)
|
||||
return acct->isFieldPresent(sfAMMID);
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -164,10 +167,10 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
auto clawbackDisabled = [&](Issue const& issue) -> TER {
|
||||
if (isXRP(issue))
|
||||
return tesSUCCESS;
|
||||
auto const sle = ctx.view.read(keylet::account(issue.account));
|
||||
if (!sle)
|
||||
AccountRoot const acct(issue.account, ctx.view);
|
||||
if (!acct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
if (sle->getFlags() & lsfAllowTrustLineClawback)
|
||||
if (acct->getFlags() & lsfAllowTrustLineClawback)
|
||||
return tecNO_PERMISSION;
|
||||
return tesSUCCESS;
|
||||
};
|
||||
@@ -178,7 +181,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
static std::pair<TER, bool>
|
||||
applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Journal j_)
|
||||
applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& accountID_, beast::Journal j_)
|
||||
{
|
||||
auto const amount = ctx_.tx[sfAmount];
|
||||
auto const amount2 = ctx_.tx[sfAmount2];
|
||||
@@ -221,7 +224,7 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J
|
||||
ammSle->setFieldIssue(sfAsset, STIssue{sfAsset, issue1});
|
||||
ammSle->setFieldIssue(sfAsset2, STIssue{sfAsset2, issue2});
|
||||
// AMM creator gets the auction slot and the voting slot.
|
||||
initializeFeeAuctionVote(ctx_.view(), ammSle, account_, lptIss, ctx_.tx[sfTradingFee]);
|
||||
initializeFeeAuctionVote(ctx_.view(), ammSle, accountID_, lptIss, ctx_.tx[sfTradingFee]);
|
||||
|
||||
// Add owner directory to link the root account and AMM object.
|
||||
if (auto ter = dirLink(sb, accountId, ammSle); ter)
|
||||
@@ -232,7 +235,7 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J
|
||||
sb.insert(ammSle);
|
||||
|
||||
// Send LPT to LP.
|
||||
auto res = accountSend(sb, accountId, account_, lpTokens, ctx_.journal);
|
||||
auto res = accountSend(sb, accountId, accountID_, lpTokens, ctx_.journal);
|
||||
if (!isTesSuccess(res))
|
||||
{
|
||||
JLOG(j_.debug()) << "AMM Instance: failed to send LPT " << lpTokens;
|
||||
@@ -241,7 +244,7 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J
|
||||
|
||||
auto sendAndTrustSet = [&](STAmount const& amount) -> TER {
|
||||
if (auto const res =
|
||||
accountSend(sb, account_, accountId, amount, ctx_.journal, WaiveTransferFee::Yes))
|
||||
accountSend(sb, accountID_, accountId, amount, ctx_.journal, WaiveTransferFee::Yes))
|
||||
return res;
|
||||
// Set AMM flag on AMM trustline
|
||||
if (!isXRP(amount))
|
||||
@@ -296,7 +299,7 @@ AMMCreate::doApply()
|
||||
// as we go on processing transactions.
|
||||
Sandbox sb(&ctx_.view());
|
||||
|
||||
auto const result = applyCreate(ctx_, sb, account_, j_);
|
||||
auto const result = applyCreate(ctx_, sb, accountID_, j_);
|
||||
if (result.second)
|
||||
sb.apply(ctx_.rawView());
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// Have to check again in deposit() because
|
||||
// amounts might be derived based on tokens or
|
||||
// limits.
|
||||
AccountRoot wrappedAcct(accountID, ctx.view);
|
||||
auto balance = [&](auto const& deposit) -> TER {
|
||||
if (isXRP(deposit))
|
||||
{
|
||||
@@ -194,7 +195,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// Adjust the reserve if LP doesn't have LPToken trustline
|
||||
auto const sle =
|
||||
ctx.view.read(keylet::line(accountID, lpIssue.account, lpIssue.currency));
|
||||
if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
|
||||
if (wrappedAcct.xrpLiquid(!sle, ctx.j) >= deposit)
|
||||
return TER(tesSUCCESS);
|
||||
if (sle)
|
||||
return tecUNFUNDED_AMM;
|
||||
@@ -311,7 +312,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// We checked above but need to check again if depositing IOU only.
|
||||
if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero)
|
||||
{
|
||||
STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
|
||||
STAmount const xrpBalance = wrappedAcct.xrpLiquid(1, ctx.j);
|
||||
// Insufficient reserve
|
||||
if (xrpBalance <= beast::zero)
|
||||
{
|
||||
@@ -347,7 +348,7 @@ AMMDeposit::applyGuts(Sandbox& sb)
|
||||
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
|
||||
auto const tfee = (lptAMMBalance == beast::zero)
|
||||
? ctx_.tx[~sfTradingFee].value_or(0)
|
||||
: getTradingFee(ctx_.view(), *ammSle, account_);
|
||||
: getTradingFee(ctx_.view(), *ammSle, accountID_);
|
||||
|
||||
auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
|
||||
|
||||
@@ -418,7 +419,7 @@ AMMDeposit::applyGuts(Sandbox& sb)
|
||||
// LP depositing into AMM empty state gets the auction slot
|
||||
// and the voting
|
||||
if (lptAMMBalance == beast::zero)
|
||||
initializeFeeAuctionVote(sb, ammSle, account_, lptAMMBalance.issue(), tfee);
|
||||
initializeFeeAuctionVote(sb, ammSle, accountID_, lptAMMBalance.issue(), tfee);
|
||||
|
||||
sb.update(ammSle);
|
||||
}
|
||||
@@ -463,15 +464,15 @@ AMMDeposit::deposit(
|
||||
{
|
||||
auto const& lpIssue = lpTokensDeposit.issue();
|
||||
// Adjust the reserve if LP doesn't have LPToken trustline
|
||||
auto const sle = view.read(keylet::line(account_, lpIssue.account, lpIssue.currency));
|
||||
if (xrpLiquid(view, account_, !sle, j_) >= depositAmount)
|
||||
auto const sle = view.read(keylet::line(accountID_, lpIssue.account, lpIssue.currency));
|
||||
if (account_.xrpLiquid(!sle, j_) >= depositAmount)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
else if (
|
||||
account_ == depositAmount.issue().account ||
|
||||
accountID_ == depositAmount.issue().account ||
|
||||
accountHolds(
|
||||
view,
|
||||
account_,
|
||||
accountID_,
|
||||
depositAmount.issue(),
|
||||
FreezeHandling::fhIGNORE_FREEZE,
|
||||
ctx_.journal) >= depositAmount)
|
||||
@@ -518,7 +519,7 @@ AMMDeposit::deposit(
|
||||
}
|
||||
|
||||
auto res = accountSend(
|
||||
view, account_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes);
|
||||
view, accountID_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes);
|
||||
if (!isTesSuccess(res))
|
||||
{
|
||||
JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << amountDepositActual;
|
||||
@@ -537,7 +538,12 @@ AMMDeposit::deposit(
|
||||
}
|
||||
|
||||
res = accountSend(
|
||||
view, account_, ammAccount, *amount2DepositActual, ctx_.journal, WaiveTransferFee::Yes);
|
||||
view,
|
||||
accountID_,
|
||||
ammAccount,
|
||||
*amount2DepositActual,
|
||||
ctx_.journal,
|
||||
WaiveTransferFee::Yes);
|
||||
if (!isTesSuccess(res))
|
||||
{
|
||||
JLOG(ctx_.journal.debug())
|
||||
@@ -547,7 +553,7 @@ AMMDeposit::deposit(
|
||||
}
|
||||
|
||||
// Deposit LP tokens
|
||||
res = accountSend(view, ammAccount, account_, lpTokensDepositActual, ctx_.journal);
|
||||
res = accountSend(view, ammAccount, accountID_, lpTokensDepositActual, ctx_.journal);
|
||||
if (!isTesSuccess(res))
|
||||
{
|
||||
JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/tx/transactors/dex/AMMHelpers.h>
|
||||
@@ -184,8 +185,8 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const
|
||||
{
|
||||
if (isXRP(issue))
|
||||
{
|
||||
if (auto const sle = view.read(keylet::account(ammAccountID)))
|
||||
return (*sle)[sfBalance];
|
||||
if (AccountRoot const acct(ammAccountID, view); acct)
|
||||
return (*acct)[sfBalance];
|
||||
}
|
||||
else if (
|
||||
auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency));
|
||||
@@ -255,8 +256,8 @@ deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Jo
|
||||
}
|
||||
|
||||
auto const ammAccountID = (*ammSle)[sfAccount];
|
||||
auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
|
||||
if (!sleAMMRoot)
|
||||
WritableAccountRoot ammRoot(ammAccountID, sb);
|
||||
if (!ammRoot)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
|
||||
@@ -287,7 +288,7 @@ deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Jo
|
||||
}
|
||||
|
||||
sb.erase(ammSle);
|
||||
sb.erase(sleAMMRoot);
|
||||
ammRoot.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -56,14 +56,14 @@ AMMVote::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
static std::pair<TER, bool>
|
||||
applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Journal j_)
|
||||
applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& accountID_, beast::Journal j_)
|
||||
{
|
||||
auto const feeNew = ctx_.tx[sfTradingFee];
|
||||
auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
|
||||
if (!ammSle)
|
||||
return {tecINTERNAL, false};
|
||||
STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance];
|
||||
auto const lpTokensNew = ammLPHolds(sb, *ammSle, account_, ctx_.journal);
|
||||
auto const lpTokensNew = ammLPHolds(sb, *ammSle, accountID_, ctx_.journal);
|
||||
std::optional<STAmount> minTokens;
|
||||
std::size_t minPos{0};
|
||||
AccountID minAccount{0};
|
||||
@@ -90,7 +90,7 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou
|
||||
auto feeVal = entry[sfTradingFee];
|
||||
STObject newEntry = STObject::makeInnerObject(sfVoteEntry);
|
||||
// The account already has the vote entry.
|
||||
if (account == account_)
|
||||
if (account == accountID_)
|
||||
{
|
||||
lpTokens = lpTokensNew;
|
||||
feeVal = feeNew;
|
||||
@@ -132,7 +132,7 @@ applyVote(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::Jou
|
||||
sfVoteWeight,
|
||||
static_cast<std::int64_t>(
|
||||
Number(lpTokensNew) * VOTE_WEIGHT_SCALE_FACTOR / lptAMMBalance));
|
||||
newEntry.setAccountID(sfAccount, account_);
|
||||
newEntry.setAccountID(sfAccount, accountID_);
|
||||
num += feeNew * lpTokensNew;
|
||||
den += lpTokensNew;
|
||||
if (minPos)
|
||||
@@ -214,7 +214,7 @@ AMMVote::doApply()
|
||||
// as we go on processing transactions.
|
||||
Sandbox sb(&ctx_.view());
|
||||
|
||||
auto const result = applyVote(ctx_, sb, account_, j_);
|
||||
auto const result = applyVote(ctx_, sb, accountID_, j_);
|
||||
if (result.second)
|
||||
sb.apply(ctx_.rawView());
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/dex/AMMHelpers.h>
|
||||
@@ -269,8 +270,8 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
||||
if (!ammSle)
|
||||
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
|
||||
auto const ammAccountID = (*ammSle)[sfAccount];
|
||||
auto const accountSle = sb.read(keylet::account(ammAccountID));
|
||||
if (!accountSle)
|
||||
AccountRoot const ammAcct(ammAccountID, sb);
|
||||
if (!ammAcct)
|
||||
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
|
||||
auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
|
||||
auto const lpTokensWithdraw =
|
||||
@@ -280,11 +281,11 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
||||
// might not match the LP's trustline balance
|
||||
if (sb.rules().enabled(fixAMMv1_1))
|
||||
{
|
||||
if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokens, ammSle, account_); !res)
|
||||
if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokens, ammSle, accountID_); !res)
|
||||
return {res.error(), false};
|
||||
}
|
||||
|
||||
auto const tfee = getTradingFee(ctx_.view(), *ammSle, account_);
|
||||
auto const tfee = getTradingFee(ctx_.view(), *ammSle, accountID_);
|
||||
|
||||
auto const expected = ammHolds(
|
||||
sb,
|
||||
@@ -413,7 +414,7 @@ AMMWithdraw::withdraw(
|
||||
view,
|
||||
ammSle,
|
||||
ammAccount,
|
||||
account_,
|
||||
accountID_,
|
||||
amountBalance,
|
||||
amountWithdraw,
|
||||
amount2Withdraw,
|
||||
@@ -532,11 +533,11 @@ AMMWithdraw::withdraw(
|
||||
return tesSUCCESS;
|
||||
if (!view.exists(keylet::line(account, issue)))
|
||||
{
|
||||
auto const sleAccount = view.read(keylet::account(account));
|
||||
if (!sleAccount)
|
||||
AccountRoot const acct(account, view);
|
||||
if (!acct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
auto const balance = (*sleAccount)[sfBalance].xrp();
|
||||
std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);
|
||||
auto const balance = acct->getFieldAmount(sfBalance).xrp();
|
||||
std::uint32_t const ownerCount = acct->at(sfOwnerCount);
|
||||
|
||||
// See also TrustSet::doApply()
|
||||
XRPAmount const reserve(
|
||||
@@ -628,7 +629,7 @@ AMMWithdraw::equalWithdrawTokens(
|
||||
std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = equalWithdrawTokens(
|
||||
view,
|
||||
ammSle,
|
||||
account_,
|
||||
accountID_,
|
||||
ammAccount,
|
||||
amountBalance,
|
||||
amount2Balance,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/OfferHelpers.h>
|
||||
#include <xrpl/protocol/st.h>
|
||||
#include <xrpl/tx/transactors/dex/OfferCancel.h>
|
||||
@@ -25,11 +27,11 @@ OfferCancel::preclaim(PreclaimContext const& ctx)
|
||||
auto const id = ctx.tx[sfAccount];
|
||||
auto const offerSequence = ctx.tx[sfOfferSequence];
|
||||
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
if (!sle)
|
||||
AccountRoot const acct(id, ctx.view);
|
||||
if (!acct)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
if ((*sle)[sfSequence] <= offerSequence)
|
||||
if (acct->getFieldU32(sfSequence) <= offerSequence)
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: "
|
||||
<< "Sequence " << offerSequence << " is invalid.";
|
||||
@@ -46,14 +48,13 @@ OfferCancel::doApply()
|
||||
{
|
||||
auto const offerSequence = ctx_.tx[sfOfferSequence];
|
||||
|
||||
auto const sle = view().read(keylet::account(account_));
|
||||
if (!sle)
|
||||
if (AccountRoot const acct(accountID_, view()); !acct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (auto sleOffer = view().peek(keylet::offer(account_, offerSequence)))
|
||||
if (auto sleOffer = view().peek(keylet::offer(accountID_, offerSequence)))
|
||||
{
|
||||
JLOG(j_.debug()) << "Trying to cancel offer #" << offerSequence;
|
||||
return offerDelete(view(), sleOffer, ctx_.registry.getJournal("View"));
|
||||
return offerDelete(view(), sleOffer, ctx_.registry.journal("View"));
|
||||
}
|
||||
|
||||
JLOG(j_.debug()) << "Offer #" << offerSequence << " can't be found.";
|
||||
|
||||
@@ -138,15 +138,17 @@ OfferCreate::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
auto const cancelSequence = ctx.tx[~sfOfferSequence];
|
||||
|
||||
auto const sleCreator = ctx.view.read(keylet::account(id));
|
||||
if (!sleCreator)
|
||||
AccountRoot const acctCreator(id, ctx.view);
|
||||
if (!acctCreator)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
std::uint32_t const uAccountSequence = sleCreator->getFieldU32(sfSequence);
|
||||
std::uint32_t const uAccountSequence = acctCreator->getFieldU32(sfSequence);
|
||||
|
||||
auto viewJ = ctx.registry.getJournal("View");
|
||||
auto viewJ = ctx.registry.journal("View");
|
||||
AccountRoot wrappedPays(uPaysIssuerID, ctx.view);
|
||||
AccountRoot wrappedGets(uGetsIssuerID, ctx.view);
|
||||
|
||||
if (isGlobalFrozen(ctx.view, uPaysIssuerID) || isGlobalFrozen(ctx.view, uGetsIssuerID))
|
||||
if (wrappedPays.isGlobalFrozen() || wrappedGets.isGlobalFrozen())
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "Offer involves frozen asset";
|
||||
return tecFROZEN;
|
||||
@@ -205,9 +207,9 @@ OfferCreate::checkAcceptAsset(
|
||||
// Only valid for custom currencies
|
||||
XRPL_ASSERT(!isXRP(issue.currency), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP");
|
||||
|
||||
auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
AccountRoot const issuerAcct(issue.account, view);
|
||||
|
||||
if (!issuerAccount)
|
||||
if (!issuerAcct)
|
||||
{
|
||||
JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: "
|
||||
<< to_string(issue.account);
|
||||
@@ -221,7 +223,7 @@ OfferCreate::checkAcceptAsset(
|
||||
if (issue.account == id)
|
||||
return tesSUCCESS;
|
||||
|
||||
if (((*issuerAccount)[sfFlags] & lsfRequireAuth) != 0u)
|
||||
if ((issuerAcct->getFlags() & lsfRequireAuth) != 0u)
|
||||
{
|
||||
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
|
||||
|
||||
@@ -281,7 +283,7 @@ OfferCreate::flowCross(
|
||||
// cause a user's available balance to go to 0 (by causing it to dip
|
||||
// below the reserve) so we check this case again.
|
||||
STAmount const inStartBalance =
|
||||
accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
|
||||
accountFunds(psb, accountID_, takerAmount.in, fhZERO_IF_FROZEN, j_);
|
||||
if (inStartBalance <= beast::zero)
|
||||
{
|
||||
// The account balance can't cover even part of the offer.
|
||||
@@ -294,9 +296,10 @@ OfferCreate::flowCross(
|
||||
// offer taker. Set sendMax to allow for the gateway's cut.
|
||||
Rate gatewayXferRate{QUALITY_ONE};
|
||||
STAmount sendMax = takerAmount.in;
|
||||
if (!sendMax.native() && (account_ != sendMax.getIssuer()))
|
||||
if (!sendMax.native() && (accountID_ != sendMax.getIssuer()))
|
||||
{
|
||||
gatewayXferRate = transferRate(psb, sendMax.getIssuer());
|
||||
WritableAccountRoot wrappedAcct(sendMax.getIssuer(), psb);
|
||||
gatewayXferRate = wrappedAcct.transferRate();
|
||||
if (gatewayXferRate.value != QUALITY_ONE)
|
||||
{
|
||||
sendMax =
|
||||
@@ -357,8 +360,8 @@ OfferCreate::flowCross(
|
||||
auto const result = flow(
|
||||
psb,
|
||||
deliver,
|
||||
account_,
|
||||
account_,
|
||||
accountID_,
|
||||
accountID_,
|
||||
paths,
|
||||
true, // default path
|
||||
(txFlags & tfFillOrKill) == 0u, // partial payment
|
||||
@@ -383,7 +386,7 @@ OfferCreate::flowCross(
|
||||
if (isTesSuccess(result.result()))
|
||||
{
|
||||
STAmount const takerInBalance =
|
||||
accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
|
||||
accountFunds(psb, accountID_, takerAmount.in, fhZERO_IF_FROZEN, j_);
|
||||
|
||||
if (takerInBalance <= beast::zero)
|
||||
{
|
||||
@@ -536,14 +539,14 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
// end up on the books.
|
||||
auto uRate = getRate(saTakerGets, saTakerPays);
|
||||
|
||||
auto viewJ = ctx_.registry.getJournal("View");
|
||||
auto viewJ = ctx_.registry.journal("View");
|
||||
|
||||
TER result = tesSUCCESS;
|
||||
|
||||
// Process a cancellation request that's passed along with an offer.
|
||||
if (cancelSequence)
|
||||
{
|
||||
auto const sleCancel = sb.peek(keylet::offer(account_, *cancelSequence));
|
||||
auto const sleCancel = sb.peek(keylet::offer(accountID_, *cancelSequence));
|
||||
|
||||
// It's not an error to not find the offer to cancel: it might have
|
||||
// been consumed or removed. If it is found, however, it's an error
|
||||
@@ -576,15 +579,15 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
std::uint8_t uTickSize = Quality::maxTickSize;
|
||||
if (!isXRP(uPaysIssuerID))
|
||||
{
|
||||
auto const sle = sb.read(keylet::account(uPaysIssuerID));
|
||||
if (sle && sle->isFieldPresent(sfTickSize))
|
||||
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
|
||||
AccountRoot const acctPays(uPaysIssuerID, sb);
|
||||
if (acctPays && acctPays->isFieldPresent(sfTickSize))
|
||||
uTickSize = std::min(uTickSize, (*acctPays)[sfTickSize]);
|
||||
}
|
||||
if (!isXRP(uGetsIssuerID))
|
||||
{
|
||||
auto const sle = sb.read(keylet::account(uGetsIssuerID));
|
||||
if (sle && sle->isFieldPresent(sfTickSize))
|
||||
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
|
||||
AccountRoot const acctGets(uGetsIssuerID, sb);
|
||||
if (acctGets && acctGets->isFieldPresent(sfTickSize))
|
||||
uTickSize = std::min(uTickSize, (*acctGets)[sfTickSize]);
|
||||
}
|
||||
if (uTickSize < Quality::maxTickSize)
|
||||
{
|
||||
@@ -732,12 +735,12 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
return {tesSUCCESS, true};
|
||||
}
|
||||
|
||||
auto const sleCreator = sb.peek(keylet::account(account_));
|
||||
if (!sleCreator)
|
||||
WritableAccountRoot wrappedCreator(accountID_, sb);
|
||||
if (!wrappedCreator)
|
||||
return {tefINTERNAL, false};
|
||||
|
||||
{
|
||||
XRPAmount reserve = sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
|
||||
XRPAmount reserve = sb.fees().accountReserve(wrappedCreator->getFieldU32(sfOwnerCount) + 1);
|
||||
|
||||
if (preFeeBalance_ < reserve)
|
||||
{
|
||||
@@ -757,11 +760,11 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
}
|
||||
|
||||
// We need to place the remainder of the offer into its order book.
|
||||
auto const offer_index = keylet::offer(account_, offerSequence);
|
||||
auto const offer_index = keylet::offer(accountID_, offerSequence);
|
||||
|
||||
// Add offer to owner's directory.
|
||||
auto const ownerNode =
|
||||
sb.dirInsert(keylet::ownerDir(account_), offer_index, describeOwnerDir(account_));
|
||||
sb.dirInsert(keylet::ownerDir(accountID_), offer_index, describeOwnerDir(accountID_));
|
||||
|
||||
if (!ownerNode)
|
||||
{
|
||||
@@ -772,7 +775,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
}
|
||||
|
||||
// Update owner count.
|
||||
adjustOwnerCount(sb, sleCreator, 1, viewJ);
|
||||
wrappedCreator.adjustOwnerCount(1, viewJ);
|
||||
|
||||
JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue()) << " : "
|
||||
<< to_string(saTakerGets.issue())
|
||||
@@ -818,7 +821,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
}
|
||||
|
||||
auto sleOffer = std::make_shared<SLE>(offer_index);
|
||||
sleOffer->setAccountID(sfAccount, account_);
|
||||
sleOffer->setAccountID(sfAccount, accountID_);
|
||||
sleOffer->setFieldU32(sfSequence, offerSequence);
|
||||
sleOffer->setFieldH256(sfBookDirectory, dir.key);
|
||||
sleOffer->setFieldAmount(sfTakerPays, saTakerPays);
|
||||
|
||||
@@ -38,12 +38,12 @@ DIDDelete::deleteSLE(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(owner));
|
||||
if (!sleOwner)
|
||||
WritableAccountRoot wrappedOwner(owner, view);
|
||||
if (!wrappedOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleOwner, -1, j);
|
||||
view.update(sleOwner);
|
||||
wrappedOwner.adjustOwnerCount(-1, j);
|
||||
wrappedOwner.update();
|
||||
|
||||
// Remove object from ledger
|
||||
view.erase(sle);
|
||||
@@ -53,7 +53,7 @@ DIDDelete::deleteSLE(
|
||||
TER
|
||||
DIDDelete::doApply()
|
||||
{
|
||||
return deleteSLE(ctx_, keylet::did(account_), account_);
|
||||
return deleteSLE(ctx_, keylet::did(accountID_), accountID_);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -51,14 +51,14 @@ DIDSet::preflight(PreflightContext const& ctx)
|
||||
static TER
|
||||
addSLE(ApplyContext& ctx, std::shared_ptr<SLE> const& sle, AccountID const& owner)
|
||||
{
|
||||
auto const sleAccount = ctx.view().peek(keylet::account(owner));
|
||||
if (!sleAccount)
|
||||
WritableAccountRoot wrappedAcct(owner, ctx.view());
|
||||
if (!wrappedAcct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Check reserve availability for new object creation
|
||||
{
|
||||
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
|
||||
auto const reserve = ctx.view().fees().accountReserve((*sleAccount)[sfOwnerCount] + 1);
|
||||
auto const balance = STAmount((*wrappedAcct)[sfBalance]).xrp();
|
||||
auto const reserve = ctx.view().fees().accountReserve((*wrappedAcct)[sfOwnerCount] + 1);
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -75,8 +75,8 @@ addSLE(ApplyContext& ctx, std::shared_ptr<SLE> const& sle, AccountID const& owne
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*sle)[sfOwnerNode] = *page;
|
||||
}
|
||||
adjustOwnerCount(ctx.view(), sleAccount, 1, ctx.journal);
|
||||
ctx.view().update(sleAccount);
|
||||
wrappedAcct.adjustOwnerCount(1, ctx.journal);
|
||||
wrappedAcct.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ TER
|
||||
DIDSet::doApply()
|
||||
{
|
||||
// Edit ledger object if it already exists
|
||||
Keylet const didKeylet = keylet::did(account_);
|
||||
Keylet const didKeylet = keylet::did(accountID_);
|
||||
if (auto const sleDID = ctx_.view().peek(didKeylet))
|
||||
{
|
||||
auto update = [&](auto const& sField) {
|
||||
@@ -116,7 +116,7 @@ DIDSet::doApply()
|
||||
|
||||
// Create new ledger object otherwise
|
||||
auto const sleDID = std::make_shared<SLE>(didKeylet);
|
||||
(*sleDID)[sfAccount] = account_;
|
||||
(*sleDID)[sfAccount] = accountID_;
|
||||
|
||||
auto set = [&](auto const& sField) {
|
||||
if (auto const field = ctx_.tx[~sField]; field && !field->empty())
|
||||
@@ -132,7 +132,7 @@ DIDSet::doApply()
|
||||
return tecEMPTY_DID;
|
||||
}
|
||||
|
||||
return addSLE(ctx_, sleDID, account_);
|
||||
return addSLE(ctx_, sleDID, accountID_);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -149,13 +149,13 @@ EscrowCancel::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||
WritableAccountRoot wrappedAcct(account, ctx_.view());
|
||||
STAmount const amount = slep->getFieldAmount(sfAmount);
|
||||
|
||||
// Transfer amount back to the owner
|
||||
if (isXRP(amount))
|
||||
{
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + amount;
|
||||
(*wrappedAcct)[sfBalance] = (*wrappedAcct)[sfBalance] + amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -163,13 +163,13 @@ EscrowCancel::doApply()
|
||||
return temDISABLED; // LCOV_EXCL_LINE
|
||||
|
||||
auto const issuer = amount.getIssuer();
|
||||
bool const createAsset = account == account_;
|
||||
bool const createAsset = account == accountID_;
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.view(),
|
||||
parityRate,
|
||||
slep,
|
||||
slep, // Bug: should be wrappedAcct, will be fixed by amendment in #6171
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
@@ -195,8 +195,8 @@ EscrowCancel::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
|
||||
ctx_.view().update(sle);
|
||||
wrappedAcct.adjustOwnerCount(-1, ctx_.journal);
|
||||
wrappedAcct.update();
|
||||
|
||||
// Remove escrow from ledger
|
||||
ctx_.view().erase(slep);
|
||||
|
||||
@@ -169,10 +169,10 @@ escrowCreatePreclaimHelper<Issue>(
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
AccountRoot const acctIssuer(issuer, ctx.view);
|
||||
if (!acctIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
if (!acctIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
@@ -304,15 +304,15 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
|
||||
AccountID const account{ctx.tx[sfAccount]};
|
||||
AccountID const dest{ctx.tx[sfDestination]};
|
||||
|
||||
auto const sled = ctx.view.read(keylet::account(dest));
|
||||
if (!sled)
|
||||
AccountRoot const acctDest(dest, ctx.view);
|
||||
if (!acctDest)
|
||||
return tecNO_DST;
|
||||
|
||||
// Pseudo-accounts cannot receive escrow. Note, this is not amendment-gated
|
||||
// because all writes to pseudo-account discriminator fields **are**
|
||||
// amendment gated, hence the behaviour of this check will always match the
|
||||
// currently active amendments.
|
||||
if (isPseudoAccount(sled))
|
||||
if (acctDest.isPseudoAccount())
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if (!isXRP(amount))
|
||||
@@ -389,16 +389,15 @@ EscrowCreate::doApply()
|
||||
if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
if (!account_)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Check reserve and funds availability
|
||||
STAmount const amount{ctx_.tx[sfAmount]};
|
||||
|
||||
auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1);
|
||||
auto const reserve = ctx_.view().fees().accountReserve((*account_)[sfOwnerCount] + 1);
|
||||
|
||||
auto const balance = sle->getFieldAmount(sfBalance).xrp();
|
||||
auto const balance = account_->getFieldAmount(sfBalance).xrp();
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
@@ -411,19 +410,19 @@ EscrowCreate::doApply()
|
||||
|
||||
// Check destination account
|
||||
{
|
||||
auto const sled = ctx_.view().read(keylet::account(ctx_.tx[sfDestination]));
|
||||
if (!sled)
|
||||
AccountRoot const acctDest(ctx_.tx[sfDestination], ctx_.view());
|
||||
if (!acctDest)
|
||||
return tecNO_DST; // LCOV_EXCL_LINE
|
||||
if ((((*sled)[sfFlags] & lsfRequireDestTag) != 0u) && !ctx_.tx[~sfDestinationTag])
|
||||
if (((acctDest->getFlags() & lsfRequireDestTag) != 0u) && !ctx_.tx[~sfDestinationTag])
|
||||
return tecDST_TAG_NEEDED;
|
||||
}
|
||||
|
||||
// Create escrow in ledger. Note that we we use the value from the
|
||||
// sequence or ticket. For more explanation see comments in SeqProxy.h.
|
||||
Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue());
|
||||
Keylet const escrowKeylet = keylet::escrow(accountID_, ctx_.tx.getSeqValue());
|
||||
auto const slep = std::make_shared<SLE>(escrowKeylet);
|
||||
(*slep)[sfAmount] = amount;
|
||||
(*slep)[sfAccount] = account_;
|
||||
(*slep)[sfAccount] = accountID_;
|
||||
(*slep)[~sfCondition] = ctx_.tx[~sfCondition];
|
||||
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
|
||||
(*slep)[sfDestination] = ctx_.tx[sfDestination];
|
||||
@@ -448,7 +447,7 @@ EscrowCreate::doApply()
|
||||
// Add escrow to sender's owner directory
|
||||
{
|
||||
auto page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(account_), escrowKeylet, describeOwnerDir(account_));
|
||||
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfOwnerNode] = *page;
|
||||
@@ -456,7 +455,7 @@ EscrowCreate::doApply()
|
||||
|
||||
// If it's not a self-send, add escrow to recipient's owner directory.
|
||||
AccountID const dest = ctx_.tx[sfDestination];
|
||||
if (dest != account_)
|
||||
if (dest != accountID_)
|
||||
{
|
||||
auto page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
|
||||
@@ -469,7 +468,7 @@ EscrowCreate::doApply()
|
||||
// track the total locked balance. For MPT, this isn't necessary because the
|
||||
// locked balance is already stored directly in the MPTokenIssuance object.
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
if (!isXRP(amount) && issuer != account_ && issuer != dest && !amount.holds<MPTIssue>())
|
||||
if (!isXRP(amount) && issuer != accountID_ && issuer != dest && !amount.holds<MPTIssue>())
|
||||
{
|
||||
auto page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
|
||||
@@ -481,13 +480,13 @@ EscrowCreate::doApply()
|
||||
// Deduct owner's balance
|
||||
if (isXRP(amount))
|
||||
{
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - amount;
|
||||
(*account_)[sfBalance] = (*account_)[sfBalance] - amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowLockApplyHelper<T>(ctx_.view(), issuer, account_, amount, j_);
|
||||
return escrowLockApplyHelper<T>(ctx_.view(), issuer, accountID_, amount, j_);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
@@ -497,8 +496,8 @@ EscrowCreate::doApply()
|
||||
}
|
||||
|
||||
// increment owner count
|
||||
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
|
||||
ctx_.view().update(sle);
|
||||
account_.adjustOwnerCount(1, ctx_.journal);
|
||||
account_.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -289,11 +289,11 @@ EscrowFinish::doApply()
|
||||
|
||||
// NOTE: Escrow payments cannot be used to fund accounts.
|
||||
AccountID const destID = (*slep)[sfDestination];
|
||||
auto const sled = ctx_.view().peek(keylet::account(destID));
|
||||
if (!sled)
|
||||
WritableAccountRoot dest(destID, ctx_.view());
|
||||
if (!dest.exists())
|
||||
return tecNO_DST;
|
||||
|
||||
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dest, ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
@@ -327,7 +327,7 @@ EscrowFinish::doApply()
|
||||
// Transfer amount to destination
|
||||
if (isXRP(amount))
|
||||
{
|
||||
(*sled)[sfBalance] = (*sled)[sfBalance] + amount;
|
||||
(*dest)[sfBalance] = (*dest)[sfBalance] + amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -338,13 +338,13 @@ EscrowFinish::doApply()
|
||||
? xrpl::Rate(slep->getFieldU32(sfTransferRate))
|
||||
: parityRate;
|
||||
auto const issuer = amount.getIssuer();
|
||||
bool const createAsset = destID == account_;
|
||||
bool const createAsset = destID == accountID_;
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.view(),
|
||||
lockedRate,
|
||||
sled,
|
||||
dest,
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
@@ -370,12 +370,12 @@ EscrowFinish::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
ctx_.view().update(sled);
|
||||
dest.update();
|
||||
|
||||
// Adjust source owner count
|
||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||
adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
|
||||
ctx_.view().update(sle);
|
||||
WritableAccountRoot wrappedAcct(account, ctx_.view());
|
||||
wrappedAcct.adjustOwnerCount(-1, ctx_.journal);
|
||||
wrappedAcct.update();
|
||||
|
||||
// Remove escrow from ledger
|
||||
ctx_.view().erase(slep);
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/tx/transactors/token/MPTokenAuthorize.h>
|
||||
|
||||
#include <variant>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <ValidIssueType T>
|
||||
@@ -19,7 +21,7 @@ TER
|
||||
escrowUnlockApplyHelper(
|
||||
ApplyView& view,
|
||||
Rate lockedRate,
|
||||
std::shared_ptr<SLE> const& sleDest,
|
||||
std::variant<std::shared_ptr<SLE>, WritableAccountRoot> dest,
|
||||
STAmount const& xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
@@ -33,7 +35,7 @@ inline TER
|
||||
escrowUnlockApplyHelper<Issue>(
|
||||
ApplyView& view,
|
||||
Rate lockedRate,
|
||||
std::shared_ptr<SLE> const& sleDest,
|
||||
std::variant<std::shared_ptr<SLE>, WritableAccountRoot> dest,
|
||||
STAmount const& xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
@@ -55,8 +57,14 @@ escrowUnlockApplyHelper<Issue>(
|
||||
|
||||
if (!view.exists(trustLineKey) && createAsset)
|
||||
{
|
||||
// For backwards compatibility: if dest is not WritableAccountRoot, return error
|
||||
if (!std::holds_alternative<WritableAccountRoot>(dest))
|
||||
return tefEXCEPTION;
|
||||
|
||||
auto& wrappedDest = std::get<WritableAccountRoot>(dest);
|
||||
|
||||
// Can the account cover the trust line's reserve?
|
||||
if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
|
||||
if (std::uint32_t const ownerCount = {wrappedDest->at(sfOwnerCount)};
|
||||
xrpBalance < view.fees().accountReserve(ownerCount + 1))
|
||||
{
|
||||
JLOG(journal.trace()) << "Trust line does not exist. "
|
||||
@@ -76,9 +84,9 @@ escrowUnlockApplyHelper<Issue>(
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
wrappedDest, // Account to add to
|
||||
false, // authorize account
|
||||
(sleDest->getFlags() & lsfDefaultRipple) == 0,
|
||||
(wrappedDest->getFlags() & lsfDefaultRipple) == 0,
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
@@ -92,7 +100,7 @@ escrowUnlockApplyHelper<Issue>(
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
view.update(sleDest);
|
||||
wrappedDest.update();
|
||||
}
|
||||
|
||||
if (!view.exists(trustLineKey) && !receiverIssuer)
|
||||
@@ -163,7 +171,7 @@ inline TER
|
||||
escrowUnlockApplyHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
Rate lockedRate,
|
||||
std::shared_ptr<SLE> const& sleDest,
|
||||
std::variant<std::shared_ptr<SLE>, WritableAccountRoot> dest,
|
||||
STAmount const& xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
@@ -179,7 +187,13 @@ escrowUnlockApplyHelper<MPTIssue>(
|
||||
auto const issuanceKey = keylet::mptIssuance(mptID);
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer)
|
||||
{
|
||||
if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
|
||||
// For backwards compatibility: if dest is not WritableAccountRoot, return error
|
||||
if (!std::holds_alternative<WritableAccountRoot>(dest))
|
||||
return tefEXCEPTION;
|
||||
|
||||
auto& wrappedDest = std::get<WritableAccountRoot>(dest);
|
||||
|
||||
if (std::uint32_t const ownerCount = {wrappedDest->at(sfOwnerCount)};
|
||||
xrpBalance < view.fees().accountReserve(ownerCount + 1))
|
||||
{
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -192,7 +206,7 @@ escrowUnlockApplyHelper<MPTIssue>(
|
||||
}
|
||||
|
||||
// update owner count.
|
||||
adjustOwnerCount(view, sleDest, 1, journal);
|
||||
wrappedDest.adjustOwnerCount(1, journal);
|
||||
}
|
||||
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer)
|
||||
|
||||
@@ -1900,4 +1900,68 @@ loanMakePayment(
|
||||
"xrpl::loanMakePayment : fee paid is valid");
|
||||
return totalParts;
|
||||
}
|
||||
|
||||
// An owner count cannot be negative. If adjustment would cause a negative
|
||||
// owner count, clamp the owner count at 0. Similarly for overflow. This
|
||||
// adjustment allows the ownerCount to be adjusted up or down in multiple steps.
|
||||
// If id != std::nullopt, then do error reporting.
|
||||
//
|
||||
// Returns adjusted owner count.
|
||||
static std::uint32_t
|
||||
confineOwnerCount(
|
||||
std::uint32_t current,
|
||||
std::int32_t adjustment,
|
||||
std::optional<AccountID> const& id = std::nullopt,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
{
|
||||
std::uint32_t adjusted{current + adjustment};
|
||||
if (adjustment > 0)
|
||||
{
|
||||
// Overflow is well defined on unsigned
|
||||
if (adjusted < current)
|
||||
{
|
||||
if (id)
|
||||
{
|
||||
JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!";
|
||||
}
|
||||
adjusted = std::numeric_limits<std::uint32_t>::max();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Underflow is well defined on unsigned
|
||||
if (adjusted > current)
|
||||
{
|
||||
if (id)
|
||||
{
|
||||
JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!";
|
||||
}
|
||||
adjusted = 0;
|
||||
XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set");
|
||||
}
|
||||
}
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
void
|
||||
adjustOwnerCount(
|
||||
std::shared_ptr<SLE> const& sle,
|
||||
ApplyView& view,
|
||||
std::int32_t amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
// This function is only used for LoanBrokers, so assert that
|
||||
// AccountRoot should use WritableAccountRoot.adjustOwnerCount instead
|
||||
XRPL_ASSERT(sle->getType() == ltLOAN_BROKER, "xrpl::adjustOwnerCount : sle is loan broker");
|
||||
if (!sle)
|
||||
return;
|
||||
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 adjusted = confineOwnerCount(current, amount, id, j);
|
||||
view.adjustOwnerCountHook(id, current, adjusted);
|
||||
sle->at(sfOwnerCount) = adjusted;
|
||||
view.update(sle);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h>
|
||||
//
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/STTakesAsset.h>
|
||||
#include <xrpl/tx/transactors/lending/LendingHelpers.h>
|
||||
@@ -81,16 +82,16 @@ determineBrokerID(ReadView const& view, STTx const& tx)
|
||||
// Thus, Amount.issuer _should_ be the loan broker's
|
||||
// pseudo-account, but we don't know yet whether it is.
|
||||
auto const maybePseudo = dstAmount->getIssuer();
|
||||
auto const sle = view.read(keylet::account(maybePseudo));
|
||||
AccountRoot const acct(maybePseudo, view);
|
||||
|
||||
// If the account was not found, the transaction can't go further.
|
||||
if (!sle)
|
||||
if (!acct)
|
||||
return Unexpected{tecNO_ENTRY};
|
||||
|
||||
// If the account was found, and has a LoanBrokerID (and therefore
|
||||
// is a pseudo-account), that's the
|
||||
// answer we need.
|
||||
if (auto const brokerID = sle->at(~sfLoanBrokerID))
|
||||
if (auto const brokerID = acct->at(~sfLoanBrokerID))
|
||||
return *brokerID;
|
||||
|
||||
// If the account does not have a LoanBrokerID, the transaction
|
||||
@@ -273,8 +274,8 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
|
||||
return tecINTERNAL; // tecINSUFFICIENT_FUNDS; LCOV_EXCL_LINE
|
||||
|
||||
// Check if the vault asset issuer has the correct flags
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(vaultAsset.getIssuer()));
|
||||
if (!sleIssuer)
|
||||
AccountRoot const acctIssuer(vaultAsset.getIssuer(), ctx.view);
|
||||
if (!acctIssuer)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(ctx.j.fatal()) << "Issuer account does not exist.";
|
||||
@@ -283,7 +284,7 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
return std::visit(
|
||||
[&]<typename T>(T const&) { return preclaimHelper<T>(ctx, *sleIssuer, clawAmount); },
|
||||
[&]<typename T>(T const&) { return preclaimHelper<T>(ctx, *acctIssuer.sle(), clawAmount); },
|
||||
vaultAsset.value());
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,8 @@ LoanBrokerCoverDeposit::doApply()
|
||||
auto const brokerPseudoID = broker->at(sfAccount);
|
||||
|
||||
// Transfer assets from depositor to pseudo-account.
|
||||
if (auto ter = accountSend(view(), account_, brokerPseudoID, amount, j_, WaiveTransferFee::Yes))
|
||||
if (auto ter =
|
||||
accountSend(view(), accountID_, brokerPseudoID, amount, j_, WaiveTransferFee::Yes))
|
||||
return ter;
|
||||
|
||||
// Increase the LoanBroker's CoverAvailable by Amount
|
||||
|
||||
@@ -149,7 +149,7 @@ LoanBrokerCoverWithdraw::doApply()
|
||||
|
||||
auto const brokerID = tx[sfLoanBrokerID];
|
||||
auto const amount = tx[sfAmount];
|
||||
auto const dstAcct = tx[~sfDestination].value_or(account_);
|
||||
auto const dstAcct = tx[~sfDestination].value_or(accountID_);
|
||||
|
||||
auto broker = view().peek(keylet::loanbroker(brokerID));
|
||||
if (!broker)
|
||||
@@ -169,7 +169,7 @@ LoanBrokerCoverWithdraw::doApply()
|
||||
|
||||
associateAsset(*broker, vaultAsset);
|
||||
|
||||
return doWithdraw(view(), tx, account_, dstAcct, brokerPseudoID, preFeeBalance_, amount, j_);
|
||||
return doWithdraw(view(), tx, accountID_, dstAcct, brokerPseudoID, preFeeBalance_, amount, j_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -115,7 +115,7 @@ LoanBrokerDelete::doApply()
|
||||
auto const brokerPseudoID = broker->at(sfAccount);
|
||||
|
||||
if (!view().dirRemove(
|
||||
keylet::ownerDir(account_), broker->at(sfOwnerNode), broker->key(), false))
|
||||
keylet::ownerDir(accountID_), broker->at(sfOwnerNode), broker->key(), false))
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
@@ -128,25 +128,25 @@ LoanBrokerDelete::doApply()
|
||||
{
|
||||
auto const coverAvailable = STAmount{vaultAsset, broker->at(sfCoverAvailable)};
|
||||
if (auto const ter = accountSend(
|
||||
view(), brokerPseudoID, account_, coverAvailable, j_, WaiveTransferFee::Yes))
|
||||
view(), brokerPseudoID, accountID_, coverAvailable, j_, WaiveTransferFee::Yes))
|
||||
return ter;
|
||||
}
|
||||
|
||||
if (auto ter = removeEmptyHolding(view(), brokerPseudoID, vaultAsset, j_))
|
||||
return ter;
|
||||
|
||||
auto brokerPseudoSLE = view().peek(keylet::account(brokerPseudoID));
|
||||
if (!brokerPseudoSLE)
|
||||
WritableAccountRoot brokerPseudo(brokerPseudoID, view());
|
||||
if (!brokerPseudo)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
// Making the payment and removing the empty holding should have deleted any
|
||||
// obligations associated with the broker or broker pseudo-account.
|
||||
if (*brokerPseudoSLE->at(sfBalance))
|
||||
if (*brokerPseudo->at(sfBalance))
|
||||
{
|
||||
JLOG(j_.warn()) << "LoanBrokerDelete: Pseudo-account has a balance";
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (brokerPseudoSLE->at(sfOwnerCount) != 0)
|
||||
if (brokerPseudo->at(sfOwnerCount) != 0)
|
||||
{
|
||||
JLOG(j_.warn()) << "LoanBrokerDelete: Pseudo-account still owns objects";
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
@@ -157,18 +157,18 @@ LoanBrokerDelete::doApply()
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
view().erase(brokerPseudoSLE);
|
||||
brokerPseudo.erase();
|
||||
|
||||
view().erase(broker);
|
||||
|
||||
{
|
||||
auto owner = view().peek(keylet::account(account_));
|
||||
if (!owner)
|
||||
WritableAccountRoot wrappedOwner(accountID_, view());
|
||||
if (!wrappedOwner)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
// Decreases the owner count by two: one for the LoanBroker object, and
|
||||
// one for the pseudo-account.
|
||||
adjustOwnerCount(view(), owner, -2, j_);
|
||||
wrappedOwner.adjustOwnerCount(-2, j_);
|
||||
}
|
||||
|
||||
associateAsset(*broker, vaultAsset);
|
||||
|
||||
@@ -202,7 +202,7 @@ LoanBrokerSet::doApply()
|
||||
auto const vaultAsset = sleVault->at(sfAsset);
|
||||
auto const sequence = tx.getSeqValue();
|
||||
|
||||
auto owner = view.peek(keylet::account(account_));
|
||||
WritableAccountRoot owner(accountID_, view);
|
||||
if (!owner)
|
||||
{
|
||||
// This should be impossible
|
||||
@@ -211,16 +211,16 @@ LoanBrokerSet::doApply()
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto broker = std::make_shared<SLE>(keylet::loanbroker(account_, sequence));
|
||||
auto broker = std::make_shared<SLE>(keylet::loanbroker(accountID_, sequence));
|
||||
|
||||
if (auto const ter = dirLink(view, account_, broker))
|
||||
if (auto const ter = dirLink(view, accountID_, broker))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
if (auto const ter = dirLink(view, vaultPseudoID, broker, sfVaultNode))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
// Increases the owner count by two: one for the LoanBroker object, and
|
||||
// one for the pseudo-account.
|
||||
adjustOwnerCount(view, owner, 2, j_);
|
||||
owner.adjustOwnerCount(2, j_);
|
||||
auto const ownerCount = owner->at(sfOwnerCount);
|
||||
if (preFeeBalance_ < view.fees().accountReserve(ownerCount))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -237,7 +237,7 @@ LoanBrokerSet::doApply()
|
||||
// Initialize data fields:
|
||||
broker->at(sfSequence) = sequence;
|
||||
broker->at(sfVaultID) = vaultID;
|
||||
broker->at(sfOwner) = account_;
|
||||
broker->at(sfOwner) = accountID_;
|
||||
broker->at(sfAccount) = pseudoId;
|
||||
// The LoanSequence indexes loans created by this broker, starting at 1
|
||||
broker->at(sfLoanSequence) = 1;
|
||||
|
||||
@@ -68,8 +68,8 @@ LoanDelete::doApply()
|
||||
if (!loanSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const borrower = loanSle->at(sfBorrower);
|
||||
auto const borrowerSle = view.peek(keylet::account(borrower));
|
||||
if (!borrowerSle)
|
||||
WritableAccountRoot wrappedBorrower(borrower, view);
|
||||
if (!wrappedBorrower)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const brokerID = loanSle->at(sfLoanBrokerID);
|
||||
@@ -97,7 +97,7 @@ LoanDelete::doApply()
|
||||
// Decrement the LoanBroker's owner count.
|
||||
// The broker's owner count is solely for the number of outstanding loans,
|
||||
// and is distinct from the broker's pseudo-account's owner count
|
||||
adjustOwnerCount(view, brokerSle, -1, j_);
|
||||
adjustOwnerCount(brokerSle, view, -1, j_);
|
||||
// If there are no loans left, then any remaining debt must be forgiven,
|
||||
// because there is no other way to pay it back.
|
||||
if (brokerSle->at(sfOwnerCount) == 0)
|
||||
@@ -117,7 +117,7 @@ LoanDelete::doApply()
|
||||
}
|
||||
}
|
||||
// Decrement the borrower's owner count
|
||||
adjustOwnerCount(view, borrowerSle, -1, j_);
|
||||
wrappedBorrower.adjustOwnerCount(-1, j_);
|
||||
|
||||
// These associations shouldn't do anything, but do them just to be safe
|
||||
associateAsset(*loanSle, vaultAsset);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/tx/transactors/lending/LoanPay.h>
|
||||
//
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STTakesAsset.h>
|
||||
@@ -286,7 +287,7 @@ LoanPay::doApply()
|
||||
}();
|
||||
|
||||
auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
|
||||
auto const brokerPayeeSle = view.peek(keylet::account(brokerPayee));
|
||||
WritableAccountRoot brokerPayeeAcct(brokerPayee, view);
|
||||
if (!sendBrokerFeeToOwner)
|
||||
{
|
||||
// If we can't send the fee to the owner, and the pseudo-account is
|
||||
@@ -488,13 +489,13 @@ LoanPay::doApply()
|
||||
#if !NDEBUG
|
||||
auto const accountBalanceBefore = accountHolds(
|
||||
view,
|
||||
account_,
|
||||
accountID_,
|
||||
asset,
|
||||
fhIGNORE_FREEZE,
|
||||
ahIGNORE_AUTH,
|
||||
j_,
|
||||
SpendableHandling::shFULL_BALANCE);
|
||||
auto const vaultBalanceBefore = account_ == vaultPseudoAccount
|
||||
auto const vaultBalanceBefore = accountID_ == vaultPseudoAccount
|
||||
? STAmount{asset, 0}
|
||||
: accountHolds(
|
||||
view,
|
||||
@@ -504,7 +505,7 @@ LoanPay::doApply()
|
||||
ahIGNORE_AUTH,
|
||||
j_,
|
||||
SpendableHandling::shFULL_BALANCE);
|
||||
auto const brokerBalanceBefore = account_ == brokerPayee
|
||||
auto const brokerBalanceBefore = accountID_ == brokerPayee
|
||||
? STAmount{asset, 0}
|
||||
: accountHolds(
|
||||
view,
|
||||
@@ -524,11 +525,11 @@ LoanPay::doApply()
|
||||
|
||||
if (totalPaidToBroker != beast::zero)
|
||||
{
|
||||
if (brokerPayee == account_)
|
||||
if (brokerPayee == accountID_)
|
||||
{
|
||||
// The broker may have deleted their holding. Recreate it if needed
|
||||
if (auto const ter = addEmptyHolding(
|
||||
view, brokerPayee, brokerPayeeSle->at(sfBalance).value().xrp(), asset, j_);
|
||||
view, brokerPayee, brokerPayeeAcct->at(sfBalance).value().xrp(), asset, j_);
|
||||
ter && ter != tecDUPLICATE)
|
||||
{
|
||||
// ignore tecDUPLICATE. That means the holding already exists,
|
||||
@@ -542,7 +543,7 @@ LoanPay::doApply()
|
||||
|
||||
if (auto const ter = accountSendMulti(
|
||||
view,
|
||||
account_,
|
||||
accountID_,
|
||||
asset,
|
||||
{{vaultPseudoAccount, totalPaidToVaultRounded}, {brokerPayee, totalPaidToBroker}},
|
||||
j_,
|
||||
@@ -565,13 +566,13 @@ LoanPay::doApply()
|
||||
|
||||
auto const accountBalanceAfter = accountHolds(
|
||||
view,
|
||||
account_,
|
||||
accountID_,
|
||||
asset,
|
||||
fhIGNORE_FREEZE,
|
||||
ahIGNORE_AUTH,
|
||||
j_,
|
||||
SpendableHandling::shFULL_BALANCE);
|
||||
auto const vaultBalanceAfter = account_ == vaultPseudoAccount
|
||||
auto const vaultBalanceAfter = accountID_ == vaultPseudoAccount
|
||||
? STAmount{asset, 0}
|
||||
: accountHolds(
|
||||
view,
|
||||
@@ -581,7 +582,7 @@ LoanPay::doApply()
|
||||
ahIGNORE_AUTH,
|
||||
j_,
|
||||
SpendableHandling::shFULL_BALANCE);
|
||||
auto const brokerBalanceAfter = account_ == brokerPayee
|
||||
auto const brokerBalanceAfter = accountID_ == brokerPayee
|
||||
? STAmount{asset, 0}
|
||||
: accountHolds(
|
||||
view,
|
||||
@@ -600,7 +601,7 @@ LoanPay::doApply()
|
||||
XRPL_ASSERT_PARTS(
|
||||
accountBalanceAfter >= beast::zero, "xrpl::LoanPay::doApply", "positive account balance");
|
||||
XRPL_ASSERT_PARTS(
|
||||
accountBalanceAfter < accountBalanceBefore || account_ == asset.getIssuer(),
|
||||
accountBalanceAfter < accountBalanceBefore || accountID_ == asset.getIssuer(),
|
||||
"xrpl::LoanPay::doApply",
|
||||
"account balance decreased");
|
||||
XRPL_ASSERT_PARTS(
|
||||
|
||||
@@ -258,7 +258,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
auto const brokerPseudo = brokerSle->at(sfAccount);
|
||||
|
||||
auto const borrower = counterparty == brokerOwner ? account : counterparty;
|
||||
if (auto const borrowerSle = ctx.view.read(keylet::account(borrower)); !borrowerSle)
|
||||
if (auto const wrappedBorrower = AccountRoot(borrower, ctx.view); !wrappedBorrower)
|
||||
{
|
||||
// It may not be possible to hit this case, because it'll fail the
|
||||
// signature check with terNO_ACCOUNT.
|
||||
@@ -348,8 +348,8 @@ LoanSet::doApply()
|
||||
if (!brokerSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const brokerOwner = brokerSle->at(sfOwner);
|
||||
auto const brokerOwnerSle = view.peek(keylet::account(brokerOwner));
|
||||
if (!brokerOwnerSle)
|
||||
WritableAccountRoot brokerOwnerAcct(brokerOwner, view);
|
||||
if (!brokerOwnerAcct)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vaultSle = view.peek(keylet ::vault(brokerSle->at(sfVaultID)));
|
||||
@@ -359,16 +359,16 @@ LoanSet::doApply()
|
||||
Asset const vaultAsset = vaultSle->at(sfAsset);
|
||||
|
||||
auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner);
|
||||
auto const borrower = counterparty == brokerOwner ? account_ : counterparty;
|
||||
auto const borrowerSle = view.peek(keylet::account(borrower));
|
||||
if (!borrowerSle)
|
||||
auto const borrower = counterparty == brokerOwner ? accountID_ : counterparty;
|
||||
WritableAccountRoot wrappedBorrower(borrower, view);
|
||||
if (!wrappedBorrower)
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
auto const brokerPseudo = brokerSle->at(sfAccount);
|
||||
auto const brokerPseudoSle = view.peek(keylet::account(brokerPseudo));
|
||||
if (!brokerPseudoSle)
|
||||
WritableAccountRoot brokerPseudoAcct(brokerPseudo, view);
|
||||
if (!brokerPseudoAcct)
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
@@ -474,11 +474,11 @@ LoanSet::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
adjustOwnerCount(view, borrowerSle, 1, j_);
|
||||
wrappedBorrower.adjustOwnerCount(1, j_);
|
||||
{
|
||||
auto const ownerCount = borrowerSle->at(sfOwnerCount);
|
||||
auto const ownerCount = wrappedBorrower->at(sfOwnerCount);
|
||||
auto const balance =
|
||||
account_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp();
|
||||
accountID_ == borrower ? preFeeBalance_ : wrappedBorrower->at(sfBalance).value().xrp();
|
||||
if (balance < view.fees().accountReserve(ownerCount))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
@@ -490,11 +490,11 @@ LoanSet::doApply()
|
||||
// Create a holding for the borrower if one does not already exist.
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
borrower == account_ || borrower == counterparty,
|
||||
borrower == accountID_ || borrower == counterparty,
|
||||
"xrpl::LoanSet::doApply",
|
||||
"borrower signed transaction");
|
||||
if (auto const ter = addEmptyHolding(
|
||||
view, borrower, borrowerSle->at(sfBalance).value().xrp(), vaultAsset, j_);
|
||||
view, borrower, wrappedBorrower->at(sfBalance).value().xrp(), vaultAsset, j_);
|
||||
ter && ter != tecDUPLICATE)
|
||||
{
|
||||
// ignore tecDUPLICATE. That means the holding already exists, and
|
||||
@@ -512,12 +512,12 @@ LoanSet::doApply()
|
||||
// Create the holding if it doesn't already exist (necessary for MPTs).
|
||||
// The owner may have deleted their MPT / line at some point.
|
||||
XRPL_ASSERT_PARTS(
|
||||
brokerOwner == account_ || brokerOwner == counterparty,
|
||||
brokerOwner == accountID_ || brokerOwner == counterparty,
|
||||
"xrpl::LoanSet::doApply",
|
||||
"broker owner signed transaction");
|
||||
|
||||
if (auto const ter = addEmptyHolding(
|
||||
view, brokerOwner, brokerOwnerSle->at(sfBalance).value().xrp(), vaultAsset, j_);
|
||||
view, brokerOwner, brokerOwnerAcct->at(sfBalance).value().xrp(), vaultAsset, j_);
|
||||
ter && ter != tecDUPLICATE)
|
||||
{
|
||||
// ignore tecDUPLICATE. That means the holding already exists,
|
||||
@@ -595,7 +595,7 @@ LoanSet::doApply()
|
||||
adjustImpreciseNumber(brokerSle->at(sfDebtTotal), newDebtDelta, vaultAsset, vaultScale);
|
||||
// The broker's owner count is solely for the number of outstanding loans,
|
||||
// and is distinct from the broker's pseudo-account's owner count
|
||||
adjustOwnerCount(view, brokerSle, 1, j_);
|
||||
adjustOwnerCount(brokerSle, view, 1, j_);
|
||||
loanSequenceProxy += 1;
|
||||
// The sequence should be extremely unlikely to roll over, but fail if it
|
||||
// does
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
@@ -348,11 +349,11 @@ NFTokenAcceptOffer::transferNFToken(
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
auto const sleBuyer = view().read(keylet::account(buyer));
|
||||
if (!sleBuyer)
|
||||
AccountRoot const acctBuyer(buyer, view());
|
||||
if (!acctBuyer)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::uint32_t const buyerOwnerCountBefore = sleBuyer->getFieldU32(sfOwnerCount);
|
||||
std::uint32_t const buyerOwnerCountBefore = acctBuyer->getFieldU32(sfOwnerCount);
|
||||
|
||||
auto const insertRet = nft::insertToken(view(), buyer, std::move(tokenAndPage->token));
|
||||
|
||||
@@ -369,9 +370,9 @@ NFTokenAcceptOffer::transferNFToken(
|
||||
// the deduction of the potential offer price. A small caveat here is
|
||||
// that the balance has already deducted the transaction fee, meaning
|
||||
// that the reserve requirement is a few drops higher.
|
||||
auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance);
|
||||
auto const buyerBalance = acctBuyer->getFieldAmount(sfBalance);
|
||||
|
||||
auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
|
||||
auto const buyerOwnerCountAfter = acctBuyer->getFieldU32(sfOwnerCount);
|
||||
if (buyerOwnerCountAfter > buyerOwnerCountBefore)
|
||||
{
|
||||
if (auto const reserve = view().fees().accountReserve(buyerOwnerCountAfter);
|
||||
@@ -388,8 +389,8 @@ NFTokenAcceptOffer::acceptOffer(std::shared_ptr<SLE> const& offer)
|
||||
{
|
||||
bool const isSell = offer->isFlag(lsfSellNFToken);
|
||||
AccountID const owner = (*offer)[sfOwner];
|
||||
AccountID const& seller = isSell ? owner : account_;
|
||||
AccountID const& buyer = isSell ? account_ : owner;
|
||||
AccountID const& seller = isSell ? owner : accountID_;
|
||||
AccountID const& buyer = isSell ? accountID_ : owner;
|
||||
|
||||
auto const nftokenID = (*offer)[sfNFTokenID];
|
||||
|
||||
@@ -510,7 +511,7 @@ NFTokenAcceptOffer::doApply()
|
||||
// Send the broker the amount they requested.
|
||||
if (auto const cut = ctx_.tx[~sfNFTokenBrokerFee]; cut && cut.value() != beast::zero)
|
||||
{
|
||||
if (auto const r = pay(buyer, account_, cut.value()); !isTesSuccess(r))
|
||||
if (auto const r = pay(buyer, accountID_, cut.value()); !isTesSuccess(r))
|
||||
return r;
|
||||
|
||||
amount -= cut.value();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -34,9 +35,9 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (auto const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account)
|
||||
{
|
||||
if (auto const sle = ctx.view.read(keylet::account(issuer)); sle)
|
||||
if (AccountRoot const acctIssuer(issuer, ctx.view); acctIssuer)
|
||||
{
|
||||
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account)
|
||||
if (auto const minter = acctIssuer->at(~sfNFTokenMinter); minter != account)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
@@ -59,10 +60,10 @@ NFTokenBurn::doApply()
|
||||
if (!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (auto issuer = view().peek(keylet::account(nft::getIssuer(ctx_.tx[sfNFTokenID]))))
|
||||
if (WritableAccountRoot issuer(nft::getIssuer(ctx_.tx[sfNFTokenID]), view()); issuer)
|
||||
{
|
||||
(*issuer)[~sfBurnedNFTokens] = (*issuer)[~sfBurnedNFTokens].value_or(0) + 1;
|
||||
view().update(issuer);
|
||||
issuer.update();
|
||||
}
|
||||
|
||||
// Delete up to 500 offers in total.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/InnerObjectFormats.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
@@ -166,12 +167,12 @@ NFTokenMint::preclaim(PreclaimContext const& ctx)
|
||||
// transaction. Check that and verify that this is allowed:
|
||||
if (auto issuer = ctx.tx[~sfIssuer])
|
||||
{
|
||||
auto const sle = ctx.view.read(keylet::account(*issuer));
|
||||
AccountRoot const acctIssuer(*issuer, ctx.view);
|
||||
|
||||
if (!sle)
|
||||
if (!acctIssuer)
|
||||
return tecNO_ISSUER;
|
||||
|
||||
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != ctx.tx[sfAccount])
|
||||
if (auto const minter = acctIssuer->at(~sfNFTokenMinter); minter != ctx.tx[sfAccount])
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
@@ -202,11 +203,11 @@ NFTokenMint::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
NFTokenMint::doApply()
|
||||
{
|
||||
auto const issuer = ctx_.tx[~sfIssuer].value_or(account_);
|
||||
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
|
||||
|
||||
auto const tokenSeq = [this, &issuer]() -> Expected<std::uint32_t, TER> {
|
||||
auto const root = view().peek(keylet::account(issuer));
|
||||
if (root == nullptr)
|
||||
WritableAccountRoot root(issuer, view());
|
||||
if (!root)
|
||||
{
|
||||
// Should not happen. Checked in preclaim.
|
||||
return Unexpected(tecNO_ISSUER);
|
||||
@@ -251,7 +252,7 @@ NFTokenMint::doApply()
|
||||
if (tokenSeq + 1u == 0u || tokenSeq < offset)
|
||||
return Unexpected(tecMAX_SEQUENCE_REACHED);
|
||||
|
||||
ctx_.view().update(root);
|
||||
root.update();
|
||||
return tokenSeq;
|
||||
}();
|
||||
|
||||
@@ -259,7 +260,7 @@ NFTokenMint::doApply()
|
||||
return (tokenSeq.error());
|
||||
|
||||
std::uint32_t const ownerCountBefore =
|
||||
view().read(keylet::account(account_))->getFieldU32(sfOwnerCount);
|
||||
AccountRoot(accountID_, view())->getFieldU32(sfOwnerCount);
|
||||
|
||||
// Assemble the new NFToken.
|
||||
SOTemplate const* nfTokenTemplate =
|
||||
@@ -285,7 +286,7 @@ NFTokenMint::doApply()
|
||||
object.setFieldVL(sfURI, *uri);
|
||||
});
|
||||
|
||||
if (TER const ret = nft::insertToken(ctx_.view(), account_, std::move(newToken));
|
||||
if (TER const ret = nft::insertToken(ctx_.view(), accountID_, std::move(newToken));
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
@@ -312,8 +313,7 @@ NFTokenMint::doApply()
|
||||
// allows NFTs to be added to the page (and burn fees) without
|
||||
// requiring the reserve to be met each time. The reserve is
|
||||
// only managed when a new NFT page or sell offer is added.
|
||||
if (auto const ownerCountAfter =
|
||||
view().read(keylet::account(account_))->getFieldU32(sfOwnerCount);
|
||||
if (auto const ownerCountAfter = AccountRoot(accountID_, view())->getFieldU32(sfOwnerCount);
|
||||
ownerCountAfter > ownerCountBefore)
|
||||
{
|
||||
if (auto const reserve = view().fees().accountReserve(ownerCountAfter);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/nft/NFTokenModify.h>
|
||||
@@ -36,10 +37,10 @@ NFTokenModify::preclaim(PreclaimContext const& ctx)
|
||||
// Verify permissions for the issuer
|
||||
if (AccountID const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account)
|
||||
{
|
||||
auto const sle = ctx.view.read(keylet::account(issuer));
|
||||
if (!sle)
|
||||
AccountRoot const acctIssuer(issuer, ctx.view);
|
||||
if (!acctIssuer)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account)
|
||||
if (auto const minter = acctIssuer->at(~sfNFTokenMinter); minter != account)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,11 +247,8 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft)
|
||||
// the NFT.
|
||||
std::shared_ptr<SLE> page =
|
||||
getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) {
|
||||
adjustOwnerCount(
|
||||
view,
|
||||
view.peek(keylet::account(owner)),
|
||||
1,
|
||||
beast::Journal{beast::Journal::getNullSink()});
|
||||
WritableAccountRoot wrappedOwner(owner, view);
|
||||
wrappedOwner.adjustOwnerCount(1, beast::Journal{beast::Journal::getNullSink()});
|
||||
});
|
||||
|
||||
if (!page)
|
||||
@@ -409,11 +406,8 @@ removeToken(
|
||||
|
||||
if (cnt != 0)
|
||||
{
|
||||
adjustOwnerCount(
|
||||
view,
|
||||
view.peek(keylet::account(owner)),
|
||||
cnt,
|
||||
beast::Journal{beast::Journal::getNullSink()});
|
||||
WritableAccountRoot wrappedOwner(owner, view);
|
||||
wrappedOwner.adjustOwnerCount(cnt, beast::Journal{beast::Journal::getNullSink()});
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
@@ -448,11 +442,8 @@ removeToken(
|
||||
curr->makeFieldAbsent(sfPreviousPageMin);
|
||||
}
|
||||
|
||||
adjustOwnerCount(
|
||||
view,
|
||||
view.peek(keylet::account(owner)),
|
||||
-1,
|
||||
beast::Journal{beast::Journal::getNullSink()});
|
||||
WritableAccountRoot wrappedOwner(owner, view);
|
||||
wrappedOwner.adjustOwnerCount(-1, beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
view.update(curr);
|
||||
view.erase(prev);
|
||||
@@ -507,11 +498,8 @@ removeToken(
|
||||
view.peek(Keylet(ltNFTOKEN_PAGE, next->key()))))
|
||||
cnt++;
|
||||
|
||||
adjustOwnerCount(
|
||||
view,
|
||||
view.peek(keylet::account(owner)),
|
||||
-1 * cnt,
|
||||
beast::Journal{beast::Journal::getNullSink()});
|
||||
WritableAccountRoot wrappedOwner(owner, view);
|
||||
wrappedOwner.adjustOwnerCount(-1 * cnt, beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -654,8 +642,8 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr<SLE> const& offer)
|
||||
false))
|
||||
return false;
|
||||
|
||||
adjustOwnerCount(
|
||||
view, view.peek(keylet::account(owner)), -1, beast::Journal{beast::Journal::getNullSink()});
|
||||
WritableAccountRoot wrappedOwner(owner, view);
|
||||
wrappedOwner.adjustOwnerCount(-1, beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
view.erase(offer);
|
||||
return true;
|
||||
@@ -868,10 +856,10 @@ tokenOfferCreatePreclaim(
|
||||
|
||||
if (nftIssuer != acctID && ((nftFlags & nft::flagTransferable) == 0))
|
||||
{
|
||||
auto const root = view.read(keylet::account(nftIssuer));
|
||||
XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account");
|
||||
AccountRoot const acctRoot(nftIssuer, view);
|
||||
XRPL_ASSERT(acctRoot, "xrpl::nft::tokenOfferCreatePreclaim : non-null account");
|
||||
|
||||
if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID)
|
||||
if (auto minter = acctRoot->at(~sfNFTokenMinter); minter != acctID)
|
||||
return tefNFTOKEN_IS_NOT_TRANSFERABLE;
|
||||
}
|
||||
|
||||
@@ -893,26 +881,26 @@ tokenOfferCreatePreclaim(
|
||||
{
|
||||
// If a destination is specified, the destination must already be in
|
||||
// the ledger.
|
||||
auto const sleDst = view.read(keylet::account(*dest));
|
||||
AccountRoot const acctDst(*dest, view);
|
||||
|
||||
if (!sleDst)
|
||||
if (!acctDst)
|
||||
return tecNO_DST;
|
||||
|
||||
// check if the destination has disallowed incoming offers
|
||||
if ((sleDst->getFlags() & lsfDisallowIncomingNFTokenOffer) != 0u)
|
||||
if ((acctDst->getFlags() & lsfDisallowIncomingNFTokenOffer) != 0u)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
if (owner)
|
||||
{
|
||||
auto const sleOwner = view.read(keylet::account(*owner));
|
||||
AccountRoot const acctOwner(*owner, view);
|
||||
|
||||
// defensively check
|
||||
// it should not be possible to specify owner that doesn't exist
|
||||
if (!sleOwner)
|
||||
if (!acctOwner)
|
||||
return tecNO_TARGET;
|
||||
|
||||
if ((sleOwner->getFlags() & lsfDisallowIncomingNFTokenOffer) != 0u)
|
||||
if ((acctOwner->getFlags() & lsfDisallowIncomingNFTokenOffer) != 0u)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
@@ -944,9 +932,8 @@ tokenOfferCreateApply(
|
||||
beast::Journal j,
|
||||
std::uint32_t txFlags)
|
||||
{
|
||||
Keylet const acctKeylet = keylet::account(acctID);
|
||||
if (auto const acct = view.read(acctKeylet);
|
||||
priorBalance < view.fees().accountReserve((*acct)[sfOwnerCount] + 1))
|
||||
AccountRoot const acctRoot(acctID, view);
|
||||
if (priorBalance < view.fees().accountReserve(acctRoot->getFieldU32(sfOwnerCount) + 1))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto const offerID = keylet::nftoffer(acctID, seqProxy.value());
|
||||
@@ -998,7 +985,8 @@ tokenOfferCreateApply(
|
||||
}
|
||||
|
||||
// Update owner count.
|
||||
adjustOwnerCount(view, view.peek(acctKeylet), 1, j);
|
||||
WritableAccountRoot wrappedOwner(acctID, view);
|
||||
wrappedOwner.adjustOwnerCount(1, j);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -1015,8 +1003,8 @@ checkTrustlineAuthorized(
|
||||
|
||||
if (view.rules().enabled(fixEnforceNFTokenTrustlineV2))
|
||||
{
|
||||
auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
if (!issuerAccount)
|
||||
AccountRoot const acctIssuer(issue.account, view);
|
||||
if (!acctIssuer)
|
||||
{
|
||||
JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't "
|
||||
"receive IOUs from non-existent issuer: "
|
||||
@@ -1033,7 +1021,7 @@ checkTrustlineAuthorized(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
if (issuerAccount->isFlag(lsfRequireAuth))
|
||||
if (acctIssuer->isFlag(lsfRequireAuth))
|
||||
{
|
||||
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
|
||||
|
||||
@@ -1067,8 +1055,8 @@ checkTrustlineDeepFrozen(
|
||||
|
||||
if (view.rules().enabled(featureDeepFreeze))
|
||||
{
|
||||
auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
if (!issuerAccount)
|
||||
AccountRoot const acctIssuer(issue.account, view);
|
||||
if (!acctIssuer)
|
||||
{
|
||||
JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't "
|
||||
"receive IOUs from non-existent issuer: "
|
||||
|
||||
@@ -56,13 +56,13 @@ OracleDelete::deleteOracle(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(account));
|
||||
if (!sleOwner)
|
||||
WritableAccountRoot wrappedOwner(account, view);
|
||||
if (!wrappedOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const count = sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1;
|
||||
|
||||
adjustOwnerCount(view, sleOwner, count, j);
|
||||
wrappedOwner.adjustOwnerCount(count, j);
|
||||
|
||||
view.erase(sle);
|
||||
|
||||
@@ -72,8 +72,8 @@ OracleDelete::deleteOracle(
|
||||
TER
|
||||
OracleDelete::doApply()
|
||||
{
|
||||
if (auto sle = ctx_.view().peek(keylet::oracle(account_, ctx_.tx[sfOracleDocumentID])))
|
||||
return deleteOracle(ctx_.view(), sle, account_, j_);
|
||||
if (auto sle = ctx_.view().peek(keylet::oracle(accountID_, ctx_.tx[sfOracleDocumentID])))
|
||||
return deleteOracle(ctx_.view(), sle, accountID_, j_);
|
||||
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ OracleSet::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
OracleSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleSetter = ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount)));
|
||||
if (!sleSetter)
|
||||
AccountRoot const acctSetter(ctx.tx.getAccountID(sfAccount), ctx.view);
|
||||
if (!acctSetter)
|
||||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||
|
||||
// lastUpdateTime must be within maxLastUpdateTimeDelta seconds
|
||||
@@ -150,8 +150,8 @@ OracleSet::preclaim(PreclaimContext const& ctx)
|
||||
return tecARRAY_TOO_LARGE;
|
||||
|
||||
auto const reserve =
|
||||
ctx.view.fees().accountReserve(sleSetter->getFieldU32(sfOwnerCount) + adjustReserve);
|
||||
auto const& balance = sleSetter->getFieldAmount(sfBalance);
|
||||
ctx.view.fees().accountReserve(acctSetter->getFieldU32(sfOwnerCount) + adjustReserve);
|
||||
auto const& balance = acctSetter->getFieldAmount(sfBalance);
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -162,9 +162,9 @@ OracleSet::preclaim(PreclaimContext const& ctx)
|
||||
static bool
|
||||
adjustOwnerCount(ApplyContext& ctx, int count)
|
||||
{
|
||||
if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount])))
|
||||
if (auto wrappedAccount = WritableAccountRoot(ctx.tx.getAccountID(sfAccount), ctx.view()))
|
||||
{
|
||||
adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal);
|
||||
wrappedAccount.adjustOwnerCount(count, ctx.journal);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ setPriceDataInnerObjTemplate(STObject& obj)
|
||||
TER
|
||||
OracleSet::doApply()
|
||||
{
|
||||
auto const oracleID = keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]);
|
||||
auto const oracleID = keylet::oracle(accountID_, ctx_.tx[sfOracleDocumentID]);
|
||||
|
||||
auto populatePriceData = [](STObject& priceData, STObject const& entry) {
|
||||
setPriceDataInnerObjTemplate(priceData);
|
||||
@@ -292,7 +292,7 @@ OracleSet::doApply()
|
||||
sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]);
|
||||
|
||||
auto page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(account_), sle->key(), describeOwnerDir(account_));
|
||||
keylet::ownerDir(accountID_), sle->key(), describeOwnerDir(accountID_));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -135,8 +135,8 @@ DepositPreauth::doApply()
|
||||
{
|
||||
if (ctx_.tx.isFieldPresent(sfAuthorize))
|
||||
{
|
||||
auto const sleOwner = view().peek(keylet::account(account_));
|
||||
if (!sleOwner)
|
||||
WritableAccountRoot wrappedOwner(accountID_, view());
|
||||
if (!wrappedOwner)
|
||||
return {tefINTERNAL};
|
||||
|
||||
// A preauth counts against the reserve of the issuing account, but we
|
||||
@@ -144,7 +144,7 @@ DepositPreauth::doApply()
|
||||
// reserve to pay fees.
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
|
||||
view().fees().accountReserve(wrappedOwner->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -153,15 +153,15 @@ DepositPreauth::doApply()
|
||||
// Preclaim already verified that the Preauth entry does not yet exist.
|
||||
// Create and populate the Preauth entry.
|
||||
AccountID const auth{ctx_.tx[sfAuthorize]};
|
||||
Keylet const preauthKeylet = keylet::depositPreauth(account_, auth);
|
||||
Keylet const preauthKeylet = keylet::depositPreauth(accountID_, auth);
|
||||
auto slePreauth = std::make_shared<SLE>(preauthKeylet);
|
||||
|
||||
slePreauth->setAccountID(sfAccount, account_);
|
||||
slePreauth->setAccountID(sfAccount, accountID_);
|
||||
slePreauth->setAccountID(sfAuthorize, auth);
|
||||
view().insert(slePreauth);
|
||||
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(account_), preauthKeylet, describeOwnerDir(account_));
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), preauthKeylet, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding DepositPreauth to owner directory "
|
||||
<< to_string(preauthKeylet.key) << ": " << (page ? "success" : "failure");
|
||||
@@ -172,18 +172,18 @@ DepositPreauth::doApply()
|
||||
slePreauth->setFieldU64(sfOwnerNode, *page);
|
||||
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
adjustOwnerCount(view(), sleOwner, 1, j_);
|
||||
wrappedOwner.adjustOwnerCount(1, j_);
|
||||
}
|
||||
else if (ctx_.tx.isFieldPresent(sfUnauthorize))
|
||||
{
|
||||
auto const preauth = keylet::depositPreauth(account_, ctx_.tx[sfUnauthorize]);
|
||||
auto const preauth = keylet::depositPreauth(accountID_, ctx_.tx[sfUnauthorize]);
|
||||
|
||||
return DepositPreauth::removeFromLedger(view(), preauth.key, j_);
|
||||
}
|
||||
else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
|
||||
{
|
||||
auto const sleOwner = view().peek(keylet::account(account_));
|
||||
if (!sleOwner)
|
||||
WritableAccountRoot wrappedOwner(accountID_, view());
|
||||
if (!wrappedOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// A preauth counts against the reserve of the issuing account, but we
|
||||
@@ -191,7 +191,7 @@ DepositPreauth::doApply()
|
||||
// reserve to pay fees.
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
|
||||
view().fees().accountReserve(wrappedOwner->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -211,18 +211,18 @@ DepositPreauth::doApply()
|
||||
sortedLE.push_back(std::move(cred));
|
||||
}
|
||||
|
||||
Keylet const preauthKey = keylet::depositPreauth(account_, sortedTX);
|
||||
Keylet const preauthKey = keylet::depositPreauth(accountID_, sortedTX);
|
||||
auto slePreauth = std::make_shared<SLE>(preauthKey);
|
||||
if (!slePreauth)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
slePreauth->setAccountID(sfAccount, account_);
|
||||
slePreauth->setAccountID(sfAccount, accountID_);
|
||||
slePreauth->peekFieldArray(sfAuthorizeCredentials) = std::move(sortedLE);
|
||||
|
||||
view().insert(slePreauth);
|
||||
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(account_), preauthKey, describeOwnerDir(account_));
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), preauthKey, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
@@ -233,12 +233,12 @@ DepositPreauth::doApply()
|
||||
slePreauth->setFieldU64(sfOwnerNode, *page);
|
||||
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
adjustOwnerCount(view(), sleOwner, 1, j_);
|
||||
wrappedOwner.adjustOwnerCount(1, j_);
|
||||
}
|
||||
else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
|
||||
{
|
||||
auto const preauthKey = keylet::depositPreauth(
|
||||
account_, credentials::makeSorted(ctx_.tx.getFieldArray(sfUnauthorizeCredentials)));
|
||||
accountID_, credentials::makeSorted(ctx_.tx.getFieldArray(sfUnauthorizeCredentials)));
|
||||
return DepositPreauth::removeFromLedger(view(), preauthKey.key, j_);
|
||||
}
|
||||
|
||||
@@ -267,11 +267,11 @@ DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, b
|
||||
}
|
||||
|
||||
// If we succeeded, update the DepositPreauth owner's reserve.
|
||||
auto const sleOwner = view.peek(keylet::account(account));
|
||||
if (!sleOwner)
|
||||
WritableAccountRoot wrappedOwner(account, view);
|
||||
if (!wrappedOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleOwner, -1, j);
|
||||
wrappedOwner.adjustOwnerCount(-1, j);
|
||||
|
||||
// Remove DepositPreauth from ledger.
|
||||
view.erase(slePreauth);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
@@ -275,10 +276,9 @@ Payment::preclaim(PreclaimContext const& ctx)
|
||||
AccountID const dstAccountID(ctx.tx[sfDestination]);
|
||||
STAmount const dstAmount(ctx.tx[sfAmount]);
|
||||
|
||||
auto const k = keylet::account(dstAccountID);
|
||||
auto const sleDst = ctx.view.read(k);
|
||||
AccountRoot const dstAcct(dstAccountID, ctx.view);
|
||||
|
||||
if (!sleDst)
|
||||
if (!dstAcct)
|
||||
{
|
||||
// Destination account does not exist.
|
||||
if (!dstAmount.native())
|
||||
@@ -314,7 +314,7 @@ Payment::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
}
|
||||
else if (
|
||||
((sleDst->getFlags() & lsfRequireDestTag) != 0u) &&
|
||||
((dstAcct->getFlags() & lsfRequireDestTag) != 0u) &&
|
||||
!ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
@@ -373,30 +373,29 @@ Payment::doApply()
|
||||
AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination));
|
||||
STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount));
|
||||
bool const mptDirect = dstAmount.holds<MPTIssue>();
|
||||
STAmount const maxSourceAmount = getMaxSourceAmount(account_, dstAmount, sendMax);
|
||||
STAmount const maxSourceAmount = getMaxSourceAmount(accountID_, dstAmount, sendMax);
|
||||
|
||||
JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText()
|
||||
<< " dstAmount=" << dstAmount.getFullText();
|
||||
|
||||
// Open a ledger for editing.
|
||||
auto const k = keylet::account(dstAccountID);
|
||||
SLE::pointer sleDst = view().peek(k);
|
||||
WritableAccountRoot dst(dstAccountID, view());
|
||||
|
||||
if (!sleDst)
|
||||
if (!dst)
|
||||
{
|
||||
// Create the account.
|
||||
sleDst = std::make_shared<SLE>(k);
|
||||
sleDst->setAccountID(sfAccount, dstAccountID);
|
||||
sleDst->setFieldU32(sfSequence, view().seq());
|
||||
dst.newSLE();
|
||||
dst->setAccountID(sfAccount, dstAccountID);
|
||||
dst->setFieldU32(sfSequence, view().seq());
|
||||
|
||||
view().insert(sleDst);
|
||||
dst.insert();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tell the engine that we are intending to change the destination
|
||||
// account. The source account gets always charged a fee so it's always
|
||||
// marked as modified.
|
||||
view().update(sleDst);
|
||||
dst.update();
|
||||
}
|
||||
|
||||
bool const ripple = (hasPaths || sendMax || !dstAmount.native()) && !mptDirect;
|
||||
@@ -411,8 +410,7 @@ Payment::doApply()
|
||||
// 1. If Account == Destination, or
|
||||
// 2. If Account is deposit preauthorized by destination.
|
||||
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dst, ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
@@ -431,10 +429,10 @@ Payment::doApply()
|
||||
maxSourceAmount,
|
||||
dstAmount,
|
||||
dstAccountID,
|
||||
account_,
|
||||
accountID_,
|
||||
ctx_.tx.getFieldPathSet(sfPaths),
|
||||
ctx_.tx[~sfDomainID],
|
||||
ctx_.registry,
|
||||
ctx_.registry.logs(),
|
||||
&rcInput);
|
||||
// VFALCO NOTE We might not need to apply, depending
|
||||
// on the TER. But always applying *should*
|
||||
@@ -471,18 +469,17 @@ Payment::doApply()
|
||||
JLOG(j_.trace()) << " dstAmount=" << dstAmount.getFullText();
|
||||
auto const& mptIssue = dstAmount.get<MPTIssue>();
|
||||
|
||||
if (auto const ter = requireAuth(view(), mptIssue, account_); !isTesSuccess(ter))
|
||||
if (auto const ter = requireAuth(view(), mptIssue, accountID_); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
if (auto const ter = requireAuth(view(), mptIssue, dstAccountID); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
if (auto const ter = canTransfer(view(), mptIssue, account_, dstAccountID);
|
||||
if (auto const ter = canTransfer(view(), mptIssue, accountID_, dstAccountID);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dst, ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
@@ -491,13 +488,13 @@ Payment::doApply()
|
||||
// Transfer rate
|
||||
Rate rate{QUALITY_ONE};
|
||||
// Payment between the holders
|
||||
if (account_ != issuer && dstAccountID != issuer)
|
||||
if (accountID_ != issuer && dstAccountID != issuer)
|
||||
{
|
||||
// If globally/individually locked then
|
||||
// - can't send between holders
|
||||
// - holder can send back to issuer
|
||||
// - issuer can send to holder
|
||||
if (isAnyFrozen(view(), {account_, dstAccountID}, mptIssue))
|
||||
if (isAnyFrozen(view(), {accountID_, dstAccountID}, mptIssue))
|
||||
return tecLOCKED;
|
||||
|
||||
// Get the rate for a payment between the holders.
|
||||
@@ -525,7 +522,7 @@ Payment::doApply()
|
||||
return tecPATH_PARTIAL;
|
||||
|
||||
PaymentSandbox pv(&view());
|
||||
auto res = accountSend(pv, account_, dstAccountID, amountDeliver, ctx_.journal);
|
||||
auto res = accountSend(pv, accountID_, dstAccountID, amountDeliver, ctx_.journal);
|
||||
if (isTesSuccess(res))
|
||||
{
|
||||
pv.apply(ctx_.rawView());
|
||||
@@ -548,13 +545,13 @@ Payment::doApply()
|
||||
|
||||
// Direct XRP payment.
|
||||
|
||||
auto const sleSrc = view().peek(keylet::account(account_));
|
||||
if (!sleSrc)
|
||||
WritableAccountRoot srcAcct(accountID_, view());
|
||||
if (!srcAcct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// ownerCount is the number of entries in this ledger for this
|
||||
// account that require a reserve.
|
||||
auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
|
||||
auto const ownerCount = srcAcct->getFieldU32(sfOwnerCount);
|
||||
|
||||
// This is the total reserve in drops.
|
||||
auto const reserve = view().fees().accountReserve(ownerCount);
|
||||
@@ -580,7 +577,7 @@ Payment::doApply()
|
||||
// transaction types. Note, this is not amendment-gated because all writes
|
||||
// to pseudo-account discriminator fields **are** amendment gated, hence the
|
||||
// behaviour of this check will always match the active amendments.
|
||||
if (isPseudoAccount(sleDst))
|
||||
if (dst.isPseudoAccount())
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// The source account does have enough money. Make sure the
|
||||
@@ -607,21 +604,20 @@ Payment::doApply()
|
||||
// Get the base reserve.
|
||||
XRPAmount const dstReserve{view().fees().reserve};
|
||||
|
||||
if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve)
|
||||
if (dstAmount > dstReserve || dst->getFieldAmount(sfBalance) > dstReserve)
|
||||
{
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dst, ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
}
|
||||
|
||||
// Do the arithmetic for the transfer and make the ledger change.
|
||||
sleSrc->setFieldAmount(sfBalance, sleSrc->getFieldAmount(sfBalance) - dstAmount);
|
||||
sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount);
|
||||
srcAcct->setFieldAmount(sfBalance, srcAcct->getFieldAmount(sfBalance) - dstAmount);
|
||||
dst->setFieldAmount(sfBalance, dst->getFieldAmount(sfBalance) + dstAmount);
|
||||
|
||||
// Re-arm the password change fee if we can and need to.
|
||||
if ((sleDst->getFlags() & lsfPasswordSpent) != 0u)
|
||||
sleDst->clearFlag(lsfPasswordSpent);
|
||||
if ((dst->getFlags() & lsfPasswordSpent) != 0u)
|
||||
dst->clearFlag(lsfPasswordSpent);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -108,7 +110,7 @@ PaymentChannelClaim::doApply()
|
||||
auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count();
|
||||
if ((cancelAfter && closeTime >= *cancelAfter) ||
|
||||
(curExpiration && closeTime >= *curExpiration))
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.getJournal("View"));
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View"));
|
||||
}
|
||||
|
||||
if (txAccount != src && txAccount != dst)
|
||||
@@ -139,12 +141,12 @@ PaymentChannelClaim::doApply()
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
|
||||
auto const sled = ctx_.view().peek(keylet::account(dst));
|
||||
if (!sled)
|
||||
WritableAccountRoot dstAcct(dst, ctx_.view());
|
||||
if (!dstAcct)
|
||||
return tecNO_DST;
|
||||
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), txAccount, AccountRoot(dst, ctx_.view()), ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
@@ -152,8 +154,8 @@ PaymentChannelClaim::doApply()
|
||||
XRPAmount const reqDelta = reqBalance - chanBalance;
|
||||
XRPL_ASSERT(
|
||||
reqDelta >= beast::zero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta");
|
||||
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
|
||||
ctx_.view().update(sled);
|
||||
(*dstAcct)[sfBalance] = (*dstAcct)[sfBalance] + reqDelta;
|
||||
dstAcct.update();
|
||||
ctx_.view().update(slep);
|
||||
}
|
||||
|
||||
@@ -169,7 +171,7 @@ PaymentChannelClaim::doApply()
|
||||
{
|
||||
// Channel will close immediately if dry or the receiver closes
|
||||
if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount])
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.getJournal("View"));
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View"));
|
||||
|
||||
auto const settleExpiration =
|
||||
ctx_.view().header().parentCloseTime.time_since_epoch().count() +
|
||||
|
||||
@@ -58,14 +58,14 @@ TER
|
||||
PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const account = ctx.tx[sfAccount];
|
||||
auto const sle = ctx.view.read(keylet::account(account));
|
||||
if (!sle)
|
||||
AccountRoot const acctSrc(account, ctx.view);
|
||||
if (!acctSrc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
// Check reserve and funds availability
|
||||
{
|
||||
auto const balance = (*sle)[sfBalance];
|
||||
auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1);
|
||||
auto const balance = acctSrc->at(sfBalance);
|
||||
auto const reserve = ctx.view.fees().accountReserve(acctSrc->getFieldU32(sfOwnerCount) + 1);
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -78,11 +78,11 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
{
|
||||
// Check destination account
|
||||
auto const sled = ctx.view.read(keylet::account(dst));
|
||||
if (!sled)
|
||||
AccountRoot const acctDst(dst, ctx.view);
|
||||
if (!acctDst)
|
||||
return tecNO_DST;
|
||||
|
||||
auto const flags = sled->getFlags();
|
||||
auto const flags = acctDst->getFlags();
|
||||
|
||||
// Check if they have disallowed incoming payment channels
|
||||
if ((flags & lsfDisallowIncomingPayChan) != 0u)
|
||||
@@ -97,7 +97,7 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
|
||||
// writes to pseudo-account discriminator fields **are** amendment
|
||||
// gated, hence the behaviour of this check will always match the
|
||||
// currently active amendments.
|
||||
if (isPseudoAccount(sled))
|
||||
if (acctDst.isPseudoAccount())
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
@@ -107,9 +107,8 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
PaymentChannelCreate::doApply()
|
||||
{
|
||||
auto const account = ctx_.tx[sfAccount];
|
||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||
if (!sle)
|
||||
WritableAccountRoot wrappedOwner(accountID_, ctx_.view());
|
||||
if (!wrappedOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (ctx_.view().rules().enabled(fixPayChanCancelAfter))
|
||||
@@ -125,14 +124,14 @@ PaymentChannelCreate::doApply()
|
||||
//
|
||||
// Note that we we use the value from the sequence or ticket as the
|
||||
// payChan sequence. For more explanation see comments in SeqProxy.h.
|
||||
Keylet const payChanKeylet = keylet::payChan(account, dst, ctx_.tx.getSeqValue());
|
||||
Keylet const payChanKeylet = keylet::payChan(accountID_, dst, ctx_.tx.getSeqValue());
|
||||
auto const slep = std::make_shared<SLE>(payChanKeylet);
|
||||
|
||||
// Funds held in this channel
|
||||
(*slep)[sfAmount] = ctx_.tx[sfAmount];
|
||||
// Amount channel has already paid
|
||||
(*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed();
|
||||
(*slep)[sfAccount] = account;
|
||||
(*slep)[sfAccount] = accountID_;
|
||||
(*slep)[sfDestination] = dst;
|
||||
(*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay];
|
||||
(*slep)[sfPublicKey] = ctx_.tx[sfPublicKey];
|
||||
@@ -149,7 +148,7 @@ PaymentChannelCreate::doApply()
|
||||
// Add PayChan to owner directory
|
||||
{
|
||||
auto const page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(account), payChanKeylet, describeOwnerDir(account));
|
||||
keylet::ownerDir(accountID_), payChanKeylet, describeOwnerDir(accountID_));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfOwnerNode] = *page;
|
||||
@@ -165,9 +164,9 @@ PaymentChannelCreate::doApply()
|
||||
}
|
||||
|
||||
// Deduct owner's balance, increment owner count
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
|
||||
ctx_.view().update(sle);
|
||||
(*wrappedOwner)[sfBalance] = (*wrappedOwner)[sfBalance] - ctx_.tx[sfAmount];
|
||||
wrappedOwner.adjustOwnerCount(1, ctx_.journal);
|
||||
wrappedOwner.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/tx/transactors/payment_channel/PaymentChannelFund.h>
|
||||
|
||||
@@ -38,7 +39,7 @@ PaymentChannelFund::doApply()
|
||||
auto const cancelAfter = (*slep)[~sfCancelAfter];
|
||||
auto const closeTime = ctx_.view().header().parentCloseTime.time_since_epoch().count();
|
||||
if ((cancelAfter && closeTime >= *cancelAfter) || (expiration && closeTime >= *expiration))
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.getJournal("View"));
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.journal("View"));
|
||||
}
|
||||
|
||||
if (src != txAccount)
|
||||
@@ -60,14 +61,14 @@ PaymentChannelFund::doApply()
|
||||
ctx_.view().update(slep);
|
||||
}
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(txAccount));
|
||||
if (!sle)
|
||||
WritableAccountRoot acct(txAccount, ctx_.view());
|
||||
if (!acct)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
// Check reserve and funds availability
|
||||
auto const balance = (*sle)[sfBalance];
|
||||
auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]);
|
||||
auto const balance = (*acct)[sfBalance];
|
||||
auto const reserve = ctx_.view().fees().accountReserve((*acct)[sfOwnerCount]);
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -77,7 +78,7 @@ PaymentChannelFund::doApply()
|
||||
}
|
||||
|
||||
// do not allow adding funds if dst does not exist
|
||||
if (AccountID const dst = (*slep)[sfDestination]; !ctx_.view().read(keylet::account(dst)))
|
||||
if (AccountID const dst = (*slep)[sfDestination]; !AccountRoot(dst, ctx_.view()))
|
||||
{
|
||||
return tecNO_DST;
|
||||
}
|
||||
@@ -85,8 +86,8 @@ PaymentChannelFund::doApply()
|
||||
(*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount];
|
||||
ctx_.view().update(slep);
|
||||
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
ctx_.view().update(sle);
|
||||
(*acct)[sfBalance] = (*acct)[sfBalance] - ctx_.tx[sfAmount];
|
||||
acct.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -41,15 +41,16 @@ closeChannel(
|
||||
}
|
||||
|
||||
// Transfer amount back to owner, decrement owner count
|
||||
auto const sle = view.peek(keylet::account(src));
|
||||
if (!sle)
|
||||
WritableAccountRoot wrappedOwner(src, view);
|
||||
if (!wrappedOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(
|
||||
(*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount");
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
adjustOwnerCount(view, sle, -1, j);
|
||||
view.update(sle);
|
||||
(*wrappedOwner)[sfBalance] =
|
||||
(*wrappedOwner)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
wrappedOwner.adjustOwnerCount(-1, j);
|
||||
wrappedOwner.update();
|
||||
|
||||
// Remove PayChan from ledger
|
||||
view.erase(slep);
|
||||
|
||||
@@ -44,7 +44,7 @@ PermissionedDomainDelete::doApply()
|
||||
auto const slePd = view().peek(keylet::permissionedDomain(ctx_.tx.at(sfDomainID)));
|
||||
auto const page = (*slePd)[sfOwnerNode];
|
||||
|
||||
if (!view().dirRemove(keylet::ownerDir(account_), page, slePd->key(), true))
|
||||
if (!view().dirRemove(keylet::ownerDir(accountID_), page, slePd->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete permissioned domain directory entry.";
|
||||
@@ -52,11 +52,11 @@ PermissionedDomainDelete::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const ownerSle = view().peek(keylet::account(account_));
|
||||
auto wrappedOwner = WritableAccountRoot(accountID_, view());
|
||||
XRPL_ASSERT(
|
||||
ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0,
|
||||
wrappedOwner && wrappedOwner->getFieldU32(sfOwnerCount) > 0,
|
||||
"xrpl::PermissionedDomainDelete::doApply : nonzero owner count");
|
||||
adjustOwnerCount(view(), ownerSle, -1, ctx_.journal);
|
||||
wrappedOwner.adjustOwnerCount(-1, ctx_.journal);
|
||||
view().erase(slePd);
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -64,8 +64,8 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
PermissionedDomainSet::doApply()
|
||||
{
|
||||
auto const ownerSle = view().peek(keylet::account(account_));
|
||||
if (!ownerSle)
|
||||
WritableAccountRoot wrappedOwner(accountID_, view());
|
||||
if (!wrappedOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sortedTxCredentials =
|
||||
@@ -92,26 +92,28 @@ PermissionedDomainSet::doApply()
|
||||
{
|
||||
// Create new permissioned domain.
|
||||
// Check reserve availability for new object creation
|
||||
auto const balance = STAmount((*ownerSle)[sfBalance]).xrp();
|
||||
auto const reserve = ctx_.view().fees().accountReserve((*ownerSle)[sfOwnerCount] + 1);
|
||||
auto const balance = STAmount((*wrappedOwner)[sfBalance]).xrp();
|
||||
auto const reserve = ctx_.view().fees().accountReserve((*wrappedOwner)[sfOwnerCount] + 1);
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
Keylet const pdKeylet =
|
||||
keylet::permissionedDomain(account_, ctx_.tx.getFieldU32(sfSequence));
|
||||
keylet::permissionedDomain(accountID_, ctx_.tx.getFieldU32(sfSequence));
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
if (!slePd)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
slePd->setAccountID(sfOwner, account_);
|
||||
slePd->setAccountID(sfOwner, accountID_);
|
||||
slePd->setFieldU32(sfSequence, ctx_.tx.getFieldU32(sfSequence));
|
||||
slePd->peekFieldArray(sfAcceptedCredentials) = std::move(sortedLE);
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(account_), pdKeylet, describeOwnerDir(account_));
|
||||
view().dirInsert(keylet::ownerDir(accountID_), pdKeylet, describeOwnerDir(accountID_));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
slePd->setFieldU64(sfOwnerNode, *page);
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
adjustOwnerCount(view(), ownerSle, 1, ctx_.journal);
|
||||
wrappedOwner.adjustOwnerCount(1, ctx_.journal);
|
||||
view().insert(slePd);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ Change::doApply()
|
||||
void
|
||||
Change::preCompute()
|
||||
{
|
||||
XRPL_ASSERT(account_ == beast::zero, "xrpl::Change::preCompute : zero account");
|
||||
XRPL_ASSERT(accountID_ == beast::zero, "xrpl::Change::preCompute : zero account");
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user