mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-18 04:30:21 +00:00
Compare commits
29 Commits
mvadari/ch
...
copilot/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b1e9f2a8a | ||
|
|
4b7a21bdfe | ||
|
|
f956135e29 | ||
|
|
3e4422df28 | ||
|
|
9f5bf51b31 | ||
|
|
9ccb7742ca | ||
|
|
336b9c101e | ||
|
|
5fdbedf6ac | ||
|
|
ce4490d793 | ||
|
|
ba2ff93d6e | ||
|
|
4714160052 | ||
|
|
5147825d61 | ||
|
|
719368ae25 | ||
|
|
3e372656d3 | ||
|
|
81964068a1 | ||
|
|
2e0ea38d7d | ||
|
|
aa1f84e226 | ||
|
|
ae7076c054 | ||
|
|
9a221d1291 | ||
|
|
5e6d8a4692 | ||
|
|
11c7d912f6 | ||
|
|
b7d6cdf713 | ||
|
|
193ddcbfac | ||
|
|
3a70d9dfba | ||
|
|
03e8a68670 | ||
|
|
28143d74af | ||
|
|
ff4c538a9f | ||
|
|
9fe94c47c3 | ||
|
|
3f307f8128 |
17
.clang-tidy
17
.clang-tidy
@@ -56,17 +56,32 @@ Checks: "-*,
|
||||
|
||||
readability-*,
|
||||
-readability-avoid-const-params-in-decls,
|
||||
-readability-avoid-unconditional-preprocessor-if,
|
||||
-readability-container-data-pointer,
|
||||
-readability-delete-null-pointer,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-function-size,
|
||||
-readability-identifier-length,
|
||||
-readability-inconsistent-declaration-parameter-name,
|
||||
-readability-isolate-declaration,
|
||||
-readability-magic-numbers,
|
||||
-readability-misplaced-array-index,
|
||||
-readability-named-parameter,
|
||||
-readability-operators-representation,
|
||||
-readability-qualified-auto,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-redundant-control-flow,
|
||||
-readability-redundant-function-ptr-dereference,
|
||||
-readability-redundant-preprocessor,
|
||||
-readability-redundant-smartptr-get,
|
||||
-readability-redundant-string-cstr,
|
||||
-readability-simplify-subscript-expr,
|
||||
-readability-static-accessed-through-instance,
|
||||
-readability-uppercase-literal-suffix
|
||||
-readability-string-compare,
|
||||
-readability-uniqueptr-delete-release,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-use-anyofallof,
|
||||
-readability-use-concise-preprocessor-directives
|
||||
"
|
||||
# ---
|
||||
# bugprone-narrowing-conversions, # This will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs
|
||||
|
||||
@@ -65,7 +65,6 @@ words:
|
||||
- Btrfs
|
||||
- Buildx
|
||||
- canonicality
|
||||
- canonicalised
|
||||
- changespq
|
||||
- checkme
|
||||
- choco
|
||||
@@ -73,7 +72,6 @@ words:
|
||||
- citardauq
|
||||
- clawback
|
||||
- clawbacks
|
||||
- clippy
|
||||
- cmaketoolchain
|
||||
- coeffs
|
||||
- coldwallet
|
||||
@@ -261,7 +259,6 @@ words:
|
||||
- rocksdb
|
||||
- Rohrs
|
||||
- roundings
|
||||
- rustc
|
||||
- sahyadri
|
||||
- Satoshi
|
||||
- scons
|
||||
|
||||
@@ -159,7 +159,6 @@ test.peerfinder > xrpl.protocol
|
||||
test.protocol > test.jtx
|
||||
test.protocol > test.unit_test
|
||||
test.protocol > xrpl.basics
|
||||
test.protocol > xrpld.core
|
||||
test.protocol > xrpl.json
|
||||
test.protocol > xrpl.protocol
|
||||
test.rpc > test.jtx
|
||||
|
||||
62
.github/scripts/strategy-matrix/generate.py
vendored
62
.github/scripts/strategy-matrix/generate.py
vendored
@@ -25,16 +25,24 @@ def get_cmake_args(build_type: str, extra_args: str) -> str:
|
||||
return " ".join(args)
|
||||
|
||||
|
||||
def runs_on_event(exclude_event_types: list[str], event: str | None) -> bool:
|
||||
"""Whether a config should run for the current event.
|
||||
|
||||
'exclude_event_types' is a list of GitHub event names (e.g.
|
||||
["pull_request"]) on which the config should NOT run; an empty list means
|
||||
the config runs on every event. When no event is given (event is None), no
|
||||
filtering is applied.
|
||||
"""
|
||||
if event is None:
|
||||
return True
|
||||
return event not in exclude_event_types
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Input types — shapes of the JSON config files
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Every config must declare 'minimal'. Minimal configs form the reduced matrix
|
||||
# built for pull requests by default; the full matrix adds the rest. Packaging
|
||||
# configs declare it too, but packaging is gated in the workflow, not by it.
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class LinuxConfig:
|
||||
"""One entry in linux.json's 'configs' or 'package_configs' arrays."""
|
||||
@@ -42,11 +50,13 @@ class LinuxConfig:
|
||||
compiler: list[str]
|
||||
build_type: list[str]
|
||||
arch: list[str]
|
||||
minimal: bool
|
||||
sanitizers: list[str] = dataclasses.field(default_factory=list)
|
||||
suffix: str = ""
|
||||
extra_cmake_args: str = ""
|
||||
image: str = "" # only used by package_configs entries
|
||||
# List of GitHub event names (e.g. "pull_request") on which this config
|
||||
# should NOT run. Empty means it runs on every event.
|
||||
exclude_event_types: list[str] = dataclasses.field(default_factory=list)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -79,9 +89,11 @@ class PlatformConfig:
|
||||
"""One entry in macos.json's or windows.json's 'configs' array."""
|
||||
|
||||
build_type: list[str]
|
||||
minimal: bool
|
||||
build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug)
|
||||
extra_cmake_args: str = ""
|
||||
# List of GitHub event names (e.g. "pull_request") on which this config
|
||||
# should NOT run. Empty means it runs on every event.
|
||||
exclude_event_types: list[str] = dataclasses.field(default_factory=list)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if isinstance(self.build_type, str):
|
||||
@@ -156,18 +168,20 @@ _ARCHS: dict[str, Architecture] = {
|
||||
}
|
||||
|
||||
|
||||
def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
def expand_linux_matrix(
|
||||
linux: LinuxFile, event: str | None = None
|
||||
) -> list[MatrixEntry]:
|
||||
"""Expand a LinuxFile into a flat list of matrix entries.
|
||||
|
||||
Each config entry is expanded over the cross-product of its
|
||||
compiler, build_type, sanitizers, and architecture lists. When 'minimal' is
|
||||
true, only configs flagged as minimal are included.
|
||||
compiler, build_type, sanitizers, and architecture lists. Configs that
|
||||
exclude the current event are skipped.
|
||||
"""
|
||||
entries: list[MatrixEntry] = []
|
||||
|
||||
for distro, configs in linux.configs.items():
|
||||
for cfg in configs:
|
||||
if minimal and not cfg.minimal:
|
||||
if not runs_on_event(cfg.exclude_event_types, event):
|
||||
continue
|
||||
# An empty sanitizers list means "one entry with no sanitizer".
|
||||
effective_sanitizers = cfg.sanitizers or [""]
|
||||
@@ -226,17 +240,19 @@ def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
|
||||
return entries
|
||||
|
||||
|
||||
def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]:
|
||||
def expand_platform_matrix(
|
||||
pf: PlatformFile, event: str | None = None
|
||||
) -> list[MatrixEntry]:
|
||||
"""Expand a PlatformFile (macOS or Windows) into matrix entries.
|
||||
|
||||
When 'minimal' is true, only configs flagged as minimal are included.
|
||||
Configs that exclude the current event are skipped.
|
||||
"""
|
||||
platform_name, arch = pf.platform.split("/")
|
||||
is_windows = platform_name == "windows"
|
||||
|
||||
entries: list[MatrixEntry] = []
|
||||
for cfg in pf.configs:
|
||||
if minimal and not cfg.minimal:
|
||||
if not runs_on_event(cfg.exclude_event_types, event):
|
||||
continue
|
||||
for build_type in cfg.build_type:
|
||||
entries.append(
|
||||
@@ -276,12 +292,12 @@ if __name__ == "__main__":
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--minimal",
|
||||
help="Emit only the minimal matrix (the configs flagged 'minimal'), "
|
||||
"used for pull requests by default. If omitted, the full matrix is "
|
||||
"emitted.",
|
||||
action="store_true",
|
||||
"-e",
|
||||
"--event",
|
||||
help="The GitHub event name that triggered the workflow (e.g. 'push', "
|
||||
"'pull_request'). Configs are filtered by their 'event_type'. If "
|
||||
"omitted, no filtering is applied.",
|
||||
default=None,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -292,15 +308,15 @@ if __name__ == "__main__":
|
||||
else:
|
||||
if args.config in ("linux", None):
|
||||
matrix += expand_linux_matrix(
|
||||
LinuxFile.load(THIS_DIR / "linux.json"), args.minimal
|
||||
LinuxFile.load(THIS_DIR / "linux.json"), args.event
|
||||
)
|
||||
if args.config in ("macos", None):
|
||||
matrix += expand_platform_matrix(
|
||||
PlatformFile.load(THIS_DIR / "macos.json"), args.minimal
|
||||
PlatformFile.load(THIS_DIR / "macos.json"), args.event
|
||||
)
|
||||
if args.config in ("windows", None):
|
||||
matrix += expand_platform_matrix(
|
||||
PlatformFile.load(THIS_DIR / "windows.json"), args.minimal
|
||||
PlatformFile.load(THIS_DIR / "windows.json"), args.event
|
||||
)
|
||||
|
||||
print(f"matrix={json.dumps({'include': [dataclasses.asdict(e) for e in matrix]})}")
|
||||
|
||||
31
.github/scripts/strategy-matrix/linux.json
vendored
31
.github/scripts/strategy-matrix/linux.json
vendored
@@ -2,30 +2,16 @@
|
||||
"image_tag": "sha-e29b523",
|
||||
"configs": {
|
||||
"ubuntu": [
|
||||
{
|
||||
"compiler": ["clang"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": true
|
||||
},
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false
|
||||
},
|
||||
{
|
||||
"compiler": ["gcc", "clang"],
|
||||
"build_type": ["Debug", "Release"],
|
||||
"arch": ["arm64"],
|
||||
"minimal": false
|
||||
"arch": ["amd64", "arm64"]
|
||||
},
|
||||
|
||||
{
|
||||
"compiler": ["gcc", "clang"],
|
||||
"build_type": ["Debug", "Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"sanitizers": ["address", "undefinedbehavior"]
|
||||
},
|
||||
|
||||
@@ -33,7 +19,6 @@
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Debug"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": true,
|
||||
"suffix": "coverage",
|
||||
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
|
||||
},
|
||||
@@ -41,7 +26,6 @@
|
||||
"compiler": ["clang"],
|
||||
"build_type": ["Debug"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"suffix": "voidstar",
|
||||
"extra_cmake_args": "-Dvoidstar=ON"
|
||||
},
|
||||
@@ -49,7 +33,6 @@
|
||||
"compiler": ["clang"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"suffix": "reffee",
|
||||
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=1000"
|
||||
},
|
||||
@@ -57,9 +40,9 @@
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Debug"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"suffix": "unity",
|
||||
"extra_cmake_args": "-Dunity=ON"
|
||||
"extra_cmake_args": "-Dunity=ON",
|
||||
"exclude_event_types": ["pull_request"]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -67,8 +50,7 @@
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false
|
||||
"arch": ["amd64"]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -76,8 +58,7 @@
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false
|
||||
"arch": ["amd64"]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -87,7 +68,6 @@
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745"
|
||||
}
|
||||
],
|
||||
@@ -97,7 +77,6 @@
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745"
|
||||
}
|
||||
]
|
||||
|
||||
5
.github/scripts/strategy-matrix/macos.json
vendored
5
.github/scripts/strategy-matrix/macos.json
vendored
@@ -4,14 +4,13 @@
|
||||
"configs": [
|
||||
{
|
||||
"build_type": "Release",
|
||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||
"minimal": true
|
||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
|
||||
},
|
||||
{
|
||||
"build_type": "Debug",
|
||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||
"build_only": true,
|
||||
"minimal": false
|
||||
"exclude_event_types": ["pull_request"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
4
.github/scripts/strategy-matrix/windows.json
vendored
4
.github/scripts/strategy-matrix/windows.json
vendored
@@ -2,11 +2,11 @@
|
||||
"platform": "windows/amd64",
|
||||
"runner": ["self-hosted", "Windows", "dev-box-windows-2026"],
|
||||
"configs": [
|
||||
{ "build_type": "Release", "minimal": true },
|
||||
{ "build_type": "Release" },
|
||||
{
|
||||
"build_type": "Debug",
|
||||
"build_only": true,
|
||||
"minimal": false
|
||||
"exclude_event_types": ["pull_request"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
1
.github/workflows/conflicting-pr.yml
vendored
1
.github/workflows/conflicting-pr.yml
vendored
@@ -14,7 +14,6 @@ permissions:
|
||||
|
||||
jobs:
|
||||
main:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'IgnoreConflicts') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if PRs are dirty
|
||||
|
||||
44
.github/workflows/on-pr.yml
vendored
44
.github/workflows/on-pr.yml
vendored
@@ -1,11 +1,7 @@
|
||||
# This workflow runs workflows to check, build and test the project
|
||||
# on every meaningful change on pull_request.
|
||||
# However, it will not run if the PR is a draft
|
||||
# unless it has the 'DraftRunCI' or 'Full CI build' label.
|
||||
#
|
||||
# By default a PR builds only a minimal matrix.
|
||||
# The full matrix runs once the PR is labeled "Ready to merge" or "Full CI build".
|
||||
# For commits to PRs that target a release branch,
|
||||
# This workflow runs all workflows to check, build and test the project on
|
||||
# various Linux flavors, as well as on MacOS and Windows, on every push to a
|
||||
# user branch. However, it will not run if the pull request is a draft unless it
|
||||
# has the 'DraftRunCI' label. For commits to PRs that target a release branch,
|
||||
# it also uploads the libxrpl recipe to the Conan remote.
|
||||
name: PR
|
||||
|
||||
@@ -19,16 +15,8 @@ on:
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
# Trigger on label changes so toggling "Ready to merge" or "Full CI build"
|
||||
# switches between the minimal and full matrix without needing a new push.
|
||||
- labeled
|
||||
- unlabeled
|
||||
|
||||
concurrency:
|
||||
# A single per-ref group with cancel-in-progress means any newer run (a push
|
||||
# or a label change) supersedes the in-progress one for that ref. Keeping
|
||||
# exactly one authoritative run per ref ensures a fast do-nothing run can never
|
||||
# mask a real build's checks.
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
@@ -37,18 +25,11 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
# This job determines whether the rest of the workflow should run at all,
|
||||
# based on the current set of labels: it runs when the PR is not a draft
|
||||
# (which should also cover merge-group) or has the 'DraftRunCI' or
|
||||
# 'Full CI build' label. Whether a build then happens, and whether it is the
|
||||
# minimal or full matrix, is decided further below and in the strategy matrix.
|
||||
# This job determines whether the rest of the workflow should run. It runs
|
||||
# when the PR is not a draft (which should also cover merge-group) or
|
||||
# has the 'DraftRunCI' label.
|
||||
should-run:
|
||||
if: >-
|
||||
${{
|
||||
!github.event.pull_request.draft
|
||||
|| contains(github.event.pull_request.labels.*.name, 'DraftRunCI')
|
||||
|| contains(github.event.pull_request.labels.*.name, 'Full CI build')
|
||||
}}
|
||||
if: ${{ !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -110,17 +91,15 @@ jobs:
|
||||
# least one of:
|
||||
# * Any of the files checked in the `changes` step were modified
|
||||
# * The PR is NOT a draft and is labeled "Ready to merge"
|
||||
# * The PR is labeled "Full CI build" (draft or not)
|
||||
# * The workflow is running from the merge queue
|
||||
id: go
|
||||
env:
|
||||
FILES: ${{ steps.changes.outputs.any_changed }}
|
||||
DRAFT: ${{ github.event.pull_request.draft }}
|
||||
READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }}
|
||||
FULL: ${{ contains(github.event.pull_request.labels.*.name, 'Full CI build') }}
|
||||
MERGE: ${{ github.event_name == 'merge_group' }}
|
||||
run: |
|
||||
echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FULL == 'true' || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}"
|
||||
echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}"
|
||||
cat "${GITHUB_OUTPUT}"
|
||||
outputs:
|
||||
go: ${{ steps.go.outputs.go == 'true' }}
|
||||
@@ -163,10 +142,7 @@ jobs:
|
||||
|
||||
package:
|
||||
needs: [should-run, build-test]
|
||||
# Packaging consumes the debian/rhel release binaries, which are only built
|
||||
# by the full matrix. Skip it for pull requests that ran only the minimal
|
||||
# matrix (i.e. not yet labeled "Ready to merge" or "Full CI build").
|
||||
if: ${{ needs.should-run.outputs.go == 'true' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'Ready to merge') || contains(github.event.pull_request.labels.*.name, 'Full CI build')) }}
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
|
||||
upload-recipe:
|
||||
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
- name: Check tools
|
||||
env:
|
||||
CHECK_TOOLS_SKIP_CLONE: "1"
|
||||
run: ./bin/check-tools.sh || true
|
||||
run: ./bin/check-tools.sh
|
||||
|
||||
- name: Print build environment
|
||||
uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574
|
||||
|
||||
@@ -35,8 +35,5 @@ jobs:
|
||||
id: generate
|
||||
env:
|
||||
GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }}
|
||||
# Run only the minimal matrix for pull requests that are not yet
|
||||
# labeled "Ready to merge" or "Full CI build". Any other event (merge
|
||||
# queue, push, schedule, manual dispatch) runs the full matrix.
|
||||
GENERATE_MINIMAL: ${{ (github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'Ready to merge') && !contains(github.event.pull_request.labels.*.name, 'Full CI build')) && '--minimal' || '' }}
|
||||
run: ./generate.py ${GENERATE_CONFIG} ${GENERATE_MINIMAL} >>"${GITHUB_OUTPUT}"
|
||||
GENERATE_EVENT: ${{ github.event_name }}
|
||||
run: ./generate.py ${GENERATE_CONFIG} --event="${GENERATE_EVENT}" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
@@ -32,11 +32,6 @@ repos:
|
||||
# as standalone translation units, so they have no compile_commands.json
|
||||
# entry to lint (verify_headers checks them transitively).
|
||||
exclude: '^include/xrpl/protocol_autogen|\.ipp$'
|
||||
# run-clang-tidy --fix may edit headers included by files it is not run on,
|
||||
# so pre-commit must not split the files across parallel hook invocations.
|
||||
# The script determines the staged files itself and lets run-clang-tidy
|
||||
# handle parallelism internally.
|
||||
pass_filenames: false
|
||||
- id: fix-include-style
|
||||
name: fix include style
|
||||
entry: ./bin/pre-commit/fix_include_style.py
|
||||
|
||||
@@ -28,9 +28,6 @@ This section contains changes targeting a future version.
|
||||
|
||||
### Additions
|
||||
|
||||
- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`.
|
||||
When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present.
|
||||
|
||||
- `ledger_entry`, `account_objects`: The `Delegate` ledger entry now includes an optional `DestinationNode` field, which stores the index into the authorized account's owner directory. This field is present on entries created after bidirectional directory tracking was introduced and may appear in RPC responses for those entries. ([#6681](https://github.com/XRPLF/rippled/pull/6681))
|
||||
|
||||
- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)):
|
||||
|
||||
@@ -84,9 +84,7 @@ If you create new source files, they must be organized as follows:
|
||||
- All other non-test files must go under `src/xrpld`.
|
||||
- All test source files must go under `src/test`.
|
||||
|
||||
The source must be formatted according to the style guide below. The easiest
|
||||
way to satisfy this is to install the [`pre-commit`](#pre-commit-hooks) hooks,
|
||||
which format and lint your changes automatically on every commit.
|
||||
The source must be formatted according to the style guide below.
|
||||
|
||||
Header includes must be [levelized](.github/scripts/levelization).
|
||||
|
||||
@@ -214,61 +212,13 @@ This is a non-exhaustive list of recommended style guidelines. These are
|
||||
not always strictly enforced and serve as a way to keep the codebase
|
||||
coherent rather than a set of _thou shalt not_ commandments.
|
||||
|
||||
## Pre-commit hooks
|
||||
|
||||
We use the [`pre-commit`](https://pre-commit.com/) framework to run the
|
||||
formatting and linting tools that keep the codebase consistent. `pre-commit`
|
||||
runs each tool configured in
|
||||
[`.pre-commit-config.yaml`](./.pre-commit-config.yaml) in its own isolated
|
||||
environment, so you don't need to install most of the individual tools
|
||||
yourself. The version of each hook sourced from an external repository
|
||||
(`clang-format`, `gersemi`, etc.) is pinned in that file, so running the hooks
|
||||
locally uses exactly the same versions as CI. A few `local` hooks — most notably
|
||||
`clang-tidy` — run tools from your own environment; see
|
||||
[Installing clang-tidy](#installing-clang-tidy) for how to get those.
|
||||
|
||||
To get started, install `pre-commit` and enable the git hook scripts:
|
||||
|
||||
```bash
|
||||
pip install pre-commit
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
Once installed, the hooks run automatically on your staged files every time you
|
||||
`git commit`. You can also run them on demand:
|
||||
|
||||
```bash
|
||||
# Run all hooks against only the staged files
|
||||
pre-commit run
|
||||
|
||||
# Run all hooks against every file in the repository
|
||||
pre-commit run --all-files
|
||||
|
||||
# Run a single hook (e.g. clang-format) against all files
|
||||
pre-commit run clang-format --all-files
|
||||
```
|
||||
|
||||
The hooks configured in this repository include, among others:
|
||||
|
||||
- `clang-format` — C++/proto formatting (see [Formatting](#formatting))
|
||||
- `clang-tidy` — C++ static analysis (see [Clang-tidy](#clang-tidy)); opt in with `TIDY=1`
|
||||
- `fix-include-style`, `fix-pragma-once`, `check-doxygen-style` — C++ hygiene
|
||||
- `gersemi` — CMake formatting
|
||||
- `prettier`, `black`, `shfmt` — formatting for JavaScript/JSON/Markdown, Python, and shell
|
||||
- `cspell` — spell checking
|
||||
|
||||
The same hooks run in CI on every pull request, so running them locally before
|
||||
you push helps you avoid CI failures.
|
||||
|
||||
## Formatting
|
||||
|
||||
All code must conform to `clang-format`, according to the settings in
|
||||
[`.clang-format`](./.clang-format), unless the result would be unreasonably
|
||||
difficult to read or maintain. The `clang-format` version is pinned in
|
||||
[`.pre-commit-config.yaml`](./.pre-commit-config.yaml), so the
|
||||
[`pre-commit`](#pre-commit-hooks) hook always formats with the same version as
|
||||
CI. To demarcate lines that should be left as-is, surround them with comments
|
||||
like this:
|
||||
All code must conform to `clang-format` version 22,
|
||||
according to the settings in [`.clang-format`](./.clang-format),
|
||||
unless the result would be unreasonably difficult to read or maintain.
|
||||
To demarcate lines that should be left as-is, surround them with comments like
|
||||
this:
|
||||
|
||||
```
|
||||
// clang-format off
|
||||
@@ -276,21 +226,9 @@ like this:
|
||||
// clang-format on
|
||||
```
|
||||
|
||||
The easiest way to format your changes is to let the `pre-commit` hook run
|
||||
automatically on commit, or to run it manually:
|
||||
|
||||
```bash
|
||||
pre-commit run clang-format --all-files
|
||||
```
|
||||
|
||||
You can also format individual files in place by running `clang-format -i <file>...`
|
||||
You can format individual files in place by running `clang-format -i <file>...`
|
||||
from any directory within this project.
|
||||
|
||||
> [!NOTE]
|
||||
> This uses whatever `clang-format` version is installed locally, which may
|
||||
> differ from the pinned version used by `pre-commit` and CI, so the results
|
||||
> can vary.
|
||||
|
||||
There is a Continuous Integration job that runs clang-format on pull requests. If the code doesn't comply, a patch file that corrects auto-fixable formatting issues is generated.
|
||||
|
||||
To download the patch file:
|
||||
@@ -301,6 +239,13 @@ To download the patch file:
|
||||
4. Download the zip file and extract it to your local git repository. Run `git apply [patch-file-name]`.
|
||||
5. Commit and push.
|
||||
|
||||
You can install a pre-commit hook to automatically run `clang-format` before every commit:
|
||||
|
||||
```
|
||||
pip3 install pre-commit
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
## Clang-tidy
|
||||
|
||||
All code must pass `clang-tidy` checks according to the settings in [`.clang-tidy`](./.clang-tidy).
|
||||
@@ -322,7 +267,7 @@ Before running clang-tidy, you must build the project to generate required files
|
||||
|
||||
#### Via pre-commit (recommended)
|
||||
|
||||
If you have already installed the [`pre-commit`](#pre-commit-hooks) hooks, you can run clang-tidy on your staged files using:
|
||||
If you have already installed the pre-commit hooks (see above), you can run clang-tidy on your staged files using:
|
||||
|
||||
```
|
||||
TIDY=1 pre-commit run clang-tidy
|
||||
|
||||
@@ -110,23 +110,6 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Rust toolchain. Part of the Nix commonPackages, so available on both Linux
|
||||
# and macOS. The cargo plugins are invoked through cargo (`cargo <sub>`), which
|
||||
# resolves the matching `cargo-<sub>` binary on PATH; `--version` is offline and
|
||||
# does not need a Cargo project.
|
||||
if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
echo
|
||||
echo "Rust toolchain:"
|
||||
check cargo
|
||||
check cargo-audit cargo audit --version
|
||||
check cargo-llvm-cov cargo llvm-cov --version
|
||||
check cargo-nextest cargo nextest --version
|
||||
check clippy clippy-driver --version
|
||||
check rust-analyzer
|
||||
check rustc
|
||||
check rustfmt
|
||||
fi
|
||||
|
||||
# GCC is the default compiler on Linux. macOS uses the system Apple Clang
|
||||
# instead, so GCC/g++/gcov are not expected there.
|
||||
if [ "${os}" = "linux" ]; then
|
||||
|
||||
@@ -1,46 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pre-commit hook that runs clang-tidy on staged files using run-clang-tidy.
|
||||
"""Pre-commit hook that runs clang-tidy on changed files using run-clang-tidy.
|
||||
|
||||
The script determines the staged files itself (see `pass_filenames: false` in
|
||||
.pre-commit-config.yaml) so run-clang-tidy is run once and handles parallelism
|
||||
internally: pre-commit would otherwise split the files across parallel hook
|
||||
invocations that race when fixes edit a shared header.
|
||||
|
||||
Fixes are collected with `-export-fixes` and applied by clang-apply-replacements
|
||||
in a separate step rather than with run-clang-tidy's `-fix`. The `add_module`
|
||||
build isolates each module's headers behind a per-module symlink directory
|
||||
(build/modules/<module>/...), so a header reachable from several translation
|
||||
units is referenced through different paths that all resolve to the same source
|
||||
file. clang-apply-replacements deduplicates identical replacements by their
|
||||
literal path, so those paths must be canonicalised to the real source path
|
||||
first; otherwise the same fix is applied once per path and corrupts the header.
|
||||
The set of files is chosen by pre-commit (see .pre-commit-config.yaml), which
|
||||
filters to C/C++ sources and excludes `.ipp` fragments. Headers are linted
|
||||
directly: the `verify_headers` build option (ON by default) compiles every
|
||||
`.h`/`.hpp` on its own, so each header is the main file of its own
|
||||
compile_commands.json entry and run-clang-tidy can analyse it just like a
|
||||
`.cpp`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
CLANG_TIDY_VERSION = 22
|
||||
|
||||
# Extensions run-clang-tidy can analyse: `.cpp` translation units and, thanks to
|
||||
# the `verify_headers` build option, `.h`/`.hpp` headers (each has its own
|
||||
# compile_commands.json entry). `.ipp` fragments have no entry and are skipped.
|
||||
TIDY_EXTENSIONS = {".cpp", ".h", ".hpp"}
|
||||
|
||||
# A single-quoted `FilePath:` entry in an -export-fixes YAML file, allowing the
|
||||
# `- ` marker that precedes it inside a `Replacements:` sequence. clang-tidy
|
||||
# emits paths single-quoted and doubles any embedded quote per YAML rules.
|
||||
FILEPATH_RE = re.compile(r"^(\s*(?:-\s+)?FilePath:\s*)'((?:[^']|'')*)'\s*$")
|
||||
|
||||
|
||||
def find_tool(name: str) -> str | None:
|
||||
for candidate in (f"{name}-{CLANG_TIDY_VERSION}", name):
|
||||
def find_run_clang_tidy() -> str | None:
|
||||
for candidate in (f"run-clang-tidy-{CLANG_TIDY_VERSION}", "run-clang-tidy"):
|
||||
if path := shutil.which(candidate):
|
||||
return path
|
||||
return None
|
||||
@@ -54,43 +35,23 @@ def find_build_dir(repo_root: Path) -> Path | None:
|
||||
return None
|
||||
|
||||
|
||||
def staged_files(repo_root: Path) -> list[Path]:
|
||||
"""Return absolute paths of staged, lint-able C/C++ files.
|
||||
|
||||
`--diff-filter=d` excludes deletions so we never lint a removed file.
|
||||
"""
|
||||
output = subprocess.check_output(
|
||||
["git", "diff", "--staged", "--name-only", "--diff-filter=d", "--"]
|
||||
+ [f"*{ext}" for ext in TIDY_EXTENSIONS],
|
||||
text=True,
|
||||
cwd=repo_root,
|
||||
)
|
||||
return [repo_root / rel for rel in output.splitlines() if rel]
|
||||
|
||||
|
||||
def canonicalize_fix_paths(fixes_dir: Path) -> None:
|
||||
"""Rewrite every `FilePath` in the exported fixes to its real source path.
|
||||
|
||||
A header included through a module's isolation symlink is recorded under that
|
||||
symlink's path; collapsing all paths to the same real file lets
|
||||
clang-apply-replacements recognise the per-translation-unit duplicates and
|
||||
apply each fix once.
|
||||
"""
|
||||
for yaml in fixes_dir.glob("*.yaml"):
|
||||
lines = []
|
||||
for line in yaml.read_text().splitlines():
|
||||
if m := FILEPATH_RE.match(line):
|
||||
path = m.group(2).replace("''", "'")
|
||||
real = os.path.realpath(path).replace("'", "''")
|
||||
line = f"{m.group(1)}'{real}'"
|
||||
lines.append(line)
|
||||
yaml.write_text("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
def main():
|
||||
if not os.environ.get("TIDY"):
|
||||
return 0
|
||||
|
||||
files = sys.argv[1:]
|
||||
if not files:
|
||||
return 0
|
||||
|
||||
run_clang_tidy = find_run_clang_tidy()
|
||||
if not run_clang_tidy:
|
||||
print(
|
||||
f"clang-tidy check failed: TIDY is enabled but neither "
|
||||
f"'run-clang-tidy-{CLANG_TIDY_VERSION}' nor 'run-clang-tidy' was found in PATH.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
repo_root = Path(
|
||||
subprocess.check_output(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
@@ -98,29 +59,6 @@ def main():
|
||||
text=True,
|
||||
).strip()
|
||||
)
|
||||
|
||||
files = staged_files(repo_root)
|
||||
if not files:
|
||||
return 0
|
||||
|
||||
run_clang_tidy = find_tool("run-clang-tidy")
|
||||
clang_apply_replacements = find_tool("clang-apply-replacements")
|
||||
missing = [
|
||||
name
|
||||
for name, path in (
|
||||
("run-clang-tidy", run_clang_tidy),
|
||||
("clang-apply-replacements", clang_apply_replacements),
|
||||
)
|
||||
if not path
|
||||
]
|
||||
if missing:
|
||||
print(
|
||||
f"clang-tidy check failed: TIDY is enabled but {' and '.join(missing)} "
|
||||
f"was not found in PATH (tried the '-{CLANG_TIDY_VERSION}' suffix too).",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
build_dir = find_build_dir(repo_root)
|
||||
if not build_dir:
|
||||
print(
|
||||
@@ -130,23 +68,11 @@ def main():
|
||||
)
|
||||
return 1
|
||||
|
||||
with tempfile.TemporaryDirectory() as fixes_dir:
|
||||
result = subprocess.run(
|
||||
[
|
||||
run_clang_tidy,
|
||||
"-quiet",
|
||||
"-p",
|
||||
build_dir,
|
||||
"-export-fixes",
|
||||
fixes_dir,
|
||||
"-allow-no-checks",
|
||||
]
|
||||
+ files
|
||||
)
|
||||
canonicalize_fix_paths(Path(fixes_dir))
|
||||
applied = subprocess.run([clang_apply_replacements, fixes_dir])
|
||||
|
||||
return result.returncode or applied.returncode
|
||||
result = subprocess.run(
|
||||
[run_clang_tidy, "-quiet", "-p", str(build_dir), "-fix", "-allow-no-checks"]
|
||||
+ files
|
||||
)
|
||||
return result.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
10
conan.lock
10
conan.lock
@@ -10,22 +10,22 @@
|
||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1782392413.075713",
|
||||
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
|
||||
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
|
||||
"openssl/3.6.3#f806de8933e3bf6f01016c6a888cee2e%1783945160.863288",
|
||||
"openssl/3.6.3#1163d4ddc603907084d08a6a0c6e580f%1782307150.583886",
|
||||
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1782392402.297166",
|
||||
"mpt-crypto/0.4.0-rc4#ffdba12f2332357f0d8b0ae944cfff52%1784138702.932355",
|
||||
"mpt-crypto/0.4.0-rc2#a580f2f9ad0e795de696aa62d54fb9af%1782425834.488828",
|
||||
"lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1782392402.164188",
|
||||
"libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1782392792.775744",
|
||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1782392402.420732",
|
||||
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
|
||||
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
|
||||
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
|
||||
"grpc/1.81.1#f729f6d75992d20f9c72828e9142d62f%1783945160.094135",
|
||||
"grpc/1.81.1#5217e6ef0544c42b46f4af35d5e7f649%1782307148.845616",
|
||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
|
||||
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
|
||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1782392402.296732",
|
||||
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1782392419.475605",
|
||||
"abseil/20250127.0#9ef01c1451a8340f9022e46238c0fbb6%1783945159.651047"
|
||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
|
||||
],
|
||||
"build_requires": [
|
||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||
@@ -38,7 +38,7 @@
|
||||
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
|
||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
|
||||
"abseil/20250127.0#9ef01c1451a8340f9022e46238c0fbb6%1783945159.651047"
|
||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
|
||||
],
|
||||
"python_requires": [],
|
||||
"overrides": {
|
||||
|
||||
@@ -134,7 +134,7 @@ class Xrpl(ConanFile):
|
||||
if self.options.jemalloc:
|
||||
self.requires("jemalloc/5.3.1")
|
||||
self.requires("lz4/1.10.0", force=True)
|
||||
self.requires("mpt-crypto/0.4.0-rc4", transitive_headers=True)
|
||||
self.requires("mpt-crypto/0.4.0-rc2", transitive_headers=True)
|
||||
self.requires("protobuf/6.33.5", force=True)
|
||||
if self.options.rocksdb:
|
||||
self.requires("rocksdb/10.5.1")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -13,6 +13,6 @@ namespace xrpl {
|
||||
* @throws runtime_error
|
||||
*/
|
||||
void
|
||||
extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst);
|
||||
extractTarLz4(std::filesystem::path const& src, std::filesystem::path const& dst);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::string
|
||||
getFileContents(
|
||||
boost::system::error_code& ec,
|
||||
boost::filesystem::path const& sourcePath,
|
||||
std::error_code& ec,
|
||||
std::filesystem::path const& sourcePath,
|
||||
std::optional<std::size_t> maxSize = std::nullopt);
|
||||
|
||||
void
|
||||
writeFileContents(
|
||||
boost::system::error_code& ec,
|
||||
boost::filesystem::path const& destPath,
|
||||
std::error_code& ec,
|
||||
std::filesystem::path const& destPath,
|
||||
std::string const& contents);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
* @return `true` if the file was opened.
|
||||
*/
|
||||
bool
|
||||
open(boost::filesystem::path const& path);
|
||||
open(std::filesystem::path const& path);
|
||||
|
||||
/**
|
||||
* Close and re-open the system file associated with the log
|
||||
@@ -133,7 +133,7 @@ private:
|
||||
|
||||
private:
|
||||
std::unique_ptr<std::ofstream> stream_;
|
||||
boost::filesystem::path path_;
|
||||
std::filesystem::path path_;
|
||||
};
|
||||
|
||||
std::mutex mutable mutex_;
|
||||
@@ -152,7 +152,7 @@ public:
|
||||
virtual ~Logs() = default;
|
||||
|
||||
bool
|
||||
open(boost::filesystem::path const& pathToLogFile);
|
||||
open(std::filesystem::path const& pathToLogFile);
|
||||
|
||||
beast::Journal::Sink&
|
||||
get(std::string const& name);
|
||||
|
||||
@@ -364,8 +364,6 @@ public:
|
||||
static constexpr internalrep kMaxRep = std::numeric_limits<rep>::max();
|
||||
static_assert(kMaxRep == 9'223'372'036'854'775'807);
|
||||
static_assert(-kMaxRep == std::numeric_limits<rep>::min() + 1);
|
||||
static constexpr internalrep kMaxRepUp = ((kMaxRep / 10) + 1) * 10;
|
||||
static_assert(kMaxRepUp == 9'223'372'036'854'775'810ULL);
|
||||
|
||||
// May need to make unchecked private
|
||||
struct Unchecked
|
||||
@@ -593,13 +591,6 @@ public:
|
||||
std::pair<T, int>
|
||||
normalizeToRange() const;
|
||||
|
||||
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
|
||||
// is negative, returns the positive value. This takes a little extra work
|
||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
||||
// UB, and can vary across compilers.
|
||||
static internalrep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
private:
|
||||
static thread_local RoundingMode mode;
|
||||
// The available ranges for mantissa
|
||||
@@ -654,6 +645,13 @@ private:
|
||||
// exponent could go out of range, so it will be checked.
|
||||
[[nodiscard]] Number
|
||||
shiftExponent(int exponentDelta) const;
|
||||
|
||||
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
|
||||
// is negative, returns the positive value. This takes a little extra work
|
||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
||||
// UB, and can vary across compilers.
|
||||
static internalrep
|
||||
externalToInternal(rep mantissa);
|
||||
};
|
||||
|
||||
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||
#include <xrpl/basics/Log.h> // IWYU pragma: keep
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/basics/scope.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -604,42 +601,8 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
std::vector<key_type> v;
|
||||
|
||||
{
|
||||
// Keep track of how many iterations are needed. Exit the loop if the number of retries gets
|
||||
// absurd. (Note that if this somehow ever happens, one more allocation will be done under
|
||||
// lock, which is undesirable, but really should be almost impossible.)
|
||||
std::size_t allocationIterations = 0;
|
||||
std::unique_lock lock(mutex_);
|
||||
for (auto size = cache_.size(); v.capacity() < size && allocationIterations < 20;
|
||||
size = cache_.size())
|
||||
{
|
||||
ScopeUnlock const unlock(lock);
|
||||
if (allocationIterations > 0)
|
||||
{
|
||||
JLOG(journal_.info())
|
||||
<< "getKeys(): Cache grew beyond allocated capacity after "
|
||||
<< allocationIterations << " prior attempt(s). Have " << v.capacity()
|
||||
<< ", need " << size << ". Retrying allocation";
|
||||
}
|
||||
// Allocate the current size plus a little extra, in case the cache grows while
|
||||
// allocating. Each time another allocation is needed, the extra also gets bigger until
|
||||
// it ultimately doubles the size + 1.
|
||||
constexpr std::size_t baseShift = 5;
|
||||
auto const bufferOffset = std::min(allocationIterations, std::size_t{baseShift});
|
||||
auto const bufferShift = baseShift - bufferOffset;
|
||||
size += (size >> bufferShift) + 1;
|
||||
v.reserve(size);
|
||||
++allocationIterations;
|
||||
}
|
||||
if (v.capacity() < cache_.size())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::TaggedCache::getKeys(): failed to allocate sufficient capacity");
|
||||
v.reserve(cache_.size());
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
XRPL_ASSERT(lock.owns_lock(), "xrpl::TaggedCache::getKeys(): owns lock");
|
||||
XRPL_ASSERT(
|
||||
v.capacity() >= cache_.size(), "xrpl::TaggedCache::getKeys(): sufficient capacity");
|
||||
std::scoped_lock const lock(mutex_);
|
||||
v.reserve(cache_.size());
|
||||
for (auto const& _ : cache_)
|
||||
v.push_back(_.first);
|
||||
}
|
||||
|
||||
@@ -308,9 +308,7 @@ public:
|
||||
XRPL_ASSERT(
|
||||
c.size() * sizeof(typename Container::value_type) == size(),
|
||||
"xrpl::BaseUInt::fromRaw(Container auto) : input size match");
|
||||
std::size_t const canCopy =
|
||||
std::min(size(), c.size() * sizeof(typename Container::value_type));
|
||||
std::memcpy(result.data_.data(), c.data(), canCopy);
|
||||
std::memcpy(result.data_.data(), c.data(), size());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -324,11 +322,7 @@ public:
|
||||
XRPL_ASSERT(
|
||||
c.size() * sizeof(typename Container::value_type) == size(),
|
||||
"xrpl::BaseUInt::operator=(Container auto) : input size match");
|
||||
std::size_t const canCopy =
|
||||
std::min(size(), c.size() * sizeof(typename Container::value_type));
|
||||
if (canCopy < size())
|
||||
*this = beast::kZero;
|
||||
std::memcpy(data_.data(), c.data(), canCopy);
|
||||
std::memcpy(data_.data(), c.data(), size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -358,13 +358,12 @@ template <class = void>
|
||||
bool
|
||||
tokenInList(boost::string_ref const& value, boost::string_ref const& token)
|
||||
{
|
||||
auto const list = makeList(value);
|
||||
// ListIterator is not default-constructible, so it does not model a std::ranges
|
||||
// sentinel/range; the classic std::any_of (which only needs an input iterator)
|
||||
// is used instead.
|
||||
// NOLINTNEXTLINE(modernize-use-ranges)
|
||||
return std::any_of(
|
||||
list.begin(), list.end(), [&token](auto const& item) { return ciEqual(item, token); });
|
||||
for (auto const& item : makeList(value))
|
||||
{
|
||||
if (ciEqual(item, token))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <bool IsRequest, class Body, class Fields>
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/runner.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
@@ -27,7 +27,7 @@ makeReason(String const& reason, char const* file, int line)
|
||||
std::string s(reason);
|
||||
if (!s.empty())
|
||||
s.append(": ");
|
||||
namespace fs = boost::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
s.append(fs::path{file}.filename().string());
|
||||
s.append("(");
|
||||
s.append(boost::lexical_cast<std::string>(line));
|
||||
|
||||
@@ -1,20 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* Generate a unique, non-existing path under @p base with an optional @p prefix
|
||||
* and a random hex suffix.
|
||||
*
|
||||
* Attempts up to @p maxAttempts paths. Throws `std::runtime_error` if a
|
||||
* unique path cannot be found or if the filesystem returns an error while
|
||||
* checking for existence.
|
||||
*/
|
||||
inline std::filesystem::path
|
||||
uniqueRandomPath(
|
||||
std::filesystem::path const& base,
|
||||
std::size_t maxAttempts = 100,
|
||||
std::string const& prefix = "")
|
||||
{
|
||||
std::random_device rd;
|
||||
for (std::size_t attempt = 0; attempt < maxAttempts; ++attempt)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << prefix << std::hex << std::setfill('0') << std::setw(8) << rd() << std::setw(8)
|
||||
<< rd();
|
||||
auto candidate = base / oss.str();
|
||||
std::error_code ec;
|
||||
bool const exists = std::filesystem::exists(candidate, ec);
|
||||
if (ec)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Unable to check path '" + candidate.string() + "': " + ec.message());
|
||||
}
|
||||
if (!exists)
|
||||
return candidate;
|
||||
}
|
||||
throw std::runtime_error("Unable to generate a unique path under '" + base.string() + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* RAII temporary directory.
|
||||
*
|
||||
* The directory and all its contents are deleted when
|
||||
* the instance of `temp_dir` is destroyed.
|
||||
* the instance of `TempDir` is destroyed.
|
||||
*/
|
||||
class TempDir
|
||||
{
|
||||
boost::filesystem::path path_;
|
||||
std::filesystem::path path_;
|
||||
|
||||
public:
|
||||
#if !GENERATING_DOCS
|
||||
@@ -28,12 +66,8 @@ public:
|
||||
*/
|
||||
TempDir()
|
||||
{
|
||||
auto const dir = boost::filesystem::temp_directory_path();
|
||||
do
|
||||
{
|
||||
path_ = dir / boost::filesystem::unique_path();
|
||||
} while (boost::filesystem::exists(path_));
|
||||
boost::filesystem::create_directory(path_);
|
||||
path_ = uniqueRandomPath(std::filesystem::temp_directory_path());
|
||||
std::filesystem::create_directory(path_);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,8 +76,8 @@ public:
|
||||
~TempDir()
|
||||
{
|
||||
// use non-throwing calls in the destructor
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::remove_all(path_, ec);
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(path_, ec);
|
||||
// TODO: warn/notify if ec set ?
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#include <xrpl/core/Job.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -44,7 +43,7 @@ public:
|
||||
*/
|
||||
struct Setup
|
||||
{
|
||||
boost::filesystem::path perfLog;
|
||||
std::filesystem::path perfLog;
|
||||
// log_interval is in milliseconds to support faster testing.
|
||||
milliseconds logInterval{seconds(1)};
|
||||
};
|
||||
@@ -149,7 +148,7 @@ public:
|
||||
};
|
||||
|
||||
PerfLog::Setup
|
||||
setupPerfLog(Section const& section, boost::filesystem::path const& configDir);
|
||||
setupPerfLog(Section const& section, std::filesystem::path const& configDir);
|
||||
|
||||
std::unique_ptr<PerfLog>
|
||||
makePerfLog(
|
||||
|
||||
@@ -41,19 +41,6 @@ public:
|
||||
std::unique_ptr<NodeStore::Backend>&& newBackend,
|
||||
std::function<void(std::string const& writableName, std::string const& archiveName)> const&
|
||||
f) = 0;
|
||||
|
||||
/**
|
||||
* Marks an online-delete rotation as in progress (or completed).
|
||||
*
|
||||
* While in flight, a read served by the archive backend is copied
|
||||
* forward into the writable backend even for ordinary
|
||||
* (duplicate == false) fetches: the archive is about to be deleted,
|
||||
* and a node body canonicalized into caches during the rotation
|
||||
* window would otherwise survive only in RAM once the archive is
|
||||
* dropped.
|
||||
*/
|
||||
virtual void
|
||||
setRotationInFlight(bool inFlight) = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/nodestore/Scheduler.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -70,22 +69,11 @@ public:
|
||||
void
|
||||
sweep() override;
|
||||
|
||||
void
|
||||
setRotationInFlight(bool inFlight) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Backend> writableBackend_;
|
||||
std::shared_ptr<Backend> archiveBackend_;
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
// True between SHAMapStore starting the cache-freshen phase and the
|
||||
// completion of rotate(). While true, archive hits on ordinary
|
||||
// (duplicate == false) fetches are copied forward into the writable
|
||||
// backend; copyForwardCount_ tallies them per rotation for the
|
||||
// summary line logged at swap.
|
||||
std::atomic<bool> rotationInFlight_{false};
|
||||
std::atomic<std::uint64_t> copyForwardCount_{0};
|
||||
|
||||
std::shared_ptr<NodeObject>
|
||||
fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
|
||||
override;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -422,8 +423,21 @@ struct KeyletDesc
|
||||
bool includeInTests{};
|
||||
};
|
||||
|
||||
// This list should include all of the keylet functions that take a single AccountID parameter.
|
||||
extern std::array<KeyletDesc<AccountID const&>, 6> const kDirectAccountKeylets;
|
||||
// This list should include all of the keylet functions that take a single
|
||||
// AccountID parameter.
|
||||
std::array<KeyletDesc<AccountID const&>, 6> const kDirectAccountKeylets{
|
||||
{{.function = &keylet::account, .expectedLEName = jss::AccountRoot, .includeInTests = false},
|
||||
{.function = &keylet::ownerDir, .expectedLEName = jss::DirectoryNode, .includeInTests = true},
|
||||
{.function = &keylet::signerList, .expectedLEName = jss::SignerList, .includeInTests = true},
|
||||
// It's normally impossible to create an item at nftpage_min, but
|
||||
// test it anyway, since the invariant checks for it.
|
||||
{.function = &keylet::nftokenPageMin,
|
||||
.expectedLEName = jss::NFTokenPage,
|
||||
.includeInTests = true},
|
||||
{.function = &keylet::nftokenPageMax,
|
||||
.expectedLEName = jss::NFTokenPage,
|
||||
.includeInTests = true},
|
||||
{.function = &keylet::did, .expectedLEName = jss::DID, .includeInTests = true}}};
|
||||
|
||||
MPTID
|
||||
makeMptID(std::uint32_t sequence, AccountID const& account);
|
||||
|
||||
@@ -110,7 +110,6 @@ JSS(accounts); // in: LedgerEntry, Subscribe, handlers/Ledger
|
||||
JSS(accounts_proposed); // in: Subscribe, Unsubscribe
|
||||
JSS(action); //
|
||||
JSS(active); // out: OverlayImpl
|
||||
JSS(actor); // in/out: AccountTx
|
||||
JSS(acquiring); // out: LedgerRequest
|
||||
JSS(address); // out: PeerImp
|
||||
JSS(affected); // out: AcceptedLedgerTx
|
||||
@@ -134,7 +133,6 @@ JSS(attestation_reward_account); //
|
||||
JSS(auction_slot); // out: amm_info
|
||||
JSS(authorized); // out: AccountLines
|
||||
JSS(authorize); // out: delegate
|
||||
JSS(authorizer); // in/out: AccountTx
|
||||
JSS(authorized_credentials); // in: ledger_entry DepositPreauth
|
||||
JSS(auth_accounts); // out: amm_info
|
||||
JSS(auth_change); // out: AccountInfo
|
||||
@@ -193,7 +191,6 @@ JSS(converge_time); // out: NetworkOPs
|
||||
JSS(converge_time_s); // out: NetworkOPs
|
||||
JSS(cookie); // out: NetworkOPs
|
||||
JSS(count); // in: AccountTx*, ValidatorList
|
||||
JSS(counter_party); // in/out: AccountTx
|
||||
JSS(counters); // in/out: retrieve counters
|
||||
JSS(credentials); // in: deposit_authorized
|
||||
JSS(credential_type); // in: LedgerEntry DepositPreauth
|
||||
@@ -273,7 +270,6 @@ JSS(freeze); // out: AccountLines
|
||||
JSS(freeze_peer); // out: AccountLines
|
||||
JSS(deep_freeze); // out: AccountLines
|
||||
JSS(deep_freeze_peer); // out: AccountLines
|
||||
JSS(delegate_filter); // in/out: AccountTx
|
||||
JSS(frozen_balances); // out: GatewayBalances
|
||||
JSS(full); // in: LedgerClearer, handlers/Ledger
|
||||
JSS(full_reply); // out: PathFind
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
#include <xrpl/core/StartUpType.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <soci/statement.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -80,7 +79,7 @@ public:
|
||||
|
||||
StartUpType startUp = StartUpType::Normal;
|
||||
bool standAlone = false;
|
||||
boost::filesystem::path dataDir;
|
||||
std::filesystem::path dataDir;
|
||||
// Indicates whether or not to return the `globalPragma`
|
||||
// from commonPragma()
|
||||
bool useGlobalPragma = false;
|
||||
@@ -143,7 +142,7 @@ public:
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
boost::filesystem::path const& dataDir,
|
||||
std::filesystem::path const& dataDir,
|
||||
std::string const& dbName,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
@@ -155,7 +154,7 @@ public:
|
||||
// Use this constructor to setup checkpointing
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
boost::filesystem::path const& dataDir,
|
||||
std::filesystem::path const& dataDir,
|
||||
std::string const& dbName,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
@@ -190,7 +189,7 @@ private:
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
boost::filesystem::path const& pPath,
|
||||
std::filesystem::path const& pPath,
|
||||
std::vector<std::string> const* commonPragma,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/TxSearched.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include <concepts>
|
||||
@@ -46,22 +45,6 @@ struct LedgerRange
|
||||
uint32_t max;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible delegate types that can occur during filtering in account_tx
|
||||
*/
|
||||
enum class DelegateType {
|
||||
Actor, ///< Another account signed and submitted transactions on behalf of this account (this
|
||||
///< account is the owner/delegator).
|
||||
Authorizer ///< This account signed and submitted transactions on behalf of another account
|
||||
///< (this account is the signer/delegatee).
|
||||
};
|
||||
|
||||
struct DelegateFilter
|
||||
{
|
||||
DelegateType type = DelegateType::Actor;
|
||||
std::optional<AccountID> counterparty;
|
||||
};
|
||||
|
||||
class RelationalDatabase
|
||||
{
|
||||
public:
|
||||
@@ -98,7 +81,6 @@ public:
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::uint32_t limit = 0;
|
||||
bool bAdmin = false;
|
||||
std::optional<DelegateFilter> delegate;
|
||||
};
|
||||
|
||||
using AccountTx = std::pair<std::shared_ptr<Transaction>, std::shared_ptr<TxMeta>>;
|
||||
@@ -118,7 +100,6 @@ public:
|
||||
bool forward = false;
|
||||
uint32_t limit = 0;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::optional<DelegateFilter> delegate;
|
||||
};
|
||||
|
||||
struct AccountTxResult
|
||||
@@ -127,7 +108,6 @@ public:
|
||||
LedgerRange ledgerRange{};
|
||||
uint32_t limit = 0;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::optional<DelegateFilter> delegate;
|
||||
};
|
||||
|
||||
virtual ~RelationalDatabase() = default;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <soci/session.h>
|
||||
|
||||
#include <memory>
|
||||
#ifdef __clang__
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
@@ -120,6 +120,6 @@ makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, Ser
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#ifdef __clang__
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct SavedState
|
||||
|
||||
@@ -71,18 +71,9 @@ public:
|
||||
/**
|
||||
* @brief called for each ledger entry in the current transaction.
|
||||
*
|
||||
* @param isDelete true if the SLE is being deleted.
|
||||
* @param before ledger entry before modification by the transaction. `before` will be null if
|
||||
* the entry is new.
|
||||
* @param after ledger entry after modification by the transaction. Always non-null. When
|
||||
* deleting, `after` may differ from `before`. Whether that is important is up to the
|
||||
* individual invariant check.
|
||||
*
|
||||
* @note `after` IS NEVER NULL. `isDelete` is the only correct way to check for deletions.
|
||||
* Do not make logic or branching decisions on whether on `after` is set, because it will
|
||||
* always be set. Treat a null `after` as a programming error (with XRPL_ASSERT). An
|
||||
* invariant MAY check for null defensively, if it makes more sense, but an assertion is
|
||||
* preferred for new invariants.
|
||||
* @param isDelete true if the SLE is being deleted
|
||||
* @param before ledger entry before modification by the transaction
|
||||
* @param after ledger entry after modification by the transaction
|
||||
*/
|
||||
void
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
|
||||
@@ -325,26 +316,17 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Token holder's trustline/MPT balance cannot be invalid
|
||||
* after Clawback.
|
||||
* @brief Invariant: Token holder's trustline balance cannot be negative after
|
||||
* Clawback.
|
||||
*
|
||||
* We iterate all the trust lines affected by this transaction and ensure
|
||||
* that no more than one trustline is modified, and also holder's balance is
|
||||
* non-negative. When featureMPTokensV2 is enabled, also verify the holder's
|
||||
* raw trustline/MPToken balance decreased by the clawed amount.
|
||||
* non-negative.
|
||||
*/
|
||||
class ValidClawback
|
||||
{
|
||||
struct EntryChange
|
||||
{
|
||||
SLE::const_pointer before;
|
||||
SLE::const_pointer after;
|
||||
};
|
||||
|
||||
std::uint32_t trustlinesChanged_ = 0;
|
||||
std::uint32_t mptokensChanged_ = 0;
|
||||
EntryChange iou_;
|
||||
EntryChange mpt_;
|
||||
|
||||
public:
|
||||
void
|
||||
@@ -458,7 +440,7 @@ using InvariantChecks = std::tuple<
|
||||
ValidLoan,
|
||||
ValidVault,
|
||||
ValidConfidentialMPToken,
|
||||
ValidMPTBalanceChanges,
|
||||
ValidMPTPayment,
|
||||
ValidAmounts,
|
||||
ValidMPTTransfer,
|
||||
ObjectHasPseudoAccount,
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
* OutstandingAmount after application equals OutstandingAmount before
|
||||
* application plus the net holder balance delta.
|
||||
*/
|
||||
class ValidMPTBalanceChanges
|
||||
class ValidMPTPayment
|
||||
{
|
||||
enum class Order { Before = 0, After = 1 };
|
||||
struct MPTData
|
||||
|
||||
@@ -75,13 +75,9 @@ COPY bin/check-tools.sh /tmp/check-tools.sh
|
||||
RUN /tmp/check-tools.sh
|
||||
|
||||
# Sanity-check that the g++/clang++ are able to build binaries, including sanitizer-instrumented ones.
|
||||
COPY nix/docker/test_files/cpp/ /tmp/test_files/cpp/
|
||||
RUN /tmp/test_files/cpp/compile-sources.sh /tmp/test_files/cpp/sources /tmp/cpp-bins
|
||||
|
||||
# Sanity-check that rustc is able to build binaries, including ones that rely on
|
||||
# the runtime overflow check.
|
||||
COPY nix/docker/test_files/rust/ /tmp/test_files/rust/
|
||||
RUN /tmp/test_files/rust/compile-sources.sh /tmp/test_files/rust/sources /tmp/rust-bins
|
||||
COPY nix/docker/test_files/cpp_sources/ /tmp/cpp_sources/
|
||||
COPY nix/docker/test_files/compile-cpp-sources.sh /tmp/compile-cpp-sources.sh
|
||||
RUN /tmp/compile-cpp-sources.sh /tmp/cpp_sources /tmp/bins
|
||||
|
||||
# Tester: start from a clean BASE_IMAGE, install sanitizer runtime libraries,
|
||||
# and run the compiled test binaries to verify they execute correctly.
|
||||
@@ -98,18 +94,15 @@ SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
|
||||
|
||||
# Sanity-check that the built binaries run correctly in the vanilla base image, with the necessary sanitizer runtime libraries installed.
|
||||
COPY bin/install-sanitizer-libs.sh /tmp/install-sanitizer-libs.sh
|
||||
COPY nix/docker/test_files/cpp/run-binaries.sh /tmp/test_files/cpp/run-binaries.sh
|
||||
COPY nix/docker/test_files/rust/run-binaries.sh /tmp/test_files/rust/run-binaries.sh
|
||||
COPY --from=final /tmp/cpp-bins /tmp/cpp-bins
|
||||
COPY --from=final /tmp/rust-bins /tmp/rust-bins
|
||||
COPY nix/docker/test_files/run-test-binaries.sh /tmp/run-test-binaries.sh
|
||||
COPY --from=final /tmp/bins /tmp/bins
|
||||
|
||||
RUN <<EOF
|
||||
if echo "${BASE_IMAGE}" | grep -qiE 'nixos'; then
|
||||
echo "Skipping runnning binaries on NixOS."
|
||||
else
|
||||
/tmp/install-sanitizer-libs.sh
|
||||
/tmp/test_files/cpp/run-binaries.sh /tmp/cpp-bins
|
||||
/tmp/test_files/rust/run-binaries.sh /tmp/rust-bins
|
||||
/tmp/run-test-binaries.sh /tmp/bins
|
||||
fi
|
||||
touch /tmp/tests-passed
|
||||
EOF
|
||||
|
||||
@@ -42,12 +42,9 @@ work without `ca-certificates` being installed in the base image.
|
||||
- installs the dynamic linker if the base image lacks one (see
|
||||
[How libc is handled](#how-libc-is-handled)),
|
||||
- runs [`bin/check-tools.sh`](../../bin/check-tools.sh) to verify every
|
||||
expected tool is present and runnable.
|
||||
expected tool is present and runnable, and
|
||||
- compiles the C++ test programs in
|
||||
[`test_files/cpp/sources/`](./test_files/cpp/sources) with both `g++` and
|
||||
`clang++`, and sanitizers, and
|
||||
- compiles the Rust test programs in
|
||||
[`test_files/rust/sources/`](./test_files/rust/sources) with `rustc`.
|
||||
[`test_files/`](./test_files) with both `g++` and `clang++`, and sanitizers.
|
||||
3. **`tester`** — Start again from a clean `BASE_IMAGE` (no Nix toolchain),
|
||||
install only the sanitizer runtime libraries
|
||||
([`install-sanitizer-libs.sh`](./install-sanitizer-libs.sh)), and run the
|
||||
@@ -76,12 +73,11 @@ toolchain being present at runtime. Two pieces make that work:
|
||||
[`loader-path.sh`](./loader-path.sh) reports the expected loader path for the
|
||||
current architecture, so we can patch the binaries to use the correct loader.
|
||||
|
||||
The build then verifies all of this end to end: the C++ test programs in
|
||||
`test_files/cpp/sources/` (a regular binary plus ASan/TSan/UBSan variants) and
|
||||
the Rust test programs in `test_files/rust/sources/` (a hello binary plus panic
|
||||
and overflow-check variants) are compiled in `final`, their `PT_INTERP` is
|
||||
patched to the target loader, and they are run in the clean `tester` stage to
|
||||
confirm each emits the expected diagnostic on a stock base image.
|
||||
The build then verifies all of this end to end: the test programs in
|
||||
`test_files/` (a regular binary plus ASan/TSan/UBSan variants) are compiled in
|
||||
`final`, their `PT_INTERP` is patched to the target loader, and they are run in
|
||||
the clean `tester` stage to confirm each emits the expected sanitizer
|
||||
diagnostic on a stock base image.
|
||||
|
||||
## Files
|
||||
|
||||
@@ -89,7 +85,6 @@ confirm each emits the expected diagnostic on a stock base image.
|
||||
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| [`./Dockerfile`](./Dockerfile) | Multi-stage build described above. |
|
||||
| [`./loader-path.sh`](./loader-path.sh) | Print the dynamic-linker (`PT_INTERP`) path for the current architecture. |
|
||||
| [`./test_files/cpp/`](./test_files/cpp) | C++ sanitizer smoke test: sources + compile/run scripts. |
|
||||
| [`./test_files/rust/`](./test_files/rust) | Rust rustc smoke test: sources + compile/run scripts. |
|
||||
| [`./test_files/`](./test_files) | C++ sources and scripts to compile and run the sanitizer smoke tests. |
|
||||
| [`/bin/check-tools.sh`](../../bin/check-tools.sh) | Verify every expected tools are present and runnable. |
|
||||
| [`/bin/install-sanitizer-libs.sh`](../../bin/install-sanitizer-libs.sh) | Install `libasan`/`libtsan`/`libubsan` runtimes on the supported base images. |
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Compile all Rust test binaries during the Docker image build.
|
||||
# Each binary has the target system's ELF PT_INTERP (dynamic-linker path)
|
||||
# baked in so it can run on the (potentially minimal) final BASE_IMAGE.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
src_dir="${1:?usage: $0 <src_dir> <dst_dir>}"
|
||||
dst_dir="${2:?usage: $0 <src_dir> <dst_dir>}"
|
||||
|
||||
loader="$(/tmp/loader-path.sh)"
|
||||
|
||||
mkdir -p "${dst_dir}"
|
||||
|
||||
function compile() {
|
||||
local name="${1}"
|
||||
local extra_flags="${2:-}"
|
||||
|
||||
local src="${src_dir}/${name}.rs"
|
||||
local binary="${dst_dir}/${name}"
|
||||
|
||||
echo "=== Compiling ${name} with rustc ==="
|
||||
# -O optimizes (opt-level 2); Rust statically links its own std, so the
|
||||
# only dynamic dependency left is the system glibc (+ libgcc_s), exactly
|
||||
# like the C++ binaries.
|
||||
local compile_cmd="rustc --edition 2021 -O -g ${extra_flags} \
|
||||
${src} -o ${binary}"
|
||||
echo "Compile cmd: ${compile_cmd}"
|
||||
eval "${compile_cmd}"
|
||||
|
||||
echo "=== Patching ${binary} to use ${loader} as PT_INTERP ==="
|
||||
local patch_cmd="patchelf --set-interpreter ${loader} --remove-rpath ${binary}"
|
||||
echo "Patch cmd: ${patch_cmd}"
|
||||
eval "${patch_cmd}"
|
||||
}
|
||||
|
||||
# `-O` disables overflow checks by default, so `overflow` re-enables them
|
||||
# explicitly to exercise the runtime overflow check.
|
||||
compile hello
|
||||
compile panic
|
||||
compile overflow "-C overflow-checks=on"
|
||||
|
||||
echo "=== All binaries compiled ==="
|
||||
|
||||
ls -la "${dst_dir}"
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Run pre-compiled Rust binaries and confirm each emits its expected diagnostic.
|
||||
# Binaries must already exist in <bins_dir> as <name> for name in
|
||||
# {hello,panic,overflow}.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
bins_dir="${1:?usage: $0 <bins_dir>}"
|
||||
|
||||
failed_binaries=()
|
||||
|
||||
# Run a binary and verify its exit code and output.
|
||||
# Usage: run <binary> <expected_output> <expected_rc>
|
||||
function run() {
|
||||
local binary="${1}"
|
||||
local expected_output="${2}"
|
||||
local expected_rc="${3}"
|
||||
|
||||
local out_file
|
||||
out_file="$(mktemp)"
|
||||
|
||||
echo "=== Run ${binary} ==="
|
||||
set +e
|
||||
"${binary}" >"${out_file}" 2>&1
|
||||
local rc=$?
|
||||
set -e
|
||||
|
||||
cat "${out_file}"
|
||||
|
||||
local failed=0
|
||||
if [ "${expected_rc}" = "nonzero" ]; then
|
||||
if [ "${rc}" -eq 0 ]; then
|
||||
echo "ERROR: expected non-zero exit code from ${binary}, got ${rc}" >&2
|
||||
failed=1
|
||||
fi
|
||||
elif [ "${rc}" -ne "${expected_rc}" ]; then
|
||||
echo "ERROR: expected exit code ${expected_rc} from ${binary}, got ${rc}" >&2
|
||||
failed=1
|
||||
fi
|
||||
|
||||
if ! grep -q "${expected_output}" "${out_file}"; then
|
||||
echo "ERROR: expected '${expected_output}' from ${binary}" >&2
|
||||
failed=1
|
||||
fi
|
||||
|
||||
if [ "${failed}" -eq 0 ]; then
|
||||
echo "OK: '${expected_output}' detected"
|
||||
else
|
||||
failed_binaries+=("${binary}")
|
||||
fi
|
||||
}
|
||||
|
||||
declare -A expect=(
|
||||
[hello]="Hello from main thread"
|
||||
[panic]="explicit panic from test"
|
||||
[overflow]="attempt to add with overflow"
|
||||
)
|
||||
|
||||
for name in hello panic overflow; do
|
||||
binary="${bins_dir}/${name}"
|
||||
|
||||
if [ "${name}" = "hello" ]; then
|
||||
expected_rc=0
|
||||
else
|
||||
expected_rc=nonzero
|
||||
fi
|
||||
run "${binary}" "${expect[$name]}" "${expected_rc}"
|
||||
done
|
||||
|
||||
if [ "${#failed_binaries[@]}" -gt 0 ]; then
|
||||
echo "ERROR: the following binaries failed:" >&2
|
||||
printf ' %s\n' "${failed_binaries[@]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,16 +0,0 @@
|
||||
use std::thread;
|
||||
|
||||
fn main() {
|
||||
const NUM_THREADS: usize = 10;
|
||||
let mut handles = Vec::with_capacity(NUM_THREADS);
|
||||
for id in 0..NUM_THREADS {
|
||||
handles.push(thread::spawn(move || {
|
||||
println!("Hello from thread {id}");
|
||||
}));
|
||||
}
|
||||
for handle in handles {
|
||||
handle.join().expect("worker thread panicked");
|
||||
}
|
||||
|
||||
println!("Hello from main thread");
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
use std::hint::black_box;
|
||||
|
||||
// Rust analogue of the C++ UBSan check: with overflow checks enabled the
|
||||
// compiler inserts a runtime check that panics on signed integer overflow.
|
||||
// `black_box` keeps the operands opaque so the addition is evaluated at
|
||||
// runtime rather than being rejected by the compile-time overflow lint.
|
||||
fn main() {
|
||||
let max = black_box(i32::MAX);
|
||||
let one = black_box(1);
|
||||
println!("Current max: {max}");
|
||||
let overflowed = max + one;
|
||||
println!("Overflowed result: {overflowed}");
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
fn main() {
|
||||
// Verify the panic runtime works: a panic must print its message to stderr
|
||||
// and exit with a non-zero status (Rust's default panic exit code is 101).
|
||||
panic!("explicit panic from test");
|
||||
}
|
||||
@@ -62,15 +62,5 @@ in
|
||||
runClangTidy
|
||||
vim
|
||||
zip
|
||||
# Rust packages
|
||||
cargo
|
||||
cargo-audit
|
||||
cargo-llvm-cov
|
||||
cargo-nextest
|
||||
clippy
|
||||
corrosion
|
||||
rust-analyzer
|
||||
rustc
|
||||
rustfmt
|
||||
];
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ src:test/beast/beast_PropertyStream_test.cpp
|
||||
src:src/test/app/Invariants_test.cpp
|
||||
|
||||
# ASan false positive: stack-use-after-scope in ErrorCodes.h inline functions.
|
||||
# When Clang inlines the StaticString overloads (e.g. invalidFieldError(StaticString)),
|
||||
# When Clang inlines the StaticString overloads (e.g. invalid_field_error(StaticString)),
|
||||
# ASan scope-poisons the temporary std::string before the inlined callee finishes reading
|
||||
# through the const ref. This corrupts the coroutine stack and crashes the Simulate test.
|
||||
# See asan.supp comments for full explanation and planned fix.
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
void
|
||||
extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst)
|
||||
extractTarLz4(std::filesystem::path const& src, std::filesystem::path const& dst)
|
||||
{
|
||||
if (!is_regular_file(src))
|
||||
if (!std::filesystem::is_regular_file(src))
|
||||
Throw<std::runtime_error>("Invalid source file");
|
||||
|
||||
using archive_ptr = std::unique_ptr<struct archive, void (*)(struct archive*)>;
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
#include <xrpl/basics/FileUtilities.h>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/system/detail/errc.hpp>
|
||||
#include <boost/system/detail/error_code.hpp>
|
||||
#include <boost/system/errc.hpp>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::string
|
||||
getFileContents(
|
||||
boost::system::error_code& ec,
|
||||
boost::filesystem::path const& sourcePath,
|
||||
std::error_code& ec,
|
||||
std::filesystem::path const& sourcePath,
|
||||
std::optional<std::size_t> maxSize)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
using namespace boost::system::errc;
|
||||
using namespace std::filesystem;
|
||||
|
||||
path const fullPath{canonical(sourcePath, ec)};
|
||||
if (ec)
|
||||
@@ -32,15 +27,15 @@ getFileContents(
|
||||
if (maxSize && (file_size(fullPath, ec) > *maxSize || ec))
|
||||
{
|
||||
if (!ec)
|
||||
ec = make_error_code(file_too_large);
|
||||
ec = make_error_code(std::errc::file_too_large);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::ifstream fileStream(fullPath.string(), std::ios::in);
|
||||
std::ifstream fileStream(fullPath, std::ios::in);
|
||||
|
||||
if (!fileStream)
|
||||
{
|
||||
ec = make_error_code(static_cast<errc_t>(errno));
|
||||
ec.assign(errno, std::generic_category());
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -49,7 +44,7 @@ getFileContents(
|
||||
|
||||
if (fileStream.bad())
|
||||
{
|
||||
ec = make_error_code(static_cast<errc_t>(errno));
|
||||
ec.assign(errno, std::generic_category());
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -58,18 +53,15 @@ getFileContents(
|
||||
|
||||
void
|
||||
writeFileContents(
|
||||
boost::system::error_code& ec,
|
||||
boost::filesystem::path const& destPath,
|
||||
std::error_code& ec,
|
||||
std::filesystem::path const& destPath,
|
||||
std::string const& contents)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
using namespace boost::system::errc;
|
||||
|
||||
std::ofstream fileStream(destPath.string(), std::ios::out | std::ios::trunc);
|
||||
std::ofstream fileStream(destPath, std::ios::out | std::ios::trunc);
|
||||
|
||||
if (!fileStream)
|
||||
{
|
||||
ec = make_error_code(static_cast<errc_t>(errno));
|
||||
ec.assign(errno, std::generic_category());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,7 +69,7 @@ writeFileContents(
|
||||
|
||||
if (fileStream.bad())
|
||||
{
|
||||
ec = make_error_code(static_cast<errc_t>(errno));
|
||||
ec.assign(errno, std::generic_category());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
@@ -54,7 +54,7 @@ Logs::File::isOpen() const noexcept
|
||||
}
|
||||
|
||||
bool
|
||||
Logs::File::open(boost::filesystem::path const& path)
|
||||
Logs::File::open(std::filesystem::path const& path)
|
||||
{
|
||||
close();
|
||||
|
||||
@@ -114,7 +114,7 @@ Logs::Logs(beast::Severity thresh) : thresh_(thresh) // default severity
|
||||
}
|
||||
|
||||
bool
|
||||
Logs::open(boost::filesystem::path const& pathToLogFile)
|
||||
Logs::open(std::filesystem::path const& pathToLogFile)
|
||||
{
|
||||
return file_.open(pathToLogFile);
|
||||
}
|
||||
|
||||
@@ -277,25 +277,6 @@ public:
|
||||
void
|
||||
doDropDigit(T& mantissa, int& exponent) noexcept;
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
doRoundDown(bool& negative, T& mantissa, int& exponent) const;
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
void
|
||||
doRound(rep& drops, std::string location) const;
|
||||
|
||||
private:
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
pushOverflow(T mantissa);
|
||||
|
||||
enum class Round {
|
||||
// The result is exact. No rounding is needed. Only used if cuspRoundingFix is Enabled330 or
|
||||
// higher.
|
||||
@@ -308,22 +289,37 @@ private:
|
||||
// The result was exactly half-way between two integers. This will round to even.
|
||||
Even = 0,
|
||||
// Round up. Always adds 1 (or subtracts 1 in some cases if cuspRoundingFix is not
|
||||
// Enabled330)
|
||||
// Enabled)
|
||||
Up = 1,
|
||||
};
|
||||
|
||||
// Indicate round direction. See Round enum above.
|
||||
// Indicate round direction: 1 is up, -1 is down, 0 is even
|
||||
// This enables the client to round towards nearest, and on
|
||||
// tie, round towards even.
|
||||
[[nodiscard]] Round
|
||||
round() const noexcept;
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
doRoundDown(bool& negative, T& mantissa, int& exponent);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
void
|
||||
doRound(rep& drops, std::string location) const;
|
||||
|
||||
private:
|
||||
void
|
||||
doPush(unsigned d) noexcept;
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
bringIntoRange(bool& negative, T& mantissa, int& exponent) const;
|
||||
bringIntoRange(bool& negative, T& mantissa, int& exponent);
|
||||
};
|
||||
|
||||
inline void
|
||||
@@ -353,7 +349,6 @@ Number::Guard::isNegative() const noexcept
|
||||
inline void
|
||||
Number::Guard::doPush(unsigned d) noexcept
|
||||
{
|
||||
XRPL_ASSERT(d < 10, "xrpl::Number::Guard::doPush : valid digit");
|
||||
xbit_ = xbit_ || ((digits_ & 0x0000'0000'0000'000F) != 0);
|
||||
digits_ >>= 4;
|
||||
digits_ |= (d & 0x0000'0000'0000'000FULL) << 60;
|
||||
@@ -401,69 +396,10 @@ Number::Guard::doDropDigit<uint128_t>(uint128_t& mantissa, int& exponent) noexce
|
||||
++exponent;
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::pushOverflow(T mantissa)
|
||||
{
|
||||
XRPL_ASSERT(mantissa <= kMaxRepUp, "xrpl::Number::Guard::pushOverflow : valid mantissa");
|
||||
if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 && mantissa >= kMaxRep &&
|
||||
mantissa < kMaxRepUp)
|
||||
{
|
||||
// Special case rounding rules for the values in the range [kMaxRep, kMaxRepUp).
|
||||
|
||||
auto constexpr spread = kMaxRepUp - kMaxRep;
|
||||
static_assert(spread == 3);
|
||||
|
||||
// Round in two steps.
|
||||
|
||||
// The first step uses the digits _already_ in the Guard to possibly round the mantissa up.
|
||||
// Ultimately, the purpose of this step is to capture rounding where the stored digits would
|
||||
// change the decision without those digits. (e.g. From just _below_ the midpoint to just
|
||||
// _above_ the midpoint for ToNearest, or from kMaxRep into the in-between for Upward. Make
|
||||
// an exception if the final digit is 9, because it can only get larger, and we don't want
|
||||
// to bump up to kMaxRepUp.
|
||||
if (mantissa % 10 < 9)
|
||||
{
|
||||
// Intentionally use integer math to get the largest value under the midpoint.
|
||||
auto constexpr kMidpoint = kMaxRep + (spread / 2);
|
||||
static_assert(kMidpoint == kMaxRep + 1);
|
||||
auto const r = round();
|
||||
if (r == Round::Up || (r == Round::Even && mantissa == kMidpoint))
|
||||
{
|
||||
++mantissa;
|
||||
}
|
||||
}
|
||||
|
||||
// The second step scales the final digit of the updated mantissa proportionally, converting
|
||||
// from (kMaxRep, kMaxRepUp) to (0 to 9]. It then pushes that scaled digit onto the guard as
|
||||
// if it was a digit that got removed, but doesn't actually remove it. This method should be
|
||||
// future-proof in case the number of mantissa bits ever changes. (Though for integer values
|
||||
// of the form 2^(2^x-1), the spread will always be the same.) Effects:
|
||||
// * For round to nearest
|
||||
// * if the updated mantissa is below the midpoint, it'll round "down" to kMaxRep
|
||||
// * if above the midpoint, it'll round "up" to kMaxRepUp
|
||||
// * it can never be exactly at the midpoint, because kMaxRepUp is always even, and
|
||||
// kMaxRep is always odd, so don't worry about that case.
|
||||
// * For round upward, will round up to kMaxRepUp for positive values, down to kMaxRep for
|
||||
// negative.
|
||||
// * For round downward, does the opposite of upward.
|
||||
// * For round toward zero, always rounds down to kMaxRep.
|
||||
|
||||
auto const diff = mantissa - kMaxRep;
|
||||
auto const digit = static_cast<unsigned>((diff * 10) / spread);
|
||||
XRPL_ASSERT(
|
||||
digit < 10u && digit != 5, "xrpl::Number::Guard::pushOverflow : valid overflow digit");
|
||||
|
||||
// Don't remove the digit from the mantissa, but add it to the guard as if it was.
|
||||
push(digit);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns:
|
||||
// Exact if Guard is _zero_, and appropriate amendments are enabled
|
||||
// Down if Guard is less than half
|
||||
// Even if Guard is exactly half
|
||||
// Up if Guard is greater than half
|
||||
// -1 if Guard is less than half
|
||||
// 0 if Guard is exactly half
|
||||
// 1 if Guard is greater than half
|
||||
Number::Guard::Round
|
||||
Number::Guard::round() const noexcept
|
||||
{
|
||||
@@ -509,23 +445,17 @@ Number::Guard::round() const noexcept
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent) const
|
||||
Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent)
|
||||
{
|
||||
// Bring mantissa back into the minMantissa / maxMantissa range AFTER
|
||||
// rounding.
|
||||
if (mantissa < minMantissa &&
|
||||
(cuspRoundingFix < MantissaRange::CuspRoundingFix::Enabled330 || mantissa != 0))
|
||||
// rounding
|
||||
if (mantissa < minMantissa)
|
||||
{
|
||||
mantissa *= 10;
|
||||
--exponent;
|
||||
}
|
||||
// mantissa should never be 0, but if it _is_ assert, but fall back to making the result kZero.
|
||||
if (exponent < kMinExponent ||
|
||||
(cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 && mantissa == 0))
|
||||
if (exponent < kMinExponent)
|
||||
{
|
||||
// Engineers: If you hit this assert, you probably did something wrong in the operation
|
||||
// leading up to the rounding work.
|
||||
XRPL_ASSERT(mantissa != 0, "xrpl::Number::Guard::bringIntoRange : valid mantissa");
|
||||
static constexpr Number kZero = Number{};
|
||||
|
||||
negative = kZero.negative_;
|
||||
@@ -538,9 +468,7 @@ template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location)
|
||||
{
|
||||
pushOverflow(mantissa);
|
||||
|
||||
auto const r = round();
|
||||
auto r = round();
|
||||
if (r == Round::Up || (r == Round::Even && (mantissa & 1) == 1))
|
||||
{
|
||||
auto const safeToIncrement = [this](auto const& mantissa) {
|
||||
@@ -557,29 +485,18 @@ Number::Guard::doRoundUp(bool& negative, T& mantissa, int& exponent, std::string
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 &&
|
||||
mantissa > kMaxRep && mantissa < kMaxRepUp)
|
||||
{
|
||||
// When rounding up a value in between kMaxRep, and kMaxRepUp, round to
|
||||
// kMaxRepUp. Note that the decision for this rounding is dominated by the
|
||||
// results of pushOverflow.
|
||||
mantissa = kMaxRepUp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Incrementing the mantissa will require dividing, which will require rounding.
|
||||
// So _don't_ increment the mantissa. Instead, divide and round recursively. It
|
||||
// should be impossible to recurse more than once, because once the mantissa is
|
||||
// divided by 10, it will be _well_ under maxMantissa and kMaxRep, so adding 1
|
||||
// will have no chance of bringing it back over.
|
||||
doDropDigit(mantissa, exponent);
|
||||
XRPL_ASSERT_PARTS(
|
||||
safeToIncrement(mantissa),
|
||||
"xrpl::Number::Guard::doRoundUp",
|
||||
"can't recurse more than once");
|
||||
doRoundUp(negative, mantissa, exponent, location);
|
||||
return;
|
||||
}
|
||||
// Incrementing the mantissa will require dividing, which will require rounding. So
|
||||
// _don't_ increment the mantissa. Instead, divide and round recursively. It should
|
||||
// be impossible to recurse more than once, because once the mantissa is divided by
|
||||
// 10, it will be _well_ under maxMantissa and kMaxRep, so adding 1 will have no
|
||||
// chance of bringing it back over.
|
||||
doDropDigit(mantissa, exponent);
|
||||
XRPL_ASSERT_PARTS(
|
||||
safeToIncrement(mantissa),
|
||||
"xrpl::Number::Guard::doRoundUp",
|
||||
"can't recurse more than once");
|
||||
doRoundUp(negative, mantissa, exponent, location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -597,14 +514,6 @@ Number::Guard::doRoundUp(bool& negative, T& mantissa, int& exponent, std::string
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (
|
||||
cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 && mantissa > kMaxRep &&
|
||||
mantissa < kMaxRepUp)
|
||||
{
|
||||
// When rounding down a value in between kMaxRep, and kMaxRepUp, round to kMaxRep.
|
||||
// Note that the decision for this rounding is dominated by the results of pushOverflow.
|
||||
mantissa = kMaxRep;
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent);
|
||||
if (exponent > kMaxExponent)
|
||||
Throw<std::overflow_error>(std::string(location));
|
||||
@@ -612,10 +521,8 @@ Number::Guard::doRoundUp(bool& negative, T& mantissa, int& exponent, std::string
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent) const
|
||||
Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent)
|
||||
{
|
||||
// Do not pushOverflow here.
|
||||
|
||||
auto r = round();
|
||||
if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330)
|
||||
{
|
||||
@@ -650,8 +557,6 @@ Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent) const
|
||||
void
|
||||
Number::Guard::doRound(rep& drops, std::string location) const
|
||||
{
|
||||
// Do not pushOverflow here.
|
||||
|
||||
auto r = round();
|
||||
if (r == Round::Up || (r == Round::Even && (drops & 1) == 1))
|
||||
{
|
||||
@@ -668,8 +573,6 @@ Number::Guard::doRound(rep& drops, std::string location) const
|
||||
}
|
||||
++drops;
|
||||
}
|
||||
XRPL_ASSERT(drops >= 0, "xrpl::Number::Guard::doRound : positive magnitude");
|
||||
|
||||
if (isNegative())
|
||||
drops = -drops;
|
||||
}
|
||||
@@ -719,9 +622,7 @@ doNormalize(
|
||||
{
|
||||
static constexpr auto kMinExponent = Number::kMinExponent;
|
||||
static constexpr auto kMaxExponent = Number::kMaxExponent;
|
||||
auto const repLimit = cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330
|
||||
? Number::kMaxRepUp
|
||||
: Number::kMaxRep;
|
||||
static constexpr auto kMaxRep = Number::kMaxRep;
|
||||
|
||||
using Guard = Number::Guard;
|
||||
|
||||
@@ -771,17 +672,17 @@ doNormalize(
|
||||
// 9,900,000,000,000,123,450 or 9,900,000,000,000,123,460.
|
||||
// mantissa() will return mantissa / 10, and exponent() will return
|
||||
// exponent + 1.
|
||||
if (m > repLimit)
|
||||
if (m > kMaxRep)
|
||||
{
|
||||
if (exponent >= kMaxExponent)
|
||||
throw std::overflow_error("Number::normalize 1.5");
|
||||
g.doDropDigit(m, exponent);
|
||||
}
|
||||
// Before modification, m should be within the min/max range. After
|
||||
// modification, it must be less than repLimit. In other words, the original
|
||||
// value should have been no more than repLimit * 10.
|
||||
// (repLimit * 10 > maxMantissa)
|
||||
XRPL_ASSERT_PARTS(m <= repLimit, "xrpl::doNormalize", "intermediate mantissa fits in limit");
|
||||
// modification, it must be less than kMaxRep. In other words, the original
|
||||
// value should have been no more than kMaxRep * 10.
|
||||
// (kMaxRep * 10 > maxMantissa)
|
||||
XRPL_ASSERT_PARTS(m <= kMaxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64");
|
||||
mantissa = m;
|
||||
|
||||
g.doRoundUp(negative, mantissa, exponent, "Number::normalize 2");
|
||||
@@ -913,9 +814,6 @@ Number::operator+=(Number const& y)
|
||||
auto const& maxMantissa = g.maxMantissa;
|
||||
auto const cuspRoundingFix = g.cuspRoundingFix;
|
||||
|
||||
auto const repLimit =
|
||||
cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 ? kMaxRepUp : kMaxRep;
|
||||
|
||||
// Bring the exponents of both values into agreement, so the mantissas are on the same scale
|
||||
// and can be added directly together.
|
||||
|
||||
@@ -1000,7 +898,7 @@ Number::operator+=(Number const& y)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xm > maxMantissa || xm > repLimit)
|
||||
if (xm > maxMantissa || xm > kMaxRep)
|
||||
{
|
||||
g.doDropDigit(xm, xe);
|
||||
}
|
||||
@@ -1044,7 +942,7 @@ Number::operator+=(Number const& y)
|
||||
{
|
||||
// Grow xm/xe and pull digits out of the Guard until it's back in the
|
||||
// minMantissa/maxMantissa range.
|
||||
while (xm < minMantissa && xm * 10 <= repLimit)
|
||||
while (xm < minMantissa && xm * 10 <= kMaxRep)
|
||||
{
|
||||
xm *= 10;
|
||||
xm -= g.pop();
|
||||
@@ -1118,10 +1016,8 @@ Number::operator*=(Number const& y)
|
||||
g.setNegative();
|
||||
|
||||
auto const& maxMantissa = g.maxMantissa;
|
||||
auto const repLimit =
|
||||
g.cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 ? kMaxRepUp : kMaxRep;
|
||||
|
||||
while (zm > maxMantissa || zm > repLimit)
|
||||
while (zm > maxMantissa || zm > kMaxRep)
|
||||
{
|
||||
g.doDropDigit(zm, ze);
|
||||
}
|
||||
@@ -1386,11 +1282,8 @@ to_string(Number const& amount)
|
||||
}
|
||||
std::string ret = negative ? "-" : "";
|
||||
ret.append(std::to_string(mantissa));
|
||||
if (exponent != 0)
|
||||
{
|
||||
ret.append(1, 'e');
|
||||
ret.append(std::to_string(exponent));
|
||||
}
|
||||
ret.append(1, 'e');
|
||||
ret.append(std::to_string(exponent));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,11 +127,15 @@ Workers::deleteWorkers(beast::LockFreeStack<Worker>& stack)
|
||||
{
|
||||
Worker const* const worker = stack.popFront();
|
||||
|
||||
if (worker == nullptr)
|
||||
if (worker != nullptr)
|
||||
{
|
||||
// This call blocks until the thread orderly exits
|
||||
delete worker;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
|
||||
// This call blocks until the thread orderly exits
|
||||
delete worker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -314,7 +314,8 @@ Value::~Value()
|
||||
|
||||
case ValueType::Array:
|
||||
case ValueType::Object:
|
||||
delete value_.mapVal;
|
||||
if (value_.mapVal != nullptr)
|
||||
delete value_.mapVal;
|
||||
break;
|
||||
|
||||
// LCOV_EXCL_START
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
@@ -81,9 +80,13 @@ isAnyFrozen(
|
||||
return true;
|
||||
}
|
||||
|
||||
return std::ranges::any_of(accounts, [&](auto const& account) {
|
||||
return isVaultPseudoAccountFrozen(view, account, mptIssue, depth);
|
||||
});
|
||||
for (auto const& account : accounts)
|
||||
{
|
||||
if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Rate
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
@@ -106,9 +105,12 @@ isAnyFrozen(
|
||||
std::initializer_list<AccountID> const& accounts,
|
||||
Issue const& issue)
|
||||
{
|
||||
return std::ranges::any_of(accounts, [&](auto const& account) {
|
||||
return isFrozen(view, account, issue.currency, issue.account);
|
||||
});
|
||||
for (auto const& account : accounts)
|
||||
{
|
||||
if (isFrozen(view, account, issue.currency, issue.account))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <xrpl/nodestore/Scheduler.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
@@ -53,7 +52,6 @@ DatabaseRotatingImp::rotate(
|
||||
// callback finishes. Only then will the archive directory be
|
||||
// deleted.
|
||||
std::shared_ptr<NodeStore::Backend> oldArchiveBackend;
|
||||
std::uint64_t copyForwards = 0;
|
||||
{
|
||||
std::scoped_lock const lock(mutex_);
|
||||
|
||||
@@ -64,28 +62,11 @@ DatabaseRotatingImp::rotate(
|
||||
newArchiveBackendName = archiveBackend_->getName();
|
||||
|
||||
writableBackend_ = std::move(newBackend);
|
||||
|
||||
copyForwards = copyForwardCount_.exchange(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (copyForwards > 0)
|
||||
{
|
||||
JLOG(j_.warn()) << "Rotating: copied forward " << copyForwards
|
||||
<< " archive-served reads into the writable backend "
|
||||
"during the rotation window";
|
||||
}
|
||||
|
||||
f(newWritableBackendName, newArchiveBackendName);
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseRotatingImp::setRotationInFlight(bool inFlight)
|
||||
{
|
||||
rotationInFlight_.store(inFlight, std::memory_order_release);
|
||||
JLOG(j_.debug()) << "Rotating: copy-forward on archive reads "
|
||||
<< (inFlight ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
std::string
|
||||
DatabaseRotatingImp::getName() const
|
||||
{
|
||||
@@ -196,18 +177,9 @@ DatabaseRotatingImp::fetchNodeObject(
|
||||
writable = writableBackend_;
|
||||
}
|
||||
|
||||
// Update writable backend with data from the archive backend.
|
||||
// While a rotation is in flight, ordinary (duplicate == false)
|
||||
// reads served by the archive are copied forward too: the
|
||||
// archive is about to be deleted, and a body canonicalized
|
||||
// into the cache after the freshen getKeys() snapshot would
|
||||
// otherwise survive only in RAM once the archive is dropped.
|
||||
if (duplicate || rotationInFlight_.load(std::memory_order_acquire))
|
||||
{
|
||||
if (!duplicate)
|
||||
copyForwardCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
// Update writable backend with data from the archive backend
|
||||
if (duplicate)
|
||||
writable->store(nodeObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#include <xrpl/nodestore/detail/EncodedBlob.h>
|
||||
#include <xrpl/nodestore/detail/codec.h>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/system/detail/errc.hpp>
|
||||
|
||||
#include <nudb/context.hpp>
|
||||
@@ -36,12 +34,14 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
void
|
||||
open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt) override
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
using namespace std::filesystem;
|
||||
if (db.is_open())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -194,11 +194,12 @@ public:
|
||||
|
||||
if (deletePath)
|
||||
{
|
||||
boost::filesystem::remove_all(name, ec);
|
||||
if (ec)
|
||||
std::error_code fsec;
|
||||
std::filesystem::remove_all(name, fsec);
|
||||
if (fsec)
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
<< "Filesystem remove_all of " << name << " failed with: " << ec.message();
|
||||
JLOG(j.fatal()) << "Filesystem remove_all of " << name
|
||||
<< " failed with: " << fsec.message();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,7 +353,7 @@ private:
|
||||
static std::size_t
|
||||
parseBlockSize(std::string const& name, Section const& keyValues, beast::Journal journal)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
using namespace std::filesystem;
|
||||
auto const folder = path(name);
|
||||
auto const kp = (folder / "nudb.key").string();
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
|
||||
#include <xrpl/nodestore/detail/EncodedBlob.h>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <rocksdb/advanced_options.h>
|
||||
#include <rocksdb/cache.h>
|
||||
#include <rocksdb/compression_type.h>
|
||||
@@ -37,6 +34,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
@@ -262,8 +260,8 @@ public:
|
||||
db.reset();
|
||||
if (deletePath_)
|
||||
{
|
||||
boost::filesystem::path const dir = name;
|
||||
boost::filesystem::remove_all(dir);
|
||||
std::filesystem::path const dir = name;
|
||||
std::filesystem::remove_all(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol/nftPageMask.h>
|
||||
|
||||
#include <boost/endian/conversion.hpp>
|
||||
@@ -33,23 +32,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// This list should include all of the keylet functions that take a single
|
||||
// AccountID parameter. Declared in Indexes.h; defined here so the header need
|
||||
// not include jss.h.
|
||||
std::array<KeyletDesc<AccountID const&>, 6> const kDirectAccountKeylets{
|
||||
{{.function = &keylet::account, .expectedLEName = jss::AccountRoot, .includeInTests = false},
|
||||
{.function = &keylet::ownerDir, .expectedLEName = jss::DirectoryNode, .includeInTests = true},
|
||||
{.function = &keylet::signerList, .expectedLEName = jss::SignerList, .includeInTests = true},
|
||||
// It's normally impossible to create an item at nftpage_min, but
|
||||
// test it anyway, since the invariant checks for it.
|
||||
{.function = &keylet::nftokenPageMin,
|
||||
.expectedLEName = jss::NFTokenPage,
|
||||
.includeInTests = true},
|
||||
{.function = &keylet::nftokenPageMax,
|
||||
.expectedLEName = jss::NFTokenPage,
|
||||
.includeInTests = true},
|
||||
{.function = &keylet::did, .expectedLEName = jss::DID, .includeInTests = true}}};
|
||||
|
||||
/**
|
||||
* Type-specific prefix for calculating ledger indices.
|
||||
*
|
||||
|
||||
@@ -45,7 +45,7 @@ setCurrentTransactionRules(std::optional<Rules> r)
|
||||
// amendments must also be added to useRulesGuards.
|
||||
bool const enableLargeNumbers =
|
||||
!r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol));
|
||||
// If enableLargeNumbers is true, then useRulesGuards must also return true.
|
||||
// If enableLargeNumbers is true, then useRulesGuard must also return true.
|
||||
// However, the reverse is not true. Other amendments can cause the rules guard to be used,
|
||||
// even though large numbers are _not_ used.
|
||||
XRPL_ASSERT(
|
||||
|
||||
@@ -255,47 +255,8 @@ numberFromJson(SField const& field, json::Value const& value)
|
||||
Throw<std::runtime_error>("not a number");
|
||||
}
|
||||
|
||||
Number const num{parts.negative, parts.mantissa, parts.exponent, Number::Normalized{}};
|
||||
|
||||
// Canonicalize "parts" and "num" with each other by getting rid of trailing 0s until either the
|
||||
// exponents match, or there are no more 0s. If the two results don't match exactly, then the
|
||||
// value has been rounded one way or another, and should not be used, because it may lead to an
|
||||
// unexpected result. canonicalizeParts is not to be confused with Number::canonicalize, because
|
||||
// they have completely different goals.
|
||||
auto canonicalizeParts = [](NumberParts p, int otherExponent) {
|
||||
if (p.mantissa == 0)
|
||||
return NumberParts{};
|
||||
|
||||
while (p.exponent < otherExponent && p.mantissa % 10 == 0)
|
||||
{
|
||||
p.mantissa /= 10;
|
||||
++p.exponent;
|
||||
}
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
auto const numberMantissa = num.mantissa();
|
||||
auto const numberExponent = num.exponent();
|
||||
|
||||
auto const canonicalParts = canonicalizeParts(parts, numberExponent);
|
||||
|
||||
auto const canonicalNum = canonicalizeParts(
|
||||
NumberParts{
|
||||
.mantissa = Number::externalToInternal(numberMantissa),
|
||||
.exponent = numberExponent,
|
||||
.negative = numberMantissa < 0,
|
||||
},
|
||||
canonicalParts.exponent);
|
||||
|
||||
if (canonicalParts.mantissa != canonicalNum.mantissa ||
|
||||
canonicalParts.exponent != canonicalNum.exponent ||
|
||||
canonicalParts.negative != canonicalNum.negative)
|
||||
{
|
||||
Throw<std::runtime_error>("number cannot be represented");
|
||||
}
|
||||
|
||||
return STNumber{field, num};
|
||||
return STNumber{
|
||||
field, Number{parts.negative, parts.mantissa, parts.exponent, Number::Normalized{}}};
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -710,7 +710,7 @@ STObject::getFieldNumber(SField const& field) const
|
||||
void
|
||||
STObject::set(std::unique_ptr<STBase> v)
|
||||
{
|
||||
set(std::move(*v));
|
||||
set(std::move(*v.get()));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
@@ -160,10 +159,13 @@ STPathSet::isDefault() const
|
||||
bool
|
||||
STPath::hasSeen(AccountID const& account, PathAsset const& asset, AccountID const& issuer) const
|
||||
{
|
||||
return std::ranges::any_of(path_, [&](auto& p) {
|
||||
return p.getAccountID() == account && p.getPathAsset() == asset &&
|
||||
p.getIssuerID() == issuer;
|
||||
});
|
||||
for (auto& p : path_)
|
||||
{
|
||||
if (p.getAccountID() == account && p.getPathAsset() == asset && p.getIssuerID() == issuer)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
json::Value
|
||||
|
||||
@@ -64,7 +64,7 @@ to_string(Currency const& currency)
|
||||
bool
|
||||
toCurrency(Currency& currency, std::string const& code)
|
||||
{
|
||||
if (code.empty() || code == systemCurrencyCode())
|
||||
if (code.empty() || (code.compare(systemCurrencyCode()) == 0))
|
||||
{
|
||||
currency = beast::kZero;
|
||||
return true;
|
||||
|
||||
@@ -5,19 +5,17 @@
|
||||
#include <xrpl/core/JobQueue.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <soci/blob.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#ifdef __clang__
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
@@ -45,8 +43,8 @@ getSociSqliteInit(std::string const& name, std::string const& dir, std::string c
|
||||
Throw<std::runtime_error>(
|
||||
"Sqlite databases must specify a dir and a name. Name: " + name + " Dir: " + dir);
|
||||
}
|
||||
boost::filesystem::path file(dir);
|
||||
if (is_directory(file))
|
||||
std::filesystem::path file(dir);
|
||||
if (std::filesystem::is_directory(file))
|
||||
file /= name + ext;
|
||||
return file.string();
|
||||
}
|
||||
@@ -342,6 +340,6 @@ makeCheckpointer(
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#ifdef __clang__
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
#include <xrpl/rdb/DBInit.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/format.hpp> // IWYU pragma: keep
|
||||
|
||||
#include <soci/into.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
@@ -20,12 +19,12 @@ namespace xrpl {
|
||||
bool
|
||||
doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j)
|
||||
{
|
||||
boost::filesystem::path const dbPath = setup.dataDir / kTxDbName;
|
||||
std::filesystem::path const dbPath = setup.dataDir / kTxDbName;
|
||||
|
||||
uintmax_t const dbSize = file_size(dbPath);
|
||||
uintmax_t const dbSize = std::filesystem::file_size(dbPath);
|
||||
XRPL_ASSERT(dbSize != static_cast<uintmax_t>(-1), "xrpl::doVacuumDB : file_size succeeded");
|
||||
|
||||
if (auto available = space(dbPath.parent_path()).available; available < dbSize)
|
||||
if (auto available = std::filesystem::space(dbPath.parent_path()).available; available < dbSize)
|
||||
{
|
||||
std::cerr << "The database filesystem must have at least as "
|
||||
"much free space as the size of "
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
template <class T>
|
||||
inline bool
|
||||
checkBounds(T const& value, T const& min, T const& max)
|
||||
{
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool
|
||||
checkMax(T const& value, T const& max)
|
||||
{
|
||||
return value <= max;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool
|
||||
checkSize(T const& value, std::size_t const max)
|
||||
{
|
||||
return value.size() <= max;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool
|
||||
checkSizeNonEmpty(T const& value, std::size_t const max)
|
||||
{
|
||||
return value.size() <= max && !value.empty();
|
||||
}
|
||||
|
||||
// Checks whether a hash-like identifier field (e.g. a uint256 object ID) is
|
||||
// unset/zero.
|
||||
template <class T>
|
||||
inline bool
|
||||
isZeroId(T const& id)
|
||||
{
|
||||
return id == beast::kZero;
|
||||
}
|
||||
|
||||
// Checks whether an amount is a strictly positive XRP amount.
|
||||
inline bool
|
||||
isPositiveXRPAmount(xrpl::STAmount const& amount)
|
||||
{
|
||||
return xrpl::isXRP(amount) && amount > beast::kZero;
|
||||
}
|
||||
|
||||
// Checks whether an amount (of any asset type) is strictly positive.
|
||||
inline bool
|
||||
isPositiveAmount(xrpl::STAmount const& amount)
|
||||
{
|
||||
return amount > beast::kZero;
|
||||
}
|
||||
|
||||
// Checks whether a currency code is the reserved "bad"/XRP currency code,
|
||||
// i.e. not a valid IOU currency.
|
||||
inline bool
|
||||
isBadCurrency(xrpl::Currency const& currency)
|
||||
{
|
||||
return xrpl::badCurrency() == currency;
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -13,7 +12,6 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -90,15 +88,17 @@ ValidBookDirectory::finalize(
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::ranges::all_of(rootIndexes_, [&](auto const& rootIndex) {
|
||||
for (auto const& rootIndex : rootIndexes_)
|
||||
{
|
||||
auto const root = view.read(Keylet(ltDIR_NODE, rootIndex));
|
||||
if (!root)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: book directory root missing";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -74,8 +73,8 @@ TransfersNotFrozen::finalize(
|
||||
*/
|
||||
[[maybe_unused]] bool const enforce = view.rules().enabled(featureDeepFreeze);
|
||||
|
||||
return std::ranges::all_of(balanceChanges_, [&](auto const& entry) {
|
||||
auto const& [issue, changes] = entry;
|
||||
for (auto const& [issue, changes] : balanceChanges_)
|
||||
{
|
||||
auto const issuerSle = findIssuer(issue.account, view);
|
||||
// It should be impossible for the issuer to not be found, but check
|
||||
// just in case so xrpld doesn't crash in release.
|
||||
@@ -87,11 +86,20 @@ TransfersNotFrozen::finalize(
|
||||
enforce,
|
||||
"xrpl::TransfersNotFrozen::finalize : enforce "
|
||||
"invariant.");
|
||||
return !enforce;
|
||||
if (enforce)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
return validateIssuerChanges(issuerSle, changes, tx, j, enforce);
|
||||
});
|
||||
if (!validateIssuerChanges(issuerSle, changes, tx, j, enforce))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -162,37 +162,33 @@ XRPNotCreated::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref a
|
||||
}
|
||||
}
|
||||
|
||||
if (!after)
|
||||
if (after)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::XRPNotCreated::visitEntry : after can't be null");
|
||||
return;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
switch (after->getType())
|
||||
{
|
||||
case ltACCOUNT_ROOT:
|
||||
drops_ += (*after)[sfBalance].xrp().drops();
|
||||
break;
|
||||
case ltPAYCHAN:
|
||||
if (!isDelete)
|
||||
drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
|
||||
break;
|
||||
case ltESCROW:
|
||||
if (!isDelete && isXRP((*after)[sfAmount]))
|
||||
drops_ += (*after)[sfAmount].xrp().drops();
|
||||
break;
|
||||
case ltSPONSORSHIP:
|
||||
if (!isDelete && after->isFieldPresent(sfFeeAmount))
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
isXRP((*after)[sfFeeAmount]),
|
||||
"XRPNotCreated::visitEntry : Sponsorship.FeeAmount is XRP");
|
||||
drops_ += (*after)[sfFeeAmount].xrp().drops();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
switch (after->getType())
|
||||
{
|
||||
case ltACCOUNT_ROOT:
|
||||
drops_ += (*after)[sfBalance].xrp().drops();
|
||||
break;
|
||||
case ltPAYCHAN:
|
||||
if (!isDelete)
|
||||
drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
|
||||
break;
|
||||
case ltESCROW:
|
||||
if (!isDelete && isXRP((*after)[sfAmount]))
|
||||
drops_ += (*after)[sfAmount].xrp().drops();
|
||||
break;
|
||||
case ltSPONSORSHIP:
|
||||
if (!isDelete && after->isFieldPresent(sfFeeAmount))
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
isXRP((*after)[sfFeeAmount]),
|
||||
"XRPNotCreated::visitEntry : Sponsorship.FeeAmount is XRP");
|
||||
drops_ += (*after)[sfFeeAmount].xrp().drops();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +485,7 @@ AccountRootsDeletedClean::finalize(
|
||||
// feature is enabled. Enabled, or not, though, a fatal-level message will
|
||||
// be logged
|
||||
[[maybe_unused]] bool const enforce = view.rules().enabled(fixCleanup3_2_0) ||
|
||||
view.rules().enabled(featureSponsor) || view.rules().enabled(featureSingleAssetVault) ||
|
||||
view.rules().enabled(featureSingleAssetVault) ||
|
||||
view.rules().enabled(featureLendingProtocol);
|
||||
|
||||
auto const objectExists = [&view, enforce, &j](auto const& keylet) {
|
||||
@@ -803,49 +799,14 @@ ValidNewAccountRoot::finalize(
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static std::optional<STAmount>
|
||||
clawbackTrustLineBalanceInHolderTerms(
|
||||
SLE::const_pointer const& sle,
|
||||
AccountID const& holder,
|
||||
AccountID const& issuer,
|
||||
Currency const& currency)
|
||||
{
|
||||
if (!sle)
|
||||
return STAmount{Issue{currency, issuer}};
|
||||
|
||||
if (sle->getType() != ltRIPPLE_STATE ||
|
||||
sle->key() != keylet::trustLine(holder, issuer, currency).key)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
STAmount balance = sle->getFieldAmount(sfBalance);
|
||||
if (holder > issuer)
|
||||
balance.negate();
|
||||
balance.get<Issue>().account = issuer;
|
||||
return balance;
|
||||
}
|
||||
|
||||
void
|
||||
ValidClawback::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
|
||||
ValidClawback::visitEntry(bool, SLE::const_ref before, SLE::const_ref)
|
||||
{
|
||||
if (before && before->getType() == ltRIPPLE_STATE)
|
||||
{
|
||||
trustlinesChanged_++;
|
||||
iou_.before = before;
|
||||
}
|
||||
|
||||
if (!isDelete && after && after->getType() == ltRIPPLE_STATE)
|
||||
iou_.after = after;
|
||||
|
||||
if (before && before->getType() == ltMPTOKEN)
|
||||
{
|
||||
mptokensChanged_++;
|
||||
mpt_.before = before;
|
||||
}
|
||||
|
||||
if (!isDelete && after && after->getType() == ltMPTOKEN)
|
||||
mpt_.after = after;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -874,109 +835,31 @@ ValidClawback::finalize(
|
||||
}
|
||||
|
||||
bool const mptV2Enabled = view.rules().enabled(featureMPTokensV2);
|
||||
if (trustlinesChanged_ != 0 && mptokensChanged_ != 0)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: trustline and MPToken both changed.";
|
||||
if (mptV2Enabled)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trustlinesChanged_ == 1 || (mptV2Enabled && mptokensChanged_ == 1))
|
||||
{
|
||||
AccountID const issuer = tx.getAccountID(sfAccount);
|
||||
STAmount const& amount = tx.getFieldAmount(sfAmount);
|
||||
|
||||
return amount.asset().visit(
|
||||
AccountID const& holder = amount.getIssuer();
|
||||
STAmount const holderBalance = amount.asset().visit(
|
||||
[&](Issue const& issue) {
|
||||
AccountID const issuer = tx.getAccountID(sfAccount);
|
||||
AccountID const& holder = amount.getIssuer();
|
||||
STAmount const holderBalance = accountHolds(
|
||||
return accountHolds(
|
||||
view, holder, issue.currency, issuer, FreezeHandling::IgnoreFreeze, j);
|
||||
|
||||
if (holderBalance.signum() < 0)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: trustline or MPT balance is negative";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!iou_.before)
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
<< "Invariant failed: trustline clawback changed the wrong line";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
auto const beforeBalance = clawbackTrustLineBalanceInHolderTerms(
|
||||
iou_.before, holder, issuer, issue.currency);
|
||||
auto const afterBalance = clawbackTrustLineBalanceInHolderTerms(
|
||||
iou_.after, holder, issuer, issue.currency);
|
||||
if (!beforeBalance || !afterBalance)
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
<< "Invariant failed: trustline clawback changed the wrong line";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
STAmount clawAmount = amount;
|
||||
clawAmount.get<Issue>().account = issuer;
|
||||
if (clawAmount <= beast::kZero)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: trustline clawback amount is invalid";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
if (*afterBalance > *beforeBalance ||
|
||||
(*beforeBalance - *afterBalance) != std::min(*beforeBalance, clawAmount))
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
<< "Invariant failed: trustline clawback balance change is invalid";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[&](MPTIssue const& issue) {
|
||||
auto const holder = tx[~sfHolder];
|
||||
if (!holder)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: MPT clawback missing holder";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
if (!mpt_.before || !mpt_.after)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: MPT clawback token is missing";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
if (mpt_.before->getAccountID(sfAccount) != *holder ||
|
||||
mpt_.after->getAccountID(sfAccount) != *holder ||
|
||||
(*mpt_.before)[sfMPTokenIssuanceID] != issue.getMptID() ||
|
||||
(*mpt_.after)[sfMPTokenIssuanceID] != issue.getMptID())
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: MPT clawback changed the wrong token";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
auto const before = mpt_.before->getFieldU64(sfMPTAmount);
|
||||
auto const after = mpt_.after->getFieldU64(sfMPTAmount);
|
||||
if (amount.negative() || amount.mantissa() == 0)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: MPT clawback amount is invalid";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
auto const clawAmount = amount.mantissa();
|
||||
|
||||
// MPT balances are unsigned, so validate the raw holder
|
||||
// debit instead of routing through accountHolds().
|
||||
if (after > before || (before - after) != std::min(before, clawAmount))
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
<< "Invariant failed: MPT clawback balance change is invalid";
|
||||
return !mptV2Enabled;
|
||||
}
|
||||
|
||||
return true;
|
||||
return accountHolds(
|
||||
view,
|
||||
holder,
|
||||
issue,
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
AuthHandling::IgnoreAuth,
|
||||
j);
|
||||
});
|
||||
|
||||
if (holderBalance.signum() < 0)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: trustline or MPT balance is negative";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
void
|
||||
@@ -129,8 +127,8 @@ ValidLoanBroker::finalize(
|
||||
}
|
||||
}
|
||||
|
||||
return std::ranges::all_of(brokers_, [&](auto const& entry) {
|
||||
auto const& [brokerID, broker] = entry;
|
||||
for (auto const& [brokerID, broker] : brokers_)
|
||||
{
|
||||
auto const& after =
|
||||
broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanBroker(brokerID));
|
||||
|
||||
@@ -206,8 +204,8 @@ ValidLoanBroker::finalize(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -403,7 +403,7 @@ ValidMPTIssuance::finalize(
|
||||
}
|
||||
|
||||
void
|
||||
ValidMPTBalanceChanges::visitEntry(bool, SLE::const_ref before, SLE::const_ref after)
|
||||
ValidMPTPayment::visitEntry(bool, SLE::const_ref before, SLE::const_ref after)
|
||||
{
|
||||
if (overflow_)
|
||||
return;
|
||||
@@ -465,7 +465,7 @@ ValidMPTBalanceChanges::visitEntry(bool, SLE::const_ref before, SLE::const_ref a
|
||||
}
|
||||
|
||||
bool
|
||||
ValidMPTBalanceChanges::finalize(
|
||||
ValidMPTPayment::finalize(
|
||||
STTx const& tx,
|
||||
TER const result,
|
||||
XRPAmount const,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include <xrpl/tx/transactors/check/CheckCancel.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -21,9 +19,6 @@ namespace xrpl {
|
||||
NotTEC
|
||||
CheckCancel::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (ctx.rules.enabled(fixCleanup3_3_0) && ctx.tx[sfCheckID] == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/scope.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
@@ -52,9 +51,6 @@ CheckCash::checkExtraFeatures(xrpl::PreflightContext const& ctx)
|
||||
NotTEC
|
||||
CheckCash::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (ctx.rules.enabled(fixCleanup3_3_0) && ctx.tx[sfCheckID] == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
// Exactly one of Amount or DeliverMin must be present.
|
||||
auto const optAmount = ctx.tx[~sfAmount];
|
||||
auto const optDeliverMin = ctx.tx[~sfDeliverMin];
|
||||
|
||||
@@ -28,12 +28,11 @@
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
@@ -102,10 +101,10 @@ NotTEC
|
||||
escrowCreatePreflightHelper<Issue>(PreflightContext const& ctx)
|
||||
{
|
||||
STAmount const amount = ctx.tx[sfAmount];
|
||||
if (amount.native() || !isPositiveAmount(amount))
|
||||
if (amount.native() || amount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (isBadCurrency(amount.get<Issue>().currency))
|
||||
if (badCurrency() == amount.get<Issue>().currency)
|
||||
return temBAD_CURRENCY;
|
||||
|
||||
return tesSUCCESS;
|
||||
@@ -119,7 +118,7 @@ escrowCreatePreflightHelper<MPTIssue>(PreflightContext const& ctx)
|
||||
return temDISABLED;
|
||||
|
||||
auto const amount = ctx.tx[sfAmount];
|
||||
if (amount.native() || amount.mpt() > MPTAmount{kMaxMpTokenAmount} || !isPositiveAmount(amount))
|
||||
if (amount.native() || amount.mpt() > MPTAmount{kMaxMpTokenAmount} || amount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
return tesSUCCESS;
|
||||
@@ -142,7 +141,7 @@ EscrowCreate::preflight(PreflightContext const& ctx)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isPositiveXRPAmount(amount))
|
||||
if (amount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
@@ -50,7 +48,7 @@ LoanBrokerCoverClawback::preflight(PreflightContext const& ctx)
|
||||
if (!brokerID && !amount)
|
||||
return temINVALID;
|
||||
|
||||
if (brokerID && isZeroId(*brokerID))
|
||||
if (brokerID && *brokerID == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
if (amount)
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
bool
|
||||
@@ -29,11 +27,11 @@ LoanBrokerCoverDeposit::checkExtraFeatures(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
LoanBrokerCoverDeposit::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (isZeroId(ctx.tx[sfLoanBrokerID]))
|
||||
if (ctx.tx[sfLoanBrokerID] == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
auto const dstAmount = ctx.tx[sfAmount];
|
||||
if (!isPositiveAmount(dstAmount))
|
||||
if (dstAmount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (!isLegalNet(dstAmount))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
@@ -19,8 +20,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
bool
|
||||
@@ -32,11 +31,11 @@ LoanBrokerCoverWithdraw::checkExtraFeatures(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
LoanBrokerCoverWithdraw::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (isZeroId(ctx.tx[sfLoanBrokerID]))
|
||||
if (ctx.tx[sfLoanBrokerID] == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
auto const dstAmount = ctx.tx[sfAmount];
|
||||
if (!isPositiveAmount(dstAmount))
|
||||
if (dstAmount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (!isLegalNet(dstAmount))
|
||||
@@ -44,7 +43,7 @@ LoanBrokerCoverWithdraw::preflight(PreflightContext const& ctx)
|
||||
|
||||
if (auto const destination = ctx.tx[~sfDestination])
|
||||
{
|
||||
if (isZeroId(*destination))
|
||||
if (*destination == beast::kZero)
|
||||
{
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
bool
|
||||
@@ -31,7 +29,7 @@ LoanBrokerDelete::checkExtraFeatures(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
LoanBrokerDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (isZeroId(ctx.tx[sfLoanBrokerID]))
|
||||
if (ctx.tx[sfLoanBrokerID] == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
@@ -19,8 +20,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -58,13 +57,13 @@ LoanBrokerSet::preflight(PreflightContext const& ctx)
|
||||
tx.isFieldPresent(sfCoverRateLiquidation))
|
||||
return temINVALID;
|
||||
|
||||
if (isZeroId(tx[sfLoanBrokerID]))
|
||||
if (tx[sfLoanBrokerID] == beast::kZero)
|
||||
return temINVALID;
|
||||
}
|
||||
|
||||
if (auto const vaultID = tx.at(~sfVaultID))
|
||||
{
|
||||
if (isZeroId(*vaultID))
|
||||
if (*vaultID == beast::kZero)
|
||||
return temINVALID;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
bool
|
||||
@@ -29,7 +27,7 @@ LoanDelete::checkExtraFeatures(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
LoanDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (isZeroId(ctx.tx[sfLoanID]))
|
||||
if (ctx.tx[sfLoanID] == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
@@ -23,8 +24,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
namespace xrpl {
|
||||
@@ -44,7 +43,7 @@ LoanManage::getFlagsMask(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
LoanManage::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (isZeroId(ctx.tx[sfLoanID]))
|
||||
if (ctx.tx[sfLoanID] == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
// Flags are mutually exclusive
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/transactors/lending/LoanManage.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
@@ -50,10 +48,10 @@ LoanPay::getFlagsMask(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
LoanPay::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (isZeroId(ctx.tx[sfLoanID]))
|
||||
if (ctx.tx[sfLoanID] == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
if (!isPositiveAmount(ctx.tx[sfAmount]))
|
||||
if (ctx.tx[sfAmount] <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
// The loan payment flags are all mutually exclusive. If more than one is
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
@@ -140,7 +138,7 @@ LoanSet::preflight(PreflightContext const& ctx)
|
||||
return *ret;
|
||||
}
|
||||
|
||||
if (auto const brokerID = ctx.tx[~sfLoanBrokerID]; brokerID && isZeroId(*brokerID))
|
||||
if (auto const brokerID = ctx.tx[~sfLoanBrokerID]; brokerID && *brokerID == beast::kZero)
|
||||
return temINVALID;
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
#include <xrpl/tx/paths/RippleCalc.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
@@ -145,7 +143,7 @@ Payment::preflight(PreflightContext const& ctx)
|
||||
// A zero DomainID is invalid for a PermissionedDomain ledger entry because
|
||||
// keylet::permissionedDomain(uint256) uses the DomainID as the ledger key.
|
||||
if (auto const domainID = tx[~sfDomainID];
|
||||
ctx.rules.enabled(fixCleanup3_2_0) && domainID && isZeroId(*domainID))
|
||||
ctx.rules.enabled(fixCleanup3_2_0) && domainID && *domainID == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
bool const partialPaymentAllowed = tx.isFlag(tfPartialPayment);
|
||||
@@ -185,13 +183,13 @@ Payment::preflight(PreflightContext const& ctx)
|
||||
<< "Payment destination account not specified.";
|
||||
return temDST_NEEDED;
|
||||
}
|
||||
if (hasMax && !isPositiveAmount(maxSourceAmount))
|
||||
if (hasMax && maxSourceAmount <= beast::kZero)
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: bad max amount: "
|
||||
<< maxSourceAmount.getFullText();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
if (!isPositiveAmount(dstAmount))
|
||||
if (dstAmount <= beast::kZero)
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: bad dst amount: " << dstAmount.getFullText();
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
@@ -44,15 +42,15 @@ PaymentChannelClaim::getFlagsMask(PreflightContext const&)
|
||||
NotTEC
|
||||
PaymentChannelClaim::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (ctx.rules.enabled(fixCleanup3_2_0) && isZeroId(ctx.tx[sfChannel]))
|
||||
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfChannel] == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
auto const bal = ctx.tx[~sfBalance];
|
||||
if (bal && !isPositiveXRPAmount(*bal))
|
||||
if (bal && (!isXRP(*bal) || *bal <= beast::kZero))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
auto const amt = ctx.tx[~sfAmount];
|
||||
if (amt && !isPositiveXRPAmount(*amt))
|
||||
if (amt && (!isXRP(*amt) || *amt <= beast::kZero))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (bal && amt && *bal > *amt)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h>
|
||||
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
@@ -21,8 +22,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -58,7 +57,7 @@ PaymentChannelCreate::makeTxConsequences(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
PaymentChannelCreate::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (!isPositiveXRPAmount(ctx.tx[sfAmount]))
|
||||
if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::kZero))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/tx/transactors/payment_channel/PaymentChannelFund.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
@@ -19,8 +20,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
TxConsequences
|
||||
@@ -32,10 +31,10 @@ PaymentChannelFund::makeTxConsequences(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
PaymentChannelFund::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (ctx.rules.enabled(fixCleanup3_2_0) && isZeroId(ctx.tx[sfChannel]))
|
||||
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfChannel] == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
if (!isPositiveXRPAmount(ctx.tx[sfAmount]))
|
||||
if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::kZero))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -11,15 +12,13 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
NotTEC
|
||||
PermissionedDomainDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const domain = ctx.tx.getFieldH256(sfDomainID);
|
||||
if (isZeroId(domain))
|
||||
if (domain == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h>
|
||||
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
@@ -16,8 +17,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -40,7 +39,7 @@ PermissionedDomainSet::preflight(PreflightContext const& ctx)
|
||||
return err;
|
||||
|
||||
auto const domain = ctx.tx.at(~sfDomainID);
|
||||
if (domain && isZeroId(*domain))
|
||||
if (domain && *domain == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
|
||||
@@ -46,7 +44,7 @@ preflightHelper<Issue>(PreflightContext const& ctx)
|
||||
// The issuer field is used for the token holder instead
|
||||
AccountID const& holder = clawAmount.getIssuer();
|
||||
|
||||
if (issuer == holder || isXRP(clawAmount) || !isPositiveAmount(clawAmount))
|
||||
if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
return tesSUCCESS;
|
||||
@@ -69,7 +67,7 @@ preflightHelper<MPTIssue>(PreflightContext const& ctx)
|
||||
if (ctx.tx[sfAccount] == *mptHolder)
|
||||
return temMALFORMED;
|
||||
|
||||
if (clawAmount.mpt() > MPTAmount{kMaxMpTokenAmount} || !isPositiveAmount(clawAmount))
|
||||
if (clawAmount.mpt() > MPTAmount{kMaxMpTokenAmount} || clawAmount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/tx/transactors/token/MPTokenIssuanceCreate.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
@@ -20,8 +21,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
@@ -88,7 +87,7 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx)
|
||||
|
||||
if (auto const domain = ctx.tx[~sfDomainID])
|
||||
{
|
||||
if (isZeroId(*domain))
|
||||
if (*domain == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
// Domain present implies that MPTokenIssuance is not public
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -100,7 +98,7 @@ TrustSet::preflight(PreflightContext const& ctx)
|
||||
return temBAD_LIMIT;
|
||||
}
|
||||
|
||||
if (isBadCurrency(saLimitAmount.get<Issue>().currency))
|
||||
if (badCurrency() == saLimitAmount.get<Issue>().currency)
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: specifies XRP as IOU";
|
||||
return temBAD_CURRENCY;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -18,14 +19,12 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <libxrpl/tx/PreflightHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
NotTEC
|
||||
VaultDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (isZeroId(ctx.tx[sfVaultID]))
|
||||
if (ctx.tx[sfVaultID] == beast::kZero)
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "VaultDelete: zero/empty vault ID.";
|
||||
return temMALFORMED;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user