mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-19 03:02:29 +00:00
Compare commits
38 Commits
ximinez/va
...
bthomee/si
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58fdf3ae83 | ||
|
|
4f2779f17f | ||
|
|
78b2d70a11 | ||
|
|
252c6768df | ||
|
|
5ae97fa8ae | ||
|
|
eff344faf9 | ||
|
|
7e7b71d84c | ||
|
|
ffea3977f0 | ||
|
|
47a235b7be | ||
|
|
f5e2415c98 | ||
|
|
1a4c359351 | ||
|
|
e4dbaf5efc | ||
|
|
983816248a | ||
|
|
b585dc78bb | ||
|
|
918185e18f | ||
|
|
1738a69619 | ||
|
|
1bf9e6e7da | ||
|
|
0446bef7e5 | ||
|
|
7a3bf1692d | ||
|
|
c1d108e565 | ||
|
|
1ba1bf9ade | ||
|
|
7dd3e0b3cc | ||
|
|
2b14ee3018 | ||
|
|
ce31a7ed16 | ||
|
|
91a23cf80b | ||
|
|
e460ea0840 | ||
|
|
46d5c67a8d | ||
|
|
ce9ccf844a | ||
|
|
c791cae1ec | ||
|
|
7b3724b7a3 | ||
|
|
bee2d112c6 | ||
|
|
01c977bbfe | ||
|
|
3baf5454f2 | ||
|
|
24a5cbaa93 | ||
|
|
eb7c8c6c7a | ||
|
|
f27d8f3890 | ||
|
|
8345cd77df | ||
|
|
c38aabdaee |
29
.clang-tidy
29
.clang-tidy
@@ -8,12 +8,14 @@ Checks: "-*,
|
||||
bugprone-chained-comparison,
|
||||
bugprone-compare-pointer-to-member-virtual-function,
|
||||
bugprone-copy-constructor-init,
|
||||
bugprone-crtp-constructor-accessibility,
|
||||
bugprone-dangling-handle,
|
||||
bugprone-dynamic-static-initializers,
|
||||
bugprone-empty-catch,
|
||||
bugprone-fold-init-type,
|
||||
bugprone-forward-declaration-namespace,
|
||||
bugprone-inaccurate-erase,
|
||||
bugprone-inc-dec-in-conditions,
|
||||
bugprone-incorrect-enable-if,
|
||||
bugprone-incorrect-roundings,
|
||||
bugprone-infinite-loop,
|
||||
@@ -58,14 +60,19 @@ Checks: "-*,
|
||||
bugprone-suspicious-string-compare,
|
||||
bugprone-suspicious-stringview-data-usage,
|
||||
bugprone-swapped-arguments,
|
||||
bugprone-switch-missing-default-case,
|
||||
bugprone-terminating-continue,
|
||||
bugprone-throw-keyword-missing,
|
||||
bugprone-too-small-loop-variable,
|
||||
# bugprone-unchecked-optional-access, # see https://github.com/XRPLF/rippled/pull/6502
|
||||
bugprone-undefined-memory-manipulation,
|
||||
bugprone-undelegated-constructor,
|
||||
bugprone-unhandled-exception-at-new,
|
||||
bugprone-unhandled-self-assignment,
|
||||
bugprone-unique-ptr-array-mismatch,
|
||||
bugprone-unsafe-functions,
|
||||
bugprone-unused-raii,
|
||||
bugprone-unused-return-value,
|
||||
bugprone-unused-local-non-trivial-variable,
|
||||
bugprone-virtual-near-miss,
|
||||
cppcoreguidelines-no-suspend-with-lock,
|
||||
@@ -78,30 +85,24 @@ Checks: "-*,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-unused-alias-decls,
|
||||
misc-unused-using-decls,
|
||||
readability-duplicate-include,
|
||||
readability-enum-initial-value,
|
||||
readability-misleading-indentation,
|
||||
readability-non-const-parameter,
|
||||
readability-redundant-declaration,
|
||||
readability-reference-to-constructed-temporary,
|
||||
modernize-deprecated-headers,
|
||||
modernize-make-shared,
|
||||
modernize-make-unique,
|
||||
performance-implicit-conversion-in-loop,
|
||||
performance-move-constructor-init,
|
||||
performance-trivially-destructible
|
||||
performance-trivially-destructible,
|
||||
readability-duplicate-include,
|
||||
readability-enum-initial-value,
|
||||
readability-misleading-indentation,
|
||||
readability-non-const-parameter,
|
||||
readability-redundant-declaration,
|
||||
readability-reference-to-constructed-temporary
|
||||
"
|
||||
# ---
|
||||
# checks that have some issues that need to be resolved:
|
||||
#
|
||||
# bugprone-crtp-constructor-accessibility,
|
||||
# bugprone-inc-dec-in-conditions,
|
||||
# bugprone-move-forwarding-reference,
|
||||
# bugprone-switch-missing-default-case,
|
||||
# bugprone-unused-return-value,
|
||||
# bugprone-use-after-move,
|
||||
# bugprone-unhandled-self-assignment,
|
||||
# bugprone-unused-raii,
|
||||
#
|
||||
# cppcoreguidelines-misleading-capture-default-by-value,
|
||||
# cppcoreguidelines-init-variables,
|
||||
@@ -194,7 +195,7 @@ CheckOptions:
|
||||
# readability-identifier-naming.PublicMemberSuffix: ""
|
||||
# readability-identifier-naming.FunctionIgnoredRegexp: ".*tag_invoke.*"
|
||||
bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true
|
||||
# bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
|
||||
bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
|
||||
# misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp'
|
||||
#
|
||||
# HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$'
|
||||
|
||||
16
.github/pull_request_template.md
vendored
16
.github/pull_request_template.md
vendored
@@ -29,22 +29,6 @@ If a refactor, how is this better than the previous implementation?
|
||||
If there is a spec or design document for this feature, please link it here.
|
||||
-->
|
||||
|
||||
### Type of Change
|
||||
|
||||
<!--
|
||||
Please check [x] relevant options, delete irrelevant ones.
|
||||
-->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Refactor (non-breaking change that only restructures code)
|
||||
- [ ] Performance (increase or change in throughput and/or latency)
|
||||
- [ ] Tests (you added tests for code that already exists, or your new feature included in this PR)
|
||||
- [ ] Documentation update
|
||||
- [ ] Chore (no impact to binary, e.g. `.gitignore`, formatting, dropping support for older tooling)
|
||||
- [ ] Release
|
||||
|
||||
### API Impact
|
||||
|
||||
<!--
|
||||
|
||||
6
.github/scripts/levelization/README.md
vendored
6
.github/scripts/levelization/README.md
vendored
@@ -70,7 +70,7 @@ that `test` code should _never_ be included in `xrpl` or `xrpld` code.)
|
||||
|
||||
## Validation
|
||||
|
||||
The [levelization](generate.sh) script takes no parameters,
|
||||
The [levelization](generate.py) script takes no parameters,
|
||||
reads no environment variables, and can be run from any directory,
|
||||
as long as it is in the expected location in the rippled repo.
|
||||
It can be run at any time from within a checked out repo, and will
|
||||
@@ -104,7 +104,7 @@ It generates many files of [results](results):
|
||||
Github Actions workflow to test that levelization loops haven't
|
||||
changed. Unfortunately, if changes are detected, it can't tell if
|
||||
they are improvements or not, so if you have resolved any issues or
|
||||
done anything else to improve levelization, run `levelization.sh`,
|
||||
done anything else to improve levelization, run `generate.py`,
|
||||
and commit the updated results.
|
||||
|
||||
The `loops.txt` and `ordering.txt` files relate the modules
|
||||
@@ -128,7 +128,7 @@ The committed files hide the detailed values intentionally, to
|
||||
prevent false alarms and merging issues, and because it's easy to
|
||||
get those details locally.
|
||||
|
||||
1. Run `levelization.sh`
|
||||
1. Run `generate.py`
|
||||
2. Grep the modules in `paths.txt`.
|
||||
- For example, if a cycle is found `A ~= B`, simply `grep -w
|
||||
A .github/scripts/levelization/results/paths.txt | grep -w B`
|
||||
|
||||
335
.github/scripts/levelization/generate.py
vendored
Normal file
335
.github/scripts/levelization/generate.py
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Usage: generate.py
|
||||
This script takes no parameters, and can be called from any directory in the file system.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Set, Optional
|
||||
|
||||
# Compile regex patterns once at module level
|
||||
INCLUDE_PATTERN = re.compile(r"^\s*#include.*/.*\.h")
|
||||
INCLUDE_PATH_PATTERN = re.compile(r'[<"]([^>"]+)[>"]')
|
||||
|
||||
|
||||
def dictionary_sort_key(s: str) -> str:
|
||||
"""
|
||||
Create a sort key that mimics 'sort -d' (dictionary order).
|
||||
Dictionary order only considers blanks and alphanumeric characters.
|
||||
This means punctuation like '.' is ignored during sorting.
|
||||
"""
|
||||
# Keep only alphanumeric characters and spaces
|
||||
return "".join(c for c in s if c.isalnum() or c.isspace())
|
||||
|
||||
|
||||
def get_level(file_path: str) -> str:
|
||||
"""
|
||||
Extract the level from a file path (second and third directory components).
|
||||
Equivalent to bash: cut -d/ -f 2,3
|
||||
|
||||
Examples:
|
||||
src/xrpld/app/main.cpp -> xrpld.app
|
||||
src/libxrpl/protocol/STObject.cpp -> libxrpl.protocol
|
||||
include/xrpl/basics/base_uint.h -> xrpl.basics
|
||||
"""
|
||||
parts = file_path.split("/")
|
||||
|
||||
# Get fields 2 and 3 (indices 1 and 2 in 0-based indexing)
|
||||
if len(parts) >= 3:
|
||||
level = f"{parts[1]}/{parts[2]}"
|
||||
elif len(parts) >= 2:
|
||||
level = f"{parts[1]}/toplevel"
|
||||
else:
|
||||
level = file_path
|
||||
|
||||
# If the "level" indicates a file, cut off the filename
|
||||
if "." in level.split("/")[-1]: # Avoid Path object creation
|
||||
# Use the "toplevel" label as a workaround for `sort`
|
||||
# inconsistencies between different utility versions
|
||||
level = level.rsplit("/", 1)[0] + "/toplevel"
|
||||
|
||||
return level.replace("/", ".")
|
||||
|
||||
|
||||
def extract_include_level(include_line: str) -> Optional[str]:
|
||||
"""
|
||||
Extract the include path from an #include directive.
|
||||
Gets the first two directory components from the include path.
|
||||
Equivalent to bash: cut -d/ -f 1,2
|
||||
|
||||
Examples:
|
||||
#include <xrpl/basics/base_uint.h> -> xrpl.basics
|
||||
#include "xrpld/app/main/Application.h" -> xrpld.app
|
||||
"""
|
||||
# Remove everything before the quote or angle bracket
|
||||
match = INCLUDE_PATH_PATTERN.search(include_line)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
include_path = match.group(1)
|
||||
parts = include_path.split("/")
|
||||
|
||||
# Get first two fields (indices 0 and 1)
|
||||
if len(parts) >= 2:
|
||||
include_level = f"{parts[0]}/{parts[1]}"
|
||||
else:
|
||||
include_level = include_path
|
||||
|
||||
# If the "includelevel" indicates a file, cut off the filename
|
||||
if "." in include_level.split("/")[-1]: # Avoid Path object creation
|
||||
include_level = include_level.rsplit("/", 1)[0] + "/toplevel"
|
||||
|
||||
return include_level.replace("/", ".")
|
||||
|
||||
|
||||
def find_repository_directories(
|
||||
start_path: Path, depth_limit: int = 10
|
||||
) -> Tuple[Path, List[Path]]:
|
||||
"""
|
||||
Find the repository root by looking for src or include folders.
|
||||
Walks up the directory tree from the start path.
|
||||
"""
|
||||
current = start_path.resolve()
|
||||
|
||||
# Walk up the directory tree
|
||||
for _ in range(depth_limit): # Limit search depth to prevent infinite loops
|
||||
src_path = current / "src"
|
||||
include_path = current / "include"
|
||||
# Check if this directory has src or include folders
|
||||
has_src = src_path.exists()
|
||||
has_include = include_path.exists()
|
||||
|
||||
if has_src or has_include:
|
||||
return current, [src_path, include_path]
|
||||
|
||||
# Move up one level
|
||||
parent = current.parent
|
||||
if parent == current: # Reached filesystem root
|
||||
break
|
||||
current = parent
|
||||
|
||||
# If we couldn't find it, raise an error
|
||||
raise RuntimeError(
|
||||
"Could not find repository root. "
|
||||
"Expected to find a directory containing 'src' and/or 'include' folders."
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
# Change to the script's directory
|
||||
script_dir = Path(__file__).parent.resolve()
|
||||
os.chdir(script_dir)
|
||||
|
||||
# Clean up and create results directory.
|
||||
results_dir = script_dir / "results"
|
||||
if results_dir.exists():
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(results_dir)
|
||||
results_dir.mkdir()
|
||||
|
||||
# Find the repository root by searching for src and include directories.
|
||||
try:
|
||||
repo_root, scan_dirs = find_repository_directories(script_dir)
|
||||
|
||||
print(f"Found repository root: {repo_root}")
|
||||
print(f"Scanning directories:")
|
||||
for scan_dir in scan_dirs:
|
||||
print(f" - {scan_dir.relative_to(repo_root)}")
|
||||
except RuntimeError as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("\nScanning for raw includes...")
|
||||
# Find all #include directives
|
||||
raw_includes: List[Tuple[str, str]] = []
|
||||
rawincludes_file = results_dir / "rawincludes.txt"
|
||||
|
||||
# Write to file as we go to avoid storing everything in memory.
|
||||
with open(rawincludes_file, "w", buffering=8192) as raw_f:
|
||||
for dir_path in scan_dirs:
|
||||
print(f" Scanning {dir_path.relative_to(repo_root)}...")
|
||||
|
||||
for file_path in dir_path.rglob("*"):
|
||||
if not file_path.is_file():
|
||||
continue
|
||||
|
||||
try:
|
||||
rel_path_str = str(file_path.relative_to(repo_root))
|
||||
|
||||
# Read file with a large buffer for performance.
|
||||
with open(
|
||||
file_path,
|
||||
"r",
|
||||
encoding="utf-8",
|
||||
errors="ignore",
|
||||
buffering=8192,
|
||||
) as f:
|
||||
for line in f:
|
||||
# Quick check before regex
|
||||
if "#include" not in line or "boost" in line:
|
||||
continue
|
||||
|
||||
if INCLUDE_PATTERN.match(line):
|
||||
line_stripped = line.strip()
|
||||
entry = f"{rel_path_str}:{line_stripped}\n"
|
||||
print(entry, end="")
|
||||
raw_f.write(entry)
|
||||
raw_includes.append((rel_path_str, line_stripped))
|
||||
except Exception as e:
|
||||
print(f"Error reading {file_path}: {e}", file=sys.stderr)
|
||||
|
||||
# Build levelization paths and count directly (no need to sort first).
|
||||
print("Build levelization paths")
|
||||
path_counts: Dict[Tuple[str, str], int] = defaultdict(int)
|
||||
|
||||
for file_path, include_line in raw_includes:
|
||||
include_level = extract_include_level(include_line)
|
||||
if not include_level:
|
||||
continue
|
||||
|
||||
level = get_level(file_path)
|
||||
if level != include_level:
|
||||
path_counts[(level, include_level)] += 1
|
||||
|
||||
# Sort and deduplicate paths (using dictionary order like bash 'sort -d').
|
||||
print("Sort and deduplicate paths")
|
||||
|
||||
paths_file = results_dir / "paths.txt"
|
||||
with open(paths_file, "w") as f:
|
||||
# Sort using dictionary order: only alphanumeric and spaces matter
|
||||
sorted_items = sorted(
|
||||
path_counts.items(),
|
||||
key=lambda x: (dictionary_sort_key(x[0][0]), dictionary_sort_key(x[0][1])),
|
||||
)
|
||||
for (level, include_level), count in sorted_items:
|
||||
line = f"{count:7} {level} {include_level}\n"
|
||||
print(line.rstrip())
|
||||
f.write(line)
|
||||
|
||||
# Split into flat-file database
|
||||
print("Split into flat-file database")
|
||||
includes_dir = results_dir / "includes"
|
||||
included_by_dir = results_dir / "included_by"
|
||||
includes_dir.mkdir()
|
||||
included_by_dir.mkdir()
|
||||
|
||||
# Batch writes by grouping data first to avoid repeated file opens.
|
||||
includes_data: Dict[str, List[Tuple[str, int]]] = defaultdict(list)
|
||||
included_by_data: Dict[str, List[Tuple[str, int]]] = defaultdict(list)
|
||||
|
||||
# Process in sorted order to match bash script behaviour (dictionary order).
|
||||
sorted_items = sorted(
|
||||
path_counts.items(),
|
||||
key=lambda x: (dictionary_sort_key(x[0][0]), dictionary_sort_key(x[0][1])),
|
||||
)
|
||||
for (level, include_level), count in sorted_items:
|
||||
includes_data[level].append((include_level, count))
|
||||
included_by_data[include_level].append((level, count))
|
||||
|
||||
# Write all includes files in sorted order (dictionary order).
|
||||
for level in sorted(includes_data.keys(), key=dictionary_sort_key):
|
||||
entries = includes_data[level]
|
||||
with open(includes_dir / level, "w") as f:
|
||||
for include_level, count in entries:
|
||||
line = f"{include_level} {count}\n"
|
||||
print(line.rstrip())
|
||||
f.write(line)
|
||||
|
||||
# Write all included_by files in sorted order (dictionary order).
|
||||
for include_level in sorted(included_by_data.keys(), key=dictionary_sort_key):
|
||||
entries = included_by_data[include_level]
|
||||
with open(included_by_dir / include_level, "w") as f:
|
||||
for level, count in entries:
|
||||
line = f"{level} {count}\n"
|
||||
print(line.rstrip())
|
||||
f.write(line)
|
||||
|
||||
# Search for loops
|
||||
print("Search for loops")
|
||||
loops_file = results_dir / "loops.txt"
|
||||
ordering_file = results_dir / "ordering.txt"
|
||||
|
||||
loops_found: Set[Tuple[str, str]] = set()
|
||||
|
||||
# Pre-load all include files into memory to avoid repeated I/O.
|
||||
# This is the biggest optimisation - we were reading files repeatedly in nested loops.
|
||||
# Use list of tuples to preserve file order.
|
||||
includes_cache: Dict[str, List[Tuple[str, int]]] = {}
|
||||
includes_lookup: Dict[str, Dict[str, int]] = {} # For fast lookup
|
||||
|
||||
# Note: bash script uses 'for source in *' which uses standard glob sorting,
|
||||
# NOT dictionary order. So we use standard sorted() here, not dictionary_sort_key.
|
||||
for include_file in sorted(includes_dir.iterdir(), key=lambda p: p.name):
|
||||
if not include_file.is_file():
|
||||
continue
|
||||
|
||||
includes_cache[include_file.name] = []
|
||||
includes_lookup[include_file.name] = {}
|
||||
with open(include_file, "r") as f:
|
||||
for line in f:
|
||||
parts = line.strip().split()
|
||||
if len(parts) >= 2:
|
||||
include_name = parts[0]
|
||||
include_count = int(parts[1])
|
||||
includes_cache[include_file.name].append(
|
||||
(include_name, include_count)
|
||||
)
|
||||
includes_lookup[include_file.name][include_name] = include_count
|
||||
|
||||
with open(loops_file, "w", buffering=8192) as loops_f, open(
|
||||
ordering_file, "w", buffering=8192
|
||||
) as ordering_f:
|
||||
|
||||
# Use standard sorting to match bash glob expansion 'for source in *'.
|
||||
for source in sorted(includes_cache.keys()):
|
||||
source_includes = includes_cache[source]
|
||||
|
||||
for include, include_freq in source_includes:
|
||||
# Check if include file exists and references source
|
||||
if include not in includes_lookup:
|
||||
continue
|
||||
|
||||
source_freq = includes_lookup[include].get(source)
|
||||
|
||||
if source_freq is not None:
|
||||
# Found a loop
|
||||
loop_key = tuple(sorted([source, include]))
|
||||
if loop_key in loops_found:
|
||||
continue
|
||||
loops_found.add(loop_key)
|
||||
|
||||
loops_f.write(f"Loop: {source} {include}\n")
|
||||
|
||||
# If the counts are close, indicate that the two modules are
|
||||
# on the same level, though they shouldn't be.
|
||||
diff = include_freq - source_freq
|
||||
if diff > 3:
|
||||
loops_f.write(f" {source} > {include}\n\n")
|
||||
elif diff < -3:
|
||||
loops_f.write(f" {include} > {source}\n\n")
|
||||
elif source_freq == include_freq:
|
||||
loops_f.write(f" {include} == {source}\n\n")
|
||||
else:
|
||||
loops_f.write(f" {include} ~= {source}\n\n")
|
||||
else:
|
||||
ordering_f.write(f"{source} > {include}\n")
|
||||
|
||||
# Print results
|
||||
print("\nOrdering:")
|
||||
with open(ordering_file, "r") as f:
|
||||
print(f.read(), end="")
|
||||
|
||||
print("\nLoops:")
|
||||
with open(loops_file, "r") as f:
|
||||
print(f.read(), end="")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
130
.github/scripts/levelization/generate.sh
vendored
130
.github/scripts/levelization/generate.sh
vendored
@@ -1,130 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: generate.sh
|
||||
# This script takes no parameters, reads no environment variables,
|
||||
# and can be run from any directory, as long as it is in the expected
|
||||
# location in the repo.
|
||||
|
||||
pushd $( dirname $0 )
|
||||
|
||||
if [ -v PS1 ]
|
||||
then
|
||||
# if the shell is interactive, clean up any flotsam before analyzing
|
||||
git clean -ix
|
||||
fi
|
||||
|
||||
# Ensure all sorting is ASCII-order consistently across platforms.
|
||||
export LANG=C
|
||||
|
||||
rm -rfv results
|
||||
mkdir results
|
||||
includes="$( pwd )/results/rawincludes.txt"
|
||||
pushd ../../..
|
||||
echo Raw includes:
|
||||
grep -r '^[ ]*#include.*/.*\.h' include src | \
|
||||
grep -v boost | tee ${includes}
|
||||
popd
|
||||
pushd results
|
||||
|
||||
oldifs=${IFS}
|
||||
IFS=:
|
||||
mkdir includes
|
||||
mkdir included_by
|
||||
echo Build levelization paths
|
||||
exec 3< ${includes} # open rawincludes.txt for input
|
||||
while read -r -u 3 file include
|
||||
do
|
||||
level=$( echo ${file} | cut -d/ -f 2,3 )
|
||||
# If the "level" indicates a file, cut off the filename
|
||||
if [[ "${level##*.}" != "${level}" ]]
|
||||
then
|
||||
# Use the "toplevel" label as a workaround for `sort`
|
||||
# inconsistencies between different utility versions
|
||||
level="$( dirname ${level} )/toplevel"
|
||||
fi
|
||||
level=$( echo ${level} | tr '/' '.' )
|
||||
|
||||
includelevel=$( echo ${include} | sed 's/.*["<]//; s/[">].*//' | \
|
||||
cut -d/ -f 1,2 )
|
||||
if [[ "${includelevel##*.}" != "${includelevel}" ]]
|
||||
then
|
||||
# Use the "toplevel" label as a workaround for `sort`
|
||||
# inconsistencies between different utility versions
|
||||
includelevel="$( dirname ${includelevel} )/toplevel"
|
||||
fi
|
||||
includelevel=$( echo ${includelevel} | tr '/' '.' )
|
||||
|
||||
if [[ "$level" != "$includelevel" ]]
|
||||
then
|
||||
echo $level $includelevel | tee -a paths.txt
|
||||
fi
|
||||
done
|
||||
echo Sort and deduplicate paths
|
||||
sort -ds paths.txt | uniq -c | tee sortedpaths.txt
|
||||
mv sortedpaths.txt paths.txt
|
||||
exec 3>&- #close fd 3
|
||||
IFS=${oldifs}
|
||||
unset oldifs
|
||||
|
||||
echo Split into flat-file database
|
||||
exec 4<paths.txt # open paths.txt for input
|
||||
while read -r -u 4 count level include
|
||||
do
|
||||
echo ${include} ${count} | tee -a includes/${level}
|
||||
echo ${level} ${count} | tee -a included_by/${include}
|
||||
done
|
||||
exec 4>&- #close fd 4
|
||||
|
||||
loops="$( pwd )/loops.txt"
|
||||
ordering="$( pwd )/ordering.txt"
|
||||
pushd includes
|
||||
echo Search for loops
|
||||
# Redirect stdout to a file
|
||||
exec 4>&1
|
||||
exec 1>"${loops}"
|
||||
for source in *
|
||||
do
|
||||
if [[ -f "$source" ]]
|
||||
then
|
||||
exec 5<"${source}" # open for input
|
||||
while read -r -u 5 include includefreq
|
||||
do
|
||||
if [[ -f $include ]]
|
||||
then
|
||||
if grep -q -w $source $include
|
||||
then
|
||||
if grep -q -w "Loop: $include $source" "${loops}"
|
||||
then
|
||||
continue
|
||||
fi
|
||||
sourcefreq=$( grep -w $source $include | cut -d\ -f2 )
|
||||
echo "Loop: $source $include"
|
||||
# If the counts are close, indicate that the two modules are
|
||||
# on the same level, though they shouldn't be
|
||||
if [[ $(( $includefreq - $sourcefreq )) -gt 3 ]]
|
||||
then
|
||||
echo -e " $source > $include\n"
|
||||
elif [[ $(( $sourcefreq - $includefreq )) -gt 3 ]]
|
||||
then
|
||||
echo -e " $include > $source\n"
|
||||
elif [[ $sourcefreq -eq $includefreq ]]
|
||||
then
|
||||
echo -e " $include == $source\n"
|
||||
else
|
||||
echo -e " $include ~= $source\n"
|
||||
fi
|
||||
else
|
||||
echo "$source > $include" >> "${ordering}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
exec 5>&- #close fd 5
|
||||
fi
|
||||
done
|
||||
exec 1>&4 #close fd 1
|
||||
exec 4>&- #close fd 4
|
||||
cat "${ordering}"
|
||||
cat "${loops}"
|
||||
popd
|
||||
popd
|
||||
popd
|
||||
@@ -134,6 +134,7 @@ test.peerfinder > xrpld.core
|
||||
test.peerfinder > xrpld.peerfinder
|
||||
test.peerfinder > xrpl.protocol
|
||||
test.protocol > test.toplevel
|
||||
test.protocol > test.unit_test
|
||||
test.protocol > xrpl.basics
|
||||
test.protocol > xrpl.json
|
||||
test.protocol > xrpl.protocol
|
||||
@@ -171,6 +172,7 @@ test.shamap > xrpl.shamap
|
||||
test.toplevel > test.csf
|
||||
test.toplevel > xrpl.json
|
||||
test.unit_test > xrpl.basics
|
||||
test.unit_test > xrpl.protocol
|
||||
tests.libxrpl > xrpl.basics
|
||||
tests.libxrpl > xrpl.json
|
||||
tests.libxrpl > xrpl.net
|
||||
|
||||
4
.github/scripts/strategy-matrix/generate.py
vendored
4
.github/scripts/strategy-matrix/generate.py
vendored
@@ -55,7 +55,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
# fee to 500.
|
||||
# - Bookworm using GCC 15: Debug on linux/amd64, enable code
|
||||
# coverage (which will be done below).
|
||||
# - Bookworm using Clang 16: Debug on linux/arm64, enable voidstar.
|
||||
# - Bookworm using Clang 16: Debug on linux/amd64, enable voidstar.
|
||||
# - Bookworm using Clang 17: Release on linux/amd64, set the
|
||||
# reference fee to 1000.
|
||||
# - Bookworm using Clang 20: Debug on linux/amd64.
|
||||
@@ -78,7 +78,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/arm64"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"-Dvoidstar=ON {cmake_args}"
|
||||
skip = False
|
||||
|
||||
17
.github/workflows/check-signed-commits.yml
vendored
Normal file
17
.github/workflows/check-signed-commits.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Check signed commits
|
||||
|
||||
# The use of `pull_request_target` allows the action to post comments on
|
||||
# external PRs created from forks.
|
||||
on: pull_request_target
|
||||
|
||||
jobs:
|
||||
check-signed-commits:
|
||||
name: Check signed commits in PR
|
||||
runs-on: ubuntu-latest
|
||||
# The action needs write permissions to post comments on the PR.
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check signed commits in PR
|
||||
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1.2.0
|
||||
5
.github/workflows/on-pr.yml
vendored
5
.github/workflows/on-pr.yml
vendored
@@ -141,9 +141,8 @@ jobs:
|
||||
needs:
|
||||
- should-run
|
||||
- build-test
|
||||
# Only run when committing to a PR that targets a release branch in the
|
||||
# XRPLF repository.
|
||||
if: ${{ github.repository_owner == 'XRPLF' && needs.should-run.outputs.go == 'true' && startsWith(github.ref, 'refs/heads/release') }}
|
||||
# Only run when committing to a PR that targets a release branch.
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release') }}
|
||||
uses: ./.github/workflows/reusable-upload-recipe.yml
|
||||
secrets:
|
||||
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||
|
||||
3
.github/workflows/on-tag.yml
vendored
3
.github/workflows/on-tag.yml
vendored
@@ -17,8 +17,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
upload-recipe:
|
||||
# Only run when a tag is pushed to the XRPLF repository.
|
||||
if: ${{ github.repository_owner == 'XRPLF' }}
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
uses: ./.github/workflows/reusable-upload-recipe.yml
|
||||
secrets:
|
||||
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||
|
||||
4
.github/workflows/on-trigger.yml
vendored
4
.github/workflows/on-trigger.yml
vendored
@@ -92,8 +92,8 @@ jobs:
|
||||
|
||||
upload-recipe:
|
||||
needs: build-test
|
||||
# Only run when pushing to the develop branch in the XRPLF repository.
|
||||
if: ${{ github.repository_owner == 'XRPLF' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||
# Only run when pushing to the develop branch.
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||
uses: ./.github/workflows/reusable-upload-recipe.yml
|
||||
secrets:
|
||||
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||
|
||||
22
.github/workflows/reusable-build-test-config.yml
vendored
22
.github/workflows/reusable-build-test-config.yml
vendored
@@ -76,7 +76,7 @@ jobs:
|
||||
name: ${{ inputs.config_name }}
|
||||
runs-on: ${{ fromJSON(inputs.runs_on) }}
|
||||
container: ${{ inputs.image != '' && inputs.image || null }}
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }}
|
||||
env:
|
||||
# Use a namespace to keep the objects separate for each configuration.
|
||||
CCACHE_NAMESPACE: ${{ inputs.config_name }}
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload the binary (Linux)
|
||||
if: ${{ github.repository_owner == 'XRPLF' && runner.os == 'Linux' }}
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: xrpld-${{ inputs.config_name }}
|
||||
@@ -204,11 +204,17 @@ jobs:
|
||||
|
||||
- name: Set sanitizer options
|
||||
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
|
||||
env:
|
||||
CONFIG_NAME: ${{ inputs.config_name }}
|
||||
run: |
|
||||
echo "ASAN_OPTIONS=print_stacktrace=1:detect_container_overflow=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=second_deadlock_stack=1:halt_on_error=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp"
|
||||
if [[ "${CONFIG_NAME}" == *gcc* ]]; then
|
||||
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
|
||||
fi
|
||||
echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
@@ -230,6 +236,8 @@ jobs:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
run: |
|
||||
set -o pipefail
|
||||
# Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness
|
||||
[ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 ))
|
||||
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
|
||||
|
||||
- name: Show test failure summary
|
||||
@@ -266,7 +274,7 @@ jobs:
|
||||
--target coverage
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository_owner == 'XRPLF' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
with:
|
||||
disable_search: true
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Check levelization
|
||||
run: .github/scripts/levelization/generate.sh
|
||||
run: python .github/scripts/levelization/generate.py
|
||||
- name: Check for differences
|
||||
env:
|
||||
MESSAGE: |
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
removed from loops.txt, it's probably an improvement, while if
|
||||
something was added, it's probably a regression.
|
||||
|
||||
Run '.github/scripts/levelization/generate.sh' in your repo, commit
|
||||
Run '.github/scripts/levelization/generate.py' in your repo, commit
|
||||
and push the changes. See .github/scripts/levelization/README.md for
|
||||
more info.
|
||||
run: |
|
||||
|
||||
2
.github/workflows/reusable-clang-tidy.yml
vendored
2
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -51,5 +51,5 @@ jobs:
|
||||
if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.any_cpp_changed == 'true' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }}
|
||||
uses: ./.github/workflows/reusable-clang-tidy-files.yml
|
||||
with:
|
||||
files: ${{ (needs.determine-files.outputs.clang_tidy_config_changed == 'true' && '') || (inputs.check_only_changed && needs.determine-files.outputs.all_changed_files || '') }}
|
||||
files: ${{ needs.determine-files.outputs.clang_tidy_config_changed == 'true' && '' || (inputs.check_only_changed && needs.determine-files.outputs.all_changed_files || '') }}
|
||||
create_issue_on_failure: ${{ inputs.create_issue_on_failure }}
|
||||
|
||||
10
.github/workflows/reusable-upload-recipe.yml
vendored
10
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -69,22 +69,28 @@ jobs:
|
||||
conan export . --version=${{ steps.version.outputs.version }}
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
|
||||
|
||||
# When this workflow is triggered by a push event, it will always be when merging into the
|
||||
# 'develop' branch, see on-trigger.yml.
|
||||
- name: Upload Conan recipe (develop)
|
||||
if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
run: |
|
||||
conan export . --version=develop
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop
|
||||
|
||||
# When this workflow is triggered by a pull request event, it will always be when merging into
|
||||
# one of the 'release' branches, see on-pr.yml.
|
||||
- name: Upload Conan recipe (rc)
|
||||
if: ${{ startsWith(github.ref, 'refs/heads/release') }}
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
run: |
|
||||
conan export . --version=rc
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc
|
||||
|
||||
# When this workflow is triggered by a tag event, it will always be when tagging a final
|
||||
# release, see on-tag.yml.
|
||||
- name: Upload Conan recipe (release)
|
||||
if: ${{ github.event_name == 'tag' }}
|
||||
env:
|
||||
|
||||
4
.github/workflows/upload-conan-deps.yml
vendored
4
.github/workflows/upload-conan-deps.yml
vendored
@@ -103,11 +103,11 @@ jobs:
|
||||
sanitizers: ${{ matrix.sanitizers }}
|
||||
|
||||
- name: Log into Conan remote
|
||||
if: ${{ github.repository_owner == 'XRPLF' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
|
||||
run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}"
|
||||
|
||||
- name: Upload Conan packages
|
||||
if: ${{ github.repository_owner == 'XRPLF' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
|
||||
env:
|
||||
FORCE_OPTION: ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}
|
||||
run: conan upload "*" --remote="${CONAN_REMOTE_NAME}" --confirm ${FORCE_OPTION}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -75,6 +75,9 @@ DerivedData
|
||||
/.claude
|
||||
/CLAUDE.md
|
||||
|
||||
# Python
|
||||
__pycache__
|
||||
|
||||
# Direnv's directory
|
||||
/.direnv
|
||||
|
||||
|
||||
@@ -131,7 +131,6 @@ if(coverage)
|
||||
include(XrplCov)
|
||||
endif()
|
||||
|
||||
set(PROJECT_EXPORT_SET XrplExports)
|
||||
include(XrplCore)
|
||||
include(XrplInstall)
|
||||
include(XrplValidatorKeys)
|
||||
|
||||
@@ -127,26 +127,6 @@ tl;dr
|
||||
> 6. Wrap the body at 72 characters.
|
||||
> 7. Use the body to explain what and why vs. how.
|
||||
|
||||
In addition to those guidelines, please add one of the following
|
||||
prefixes to the subject line if appropriate.
|
||||
|
||||
- `fix:` - The primary purpose is to fix an existing bug.
|
||||
- `perf:` - The primary purpose is performance improvements.
|
||||
- `refactor:` - The changes refactor code without affecting
|
||||
functionality.
|
||||
- `test:` - The changes _only_ affect unit tests.
|
||||
- `docs:` - The changes _only_ affect documentation. This can
|
||||
include code comments in addition to `.md` files like this one.
|
||||
- `build:` - The changes _only_ affect the build process,
|
||||
including CMake and/or Conan settings.
|
||||
- `chore:` - Other tasks that don't affect the binary, but don't fit
|
||||
any of the other cases. e.g. formatting, git settings, updating
|
||||
Github Actions jobs.
|
||||
|
||||
Whenever possible, when updating commits after the PR is open, please
|
||||
add the PR number to the end of the subject line. e.g. `test: Add
|
||||
unit tests for Feature X (#1234)`.
|
||||
|
||||
## Pull requests
|
||||
|
||||
In general, pull requests use `develop` as the base branch.
|
||||
@@ -180,6 +160,23 @@ credibility of the existing approvals is insufficient.
|
||||
Pull requests must be merged by [squash-and-merge][squash]
|
||||
to preserve a linear history for the `develop` branch.
|
||||
|
||||
### Type of Change
|
||||
|
||||
In addition to those guidelines, please start your PR title with one of the following:
|
||||
|
||||
- `build:` - The changes _only_ affect the build process, including CMake and/or Conan settings.
|
||||
- `feat`: New feature (change which adds functionality).
|
||||
- `fix:` - The primary purpose is to fix an existing bug.
|
||||
- `docs:` - The changes _only_ affect documentation.
|
||||
- `test:` - The changes _only_ affect unit tests.
|
||||
- `ci`: Continuous Integration (changes to our CI configuration files and scripts).
|
||||
- `style`: Code style (formatting).
|
||||
- `refactor:` - The changes refactor code without affecting functionality.
|
||||
- `perf:` - The primary purpose is performance improvements.
|
||||
- `chore:` - Other tasks that don't affect the binary, but don't fit any of the other cases. e.g. `git` settings, `clang-tidy`, removing dead code, dropping support for older tooling.
|
||||
|
||||
First letter after the type prefix should be capitalized, and the type prefix should be followed by a colon and a space. e.g. `feat: Add support for Borrowing Protocol`.
|
||||
|
||||
### "Ready to merge"
|
||||
|
||||
A pull request should only have the "Ready to merge" label added when it
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
# need to represent system dependencies of the lib here
|
||||
#[=========================================================[
|
||||
Boost
|
||||
#]=========================================================]
|
||||
if(static OR APPLE OR MSVC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif()
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
if(static OR MSVC)
|
||||
set(Boost_USE_STATIC_RUNTIME ON)
|
||||
else()
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
endif()
|
||||
find_dependency(
|
||||
Boost
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
context
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread
|
||||
)
|
||||
#[=========================================================[
|
||||
OpenSSL
|
||||
#]=========================================================]
|
||||
if(NOT DEFINED OPENSSL_ROOT_DIR)
|
||||
if(DEFINED ENV{OPENSSL_ROOT})
|
||||
set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT})
|
||||
elseif(APPLE)
|
||||
find_program(homebrew brew)
|
||||
if(homebrew)
|
||||
execute_process(
|
||||
COMMAND ${homebrew} --prefix openssl
|
||||
OUTPUT_VARIABLE OPENSSL_ROOT_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" OPENSSL_ROOT_DIR)
|
||||
endif()
|
||||
|
||||
if(static OR APPLE OR MSVC)
|
||||
set(OPENSSL_USE_STATIC_LIBS ON)
|
||||
endif()
|
||||
set(OPENSSL_MSVC_STATIC_RT ON)
|
||||
find_dependency(OpenSSL REQUIRED)
|
||||
find_dependency(ZLIB)
|
||||
find_dependency(date)
|
||||
if(TARGET ZLIB::ZLIB)
|
||||
set_target_properties(
|
||||
OpenSSL::Crypto
|
||||
PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB
|
||||
)
|
||||
endif()
|
||||
@@ -2,100 +2,38 @@
|
||||
install stuff
|
||||
#]===================================================================]
|
||||
|
||||
include(create_symbolic_link)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# If no suffix is defined for executables (e.g. Windows uses .exe but Linux
|
||||
# and macOS use none), then explicitly set it to the empty string.
|
||||
if(NOT DEFINED suffix)
|
||||
set(suffix "")
|
||||
if(is_root_project AND TARGET xrpld)
|
||||
install(
|
||||
TARGETS xrpld
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime
|
||||
)
|
||||
|
||||
install(
|
||||
FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg"
|
||||
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld"
|
||||
RENAME xrpld.cfg
|
||||
COMPONENT runtime
|
||||
)
|
||||
|
||||
install(
|
||||
FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt"
|
||||
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld"
|
||||
RENAME validators.txt
|
||||
COMPONENT runtime
|
||||
)
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
common
|
||||
opts
|
||||
xrpl_boost
|
||||
xrpl_libs
|
||||
xrpl_syslibs
|
||||
xrpl.imports.main
|
||||
xrpl.libpb
|
||||
xrpl.libxrpl
|
||||
xrpl.libxrpl.basics
|
||||
xrpl.libxrpl.beast
|
||||
xrpl.libxrpl.conditions
|
||||
xrpl.libxrpl.core
|
||||
xrpl.libxrpl.crypto
|
||||
xrpl.libxrpl.git
|
||||
xrpl.libxrpl.json
|
||||
xrpl.libxrpl.rdb
|
||||
xrpl.libxrpl.ledger
|
||||
xrpl.libxrpl.net
|
||||
xrpl.libxrpl.nodestore
|
||||
xrpl.libxrpl.protocol
|
||||
xrpl.libxrpl.resource
|
||||
xrpl.libxrpl.server
|
||||
xrpl.libxrpl.shamap
|
||||
xrpl.libxrpl.tx
|
||||
antithesis-sdk-cpp
|
||||
EXPORT XrplExports
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
INCLUDES DESTINATION include
|
||||
TARGETS xrpl.libpb xrpl.libxrpl
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT development
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
|
||||
install(
|
||||
EXPORT XrplExports
|
||||
FILE XrplTargets.cmake
|
||||
NAMESPACE Xrpl::
|
||||
DESTINATION lib/cmake/xrpl
|
||||
)
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
XrplConfigVersion.cmake
|
||||
VERSION ${xrpld_version}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
if(is_root_project AND TARGET xrpld)
|
||||
install(TARGETS xrpld RUNTIME DESTINATION bin)
|
||||
set_target_properties(xrpld PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON)
|
||||
# sample configs should not overwrite existing files
|
||||
# install if-not-exists workaround as suggested by
|
||||
# https://cmake.org/Bug/view.php?id=12646
|
||||
install(
|
||||
CODE
|
||||
"
|
||||
macro (copy_if_not_exists SRC DEST NEWNAME)
|
||||
if (NOT EXISTS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DEST}/\${NEWNAME}\")
|
||||
file (INSTALL FILE_PERMISSIONS OWNER_READ OWNER_WRITE DESTINATION \"\${CMAKE_INSTALL_PREFIX}/\${DEST}\" FILES \"\${SRC}\" RENAME \"\${NEWNAME}\")
|
||||
else ()
|
||||
message (\"-- Skipping : \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DEST}/\${NEWNAME}\")
|
||||
endif ()
|
||||
endmacro()
|
||||
copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg\" etc xrpld.cfg)
|
||||
copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt\" etc validators.txt)
|
||||
"
|
||||
)
|
||||
install(
|
||||
CODE
|
||||
"
|
||||
set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\")
|
||||
include(create_symbolic_link)
|
||||
create_symbolic_link(xrpld${suffix} \
|
||||
\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/rippled${suffix})
|
||||
"
|
||||
)
|
||||
endif()
|
||||
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/XrplConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/XrplConfigVersion.cmake
|
||||
DESTINATION lib/cmake/xrpl
|
||||
COMPONENT development
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ target_compile_definitions(
|
||||
BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
>
|
||||
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
|
||||
BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||
BOOST_COROUTINES2_NO_DEPRECATION_WARNING
|
||||
BOOST_BEAST_ALLOW_DEPRECATED
|
||||
BOOST_FILESYSTEM_DEPRECATED
|
||||
>
|
||||
|
||||
@@ -50,6 +50,13 @@ if(MSVC AND CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
|
||||
message(FATAL_ERROR "Visual Studio 32-bit build is not supported.")
|
||||
endif()
|
||||
|
||||
if(voidstar AND NOT is_amd64)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"The voidstar library only supported on amd64/x86_64. Detected archictecture was: ${CMAKE_SYSTEM_PROCESSOR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(APPLE AND NOT HOMEBREW)
|
||||
find_program(HOMEBREW brew)
|
||||
endif()
|
||||
|
||||
@@ -7,7 +7,7 @@ find_package(
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
coroutine
|
||||
context
|
||||
date_time
|
||||
filesystem
|
||||
json
|
||||
@@ -26,7 +26,7 @@ target_link_libraries(
|
||||
Boost::headers
|
||||
Boost::chrono
|
||||
Boost::container
|
||||
Boost::coroutine
|
||||
Boost::context
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::json
|
||||
@@ -38,23 +38,26 @@ target_link_libraries(
|
||||
if(Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif()
|
||||
if(SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
|
||||
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
get_target_property(
|
||||
Boost_INCLUDE_DIRS
|
||||
Boost::headers
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
)
|
||||
endif()
|
||||
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
|
||||
file(
|
||||
WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt
|
||||
"src:${Boost_INCLUDE_DIRS}/*"
|
||||
)
|
||||
target_compile_options(
|
||||
opts
|
||||
INTERFACE # ignore boost headers for sanitizing
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt
|
||||
|
||||
# GCC 14+ has a false positive -Wuninitialized warning in Boost.Coroutine2's
|
||||
# state.hpp when compiled with -O3. This is due to GCC's intentional behavior
|
||||
# change (Bug #98871, #119388) where warnings from inlined system header code
|
||||
# are no longer suppressed by -isystem. The warning occurs in operator|= in
|
||||
# boost/coroutine2/detail/state.hpp when inlined from push_control_block::destroy().
|
||||
# See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119388
|
||||
if(is_gcc AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
|
||||
target_compile_options(xrpl_boost INTERFACE -Wno-uninitialized)
|
||||
endif()
|
||||
|
||||
# Boost.Context's ucontext backend has ASAN fiber-switching annotations
|
||||
# (start/finish_switch_fiber) that are compiled in when BOOST_USE_ASAN is defined.
|
||||
# This tells ASAN about coroutine stack switches, preventing false positive
|
||||
# stack-use-after-scope errors. BOOST_USE_UCONTEXT ensures the ucontext backend
|
||||
# is selected (fcontext does not support ASAN annotations).
|
||||
# These defines must match what Boost was compiled with (see conan/profiles/sanitizers).
|
||||
if(enable_asan)
|
||||
target_compile_definitions(
|
||||
xrpl_boost
|
||||
INTERFACE BOOST_USE_ASAN BOOST_USE_UCONTEXT
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -7,16 +7,21 @@ include(default)
|
||||
{% if compiler == "gcc" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% set model_code = "" %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1", "-Wno-stringop-overflow"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set model_code = "-mcmodel=large" %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set model_code = "-mcmodel=medium" %}
|
||||
{% set _ = extra_cxxflags.append("-Wno-tsan") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -29,16 +34,22 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% elif compiler == "apple-clang" or compiler == "clang" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -52,8 +63,24 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags"]
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"]
|
||||
|
||||
[options]
|
||||
{% if sanitizers %}
|
||||
{% if "address" in sanitizers %}
|
||||
# Build Boost.Context with ucontext backend (not fcontext) so that
|
||||
# ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber)
|
||||
# are compiled into the library. fcontext (assembly) has no ASAN support.
|
||||
# define=BOOST_USE_ASAN=1 is critical: it must be defined when building
|
||||
# Boost.Context itself so the ucontext backend compiles in the ASAN annotations.
|
||||
boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1
|
||||
boost/*:without_context=False
|
||||
# Boost stacktrace fails to build with some sanitizers
|
||||
boost/*:without_stacktrace=True
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
13
conanfile.py
13
conanfile.py
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
@@ -57,6 +58,9 @@ class Xrpl(ConanFile):
|
||||
"tests": False,
|
||||
"unity": False,
|
||||
"xrpld": False,
|
||||
"boost/*:without_context": False,
|
||||
"boost/*:without_coroutine": True,
|
||||
"boost/*:without_coroutine2": False,
|
||||
"date/*:header_only": True,
|
||||
"ed25519/*:shared": False,
|
||||
"grpc/*:shared": False,
|
||||
@@ -126,6 +130,12 @@ class Xrpl(ConanFile):
|
||||
if self.settings.compiler in ["clang", "gcc"]:
|
||||
self.options["boost"].without_cobalt = True
|
||||
|
||||
# Check if environment variable exists
|
||||
if "SANITIZERS" in os.environ:
|
||||
sanitizers = os.environ["SANITIZERS"]
|
||||
if "address" in sanitizers.lower():
|
||||
self.default_options["fPIC"] = False
|
||||
|
||||
def requirements(self):
|
||||
# Conan 2 requires transitive headers to be specified
|
||||
transitive_headers_opt = (
|
||||
@@ -196,7 +206,8 @@ class Xrpl(ConanFile):
|
||||
"boost::headers",
|
||||
"boost::chrono",
|
||||
"boost::container",
|
||||
"boost::coroutine",
|
||||
"boost::context",
|
||||
"boost::coroutine2",
|
||||
"boost::date_time",
|
||||
"boost::filesystem",
|
||||
"boost::json",
|
||||
|
||||
@@ -99,6 +99,7 @@ words:
|
||||
- endmacro
|
||||
- exceptioned
|
||||
- Falco
|
||||
- fcontext
|
||||
- finalizers
|
||||
- firewalled
|
||||
- fmtdur
|
||||
@@ -111,6 +112,7 @@ words:
|
||||
- gpgcheck
|
||||
- gpgkey
|
||||
- hotwallet
|
||||
- hwaddress
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
|
||||
8
docs/build/sanitizers.md
vendored
8
docs/build/sanitizers.md
vendored
@@ -89,8 +89,8 @@ cmake --build . --parallel 4
|
||||
**IMPORTANT**: ASAN with Boost produces many false positives. Use these options:
|
||||
|
||||
```bash
|
||||
export ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=path/to/asan.supp:halt_on_error=0:log_path=asan.log"
|
||||
export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsan.log"
|
||||
export ASAN_OPTIONS="include=sanitizers/suppressions/runtime-asan-options.txt:suppressions=sanitizers/suppressions/asan.supp"
|
||||
export LSAN_OPTIONS="include=sanitizers/suppressions/runtime-lsan-options.txt:suppressions=sanitizers/suppressions/lsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -108,7 +108,7 @@ export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsa
|
||||
### ThreadSanitizer (TSan)
|
||||
|
||||
```bash
|
||||
export TSAN_OPTIONS="suppressions=path/to/tsan.supp halt_on_error=0 log_path=tsan.log"
|
||||
export TSAN_OPTIONS="include=sanitizers/suppressions/runtime-tsan-options.txt:suppressions=sanitizers/suppressions/tsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -129,7 +129,7 @@ More details [here](https://github.com/google/sanitizers/wiki/AddressSanitizerLe
|
||||
### UndefinedBehaviorSanitizer (UBSan)
|
||||
|
||||
```bash
|
||||
export UBSAN_OPTIONS="suppressions=path/to/ubsan.supp:print_stacktrace=1:halt_on_error=0:log_path=ubsan.log"
|
||||
export UBSAN_OPTIONS="include=sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=sanitizers/suppressions/ubsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/sanitizers.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
|
||||
#include <exception>
|
||||
@@ -23,16 +24,28 @@ LogThrow(std::string const& title);
|
||||
When called from within a catch block, it will pass
|
||||
control to the next matching exception handler, if any.
|
||||
Otherwise, std::terminate will be called.
|
||||
|
||||
ASAN can't handle sudden jumps in control flow very well. This
|
||||
function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from
|
||||
triggering false positives, since it throws.
|
||||
*/
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Rethrow()
|
||||
{
|
||||
LogThrow("Re-throwing exception");
|
||||
throw;
|
||||
}
|
||||
|
||||
/*
|
||||
Logs and throws an exception of type E.
|
||||
|
||||
ASAN can't handle sudden jumps in control flow very well. This
|
||||
function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from
|
||||
triggering false positives, since it throws.
|
||||
*/
|
||||
|
||||
template <class E, class... Args>
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Throw(Args&&... args)
|
||||
{
|
||||
static_assert(
|
||||
|
||||
13
include/xrpl/basics/sanitizers.h
Normal file
13
include/xrpl/basics/sanitizers.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Helper to disable ASan/HwASan for specific functions
|
||||
/*
|
||||
ASAN flags some false positives with sudden jumps in control flow, like
|
||||
exceptions, or when encountering coroutine stack switches. This macro can be used to disable ASAN
|
||||
intrumentation for specific functions.
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress")))
|
||||
#else
|
||||
#define XRPL_NO_SANITIZE_ADDRESS
|
||||
#endif
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class F>
|
||||
@@ -11,16 +9,18 @@ JobQueue::Coro::Coro(Coro_create_t, JobQueue& jq, JobType type, std::string cons
|
||||
, name_(name)
|
||||
, running_(false)
|
||||
, coro_(
|
||||
// Stack size of 1MB wasn't sufficient for deep calls. ASAN tests flagged the issue. Hence
|
||||
// increasing the size to 1.5MB.
|
||||
boost::context::protected_fixedsize_stack(1536 * 1024),
|
||||
[this, fn = std::forward<F>(f)](
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
boost::coroutines2::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
yield_ = &do_yield;
|
||||
yield();
|
||||
fn(shared_from_this());
|
||||
#ifndef NDEBUG
|
||||
finished_ = true;
|
||||
#endif
|
||||
},
|
||||
boost::coroutines::attributes(megabytes(1)))
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#include <xrpl/core/detail/Workers.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <boost/coroutine/all.hpp>
|
||||
#include <boost/context/protected_fixedsize_stack.hpp>
|
||||
#include <boost/coroutine2/all.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -48,8 +49,8 @@ public:
|
||||
std::mutex mutex_;
|
||||
std::mutex mutex_run_;
|
||||
std::condition_variable cv_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::pull_type coro_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type* yield_;
|
||||
boost::coroutines2::coroutine<void>::pull_type coro_;
|
||||
boost::coroutines2::coroutine<void>::push_type* yield_;
|
||||
#ifndef NDEBUG
|
||||
bool finished_ = false;
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,18 @@ public:
|
||||
bool sslVerify,
|
||||
beast::Journal j);
|
||||
|
||||
/** Destroys the global SSL context created by initializeSSLContext().
|
||||
*
|
||||
* This releases the underlying boost::asio::ssl::context and any
|
||||
* associated OpenSSL resources. Must not be called while any
|
||||
* HTTPClient requests are in flight.
|
||||
*
|
||||
* @note Currently only called from tests during teardown. In production,
|
||||
* the SSL context lives for the lifetime of the process.
|
||||
*/
|
||||
static void
|
||||
cleanupSSLContext();
|
||||
|
||||
static void
|
||||
get(bool bSSL,
|
||||
boost::asio::io_context& io_context,
|
||||
|
||||
@@ -64,6 +64,49 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Feature names must not exceed this length (in characters, excluding the null terminator).
|
||||
static constexpr std::size_t maxFeatureNameSize = 63;
|
||||
// Reserve this exact feature-name length (in characters/bytes, excluding the null terminator)
|
||||
// so that a 32-byte uint256 (for example, in WASM or other interop contexts) can be used
|
||||
// as a compact, fixed-size feature selector without conflicting with human-readable names.
|
||||
static constexpr std::size_t reservedFeatureNameSize = 32;
|
||||
|
||||
// Both validFeatureNameSize and validFeatureName are consteval functions that can be used in
|
||||
// static_asserts to validate feature names at compile time. They are only used inside
|
||||
// enforceValidFeatureName in Feature.cpp, but are exposed here for testing. The expected
|
||||
// parameter `auto fn` is a constexpr lambda which returns a const char*, making it available
|
||||
// for compile-time evaluation. Read more in https://accu.org/journals/overload/30/172/wu/
|
||||
consteval auto
|
||||
validFeatureNameSize(auto fn) -> bool
|
||||
{
|
||||
constexpr char const* n = fn();
|
||||
// Note, std::strlen is not constexpr, we need to implement our own here.
|
||||
constexpr std::size_t N = [](auto n) {
|
||||
std::size_t ret = 0;
|
||||
for (auto ptr = n; *ptr != '\0'; ret++, ++ptr)
|
||||
;
|
||||
return ret;
|
||||
}(n);
|
||||
return N != reservedFeatureNameSize && //
|
||||
N <= maxFeatureNameSize;
|
||||
}
|
||||
|
||||
consteval auto
|
||||
validFeatureName(auto fn) -> bool
|
||||
{
|
||||
constexpr char const* n = fn();
|
||||
// Prevent the use of visually confusable characters and enforce that feature names
|
||||
// are always valid ASCII. This is needed because C++ allows Unicode identifiers.
|
||||
// Characters below 0x20 are nonprintable control characters, and characters with the 0x80 bit
|
||||
// set are non-ASCII (e.g. UTF-8 encoding of Unicode), so both are disallowed.
|
||||
for (auto ptr = n; *ptr != '\0'; ++ptr)
|
||||
{
|
||||
if (*ptr & 0x80 || *ptr < 0x20)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes };
|
||||
enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported };
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ std::size_t constexpr maxDIDDocumentLength = 256;
|
||||
std::size_t constexpr maxDIDURILength = 256;
|
||||
|
||||
/** The maximum length of an Attestation inside a DID */
|
||||
std::size_t constexpr maxDIDAttestationLength = 256;
|
||||
std::size_t constexpr maxDIDDataLength = 256;
|
||||
|
||||
/** The maximum length of a domain */
|
||||
std::size_t constexpr maxDomainLength = 256;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum TxnSql : char {
|
||||
enum class TxnSql : char {
|
||||
txnSqlNew = 'N',
|
||||
txnSqlConflict = 'C',
|
||||
txnSqlHeld = 'H',
|
||||
@@ -83,6 +83,9 @@ public:
|
||||
std::uint32_t
|
||||
getSeqValue() const;
|
||||
|
||||
AccountID
|
||||
getFeePayer() const;
|
||||
|
||||
boost::container::flat_set<AccountID>
|
||||
getMentionedAccounts() const;
|
||||
|
||||
@@ -122,7 +125,7 @@ public:
|
||||
getMetaSQL(
|
||||
Serializer rawTxn,
|
||||
std::uint32_t inLedger,
|
||||
char status,
|
||||
TxnSql status,
|
||||
std::string const& escapedMetaData) const;
|
||||
|
||||
std::vector<uint256> const&
|
||||
|
||||
@@ -16,8 +16,11 @@ namespace xrpl {
|
||||
/** A secret key. */
|
||||
class SecretKey
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t size_ = 32;
|
||||
|
||||
private:
|
||||
std::uint8_t buf_[32];
|
||||
std::uint8_t buf_[size_];
|
||||
|
||||
public:
|
||||
using const_iterator = std::uint8_t const*;
|
||||
@@ -27,9 +30,14 @@ public:
|
||||
SecretKey&
|
||||
operator=(SecretKey const&) = default;
|
||||
|
||||
bool
|
||||
operator==(SecretKey const&) = delete;
|
||||
bool
|
||||
operator!=(SecretKey const&) = delete;
|
||||
|
||||
~SecretKey();
|
||||
|
||||
SecretKey(std::array<std::uint8_t, 32> const& data);
|
||||
SecretKey(std::array<std::uint8_t, size_> const& data);
|
||||
SecretKey(Slice const& slice);
|
||||
|
||||
std::uint8_t const*
|
||||
@@ -78,16 +86,10 @@ public:
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(SecretKey const& lhs, SecretKey const& rhs)
|
||||
{
|
||||
return lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
|
||||
}
|
||||
operator==(SecretKey const& lhs, SecretKey const& rhs) = delete;
|
||||
|
||||
inline bool
|
||||
operator!=(SecretKey const& lhs, SecretKey const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
operator!=(SecretKey const& lhs, SecretKey const& rhs) = delete;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace xrpl {
|
||||
static inline std::string const&
|
||||
systemName()
|
||||
{
|
||||
static std::string const name = "ripple";
|
||||
static std::string const name = "xrpld";
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
|
||||
/** This transaction type creates an escrow object. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/escrow/Escrow.h>
|
||||
# include <xrpl/tx/transactors/escrow/EscrowCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
||||
Delegation::delegable,
|
||||
@@ -58,6 +58,9 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
||||
}))
|
||||
|
||||
/** This transaction type completes an existing escrow. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/escrow/EscrowFinish.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
|
||||
Delegation::delegable,
|
||||
uint256{},
|
||||
@@ -94,7 +97,7 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
||||
|
||||
/** This transaction type cancels an existing escrow. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/escrow/Escrow.h>
|
||||
# include <xrpl/tx/transactors/escrow/EscrowCancel.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
||||
Delegation::delegable,
|
||||
@@ -180,7 +183,7 @@ TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||
|
||||
/** This transaction type creates a new unidirectional XRP payment channel. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/payment_channel/PayChan.h>
|
||||
# include <xrpl/tx/transactors/payment_channel/PayChanCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
||||
Delegation::delegable,
|
||||
@@ -196,6 +199,9 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
||||
}))
|
||||
|
||||
/** This transaction type funds an existing unidirectional XRP payment channel. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/payment_channel/PayChanFund.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund,
|
||||
Delegation::delegable,
|
||||
uint256{},
|
||||
@@ -207,6 +213,9 @@ TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund,
|
||||
}))
|
||||
|
||||
/** This transaction type submits a claim against an existing unidirectional payment channel. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/payment_channel/PayChanClaim.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
|
||||
Delegation::delegable,
|
||||
uint256{},
|
||||
@@ -617,7 +626,7 @@ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
|
||||
|
||||
/** This transaction type creates or updates a DID */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/did/DID.h>
|
||||
# include <xrpl/tx/transactors/did/DIDSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttDID_SET, 49, DIDSet,
|
||||
Delegation::delegable,
|
||||
@@ -630,6 +639,9 @@ TRANSACTION(ttDID_SET, 49, DIDSet,
|
||||
}))
|
||||
|
||||
/** This transaction type deletes a DID */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/did/DIDDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttDID_DELETE, 50, DIDDelete,
|
||||
Delegation::delegable,
|
||||
featureDID,
|
||||
@@ -739,7 +751,7 @@ TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
||||
|
||||
/** This transaction type create an Credential instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/credentials/Credentials.h>
|
||||
# include <xrpl/tx/transactors/credentials/CredentialCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
||||
Delegation::delegable,
|
||||
@@ -753,6 +765,9 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
||||
}))
|
||||
|
||||
/** This transaction type accept an Credential object */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/credentials/CredentialAccept.h>
|
||||
#endif
|
||||
TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept,
|
||||
Delegation::delegable,
|
||||
featureCredentials,
|
||||
@@ -763,6 +778,9 @@ TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept,
|
||||
}))
|
||||
|
||||
/** This transaction type delete an Credential object */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/credentials/CredentialDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
|
||||
Delegation::delegable,
|
||||
featureCredentials,
|
||||
@@ -830,7 +848,7 @@ TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
||||
# include <xrpl/tx/transactors/vault/VaultCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureSingleAssetVault,
|
||||
createPseudoAcct | createMPTIssuance | mustModifyVault,
|
||||
({
|
||||
@@ -848,7 +866,7 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
# include <xrpl/tx/transactors/vault/VaultSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureSingleAssetVault,
|
||||
mustModifyVault,
|
||||
({
|
||||
@@ -863,7 +881,7 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
# include <xrpl/tx/transactors/vault/VaultDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureSingleAssetVault,
|
||||
mustDeleteAcct | destroyMPTIssuance | mustModifyVault,
|
||||
({
|
||||
@@ -875,7 +893,7 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||
# include <xrpl/tx/transactors/vault/VaultDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureSingleAssetVault,
|
||||
mayAuthorizeMPT | mustModifyVault,
|
||||
({
|
||||
@@ -888,7 +906,7 @@ TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||
# include <xrpl/tx/transactors/vault/VaultWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureSingleAssetVault,
|
||||
mayDeleteMPT | mayAuthorizeMPT | mustModifyVault,
|
||||
({
|
||||
@@ -903,7 +921,7 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||
# include <xrpl/tx/transactors/vault/VaultClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureSingleAssetVault,
|
||||
mayDeleteMPT | mustModifyVault,
|
||||
({
|
||||
@@ -932,7 +950,7 @@ TRANSACTION(ttBATCH, 71, Batch,
|
||||
# include <xrpl/tx/transactors/lending/LoanBrokerSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
createPseudoAcct | mayAuthorizeMPT, ({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
@@ -949,7 +967,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
|
||||
# include <xrpl/tx/transactors/lending/LoanBrokerDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
mustDeleteAcct | mayAuthorizeMPT, ({
|
||||
{sfLoanBrokerID, soeREQUIRED},
|
||||
@@ -960,7 +978,7 @@ TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
|
||||
# include <xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
noPriv, ({
|
||||
{sfLoanBrokerID, soeREQUIRED},
|
||||
@@ -972,7 +990,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit,
|
||||
# include <xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
mayAuthorizeMPT, ({
|
||||
{sfLoanBrokerID, soeREQUIRED},
|
||||
@@ -987,7 +1005,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
|
||||
# include <xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
noPriv, ({
|
||||
{sfLoanBrokerID, soeOPTIONAL},
|
||||
@@ -999,7 +1017,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback,
|
||||
# include <xrpl/tx/transactors/lending/LoanSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_SET, 80, LoanSet,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
mayAuthorizeMPT | mustModifyVault, ({
|
||||
{sfLoanBrokerID, soeREQUIRED},
|
||||
@@ -1026,7 +1044,7 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet,
|
||||
# include <xrpl/tx/transactors/lending/LoanDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_DELETE, 81, LoanDelete,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
noPriv, ({
|
||||
{sfLoanID, soeREQUIRED},
|
||||
@@ -1037,7 +1055,7 @@ TRANSACTION(ttLOAN_DELETE, 81, LoanDelete,
|
||||
# include <xrpl/tx/transactors/lending/LoanManage.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
// All of the LoanManage options will modify the vault, but the
|
||||
// transaction can succeed without options, essentially making it
|
||||
@@ -1051,7 +1069,7 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
|
||||
# include <xrpl/tx/transactors/lending/LoanPay.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_PAY, 84, LoanPay,
|
||||
Delegation::delegable,
|
||||
Delegation::notDelegable,
|
||||
featureLendingProtocol,
|
||||
mayAuthorizeMPT | mustModifyVault, ({
|
||||
{sfLoanID, soeREQUIRED},
|
||||
|
||||
@@ -114,8 +114,7 @@ protected:
|
||||
beast::Journal const j_;
|
||||
|
||||
AccountID const account_;
|
||||
XRPAmount mPriorBalance; // Balance before fees.
|
||||
XRPAmount mSourceBalance; // Balance after fees.
|
||||
XRPAmount preFeeBalance_; // Balance before fees.
|
||||
|
||||
virtual ~Transactor() = default;
|
||||
Transactor(Transactor const&) = delete;
|
||||
|
||||
@@ -27,6 +27,33 @@ namespace xrpl {
|
||||
* communicate the interface required of any invariant checker. Any invariant
|
||||
* check implementation should implement the public methods documented here.
|
||||
*
|
||||
* ## Rules for implementing `finalize`
|
||||
*
|
||||
* ### Invariants must run regardless of transaction result
|
||||
*
|
||||
* An invariant's `finalize` method MUST perform meaningful checks even when
|
||||
* the transaction has failed (i.e., `!isTesSuccess(tec)`). The following
|
||||
* pattern is almost certainly wrong and must never be used:
|
||||
*
|
||||
* @code
|
||||
* // WRONG: skipping all checks on failure defeats the purpose of invariants
|
||||
* if (!isTesSuccess(tec))
|
||||
* return true;
|
||||
* @endcode
|
||||
*
|
||||
* The entire purpose of invariants is to detect and prevent the impossible.
|
||||
* A bug or exploit could cause a failed transaction to mutate ledger state in
|
||||
* unexpected ways. Invariants are the last line of defense against such
|
||||
* scenarios.
|
||||
*
|
||||
* In general: an invariant that expects a domain-specific state change to
|
||||
* occur (e.g., a new object being created) should only expect that change
|
||||
* when the transaction succeeded. A failed VaultCreate must not have created
|
||||
* a Vault. A failed LoanSet must not have created a Loan.
|
||||
*
|
||||
* Also be aware that failed transactions, regardless of type, carry no
|
||||
* Privileges. Any privilege-gated checks must therefore also be applied to
|
||||
* failed transactions.
|
||||
*/
|
||||
class InvariantChecker_PROTOTYPE
|
||||
{
|
||||
@@ -48,7 +75,11 @@ public:
|
||||
|
||||
/**
|
||||
* @brief called after all ledger entries have been visited to determine
|
||||
* the final status of the check
|
||||
* the final status of the check.
|
||||
*
|
||||
* This method MUST perform meaningful checks even when `tec` indicates a
|
||||
* failed transaction. See the class-level documentation for the rules
|
||||
* governing how failed transactions must be handled.
|
||||
*
|
||||
* @param tx the transaction being applied
|
||||
* @param tec the current TER result of the transaction
|
||||
|
||||
29
include/xrpl/tx/transactors/credentials/CredentialAccept.h
Normal file
29
include/xrpl/tx/transactors/credentials/CredentialAccept.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CredentialAccept : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialAccept(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/credentials/CredentialCreate.h
Normal file
29
include/xrpl/tx/transactors/credentials/CredentialCreate.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CredentialCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/credentials/CredentialDelete.h
Normal file
29
include/xrpl/tx/transactors/credentials/CredentialDelete.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CredentialDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,77 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CredentialCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class CredentialDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class CredentialAccept : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialAccept(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -370,7 +370,7 @@ changeSpotPriceQuality(
|
||||
if (!amounts)
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " " << quality << " " << tfee << std::endl;
|
||||
<< to_string(pool.out) << " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,24 +4,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DIDSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DIDSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DIDDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
23
include/xrpl/tx/transactors/did/DIDSet.h
Normal file
23
include/xrpl/tx/transactors/did/DIDSet.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DIDSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DIDSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,80 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class EscrowCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class EscrowFinish : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowFinish(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class EscrowCancel : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowCancel(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
26
include/xrpl/tx/transactors/escrow/EscrowCancel.h
Normal file
26
include/xrpl/tx/transactors/escrow/EscrowCancel.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class EscrowCancel : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowCancel(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/escrow/EscrowCreate.h
Normal file
29
include/xrpl/tx/transactors/escrow/EscrowCreate.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class EscrowCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/tx/transactors/escrow/EscrowFinish.h
Normal file
35
include/xrpl/tx/transactors/escrow/EscrowFinish.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class EscrowFinish : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowFinish(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PayChanCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit PayChanCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelCreate = PayChanCreate;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class PayChanFund : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit PayChanFund(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelFund = PayChanFund;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class PayChanClaim : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit PayChanClaim(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelClaim = PayChanClaim;
|
||||
|
||||
} // namespace xrpl
|
||||
34
include/xrpl/tx/transactors/payment_channel/PayChanClaim.h
Normal file
34
include/xrpl/tx/transactors/payment_channel/PayChanClaim.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PayChanClaim : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit PayChanClaim(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelClaim = PayChanClaim;
|
||||
|
||||
} // namespace xrpl
|
||||
31
include/xrpl/tx/transactors/payment_channel/PayChanCreate.h
Normal file
31
include/xrpl/tx/transactors/payment_channel/PayChanCreate.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PayChanCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit PayChanCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelCreate = PayChanCreate;
|
||||
|
||||
} // namespace xrpl
|
||||
28
include/xrpl/tx/transactors/payment_channel/PayChanFund.h
Normal file
28
include/xrpl/tx/transactors/payment_channel/PayChanFund.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PayChanFund : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit PayChanFund(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelFund = PayChanFund;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,24 +1,6 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppressions.
|
||||
#
|
||||
# ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=sanitizers/suppressions/asan.supp:halt_on_error=0"
|
||||
#
|
||||
# The detect_container_overflow=0 option disables false positives from:
|
||||
# - Boost intrusive containers (slist_iterator.hpp, hashtable.hpp, aged_unordered_container.h)
|
||||
# - Boost context/coroutine stack switching (Workers.cpp, thread.h)
|
||||
#
|
||||
# See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
||||
|
||||
# Boost
|
||||
interceptor_name:boost/asio
|
||||
|
||||
# Leaks in Doctest tests: xrpl.test.*
|
||||
interceptor_name:src/libxrpl/net/HTTPClient.cpp
|
||||
interceptor_name:src/libxrpl/net/RegisterSSLCerts.cpp
|
||||
interceptor_name:src/tests/libxrpl/net/HTTPClient.cpp
|
||||
interceptor_name:xrpl/net/AutoSocket.h
|
||||
interceptor_name:xrpl/net/HTTPClient.h
|
||||
interceptor_name:xrpl/net/HTTPClientSSLContext.h
|
||||
interceptor_name:xrpl/net/RegisterSSLCerts.h
|
||||
|
||||
# Suppress false positive stack-buffer errors in thread stack allocation
|
||||
# Related to ASan's __asan_handle_no_return warnings (github.com/google/sanitizers/issues/189)
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
||||
|
||||
# Suppress leaks detected by asan in rippled code.
|
||||
leak:src/libxrpl/net/HTTPClient.cpp
|
||||
leak:src/libxrpl/net/RegisterSSLCerts.cpp
|
||||
leak:src/tests/libxrpl/net/HTTPClient.cpp
|
||||
leak:xrpl/net/AutoSocket.h
|
||||
leak:xrpl/net/HTTPClient.h
|
||||
leak:xrpl/net/HTTPClientSSLContext.h
|
||||
leak:xrpl/net/RegisterSSLCerts.h
|
||||
leak:ripple::HTTPClient
|
||||
leak:ripple::HTTPClientImp
|
||||
|
||||
# Suppress leaks detected by asan in boost code.
|
||||
leak:boost::asio
|
||||
leak:boost/asio
|
||||
|
||||
8
sanitizers/suppressions/runtime-asan-options.txt
Normal file
8
sanitizers/suppressions/runtime-asan-options.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
detect_container_overflow=false
|
||||
detect_stack_use_after_return=false
|
||||
debug=true
|
||||
halt_on_error=false
|
||||
print_stats=true
|
||||
print_cmdline=true
|
||||
use_sigaltstack=0
|
||||
print_stacktrace=1
|
||||
1
sanitizers/suppressions/runtime-lsan-options.txt
Normal file
1
sanitizers/suppressions/runtime-lsan-options.txt
Normal file
@@ -0,0 +1 @@
|
||||
halt_on_error=false
|
||||
3
sanitizers/suppressions/runtime-tsan-options.txt
Normal file
3
sanitizers/suppressions/runtime-tsan-options.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
halt_on_error=false
|
||||
verbosity=1
|
||||
second_deadlock_stack=1
|
||||
1
sanitizers/suppressions/runtime-ubsan-options.txt
Normal file
1
sanitizers/suppressions/runtime-ubsan-options.txt
Normal file
@@ -0,0 +1 @@
|
||||
halt_on_error=false
|
||||
@@ -27,3 +27,11 @@ src:core/JobQueue.cpp
|
||||
src:libxrpl/beast/utility/beast_Journal.cpp
|
||||
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. 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.
|
||||
[address]
|
||||
src:*ErrorCodes.h
|
||||
|
||||
@@ -182,6 +182,17 @@ signed-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
||||
# External library suppressions
|
||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
||||
|
||||
# Loan_test.cpp intentional underflow in test arithmetic
|
||||
unsigned-integer-overflow:src/test/app/Loan_test.cpp
|
||||
undefined:src/test/app/Loan_test.cpp
|
||||
|
||||
# Source tree restructured paths (libxrpl/tx/transactors/)
|
||||
# These duplicate the xrpld/app/tx/detail entries above for the new layout
|
||||
unsigned-integer-overflow:src/libxrpl/tx/transactors/oracle/SetOracle.cpp
|
||||
undefined:src/libxrpl/tx/transactors/oracle/SetOracle.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp
|
||||
undefined:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp
|
||||
|
||||
# Protobuf intentional overflows in hash functions
|
||||
# Protobuf uses intentional unsigned overflow for hash computation (stringpiece.h:393)
|
||||
unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h
|
||||
|
||||
@@ -258,7 +258,7 @@ Number::Guard::doRoundUp(
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
||||
if (exponent > maxExponent)
|
||||
throw std::overflow_error(location);
|
||||
Throw<std::overflow_error>(std::string(location));
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
@@ -298,7 +298,7 @@ Number::Guard::doRound(rep& drops, std::string location)
|
||||
// or "(maxRep + 1) / 10", neither of which will round up when
|
||||
// converting to rep, though the latter might overflow _before_
|
||||
// rounding.
|
||||
throw std::overflow_error(location); // LCOV_EXCL_LINE
|
||||
Throw<std::overflow_error>(std::string(location)); // LCOV_EXCL_LINE
|
||||
}
|
||||
++drops;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace xrpl {
|
||||
template <class Derived>
|
||||
class AsyncObject
|
||||
{
|
||||
protected:
|
||||
AsyncObject() : m_pending(0)
|
||||
{
|
||||
}
|
||||
@@ -93,6 +92,8 @@ public:
|
||||
private:
|
||||
// The number of handlers pending.
|
||||
std::atomic<int> m_pending;
|
||||
|
||||
friend Derived;
|
||||
};
|
||||
|
||||
class ResolverAsioImpl : public ResolverAsio, public AsyncObject<ResolverAsioImpl>
|
||||
|
||||
@@ -116,6 +116,7 @@ encode(void* dest, void const* src, std::size_t len)
|
||||
in += 3;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
|
||||
switch (len % 3)
|
||||
{
|
||||
case 2:
|
||||
@@ -168,7 +169,7 @@ decode(void* dest, char const* src, std::size_t len)
|
||||
break;
|
||||
++in;
|
||||
c4[i] = v;
|
||||
if (++i == 4)
|
||||
if (++i; i == 4)
|
||||
{
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
|
||||
@@ -239,6 +239,7 @@ initAuthenticated(
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
context.use_certificate_file(cert_file, boost::asio::ssl::context::pem, ec);
|
||||
|
||||
if (ec)
|
||||
@@ -298,6 +299,7 @@ initAuthenticated(
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
context.use_private_key_file(key_file, boost::asio::ssl::context::pem, ec);
|
||||
|
||||
if (ec)
|
||||
|
||||
@@ -470,6 +470,7 @@ public:
|
||||
|
||||
m_io_context.run();
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_send, ec);
|
||||
|
||||
m_socket.close();
|
||||
|
||||
@@ -729,21 +729,18 @@ Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, un
|
||||
|
||||
unsigned int surrogatePair;
|
||||
|
||||
if (*(current++) == '\\' && *(current++) == 'u')
|
||||
{
|
||||
if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair))
|
||||
{
|
||||
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
if (*current != '\\' || *(current + 1) != 'u')
|
||||
return addError(
|
||||
"expecting another \\u token to begin the second half of a "
|
||||
"unicode surrogate pair",
|
||||
"expecting another \\u token to begin the second half of a unicode surrogate pair",
|
||||
token,
|
||||
current);
|
||||
|
||||
current += 2; // skip two characters checked above
|
||||
|
||||
if (!decodeUnicodeEscapeSequence(token, current, end, surrogatePair))
|
||||
return false;
|
||||
|
||||
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -319,7 +319,7 @@ StyledWriter::writeValue(Value const& value)
|
||||
document_ += " : ";
|
||||
writeValue(childValue);
|
||||
|
||||
if (++it == members.end())
|
||||
if (++it; it == members.end())
|
||||
break;
|
||||
|
||||
document_ += ",";
|
||||
|
||||
@@ -74,8 +74,10 @@ BookDirs::const_iterator::operator++()
|
||||
XRPL_ASSERT(index_ != zero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index");
|
||||
if (!cdirNext(*view_, cur_key_, sle_, entry_, index_))
|
||||
{
|
||||
if (index_ != 0 ||
|
||||
(cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero)) == zero)
|
||||
if (index_ == 0)
|
||||
cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(zero);
|
||||
|
||||
if (index_ != 0 || cur_key_ == zero)
|
||||
{
|
||||
cur_key_ = key_;
|
||||
entry_ = 0;
|
||||
@@ -84,9 +86,7 @@ BookDirs::const_iterator::operator++()
|
||||
else if (!cdirFirst(*view_, cur_key_, sle_, entry_, index_))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::BookDirs::const_iterator::operator++ : directory is "
|
||||
"empty");
|
||||
UNREACHABLE("xrpl::BookDirs::const_iterator::operator++ : directory is empty");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ HTTPClient::initializeSSLContext(
|
||||
httpClientSSLContext.emplace(sslVerifyDir, sslVerifyFile, sslVerify, j);
|
||||
}
|
||||
|
||||
void
|
||||
HTTPClient::cleanupSSLContext()
|
||||
{
|
||||
httpClientSSLContext.reset();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Fetch a web page via http or https.
|
||||
|
||||
@@ -79,6 +79,7 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec,
|
||||
SSL_CTX_set_cert_store(ctx.native_handle(), store.release());
|
||||
|
||||
#else
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
ctx.set_default_verify_paths(ec);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/core/SemanticVersion.h>
|
||||
#include <xrpl/git/Git.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
||||
@@ -80,7 +81,7 @@ getVersionString()
|
||||
std::string const&
|
||||
getFullVersionString()
|
||||
{
|
||||
static std::string const value = "rippled-" + getVersionString();
|
||||
static std::string const value = systemName() + "-" + getVersionString();
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -395,10 +395,20 @@ featureToName(uint256 const& f)
|
||||
#pragma push_macro("XRPL_RETIRE_FIX")
|
||||
#undef XRPL_RETIRE_FIX
|
||||
|
||||
consteval auto
|
||||
enforceValidFeatureName(auto fn) -> char const*
|
||||
{
|
||||
static_assert(validFeatureName(fn), "Invalid feature name");
|
||||
static_assert(validFeatureNameSize(fn), "Invalid feature name size");
|
||||
return fn();
|
||||
}
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) \
|
||||
uint256 const feature##name = registerFeature(#name, supported, vote);
|
||||
uint256 const feature##name = \
|
||||
registerFeature(enforceValidFeatureName([] { return #name; }), supported, vote);
|
||||
#define XRPL_FIX(name, supported, vote) \
|
||||
uint256 const fix##name = registerFeature("fix" #name, supported, vote);
|
||||
uint256 const fix##name = \
|
||||
registerFeature(enforceValidFeatureName([] { return "fix" #name; }), supported, vote);
|
||||
|
||||
// clang-format off
|
||||
#define XRPL_RETIRE_FEATURE(name) \
|
||||
|
||||
@@ -23,6 +23,9 @@ STBase::STBase(SField const& n) : fName(&n)
|
||||
STBase&
|
||||
STBase::operator=(STBase const& t)
|
||||
{
|
||||
if (this == &t)
|
||||
return *this;
|
||||
|
||||
if (!fName->isUseful())
|
||||
fName = t.fName;
|
||||
return *this;
|
||||
|
||||
@@ -211,6 +211,20 @@ STTx::getSeqValue() const
|
||||
return getSeqProxy().value();
|
||||
}
|
||||
|
||||
AccountID
|
||||
STTx::getFeePayer() const
|
||||
{
|
||||
// If sfDelegate is present, the delegate account is the payer
|
||||
// note: if a delegate is specified, its authorization to act on behalf of the account is
|
||||
// enforced in `Transactor::checkPermission`
|
||||
// cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`)
|
||||
if (isFieldPresent(sfDelegate))
|
||||
return getAccountID(sfDelegate);
|
||||
|
||||
// Default payer
|
||||
return getAccountID(sfAccount);
|
||||
}
|
||||
|
||||
void
|
||||
STTx::sign(
|
||||
PublicKey const& publicKey,
|
||||
@@ -351,7 +365,7 @@ STTx::getMetaSQL(std::uint32_t inLedger, std::string const& escapedMetaData) con
|
||||
{
|
||||
Serializer s;
|
||||
add(s);
|
||||
return getMetaSQL(s, inLedger, txnSqlValidated, escapedMetaData);
|
||||
return getMetaSQL(s, inLedger, TxnSql::txnSqlValidated, escapedMetaData);
|
||||
}
|
||||
|
||||
// VFALCO This could be a free function elsewhere
|
||||
@@ -359,7 +373,7 @@ std::string
|
||||
STTx::getMetaSQL(
|
||||
Serializer rawTxn,
|
||||
std::uint32_t inLedger,
|
||||
char status,
|
||||
TxnSql status,
|
||||
std::string const& escapedMetaData) const
|
||||
{
|
||||
static boost::format bfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)");
|
||||
@@ -370,8 +384,8 @@ STTx::getMetaSQL(
|
||||
|
||||
return str(
|
||||
boost::format(bfTrans) % to_string(getTransactionID()) % format->getName() %
|
||||
toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger % status % rTxn %
|
||||
escapedMetaData);
|
||||
toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger %
|
||||
safe_cast<char>(status) % rTxn % escapedMetaData);
|
||||
}
|
||||
|
||||
static Expected<void, std::string>
|
||||
|
||||
@@ -39,6 +39,9 @@ Consumer::~Consumer()
|
||||
Consumer&
|
||||
Consumer::operator=(Consumer const& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
// remove old ref
|
||||
if (m_logic && m_entry)
|
||||
m_logic->release(*m_entry);
|
||||
|
||||
@@ -68,6 +68,7 @@ HTTPReply(int nStatus, std::string const& content, Json::Output const& output, b
|
||||
return;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
|
||||
switch (nStatus)
|
||||
{
|
||||
case 200:
|
||||
|
||||
@@ -352,8 +352,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
|
||||
if (feePaid == beast::zero)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const id = ctx.tx.isFieldPresent(sfDelegate) ? ctx.tx.getAccountID(sfDelegate)
|
||||
: ctx.tx.getAccountID(sfAccount);
|
||||
auto const id = ctx.tx.getFeePayer();
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
if (!sle)
|
||||
return terNO_ACCOUNT;
|
||||
@@ -382,32 +381,18 @@ Transactor::payFee()
|
||||
{
|
||||
auto const feePaid = ctx_.tx[sfFee].xrp();
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfDelegate))
|
||||
{
|
||||
// Delegated transactions are paid by the delegated account.
|
||||
auto const delegate = ctx_.tx.getAccountID(sfDelegate);
|
||||
auto const delegatedSle = view().peek(keylet::account(delegate));
|
||||
if (!delegatedSle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
auto const feePayer = ctx_.tx.getFeePayer();
|
||||
auto const sle = view().peek(keylet::account(feePayer));
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
delegatedSle->setFieldAmount(sfBalance, delegatedSle->getFieldAmount(sfBalance) - feePaid);
|
||||
view().update(delegatedSle);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Deduct the fee, so it's not available during the transaction.
|
||||
// Will only write the account back if the transaction succeeds.
|
||||
|
||||
mSourceBalance -= feePaid;
|
||||
sle->setFieldAmount(sfBalance, mSourceBalance);
|
||||
|
||||
// VFALCO Should we call view().rawDestroyXRP() here as well?
|
||||
}
|
||||
// Deduct the fee, so it's not available during the transaction.
|
||||
// Will only write the account back if the transaction succeeds.
|
||||
sle->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance) - feePaid);
|
||||
if (feePayer != account_)
|
||||
view().update(sle); // done in `apply()` for the account
|
||||
|
||||
// VFALCO Should we call view().rawDestroyXRP() here as well?
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -606,8 +591,7 @@ Transactor::apply()
|
||||
|
||||
if (sle)
|
||||
{
|
||||
mPriorBalance = STAmount{(*sle)[sfBalance]}.xrp();
|
||||
mSourceBalance = mPriorBalance;
|
||||
preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp();
|
||||
|
||||
TER result = consumeSeqProxy(sle);
|
||||
if (result != tesSUCCESS)
|
||||
@@ -1023,9 +1007,7 @@ Transactor::reset(XRPAmount fee)
|
||||
if (!txnAcct)
|
||||
return {tefINTERNAL, beast::zero};
|
||||
|
||||
auto const payerSle = ctx_.tx.isFieldPresent(sfDelegate)
|
||||
? view().peek(keylet::account(ctx_.tx.getAccountID(sfDelegate)))
|
||||
: txnAcct;
|
||||
auto const payerSle = view().peek(keylet::account(ctx_.tx.getFeePayer()));
|
||||
if (!payerSle)
|
||||
return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/mulDiv.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
@@ -11,7 +10,7 @@
|
||||
#include <xrpl/tx/transactors/account/DeleteAccount.h>
|
||||
#include <xrpl/tx/transactors/account/SetSignerList.h>
|
||||
#include <xrpl/tx/transactors/delegate/DelegateSet.h>
|
||||
#include <xrpl/tx/transactors/did/DID.h>
|
||||
#include <xrpl/tx/transactors/did/DIDDelete.h>
|
||||
#include <xrpl/tx/transactors/nft/NFTokenUtils.h>
|
||||
#include <xrpl/tx/transactors/oracle/DeleteOracle.h>
|
||||
#include <xrpl/tx/transactors/payment/DepositPreauth.h>
|
||||
@@ -372,9 +371,10 @@ DeleteAccount::doApply()
|
||||
return ter;
|
||||
|
||||
// Transfer any XRP remaining after the fee is paid to the destination:
|
||||
(*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance;
|
||||
(*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
|
||||
ctx_.deliver(mSourceBalance);
|
||||
auto const remainingBalance = src->getFieldAmount(sfBalance).xrp();
|
||||
(*dst)[sfBalance] = (*dst)[sfBalance] + remainingBalance;
|
||||
(*src)[sfBalance] = (*src)[sfBalance] - remainingBalance;
|
||||
ctx_.deliver(remainingBalance);
|
||||
|
||||
XRPL_ASSERT(
|
||||
(*src)[sfBalance] == XRPAmount(0), "xrpl::DeleteAccount::doApply : source balance is zero");
|
||||
@@ -388,7 +388,7 @@ DeleteAccount::doApply()
|
||||
}
|
||||
|
||||
// Re-arm the password change fee if we can and need to.
|
||||
if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
|
||||
if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
|
||||
dst->clearFlag(lsfPasswordSpent);
|
||||
|
||||
view().update(dst);
|
||||
|
||||
@@ -491,7 +491,7 @@ SetAccount::doApply()
|
||||
|
||||
if (messageKey.empty())
|
||||
{
|
||||
JLOG(j_.debug()) << "set message key";
|
||||
JLOG(j_.debug()) << "clear message key";
|
||||
sle->makeFieldAbsent(sfMessageKey);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -306,7 +306,7 @@ SetSignerList::replaceSignerList()
|
||||
// We check the reserve against the starting balance because we want to
|
||||
// allow dipping into the reserve to pay fees. This behavior is consistent
|
||||
// with CreateTicket.
|
||||
if (mPriorBalance < newReserve)
|
||||
if (preFeeBalance_ < newReserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
// Everything's ducky. Add the ltSIGNER_LIST to the ledger.
|
||||
|
||||
@@ -337,7 +337,7 @@ enum class DepositAuthPolicy { normal, dstCanBypass };
|
||||
struct TransferHelperSubmittingAccountInfo
|
||||
{
|
||||
AccountID account;
|
||||
STAmount preFeeBalance;
|
||||
STAmount preFeeBalance_;
|
||||
STAmount postFeeBalance;
|
||||
};
|
||||
|
||||
@@ -423,7 +423,7 @@ transferHelper(
|
||||
if (!submittingAccountInfo || submittingAccountInfo->account != src ||
|
||||
submittingAccountInfo->postFeeBalance != curBal)
|
||||
return curBal;
|
||||
return submittingAccountInfo->preFeeBalance;
|
||||
return submittingAccountInfo->preFeeBalance_;
|
||||
}();
|
||||
|
||||
if (availableBalance < amt + reserve)
|
||||
@@ -1852,7 +1852,8 @@ XChainCommit::doApply()
|
||||
auto const amount = ctx_.tx[sfAmount];
|
||||
auto const bridgeSpec = ctx_.tx[sfXChainBridge];
|
||||
|
||||
if (!psb.read(keylet::account(account)))
|
||||
auto const sleAccount = psb.read(keylet::account(account));
|
||||
if (!sleAccount)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleBridge = readBridge(psb, bridgeSpec);
|
||||
@@ -1863,7 +1864,7 @@ XChainCommit::doApply()
|
||||
|
||||
// Support dipping into reserves to pay the fee
|
||||
TransferHelperSubmittingAccountInfo submittingAccountInfo{
|
||||
account_, mPriorBalance, mSourceBalance};
|
||||
account_, preFeeBalance_, (*sleAccount)[sfBalance]};
|
||||
|
||||
auto const thTer = transferHelper(
|
||||
psb,
|
||||
@@ -2132,7 +2133,7 @@ XChainCreateAccountCommit::doApply()
|
||||
|
||||
// Support dipping into reserves to pay the fee
|
||||
TransferHelperSubmittingAccountInfo submittingAccountInfo{
|
||||
account_, mPriorBalance, mSourceBalance};
|
||||
account_, preFeeBalance_, (*sle)[sfBalance]};
|
||||
STAmount const toTransfer = amount + reward;
|
||||
auto const thTer = transferHelper(
|
||||
psb,
|
||||
|
||||
@@ -25,15 +25,11 @@ CancelCheck::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
using duration = NetClock::duration;
|
||||
using timepoint = NetClock::time_point;
|
||||
auto const optExpiry = (*sleCheck)[~sfExpiration];
|
||||
|
||||
// Expiration is defined in terms of the close time of the parent
|
||||
// ledger, because we definitively know the time that it closed but
|
||||
// we do not know the closing time of the ledger that is under
|
||||
// construction.
|
||||
if (!optExpiry || (ctx.view.parentCloseTime() < timepoint{duration{*optExpiry}}))
|
||||
if (!hasExpired(ctx.view, (*sleCheck)[~sfExpiration]))
|
||||
{
|
||||
// If the check is not yet expired, then only the creator or the
|
||||
// destination may cancel the check.
|
||||
|
||||
@@ -309,7 +309,7 @@ CashCheck::doApply()
|
||||
|
||||
// Can the account cover the trust line's reserve?
|
||||
if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)};
|
||||
mPriorBalance < psb.fees().accountReserve(ownerCount + 1))
|
||||
preFeeBalance_ < psb.fees().accountReserve(ownerCount + 1))
|
||||
{
|
||||
JLOG(j_.trace()) << "Trust line does not exist. "
|
||||
"Insufficent reserve to create line.";
|
||||
|
||||
@@ -140,7 +140,7 @@ CreateCheck::doApply()
|
||||
{
|
||||
STAmount const reserve{view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (mPriorBalance < reserve)
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
|
||||
113
src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp
Normal file
113
src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/credentials/CredentialAccept.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using namespace credentials;
|
||||
|
||||
std::uint32_t
|
||||
CredentialAccept::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
// 0 means "Allow any flags"
|
||||
return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
CredentialAccept::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (!ctx.tx[sfIssuer])
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed.";
|
||||
return temINVALID_ACCOUNT_ID;
|
||||
}
|
||||
|
||||
auto const credType = ctx.tx[sfCredentialType];
|
||||
if (credType.empty() || (credType.size() > maxCredentialTypeLength))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialAccept::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const subject = ctx.tx[sfAccount];
|
||||
AccountID const issuer = ctx.tx[sfIssuer];
|
||||
auto const credType(ctx.tx[sfCredentialType]);
|
||||
|
||||
if (!ctx.view.exists(keylet::account(issuer)))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "No issuer: " << to_string(issuer);
|
||||
return tecNO_ISSUER;
|
||||
}
|
||||
|
||||
auto const sleCred = ctx.view.read(keylet::credential(subject, issuer, credType));
|
||||
if (!sleCred)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "No credential: " << to_string(subject) << ", " << to_string(issuer)
|
||||
<< ", " << credType;
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
if (sleCred->getFieldU32(sfFlags) & lsfAccepted)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Credential already accepted: " << to_string(subject) << ", "
|
||||
<< to_string(issuer) << ", " << credType;
|
||||
return tecDUPLICATE;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialAccept::doApply()
|
||||
{
|
||||
AccountID const issuer{ctx_.tx[sfIssuer]};
|
||||
|
||||
// Both exist as credential object exist itself (checked in preclaim)
|
||||
auto const sleSubject = view().peek(keylet::account(account_));
|
||||
auto const sleIssuer = view().peek(keylet::account(issuer));
|
||||
|
||||
if (!sleSubject || !sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleSubject->getFieldU32(sfOwnerCount) + 1)};
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
Keylet const credentialKey = keylet::credential(account_, issuer, credType);
|
||||
auto const sleCred = view().peek(credentialKey); // Checked in preclaim()
|
||||
|
||||
if (checkExpired(sleCred, view().header().parentCloseTime))
|
||||
{
|
||||
JLOG(j_.trace()) << "Credential is expired: " << sleCred->getText();
|
||||
// delete expired credentials even if the transaction failed
|
||||
auto const err = credentials::deleteSLE(view(), sleCred, j_);
|
||||
return isTesSuccess(err) ? tecEXPIRED : err;
|
||||
}
|
||||
|
||||
sleCred->setFieldU32(sfFlags, lsfAccepted);
|
||||
view().update(sleCred);
|
||||
|
||||
adjustOwnerCount(view(), sleIssuer, -1, j_);
|
||||
adjustOwnerCount(view(), sleSubject, 1, j_);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
164
src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp
Normal file
164
src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/credentials/CredentialCreate.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/*
|
||||
Credentials
|
||||
======
|
||||
|
||||
A verifiable credentials (VC
|
||||
https://en.wikipedia.org/wiki/Verifiable_credentials), as defined by the W3C
|
||||
specification (https://www.w3.org/TR/vc-data-model-2.0/), is a
|
||||
secure and tamper-evident way to represent information about a subject, such
|
||||
as an individual, organization, or even an IoT device. These credentials are
|
||||
issued by a trusted entity and can be verified by third parties without
|
||||
directly involving the issuer at all.
|
||||
*/
|
||||
|
||||
using namespace credentials;
|
||||
|
||||
std::uint32_t
|
||||
CredentialCreate::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
// 0 means "Allow any flags"
|
||||
return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
CredentialCreate::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const& tx = ctx.tx;
|
||||
auto& j = ctx.j;
|
||||
|
||||
if (!tx[sfSubject])
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: Invalid Subject";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const uri = tx[~sfURI];
|
||||
if (uri && (uri->empty() || (uri->size() > maxCredentialURILength)))
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: invalid size of URI.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const credType = tx[sfCredentialType];
|
||||
if (credType.empty() || (credType.size() > maxCredentialTypeLength))
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: invalid size of CredentialType.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const credType(ctx.tx[sfCredentialType]);
|
||||
auto const subject = ctx.tx[sfSubject];
|
||||
|
||||
if (!ctx.view.exists(keylet::account(subject)))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Subject doesn't exist.";
|
||||
return tecNO_TARGET;
|
||||
}
|
||||
|
||||
if (ctx.view.exists(keylet::credential(subject, ctx.tx[sfAccount], credType)))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Credential already exists.";
|
||||
return tecDUPLICATE;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialCreate::doApply()
|
||||
{
|
||||
auto const subject = ctx_.tx[sfSubject];
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
Keylet const credentialKey = keylet::credential(subject, account_, credType);
|
||||
|
||||
auto const sleCred = std::make_shared<SLE>(credentialKey);
|
||||
if (!sleCred)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const optExp = ctx_.tx[~sfExpiration];
|
||||
if (optExp)
|
||||
{
|
||||
std::uint32_t const closeTime =
|
||||
ctx_.view().header().parentCloseTime.time_since_epoch().count();
|
||||
|
||||
if (closeTime > *optExp)
|
||||
{
|
||||
JLOG(j_.trace()) << "Malformed transaction: "
|
||||
"Expiration time is in the past.";
|
||||
return tecEXPIRED;
|
||||
}
|
||||
|
||||
sleCred->setFieldU32(sfExpiration, *optExp);
|
||||
}
|
||||
|
||||
auto const sleIssuer = view().peek(keylet::account(account_));
|
||||
if (!sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleIssuer->getFieldU32(sfOwnerCount) + 1)};
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
sleCred->setAccountID(sfSubject, subject);
|
||||
sleCred->setAccountID(sfIssuer, account_);
|
||||
sleCred->setFieldVL(sfCredentialType, credType);
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfURI))
|
||||
sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI));
|
||||
|
||||
{
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(account_), credentialKey, describeOwnerDir(account_));
|
||||
JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfIssuerNode, *page);
|
||||
|
||||
adjustOwnerCount(view(), sleIssuer, 1, j_);
|
||||
}
|
||||
|
||||
if (subject == account_)
|
||||
{
|
||||
sleCred->setFieldU32(sfFlags, lsfAccepted);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(subject), credentialKey, describeOwnerDir(subject));
|
||||
JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfSubjectNode, *page);
|
||||
view().update(view().peek(keylet::account(subject)));
|
||||
}
|
||||
|
||||
view().insert(sleCred);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
90
src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp
Normal file
90
src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/credentials/CredentialDelete.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using namespace credentials;
|
||||
|
||||
std::uint32_t
|
||||
CredentialDelete::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
// 0 means "Allow any flags"
|
||||
return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
CredentialDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const subject = ctx.tx[~sfSubject];
|
||||
auto const issuer = ctx.tx[~sfIssuer];
|
||||
|
||||
if (!subject && !issuer)
|
||||
{
|
||||
// Neither field is present, the transaction is malformed.
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: "
|
||||
"No Subject or Issuer fields.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
// Make sure that the passed account is valid.
|
||||
if ((subject && subject->isZero()) || (issuer && issuer->isZero()))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: Subject or Issuer "
|
||||
"field zeroed.";
|
||||
return temINVALID_ACCOUNT_ID;
|
||||
}
|
||||
|
||||
auto const credType = ctx.tx[sfCredentialType];
|
||||
if (credType.empty() || (credType.size() > maxCredentialTypeLength))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const account{ctx.tx[sfAccount]};
|
||||
auto const subject = ctx.tx[~sfSubject].value_or(account);
|
||||
auto const issuer = ctx.tx[~sfIssuer].value_or(account);
|
||||
auto const credType(ctx.tx[sfCredentialType]);
|
||||
|
||||
if (!ctx.view.exists(keylet::credential(subject, issuer, credType)))
|
||||
return tecNO_ENTRY;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialDelete::doApply()
|
||||
{
|
||||
auto const subject = ctx_.tx[~sfSubject].value_or(account_);
|
||||
auto const issuer = ctx_.tx[~sfIssuer].value_or(account_);
|
||||
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
auto const sleCred = view().peek(keylet::credential(subject, issuer, credType));
|
||||
if (!sleCred)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if ((subject != account_) && (issuer != account_) &&
|
||||
!checkExpired(sleCred, ctx_.view().header().parentCloseTime))
|
||||
{
|
||||
JLOG(j_.trace()) << "Can't delete non-expired credential.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
return deleteSLE(view(), sleCred, j_);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,341 +0,0 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/credentials/Credentials.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/*
|
||||
Credentials
|
||||
======
|
||||
|
||||
A verifiable credentials (VC
|
||||
https://en.wikipedia.org/wiki/Verifiable_credentials), as defined by the W3C
|
||||
specification (https://www.w3.org/TR/vc-data-model-2.0/), is a
|
||||
secure and tamper-evident way to represent information about a subject, such
|
||||
as an individual, organization, or even an IoT device. These credentials are
|
||||
issued by a trusted entity and can be verified by third parties without
|
||||
directly involving the issuer at all.
|
||||
*/
|
||||
|
||||
using namespace credentials;
|
||||
|
||||
// ------- CREATE --------------------------
|
||||
|
||||
std::uint32_t
|
||||
CredentialCreate::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
// 0 means "Allow any flags"
|
||||
return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
CredentialCreate::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const& tx = ctx.tx;
|
||||
auto& j = ctx.j;
|
||||
|
||||
if (!tx[sfSubject])
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: Invalid Subject";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const uri = tx[~sfURI];
|
||||
if (uri && (uri->empty() || (uri->size() > maxCredentialURILength)))
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: invalid size of URI.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const credType = tx[sfCredentialType];
|
||||
if (credType.empty() || (credType.size() > maxCredentialTypeLength))
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: invalid size of CredentialType.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const credType(ctx.tx[sfCredentialType]);
|
||||
auto const subject = ctx.tx[sfSubject];
|
||||
|
||||
if (!ctx.view.exists(keylet::account(subject)))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Subject doesn't exist.";
|
||||
return tecNO_TARGET;
|
||||
}
|
||||
|
||||
if (ctx.view.exists(keylet::credential(subject, ctx.tx[sfAccount], credType)))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Credential already exists.";
|
||||
return tecDUPLICATE;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialCreate::doApply()
|
||||
{
|
||||
auto const subject = ctx_.tx[sfSubject];
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
Keylet const credentialKey = keylet::credential(subject, account_, credType);
|
||||
|
||||
auto const sleCred = std::make_shared<SLE>(credentialKey);
|
||||
if (!sleCred)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const optExp = ctx_.tx[~sfExpiration];
|
||||
if (optExp)
|
||||
{
|
||||
std::uint32_t const closeTime =
|
||||
ctx_.view().header().parentCloseTime.time_since_epoch().count();
|
||||
|
||||
if (closeTime > *optExp)
|
||||
{
|
||||
JLOG(j_.trace()) << "Malformed transaction: "
|
||||
"Expiration time is in the past.";
|
||||
return tecEXPIRED;
|
||||
}
|
||||
|
||||
sleCred->setFieldU32(sfExpiration, ctx_.tx.getFieldU32(sfExpiration));
|
||||
}
|
||||
|
||||
auto const sleIssuer = view().peek(keylet::account(account_));
|
||||
if (!sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleIssuer->getFieldU32(sfOwnerCount) + 1)};
|
||||
if (mPriorBalance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
sleCred->setAccountID(sfSubject, subject);
|
||||
sleCred->setAccountID(sfIssuer, account_);
|
||||
sleCred->setFieldVL(sfCredentialType, credType);
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfURI))
|
||||
sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI));
|
||||
|
||||
{
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(account_), credentialKey, describeOwnerDir(account_));
|
||||
JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfIssuerNode, *page);
|
||||
|
||||
adjustOwnerCount(view(), sleIssuer, 1, j_);
|
||||
}
|
||||
|
||||
if (subject == account_)
|
||||
{
|
||||
sleCred->setFieldU32(sfFlags, lsfAccepted);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(subject), credentialKey, describeOwnerDir(subject));
|
||||
JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfSubjectNode, *page);
|
||||
view().update(view().peek(keylet::account(subject)));
|
||||
}
|
||||
|
||||
view().insert(sleCred);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// ------- DELETE --------------------------
|
||||
|
||||
std::uint32_t
|
||||
CredentialDelete::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
// 0 means "Allow any flags"
|
||||
return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
CredentialDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const subject = ctx.tx[~sfSubject];
|
||||
auto const issuer = ctx.tx[~sfIssuer];
|
||||
|
||||
if (!subject && !issuer)
|
||||
{
|
||||
// Neither field is present, the transaction is malformed.
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: "
|
||||
"No Subject or Issuer fields.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
// Make sure that the passed account is valid.
|
||||
if ((subject && subject->isZero()) || (issuer && issuer->isZero()))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: Subject or Issuer "
|
||||
"field zeroed.";
|
||||
return temINVALID_ACCOUNT_ID;
|
||||
}
|
||||
|
||||
auto const credType = ctx.tx[sfCredentialType];
|
||||
if (credType.empty() || (credType.size() > maxCredentialTypeLength))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const account{ctx.tx[sfAccount]};
|
||||
auto const subject = ctx.tx[~sfSubject].value_or(account);
|
||||
auto const issuer = ctx.tx[~sfIssuer].value_or(account);
|
||||
auto const credType(ctx.tx[sfCredentialType]);
|
||||
|
||||
if (!ctx.view.exists(keylet::credential(subject, issuer, credType)))
|
||||
return tecNO_ENTRY;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialDelete::doApply()
|
||||
{
|
||||
auto const subject = ctx_.tx[~sfSubject].value_or(account_);
|
||||
auto const issuer = ctx_.tx[~sfIssuer].value_or(account_);
|
||||
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
auto const sleCred = view().peek(keylet::credential(subject, issuer, credType));
|
||||
if (!sleCred)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if ((subject != account_) && (issuer != account_) &&
|
||||
!checkExpired(sleCred, ctx_.view().header().parentCloseTime))
|
||||
{
|
||||
JLOG(j_.trace()) << "Can't delete non-expired credential.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
return deleteSLE(view(), sleCred, j_);
|
||||
}
|
||||
|
||||
// ------- APPLY --------------------------
|
||||
|
||||
std::uint32_t
|
||||
CredentialAccept::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
// 0 means "Allow any flags"
|
||||
return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
CredentialAccept::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (!ctx.tx[sfIssuer])
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed.";
|
||||
return temINVALID_ACCOUNT_ID;
|
||||
}
|
||||
|
||||
auto const credType = ctx.tx[sfCredentialType];
|
||||
if (credType.empty() || (credType.size() > maxCredentialTypeLength))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialAccept::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const subject = ctx.tx[sfAccount];
|
||||
AccountID const issuer = ctx.tx[sfIssuer];
|
||||
auto const credType(ctx.tx[sfCredentialType]);
|
||||
|
||||
if (!ctx.view.exists(keylet::account(issuer)))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "No issuer: " << to_string(issuer);
|
||||
return tecNO_ISSUER;
|
||||
}
|
||||
|
||||
auto const sleCred = ctx.view.read(keylet::credential(subject, issuer, credType));
|
||||
if (!sleCred)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "No credential: " << to_string(subject) << ", " << to_string(issuer)
|
||||
<< ", " << credType;
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
if (sleCred->getFieldU32(sfFlags) & lsfAccepted)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Credential already accepted: " << to_string(subject) << ", "
|
||||
<< to_string(issuer) << ", " << credType;
|
||||
return tecDUPLICATE;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
CredentialAccept::doApply()
|
||||
{
|
||||
AccountID const issuer{ctx_.tx[sfIssuer]};
|
||||
|
||||
// Both exist as credential object exist itself (checked in preclaim)
|
||||
auto const sleSubject = view().peek(keylet::account(account_));
|
||||
auto const sleIssuer = view().peek(keylet::account(issuer));
|
||||
|
||||
if (!sleSubject || !sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sleSubject->getFieldU32(sfOwnerCount) + 1)};
|
||||
if (mPriorBalance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
Keylet const credentialKey = keylet::credential(account_, issuer, credType);
|
||||
auto const sleCred = view().peek(credentialKey); // Checked in preclaim()
|
||||
|
||||
if (checkExpired(sleCred, view().header().parentCloseTime))
|
||||
{
|
||||
JLOG(j_.trace()) << "Credential is expired: " << sleCred->getText();
|
||||
// delete expired credentials even if the transaction failed
|
||||
auto const err = credentials::deleteSLE(view(), sleCred, j_);
|
||||
return isTesSuccess(err) ? tecEXPIRED : err;
|
||||
}
|
||||
|
||||
sleCred->setFieldU32(sfFlags, lsfAccepted);
|
||||
view().update(sleCred);
|
||||
|
||||
adjustOwnerCount(view(), sleIssuer, -1, j_);
|
||||
adjustOwnerCount(view(), sleSubject, 1, j_);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -70,7 +70,7 @@ DelegateSet::doApply()
|
||||
STAmount const reserve{
|
||||
ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (mPriorBalance < reserve)
|
||||
if (preFeeBalance_ < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto const& permissions = ctx_.tx.getFieldArray(sfPermissions);
|
||||
|
||||
@@ -172,7 +172,7 @@ AMMClawback::applyGuts(Sandbox& sb)
|
||||
0,
|
||||
FreezeHandling::fhIGNORE_FREEZE,
|
||||
WithdrawAll::Yes,
|
||||
mPriorBalance,
|
||||
preFeeBalance_,
|
||||
ctx_.journal);
|
||||
else
|
||||
std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
|
||||
@@ -251,7 +251,7 @@ AMMClawback::equalWithdrawMatchingOneAmount(
|
||||
0,
|
||||
FreezeHandling::fhIGNORE_FREEZE,
|
||||
WithdrawAll::Yes,
|
||||
mPriorBalance,
|
||||
preFeeBalance_,
|
||||
ctx_.journal);
|
||||
|
||||
auto const& rules = sb.rules();
|
||||
@@ -282,7 +282,7 @@ AMMClawback::equalWithdrawMatchingOneAmount(
|
||||
0,
|
||||
FreezeHandling::fhIGNORE_FREEZE,
|
||||
WithdrawAll::No,
|
||||
mPriorBalance,
|
||||
preFeeBalance_,
|
||||
ctx_.journal);
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ AMMClawback::equalWithdrawMatchingOneAmount(
|
||||
0,
|
||||
FreezeHandling::fhIGNORE_FREEZE,
|
||||
WithdrawAll::No,
|
||||
mPriorBalance,
|
||||
preFeeBalance_,
|
||||
ctx_.journal);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ AMMDeposit::checkExtraFeatures(PreflightContext const& ctx)
|
||||
|
||||
std::uint32_t
|
||||
AMMDeposit::getFlagsMask(PreflightContext const& ctx)
|
||||
|
||||
{
|
||||
return tfAMMDepositMask;
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ AMMWithdraw::withdraw(
|
||||
tfee,
|
||||
FreezeHandling::fhZERO_IF_FROZEN,
|
||||
isWithdrawAll(ctx_.tx),
|
||||
mPriorBalance,
|
||||
preFeeBalance_,
|
||||
j_);
|
||||
return {ter, newLPTokenBalance};
|
||||
}
|
||||
@@ -628,7 +628,7 @@ AMMWithdraw::equalWithdrawTokens(
|
||||
tfee,
|
||||
FreezeHandling::fhZERO_IF_FROZEN,
|
||||
isWithdrawAll(ctx_.tx),
|
||||
mPriorBalance,
|
||||
preFeeBalance_,
|
||||
ctx_.journal);
|
||||
return {ter, newLPTokenBalance};
|
||||
}
|
||||
@@ -676,7 +676,7 @@ AMMWithdraw::equalWithdrawTokens(
|
||||
STAmount const& lpTokens,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHanding,
|
||||
FreezeHandling freezeHandling,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal)
|
||||
@@ -697,7 +697,7 @@ AMMWithdraw::equalWithdrawTokens(
|
||||
lptAMMBalance,
|
||||
lpTokensWithdraw,
|
||||
tfee,
|
||||
freezeHanding,
|
||||
freezeHandling,
|
||||
WithdrawAll::Yes,
|
||||
priorBalance,
|
||||
journal);
|
||||
@@ -731,7 +731,7 @@ AMMWithdraw::equalWithdrawTokens(
|
||||
lptAMMBalance,
|
||||
tokensAdj,
|
||||
tfee,
|
||||
freezeHanding,
|
||||
freezeHandling,
|
||||
withdrawAll,
|
||||
priorBalance,
|
||||
journal);
|
||||
|
||||
@@ -214,8 +214,10 @@ CreateOffer::checkAcceptAsset(
|
||||
return (flags & tapRETRY) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER};
|
||||
}
|
||||
|
||||
// An account cannot create a trustline to itself, so no line can exist
|
||||
// to be frozen. Additionally, an issuer can always accept its own
|
||||
// issuance.
|
||||
if (issue.account == id)
|
||||
// An account can always accept its own issuance.
|
||||
return tesSUCCESS;
|
||||
|
||||
if ((*issuerAccount)[sfFlags] & lsfRequireAuth)
|
||||
@@ -242,14 +244,6 @@ CreateOffer::checkAcceptAsset(
|
||||
}
|
||||
}
|
||||
|
||||
// An account can not create a trustline to itself, so no line can exist
|
||||
// to be frozen. Additionally, an issuer can always accept its own
|
||||
// issuance.
|
||||
if (issue.account == id)
|
||||
{
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
auto const trustLine = view.read(keylet::line(id, issue.account, issue.currency));
|
||||
|
||||
if (!trustLine)
|
||||
@@ -733,7 +727,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
{
|
||||
XRPAmount reserve = sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
|
||||
|
||||
if (mPriorBalance < reserve)
|
||||
if (preFeeBalance_ < reserve)
|
||||
{
|
||||
// If we are here, the signing account had an insufficient reserve
|
||||
// *prior* to our processing. If something actually crossed, then
|
||||
|
||||
59
src/libxrpl/tx/transactors/did/DIDDelete.cpp
Normal file
59
src/libxrpl/tx/transactors/did/DIDDelete.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/tx/transactors/did/DIDDelete.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
NotTEC
|
||||
DIDDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner)
|
||||
{
|
||||
auto const sle = ctx.view().peek(sleKeylet);
|
||||
if (!sle)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal);
|
||||
}
|
||||
|
||||
TER
|
||||
DIDDelete::deleteSLE(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> sle,
|
||||
AccountID const owner,
|
||||
beast::Journal j)
|
||||
{
|
||||
// Remove object from owner directory
|
||||
if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete DID from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(owner));
|
||||
if (!sleOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleOwner, -1, j);
|
||||
view.update(sleOwner);
|
||||
|
||||
// Remove object from ledger
|
||||
view.erase(sle);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
DIDDelete::doApply()
|
||||
{
|
||||
return deleteSLE(ctx_, keylet::did(account_), account_);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/transactors/did/DID.h>
|
||||
#include <xrpl/tx/transactors/did/DIDSet.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -41,13 +41,13 @@ DIDSet::preflight(PreflightContext const& ctx)
|
||||
};
|
||||
|
||||
if (isTooLong(sfURI, maxDIDURILength) || isTooLong(sfDIDDocument, maxDIDDocumentLength) ||
|
||||
isTooLong(sfData, maxDIDAttestationLength))
|
||||
isTooLong(sfData, maxDIDDataLength))
|
||||
return temMALFORMED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
static TER
|
||||
addSLE(ApplyContext& ctx, std::shared_ptr<SLE> const& sle, AccountID const& owner)
|
||||
{
|
||||
auto const sleAccount = ctx.view().peek(keylet::account(owner));
|
||||
@@ -134,54 +134,4 @@ DIDSet::doApply()
|
||||
return addSLE(ctx_, sleDID, account_);
|
||||
}
|
||||
|
||||
NotTEC
|
||||
DIDDelete::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner)
|
||||
{
|
||||
auto const sle = ctx.view().peek(sleKeylet);
|
||||
if (!sle)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal);
|
||||
}
|
||||
|
||||
TER
|
||||
DIDDelete::deleteSLE(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> sle,
|
||||
AccountID const owner,
|
||||
beast::Journal j)
|
||||
{
|
||||
// Remove object from owner directory
|
||||
if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete DID Token from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(owner));
|
||||
if (!sleOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCount(view, sleOwner, -1, j);
|
||||
view.update(sleOwner);
|
||||
|
||||
// Remove object from ledger
|
||||
view.erase(sle);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
DIDDelete::doApply()
|
||||
{
|
||||
return deleteSLE(ctx_, keylet::did(account_), account_);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user