Compare commits

..
Author SHA1 Message Date
tequ 68387927e0 Merge branch 'dev' into fix/normal-tx-consequences-factory 2026-09-15 12:48:40 +09:00
tequ aab273c575 Use normal consequences factory for standard transactions
Replace redundant custom consequence factories that returned normal consequences with the built-in Normal factory.
2026-08-03 21:57:18 +09:00
32 changed files with 324 additions and 1027 deletions

No files matched your search

-9
View File
@@ -19,15 +19,6 @@ coverage:
default:
target: auto
threshold: 2%
paths:
# PeerImp is historically hard to exercise in the current unit-test
# harness. Keep this list narrow; new testable code should remain
# covered by the default patch gate.
- "!src/xrpld/overlay/detail/PeerImp.cpp"
historically-untested:
target: 0%
paths:
- "src/xrpld/overlay/detail/PeerImp.cpp"
changes: false
github_checks:
@@ -50,10 +50,6 @@ inputs:
options:
- libstdcxx
- libcxx
conan_deps_cxxflags:
description: 'Extra cxxflags applied to Conan dependency package builds only (NOT the rippled build). JSON object keyed by Conan package pattern, e.g. {"grpc/*":["-Wno-foo"]}. Maps to <pattern>:tools.build:cxxflags.'
required: false
default: '{}'
outputs:
cache-hit:
@@ -85,8 +81,6 @@ runs:
- name: Configure Conan
shell: bash
env:
CONAN_DEPS_CXXFLAGS: ${{ inputs.conan_deps_cxxflags }}
run: |
# Create the default profile directory if it doesn't exist
mkdir -p ~/.conan2/profiles
@@ -111,14 +105,7 @@ runs:
os=${{ inputs.os }}
EOF
# [buildenv] + [conf] sections.
# Linux pins compiler executables; macOS uses the system toolchain.
# conan_deps_cxxflags (matrix-driven) optionally adds package-pattern
# scoped tools.build:cxxflags for Conan dependency builds only - typically
# grpc workarounds for newer clang's stricter diagnostics. Because these
# are profile-pattern scoped (e.g. grpc/*:...), they do NOT affect the
# consumer/rippled toolchain generated for the main build.
NEED_CONF=0
# Add buildenv and conf sections for Linux (not needed for macOS)
if [ "${{ inputs.os }}" = "Linux" ] && [ -n "${{ inputs.cc }}" ]; then
cat >> ~/.conan2/profiles/default <<EOF
@@ -129,38 +116,16 @@ runs:
[conf]
tools.build:compiler_executables={"c": "/usr/bin/${{ inputs.cc }}", "cpp": "/usr/bin/${{ inputs.cxx }}"}
EOF
NEED_CONF=1
fi
if [ -n "${CONAN_DEPS_CXXFLAGS}" ] && [ "${CONAN_DEPS_CXXFLAGS}" != "{}" ]; then
CONAN_DEPS_CXXFLAGS_LINES="$(python3 - <<'PY'
import json
import os
import sys
# Add macOS-specific conf if needed
if [ "${{ inputs.os }}" = "Macos" ]; then
cat >> ~/.conan2/profiles/default <<EOF
raw = os.environ["CONAN_DEPS_CXXFLAGS"]
data = json.loads(raw)
if not isinstance(data, dict):
sys.exit("conan_deps_cxxflags must be a JSON object like {\"grpc/*\": [\"-Wno-...\"]}")
for pattern, flags in data.items():
if not isinstance(pattern, str) or not pattern:
sys.exit("conan_deps_cxxflags keys must be non-empty Conan package patterns")
if pattern == "&":
sys.exit("conan_deps_cxxflags must target dependency package patterns, not the consumer (&)")
if not isinstance(flags, list) or not all(isinstance(flag, str) for flag in flags):
sys.exit(f"{pattern}: cxxflags must be a JSON string list")
if flags:
print(f"{pattern}:tools.build:cxxflags={json.dumps(flags, separators=(',', ':'))}")
PY
)"
if [ -n "${CONAN_DEPS_CXXFLAGS_LINES}" ] && [ "$NEED_CONF" = "0" ]; then
echo "" >> ~/.conan2/profiles/default
echo "[conf]" >> ~/.conan2/profiles/default
fi
if [ -n "${CONAN_DEPS_CXXFLAGS_LINES}" ]; then
printf '%s\n' "${CONAN_DEPS_CXXFLAGS_LINES}" >> ~/.conan2/profiles/default
fi
[conf]
# Workaround for gRPC with newer Apple Clang
tools.build:cxxflags=["-Wno-missing-template-arg-list-after-template-kw"]
EOF
fi
# Display profile for verification
-3
View File
@@ -107,9 +107,6 @@ jobs:
compiler: apple-clang
compiler_version: ${{ steps.detect-compiler.outputs.compiler_version }}
stdlib: libcxx
# grpc 1.50.1 trips clang-19+ -Werror=missing-template-arg-list-after-template-kw
# on Apple Clang. Drop when grpc is bumped past the fix.
conan_deps_cxxflags: '{"grpc/*":["-Wno-missing-template-arg-list-after-template-kw"]}'
- name: Build
uses: ./.github/actions/xahau-ga-build
+22 -66
View File
@@ -72,26 +72,15 @@ jobs:
"job_type": "build"
},
{
# Latest stable Clang for the most accurate source-based
# coverage mapping (newer language features, fewer bugs in
# llvm-cov region inference). Pulled from apt.llvm.org since
# Ubuntu 24.04 default repos cap at clang-18.
"compiler_id": "clang-20-libcxx",
"compiler": "clang",
"cc": "clang-20",
"cxx": "clang++-20",
"compiler_version": 20,
"stdlib": "libcxx",
"compiler_id": "gcc-13-libstdcxx",
"compiler": "gcc",
"cc": "gcc-13",
"cxx": "g++-13",
"gcov": "gcov-13",
"compiler_version": 13,
"stdlib": "default",
"configuration": "Debug",
"job_type": "coverage",
"coverage_tool": "llvm",
"coverage_format": "lcov",
# grpc 1.50.1 uses `Foo::template Bar(...)` without an
# angle-bracket arg list; clang-19+ promoted that to
# -Werror. Drop when grpc is bumped past the fix.
"conan_deps_cxxflags": {
"grpc/*": ["-Wno-missing-template-arg-list-after-template-kw"]
}
"job_type": "coverage"
},
{
"compiler_id": "clang-14-libstdcxx-gcc11",
@@ -142,7 +131,7 @@ jobs:
# Minimal matrix for PRs and feature branches
minimal_matrix = [
full_matrix[1], # gcc-13 (middle-ground gcc)
full_matrix[2], # clang-20 llvm-cov coverage
full_matrix[2], # gcc-13 coverage
full_matrix[3] # clang-14 (mature, stable clang)
]
@@ -218,9 +207,9 @@ jobs:
# Select the appropriate matrix
if use_full:
if force_full:
print(f"Using FULL matrix (7 configs (build x6 + clang-20 llvm-cov coverage)) - forced by [ci-nix-full-matrix] tag")
print(f"Using FULL matrix (7 configs) - forced by [ci-nix-full-matrix] tag")
else:
print(f"Using FULL matrix (7 configs (build x6 + clang-20 llvm-cov coverage)) - targeting main branch")
print(f"Using FULL matrix (7 configs) - targeting main branch")
matrix = full_matrix
else:
print(f"Using MINIMAL matrix (3 configs) - feature branch/PR")
@@ -268,25 +257,9 @@ jobs:
- name: Install build dependencies
run: |
# Bump apt's default 3 retries; papers over short upstream blips
# like the recurring ppa.launchpadcontent.net outages.
echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries
apt-get update
apt-get install -y software-properties-common
add-apt-repository ppa:ubuntu-toolchain-r/test -y
# apt.llvm.org for Clang versions newer than what Ubuntu 24.04 ships
# (24.04 default repos cap at clang-18). The bootstrap script adds
# the LLVM apt source for the requested version and runs apt-get update.
if [ "${{ matrix.compiler }}" = "clang" ] && [ "${{ matrix.compiler_version }}" -ge 19 ]; then
apt-get install -y wget gnupg lsb-release
wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh
chmod +x /tmp/llvm.sh
# `all` installs clang + libllvm + lldb + lld + the llvm-N package
# (which provides llvm-profdata-N / llvm-cov-N for coverage runs).
/tmp/llvm.sh ${{ matrix.compiler_version }} all
fi
apt-get update
apt-get install -y git python3 python-is-python3 pipx
pipx ensurepath
@@ -359,16 +332,10 @@ jobs:
pipx install "conan>=2.0,<3"
echo "$HOME/.local/bin" >> $GITHUB_PATH
# Install coverage tooling
# Install gcovr for coverage jobs
if [ "${{ matrix.job_type }}" = "coverage" ]; then
pipx install "gcovr>=7,<9"
apt-get install -y curl lcov
if [ "${{ matrix.coverage_tool }}" = "llvm" ]; then
# Native LLVM source-based coverage: llvm-profdata + llvm-cov.
# The clang-N package doesn't pull these in; the llvm-N package does.
apt-get install -y "llvm-${{ matrix.compiler_version }}"
else
pipx install "gcovr>=7,<9"
fi
fi
- name: Check environment
@@ -381,15 +348,10 @@ jobs:
which ${{ matrix.cxx }} && ${{ matrix.cxx }} --version || echo "${{ matrix.cxx }} not found"
which ccache && ccache --version || echo "ccache not found"
# Check coverage tooling
# Check gcovr for coverage jobs
if [ "${{ matrix.job_type }}" = "coverage" ]; then
if [ "${{ matrix.coverage_tool }}" = "llvm" ]; then
which "llvm-profdata-${{ matrix.compiler_version }}" && "llvm-profdata-${{ matrix.compiler_version }}" --version || echo "llvm-profdata not found"
which "llvm-cov-${{ matrix.compiler_version }}" && "llvm-cov-${{ matrix.compiler_version }}" --version || echo "llvm-cov not found"
else
which gcov && gcov --version || echo "gcov not found"
which gcovr && gcovr --version || echo "gcovr not found"
fi
which gcov && gcov --version || echo "gcov not found"
which gcovr && gcovr --version || echo "gcovr not found"
fi
echo "---- Full Environment ----"
@@ -416,7 +378,6 @@ jobs:
cc: ${{ matrix.cc }}
cxx: ${{ matrix.cxx }}
stdlib: ${{ matrix.stdlib }}
conan_deps_cxxflags: ${{ matrix.conan_deps_cxxflags && toJson(matrix.conan_deps_cxxflags) || '{}' }}
gha_cache_enabled: 'false' # Disable caching for self hosted runner
- name: Build
@@ -449,9 +410,8 @@ jobs:
cache_version: ${{ env.CACHE_VERSION }}
main_branch: ${{ env.MAIN_BRANCH_NAME }}
stdlib: ${{ matrix.stdlib }}
# Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness.
# Use *_FLAGS_DEBUG so the build action's stdlib flag (e.g. -stdlib=libc++) in CMAKE_CXX_FLAGS isn't clobbered.
cmake-args: '-Dcoverage=ON -Dcoverage_tool=${{ matrix.coverage_tool }} -Dcoverage_format=${{ matrix.coverage_format }} -Dcoverage_test_parallelism=$(($(nproc)/2)) -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_CXX_FLAGS_DEBUG="-g -O0" -DCMAKE_C_FLAGS_DEBUG="-g -O0"'
# Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness
cmake-args: '-Dcoverage=ON -Dcoverage_format=xml -Dcoverage_test_parallelism=$(($(nproc)/2)) -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_CXX_FLAGS="-O0" -DCMAKE_C_FLAGS="-O0"'
cmake-target: 'coverage'
ccache_max_size: '100G'
@@ -483,26 +443,22 @@ jobs:
- name: Move coverage report
if: matrix.job_type == 'coverage'
shell: bash
env:
COVERAGE_FILE: ${{ matrix.coverage_tool == 'llvm' && 'coverage.lcov' || 'coverage.xml' }}
run: |
mv "${{ env.build_dir }}/${COVERAGE_FILE}" ./
echo "COVERAGE_FILE=${COVERAGE_FILE}" >> "$GITHUB_ENV"
mv "${{ env.build_dir }}/coverage.xml" ./
- name: Archive coverage report
if: matrix.job_type == 'coverage'
uses: actions/upload-artifact@v4
with:
name: ${{ env.COVERAGE_FILE }}-${{ matrix.compiler_id }}
path: ${{ env.COVERAGE_FILE }}
name: coverage.xml
path: coverage.xml
retention-days: 30
- name: Upload coverage report
if: matrix.job_type == 'coverage'
uses: codecov/codecov-action@v5
with:
files: ${{ env.COVERAGE_FILE }}
flags: ${{ matrix.coverage_tool }}
files: coverage.xml
fail_ci_if_error: true
disable_search: true
verbose: true
-156
View File
@@ -1,156 +0,0 @@
#[===================================================================[
Native LLVM source-based code coverage helper.
Drives the -fprofile-instr-generate / -fcoverage-mapping pipeline:
1. Run instrumented binary with LLVM_PROFILE_FILE=...%m-%p.profraw
2. llvm-profdata merge -sparse -> coverage.profdata
3. llvm-cov export/show/report -> final report
Output filename per coverage_format:
lcov -> coverage.lcov
json -> coverage.json
txt | text -> coverage.txt
html | html-details -> <NAME>/index.html
#]===================================================================]
include(CMakeParseArguments)
# Locate llvm-profdata / llvm-cov, preferring versioned variants matching the
# Clang we're building with so we don't accidentally pair clang-20 with
# llvm-cov-14 (profile format mismatch -> hard failure).
function(_find_llvm_cov_tools)
if(LLVM_PROFDATA_PATH AND LLVM_COV_PATH)
return()
endif()
string(REGEX MATCH "^[0-9]+" _major "${CMAKE_CXX_COMPILER_VERSION}")
set(_pd_names llvm-profdata)
set(_cov_names llvm-cov)
if(_major)
list(PREPEND _pd_names "llvm-profdata-${_major}")
list(PREPEND _cov_names "llvm-cov-${_major}")
endif()
# Only delegate to xcrun when the *compiler* is AppleClang. On macOS with
# Homebrew/system clang-N, xcrun would resolve to Xcode's llvm tools which
# could be a different version - exactly the mismatch we want to avoid.
if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
execute_process(COMMAND xcrun -f llvm-profdata
OUTPUT_VARIABLE _pd_xcrun OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET RESULT_VARIABLE _pd_rc)
if(_pd_rc EQUAL 0 AND _pd_xcrun)
set(LLVM_PROFDATA_PATH "${_pd_xcrun}" CACHE FILEPATH "llvm-profdata" FORCE)
endif()
execute_process(COMMAND xcrun -f llvm-cov
OUTPUT_VARIABLE _cov_xcrun OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET RESULT_VARIABLE _cov_rc)
if(_cov_rc EQUAL 0 AND _cov_xcrun)
set(LLVM_COV_PATH "${_cov_xcrun}" CACHE FILEPATH "llvm-cov" FORCE)
endif()
endif()
if(NOT LLVM_PROFDATA_PATH)
find_program(LLVM_PROFDATA_PATH NAMES ${_pd_names})
endif()
if(NOT LLVM_COV_PATH)
find_program(LLVM_COV_PATH NAMES ${_cov_names})
endif()
endfunction()
function(setup_target_for_coverage_llvm)
set(oneValueArgs NAME FORMAT)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Cov "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
_find_llvm_cov_tools()
if(NOT LLVM_PROFDATA_PATH)
message(FATAL_ERROR "llvm-profdata not found (needed for coverage_tool=llvm)")
endif()
if(NOT LLVM_COV_PATH)
message(FATAL_ERROR "llvm-cov not found (needed for coverage_tool=llvm)")
endif()
if(NOT Cov_FORMAT)
set(Cov_FORMAT lcov)
endif()
set(_profraw_dir "${PROJECT_BINARY_DIR}/${Cov_NAME}-profraw")
set(_profdata "${PROJECT_BINARY_DIR}/${Cov_NAME}.profdata")
# Resolve binary path: accept either an absolute path or a bare target name
# (resolved against PROJECT_BINARY_DIR). Splice the resolved path back into
# Cov_EXECUTABLE so the run command invokes it via absolute path - bare
# names aren't on PATH and the build dir isn't `.` either.
list(GET Cov_EXECUTABLE 0 _exec_name)
if(IS_ABSOLUTE "${_exec_name}")
set(_binary "${_exec_name}")
else()
set(_binary "${PROJECT_BINARY_DIR}/${_exec_name}")
list(REMOVE_AT Cov_EXECUTABLE 0)
list(PREPEND Cov_EXECUTABLE "${_binary}")
endif()
# llvm-cov takes a single -ignore-filename-regex; OR our excludes together.
set(_ignore_regex "")
foreach(EXC IN LISTS Cov_EXCLUDE)
if(_ignore_regex)
string(APPEND _ignore_regex "|")
endif()
string(APPEND _ignore_regex "${EXC}")
endforeach()
set(_filter "")
if(_ignore_regex)
set(_filter "-ignore-filename-regex='${_ignore_regex}'")
endif()
# Pick llvm-cov subcommand + output file for the requested format. Each
# branch builds a single shell command string that we'll hand to bash -c.
if(Cov_FORMAT STREQUAL "lcov")
set(_output "${PROJECT_BINARY_DIR}/coverage.lcov")
set(_report_sh "${LLVM_COV_PATH} export -instr-profile='${_profdata}' -format=lcov ${_filter} '${_binary}' > '${_output}'")
elseif(Cov_FORMAT STREQUAL "json")
set(_output "${PROJECT_BINARY_DIR}/coverage.json")
set(_report_sh "${LLVM_COV_PATH} export -instr-profile='${_profdata}' -format=text ${_filter} '${_binary}' > '${_output}'")
elseif(Cov_FORMAT STREQUAL "txt" OR Cov_FORMAT STREQUAL "text")
set(_output "${PROJECT_BINARY_DIR}/coverage.txt")
set(_report_sh "${LLVM_COV_PATH} report -instr-profile='${_profdata}' ${_filter} '${_binary}' > '${_output}'")
elseif(Cov_FORMAT STREQUAL "html" OR Cov_FORMAT STREQUAL "html-details")
set(_output "${PROJECT_BINARY_DIR}/${Cov_NAME}/index.html")
set(_report_sh "${LLVM_COV_PATH} show -instr-profile='${_profdata}' -format=html -output-dir='${PROJECT_BINARY_DIR}/${Cov_NAME}' ${_filter} '${_binary}'")
else()
message(FATAL_ERROR "coverage_tool=llvm: unsupported coverage_format '${Cov_FORMAT}' (use lcov|json|txt|html)")
endif()
set(_merge_sh "${LLVM_PROFDATA_PATH} merge -sparse -o '${_profdata}' '${_profraw_dir}'/*.profraw")
if(CODE_COVERAGE_VERBOSE)
message(STATUS "[coverage:llvm] binary: ${_binary}")
message(STATUS "[coverage:llvm] profraw: ${_profraw_dir}")
message(STATUS "[coverage:llvm] profdata: ${_profdata}")
message(STATUS "[coverage:llvm] format: ${Cov_FORMAT}")
message(STATUS "[coverage:llvm] output: ${_output}")
if(_ignore_regex)
message(STATUS "[coverage:llvm] ignore: ${_ignore_regex}")
endif()
message(STATUS "[coverage:llvm] merge: ${_merge_sh}")
message(STATUS "[coverage:llvm] report: ${_report_sh}")
endif()
# %m: hash of the binary, %p: pid. Wipe the dir up front so stale profraw
# files can't leak into a fresh merge.
add_custom_target(${Cov_NAME}
COMMAND ${CMAKE_COMMAND} -E rm -rf "${_profraw_dir}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${_profraw_dir}"
COMMAND ${CMAKE_COMMAND} -E env
"LLVM_PROFILE_FILE=${_profraw_dir}/rippled-%m-%p.profraw"
${Cov_EXECUTABLE} ${Cov_EXECUTABLE_ARGS}
COMMAND bash -c "${_merge_sh}"
COMMAND bash -c "${_report_sh}"
BYPRODUCTS ${_output}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Cov_DEPENDENCIES}
VERBATIM
COMMENT "Running llvm-cov (${Cov_FORMAT}) -> ${_output}"
)
endfunction()
-15
View File
@@ -11,21 +11,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
return()
endif()
if(coverage_tool STREQUAL "llvm")
include(CodeCoverageLLVM)
setup_target_for_coverage_llvm(
NAME coverage
FORMAT ${coverage_format}
EXECUTABLE rippled
EXECUTABLE_ARGS --unittest$<$<BOOL:${coverage_test}>:=${coverage_test}> --unittest-jobs ${coverage_test_parallelism} --quiet --unittest-log
EXCLUDE "src/test" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
DEPENDENCIES rippled
)
return()
endif()
# coverage_tool == "gcov" (default): existing gcovr-driven pipeline.
include(CodeCoverage)
# The instructions for these commands come from the `CodeCoverage` module,
+4 -6
View File
@@ -28,17 +28,15 @@ target_compile_options (opts
$<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized>
$<$<BOOL:${perf}>:-fno-omit-frame-pointer>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>,$<STREQUAL:${coverage_tool},gcov>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>,$<STREQUAL:${coverage_tool},gcov>>:-g --coverage>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>,$<STREQUAL:${coverage_tool},llvm>>:-g -fprofile-instr-generate -fcoverage-mapping>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
target_link_libraries (opts
INTERFACE
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>,$<STREQUAL:${coverage_tool},gcov>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>,$<STREQUAL:${coverage_tool},gcov>>:-g --coverage>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>,$<STREQUAL:${coverage_tool},llvm>>:-g -fprofile-instr-generate -fcoverage-mapping>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
$<$<BOOL:${profile}>:-pg>
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
+1 -13
View File
@@ -29,25 +29,13 @@ if(is_gcc OR is_clang)
"Unit tests parallelism for the purpose of coverage report.")
set(coverage_format "html-details" CACHE STRING
"Output format of the coverage report.")
set(coverage_tool "gcov" CACHE STRING
"Coverage instrumentation tool: 'gcov' (default, gcc/clang via --coverage + gcovr) or 'llvm' (clang only, native source-based coverage via -fprofile-instr-generate).")
set_property(CACHE coverage_tool PROPERTY STRINGS "gcov" "llvm")
if(NOT coverage_tool MATCHES "^(gcov|llvm)$")
message(FATAL_ERROR "coverage_tool must be 'gcov' or 'llvm', got '${coverage_tool}'")
endif()
set(coverage_extra_args "" CACHE STRING
"Additional arguments to pass to gcovr (gcov tool only).")
"Additional arguments to pass to gcovr.")
set(coverage_test "" CACHE STRING
"On gcc & clang, the specific unit test(s) to run for coverage. Default is all tests.")
if(coverage_test AND NOT coverage)
set(coverage ON CACHE BOOL "gcc/clang only" FORCE)
endif()
# Validate after coverage_test may have flipped coverage on, otherwise
# `-Dcoverage_tool=llvm -Dcoverage_test=Foo` on gcc would silently slip
# past the Clang guard and produce a broken instrumentation combo.
if(coverage AND coverage_tool STREQUAL "llvm" AND NOT is_clang)
message(FATAL_ERROR "coverage_tool=llvm requires Clang (got ${CMAKE_CXX_COMPILER_ID})")
endif()
option(wextra "compile with extra gcc/clang warnings enabled" ON)
else()
set(profile OFF CACHE BOOL "gcc/clang only" FORCE)
+2 -4
View File
@@ -34,7 +34,6 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(HookOnV2_1, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(OnChainManifests, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookMap, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (GuardDepth32, Supported::yes, VoteBehavior::DefaultNo)
@@ -46,7 +45,7 @@ XRPL_FEATURE(HookAPISerializedType240, Supported::yes, VoteBehavior::DefaultNo
XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(DynamicNFT, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(Credentials, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMMClawback, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(MPTokensV1, Supported::no, VoteBehavior::DefaultNo)
// InvariantsV1_1 will be changes to Supported::yes when all the
// invariants expected to be included under it are complete.
@@ -66,8 +65,7 @@ XRPL_FEATURE(XChainBridge, Supported::no, VoteBehavior::DefaultNo
XRPL_FEATURE(AMM, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (ReducedOffersV1, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HooksUpdate2, Supported::yes, VoteBehavior::DefaultNo)
// replaced to HookOnV2_1
XRPL_FEATURE(HookOnV2, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(HookOnV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookAPI20251128, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (CronStacking, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo)
+1 -3
View File
@@ -292,9 +292,7 @@ JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(elapsed_seconds);
JSS(enabled); // out: AmendmentTable (on-ledger);
// ServerDefinitions (this server)
JSS(ledger_enabled); // out: ServerDefinitions (on-ledger)
JSS(enabled); // out: AmendmentTable
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
-3
View File
@@ -150,15 +150,12 @@ private:
env.fund(XRP(20'000), alice, bob, carol, gw1, gw2);
env.fund(XRP(20'000), dan);
env.close();
env.trust(USD1(20'000), alice, bob, carol, dan);
env.trust(USD2(1'000), alice, bob, carol, dan);
env.close();
env(pay(gw1, dan, USD1(10'050)));
env(pay(gw1, bob, USD1(50)));
env(pay(gw2, bob, USD2(50)));
env.close();
AMM ammDan(env, dan, XRP(10'000), USD1(10'050));
+4 -4
View File
@@ -104,8 +104,8 @@ class LedgerMaster_test : public beast::unit_test::suite
startLegSeq, txnIndex);
BEAST_EXPECT(
*result ==
uint256("DCAECE52F028B9D0F7CA43CBE15AB4EF7B84A8EF5D455238992E1E"
"DF2BDA1637"));
uint256("0CC11AD1AD89661689F6B6148D82CB6A7101DA4D66EC843670262D"
"0B618F5745"));
}
// success (second tx)
{
@@ -114,8 +114,8 @@ class LedgerMaster_test : public beast::unit_test::suite
startLegSeq + 1, txnIndex);
BEAST_EXPECT(
*result ==
uint256("722BD5EC9E4AE36E724B6223F5A5887927AADFD0B18A3053AAE57F"
"434F8C9FE8"));
uint256("DCAECE52F028B9D0F7CA43CBE15AB4EF7B84A8EF5D455238992E1E"
"DF2BDA1637"));
}
}
+5 -5
View File
@@ -1174,11 +1174,11 @@ class MPToken_test : public beast::unit_test::suite
JsonOptions::none)[sfAffectedNodes.fieldName];
// Issuer got 10 in the transfer fees
BEAST_EXPECT(
meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
meta[2u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
[sfOutstandingAmount.fieldName] == "9990");
// Destination account got 9'990
BEAST_EXPECT(
meta[3u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
meta[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName]
[sfMPTAmount.fieldName] == "9990");
// Source account spent 10'000
BEAST_EXPECT(
@@ -1910,8 +1910,8 @@ class MPToken_test : public beast::unit_test::suite
env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
BEAST_EXPECTS(
txHash ==
"61041108F0DAD50BAFD2F7B1102AC70B283EBDCED194CEBAF04D0184E4A70B"
"02",
"42175D43E3D236A86B0F1B07182A1B2C78AD382372CFA09A48239C395FFFEE"
"30",
txHash);
Json::Value const meta = env.rpc("tx", txHash)[jss::result][jss::meta];
auto const id = meta[jss::mpt_issuance_id].asString();
@@ -1919,7 +1919,7 @@ class MPToken_test : public beast::unit_test::suite
BEAST_EXPECT(meta.isMember(jss::mpt_issuance_id));
BEAST_EXPECT(id == to_string(mptAlice.issuanceID()));
BEAST_EXPECTS(
id == "00000002AE123A8556F3CF91154711376AFB0F894F832B3D", id);
id == "00000001AE123A8556F3CF91154711376AFB0F894F832B3D", id);
}
void
+10 -10
View File
@@ -8279,16 +8279,16 @@ private:
// validate the emitted txn ids
std::vector<std::string> const txIds = {
"2FBEF981BAC322225D13C274D0118FECC1B93A89EDBC0AB3CBD8B30A1591024E",
"3C0A12F6A322486ABDA4180DC2B424BD252FD2FA6A33553B0B5D1526C020C475",
"AFFD55D757136FC0BE665F16E0D8CFBDC0D37CF85B5D3329BE1DBD3A44BB281C",
"BBE43C01068CE511DBF24F84A928E481451FF448AF2493E1A9BE099628D1F523",
"45970753F7B52448C23D66E5E19DBBC65F5BE6055C04AC3BA9896D7D67891DB1",
"97F4E09B2AEF4D798D378B1390B0624A68F304247B358334167E93217D502A16",
"770D3B786C8E3BEF560D9589B02557B0BDFC1C7FD15C6CF42E86068A6A411EFC",
"239DFA86EE8391418170D7FDF5AA8B8EEE0BBEF5E9F928BC29C23EEF6F1858AD",
"9FAA93AC6340C87B5C722FF0E8D4D3F39B9A0CA5E9799EC4CE316BFD5E379540",
"8672B2D8AA5E9EF86929B8662FE436541B2F739052732020B874BC304601C4DB",
"9610F73CDD6590EB6B3C82E5EC55D4B4C80CD7128B98AA556F7EC9DD96AE7056",
"2F4582A29272390C0C25A80D4A3BCE5A14ACE6D86D8D0CB2C57719EB6FA881AE",
"89A301CFEF0DD781AB9032A6A2DCE0937BC0119D2CDD06033B8B2FD80968E519",
"DD8721B59024E168480B4DF8F8E93778601F0BD2E77FC991F3DA1182F5AD8B1E",
"5D735C2EE3CB8289F8E11621FDC9565F9D6D67F3AE59D65332EACE591D67945F",
"F02470E01731C968881AF4CBDEC90BB9E1F7AB0BE1CC22AF15451FB6D191096D",
"8AD65E541DECD49B1693F8C17DFD8A2B906F49C673C4FD2034FF772E2BE50C30",
"9F225229059CCC6257814D03C107884CF588C1C246A89ADFC16E50DF671B834C",
"13C2A54A14BADF3648CED05175E1CCAD713F7E5EA56D9735CF8813CD5551F281",
"87C60F41A96554587CED289F83F52DEE3CF670EEB189B067E6066B9A06056ADF",
};
Json::Value params;
params[jss::transaction] =
+29 -342
View File
@@ -1355,16 +1355,11 @@ public:
auto const baseFee = drops[jss::base_fee_no_hooks];
BEAST_EXPECT(baseFee == to_string(feeDrops));
auto const openLedgerFee = drops[jss::open_ledger_fee];
BEAST_EXPECTS(
openLedgerFee == expected,
"openLedgerFee: " + openLedgerFee.asString() +
", expected: " + expected);
BEAST_EXPECT(openLedgerFee == expected);
// verify hooks fee
auto const hooksFee = jrr[jss::result][jss::fee_hooks_feeunits];
BEAST_EXPECTS(
hooksFee == expected,
"hooksFee: " + hooksFee.asString() + ", expected: " + expected);
BEAST_EXPECT(hooksFee == expected);
}
void
@@ -1374,8 +1369,7 @@ public:
using namespace jtx;
Env env{*this, features};
bool const hookOnV2 = env.current()->rules().enabled(featureHookOnV2) ||
env.current()->rules().enabled(featureHookOnV2_1);
bool const hookOnV2 = env.current()->rules().enabled(featureHookOnV2);
auto const alice = Account{"alice"};
auto const bob = Account{"bob"};
@@ -1538,10 +1532,12 @@ public:
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
"0000"
"00";
jv[jss::HookOnOutgoing] =
"0000000000000000000000000000000000000000000000000000000000"
"000001";
"0000"
"01";
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
M("Execution: Install"),
HSFEE);
@@ -1606,10 +1602,12 @@ public:
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
"bfff"
"ff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
"bfff"
"fe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
M("Execution: Install"),
HSFEE);
@@ -1620,7 +1618,8 @@ public:
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
"0000"
"00";
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
M("Execution: Install"),
HSFEE);
@@ -1654,285 +1653,18 @@ public:
deleteHook(alice);
}
for (auto const& withFix : {false, true})
{
// test featureHookOnV2_1 preflight
auto f = features - featureHookOnV2 - featureHookOnV2_1;
if (withFix)
f = f | featureHookOnV2_1;
else
f = f | featureHookOnV2;
Env env{*this, f};
env.fund(XRP(10000), alice, bob);
env.close();
TER const expected =
env.current()->rules().enabled(featureHookOnV2_1)
? TER(temMALFORMED)
: TER(tesSUCCESS);
// 0. only one direction HookOn is not allowed
for (auto const& key : {jss::HookOnIncoming, jss::HookOnOutgoing})
{
auto noopJv = Json::Value{};
noopJv[key] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff";
env(ripple::test::jtx::hook(alice, {{noopJv}}, 0),
M("Lone direction HookOn NOOP"),
HSFEE,
ter(expected));
env.close();
if (!withFix)
BEAST_EXPECT(!env.le(keylet::hook(alice)));
}
// 1. Create Hook with HookOnIncoming/Outgoing to alice
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
// 2. Install Hook with HookOn, Incoming/Outgoing to bob
jv = Json::Value{};
jv[jss::HookHash] = accept_hash_str;
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(bob, {{jv}}, 0), HSFEE, ter(expected));
env.close();
// 3. Update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0),
HSFEE,
ter(expected));
env.close();
}
for (auto const& withFix : {false, true})
{
// test featureHookOnV2_1 install/update
auto f = features - featureHookOnV2 - featureHookOnV2_1;
if (withFix)
f = f | featureHookOnV2_1;
else
f = f | featureHookOnV2;
{
// install:: HookDefinition: HookOn -> Hook:Incoming/Outgoing
// update:: Hook: HookOn -> Hook:Incoming/Outgoing
Env env{*this, f};
env.fund(XRP(10000), alice, bob);
env.close();
// Create Hook with HookOn to alice
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
// Install Hook with Incoming/Outgoing to bob
jv = Json::Value{};
jv[jss::HookHash] = accept_hash_str;
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(bob, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(bob));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
// Update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
// Re-update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOn] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff";
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(h.isFieldPresent(sfHookOn));
if (withFix)
{
BEAST_EXPECT(!h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnIncoming));
}
else
{
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
}
}
{
// install:: HookDefinition: Incoming/Outgoing -> Hook:HookOn
// update:: Hook: Incoming/Outgoing -> Hook:HookOn
Env env{*this, f};
env.fund(XRP(10000), alice, bob);
env.close();
// Create Hook with HookOn to alice
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
// Install Hook with Incoming/Outgoing to bob
jv = Json::Value{};
jv[jss::HookHash] = accept_hash_str;
jv[jss::Flags] = hsfOVERRIDE;
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
env(ripple::test::jtx::hook(bob, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(bob));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
BEAST_EXPECT(h.isFieldPresent(sfHookOn));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnIncoming));
}
// Update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOn] =
"0000000000000000000000000000000000000000000000000000000000"
"000000";
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
if (withFix)
BEAST_EXPECT(h.isFieldPresent(sfHookOn));
// Cause different results between gcc and clang due to
// undefined behavior so we don't test it
// else
// BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(!h.isFieldPresent(sfHookOnIncoming));
}
// Re-update Hook with HookOn, Incoming/Outgoing to alice
jv = Json::Value{};
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bffff0"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffff1"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
{
auto const hooksObj = env.le(keylet::hook(alice));
BEAST_EXPECT(hooksObj && hooksObj->isFieldPresent(sfHooks));
auto const& hooks = hooksObj->getFieldArray(sfHooks);
BEAST_EXPECT(hooks.size() == 1);
auto const& h = hooks[0];
if (withFix)
BEAST_EXPECT(!h.isFieldPresent(sfHookOn));
// Cause different results between gcc and clang due to
// undefined behavior so we don't test it
// else
// BEAST_EXPECT(h.isFieldPresent(sfHookOn));
BEAST_EXPECT(h.isFieldPresent(sfHookOnOutgoing));
BEAST_EXPECT(h.isFieldPresent(sfHookOnIncoming));
}
}
}
// Fee RPC
{
auto jv = hso(accept_wasm);
jv.removeMember(jss::HookOn);
jv[jss::HookOnIncoming] =
"fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff"
"bfffff"; // Invoke high
"bfff"
"ff"; // Invoke high
jv[jss::HookOnOutgoing] =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"bffffe"; // Payment high
"bfff"
"fe"; // Payment high
env(ripple::test::jtx::hook(alice, {{jv}}, 0), HSFEE);
env.close();
@@ -3271,37 +3003,25 @@ public:
testInferHookSetOperation()
{
testcase("Test operation inference");
using namespace jtx;
auto const alice = Account{"alice"};
Env env{*this};
env.fund(XRP(10000), alice);
env(noop(alice));
SetHookCtx shCtx{
.j = env.app().journal("SetHook"),
.tx = *(env.tx()),
.app = env.app(),
.rules = env.current()->rules()};
// hsoNOOP
{
STObject hso{sfHook};
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoNOOP);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoNOOP);
}
// hsoCREATE
{
STObject hso{sfHook};
hso.setFieldVL(sfCreateCode, {1}); // non-empty create code
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoCREATE);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoCREATE);
}
// hsoDELETE
{
STObject hso{sfHook};
hso.setFieldVL(sfCreateCode, ripple::Blob{}); // empty create code
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoDELETE);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoDELETE);
}
// hsoINSTALL
@@ -3309,7 +3029,7 @@ public:
STObject hso{sfHook};
hso.setFieldH256(
sfHookHash, uint256{beast::zero}); // all zeros hook hash
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoINSTALL);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoINSTALL);
}
// hsoNSDELETE
@@ -3318,14 +3038,14 @@ public:
hso.setFieldH256(
sfHookNamespace, uint256{beast::zero}); // all zeros hook hash
hso.setFieldU32(sfFlags, hsfNSDELETE);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoNSDELETE);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoNSDELETE);
}
// hsoUPDATE
{
STObject hso{sfHook};
hso.setFieldH256(sfHookOn, UINT256_BIT[0]);
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoUPDATE);
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoUPDATE);
}
// hsoINVALID
@@ -3334,39 +3054,7 @@ public:
hso.setFieldVL(sfCreateCode, {1}); // non-empty create code
hso.setFieldH256(
sfHookHash, uint256{beast::zero}); // all zeros hook hash
BEAST_EXPECT(SetHook::inferOperation(shCtx, hso) == hsoINVALID);
for (auto fix : {true, false})
{
auto feature = supported_amendments() - featureHookOnV2 -
featureHookOnV2_1;
if (fix)
feature = feature | featureHookOnV2_1;
else
feature = feature | featureHookOnV2;
Env env{*this, feature};
SetHookCtx shCtx{
.j = env.app().journal("SetHook"),
.tx = *env.tx(),
.app = env.app(),
.rules = env.current()->rules()};
STObject hso2{sfHook};
hso2.setFieldH256(
sfHookOnOutgoing,
uint256{beast::zero}); // all zeros hook on outgoing
BEAST_EXPECT(
SetHook::inferOperation(shCtx, hso2) ==
(fix ? hsoINVALID : hsoNOOP));
STObject hso3{sfHook};
hso3.setFieldH256(
sfHookOnIncoming,
uint256{beast::zero}); // all zeros hook on incoming
BEAST_EXPECT(
SetHook::inferOperation(shCtx, hso3) ==
(fix ? hsoINVALID : hsoNOOP));
}
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoINVALID);
}
}
@@ -4589,10 +4277,10 @@ public:
uint8_t expected1[49] = {
0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U, 0x3DU, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xBCU, 0x09U, 0x95U,
0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU, 0xACU, 0x94U, 0xDBU,
0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU, 0x48U, 0x21U, 0xCCU,
0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U, 0x7CU, 0x88U
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xB8U, 0x05U, 0xD6U,
0xC3U, 0x52U, 0xDFU, 0x7AU, 0x27U, 0x76U, 0x6DU, 0xC0U, 0x20U, 0x47U,
0xB7U, 0x64U, 0x22U, 0x5AU, 0xB7U, 0x5DU, 0xF3U, 0xFAU, 0x0DU, 0xE3U,
0xBDU, 0xC6U, 0x40U, 0xBAU, 0xD0U, 0x0AU, 0x66U, 0xEBU, 0x68U,
};
// 0x5CU
// EmitNonce 32bytes
@@ -15387,8 +15075,7 @@ public:
testNSDeletePartial(features);
testPageCap(features);
testHookOnV2((features | featureHookOnV2) - featureHookOnV2_1);
testHookOnV2((features | featureHookOnV2_1) - featureHookOnV2);
testHookOnV2(features);
testHookName(features);
testFillCopy(features);
+8 -8
View File
@@ -1613,10 +1613,10 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
uint8_t expected1[49] = {
0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U, 0x3DU, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xBCU, 0x09U, 0x95U,
0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU, 0xACU, 0x94U, 0xDBU,
0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU, 0x48U, 0x21U, 0xCCU,
0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U, 0x7CU, 0x88U
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xB8U, 0x05U, 0xD6U,
0xC3U, 0x52U, 0xDFU, 0x7AU, 0x27U, 0x76U, 0x6DU, 0xC0U, 0x20U, 0x47U,
0xB7U, 0x64U, 0x22U, 0x5AU, 0xB7U, 0x5DU, 0xF3U, 0xFAU, 0x0DU, 0xE3U,
0xBDU, 0xC6U, 0x40U, 0xBAU, 0xD0U, 0x0AU, 0x66U, 0xEBU, 0x68U,
};
// 0x5CU
// EmitNonce 32bytes
@@ -1763,10 +1763,10 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
0x36U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U,
0x3DU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU,
0xBCU, 0x09U, 0x95U, 0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU,
0xACU, 0x94U, 0xDBU, 0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU,
0x48U, 0x21U, 0xCCU, 0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U,
0x7CU, 0x88U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U,
0xB8U, 0x05U, 0xD6U, 0xC3U, 0x52U, 0xDFU, 0x7AU, 0x27U, 0x76U, 0x6DU,
0xC0U, 0x20U, 0x47U, 0xB7U, 0x64U, 0x22U, 0x5AU, 0xB7U, 0x5DU, 0xF3U,
0xFAU, 0x0DU, 0xE3U, 0xBDU, 0xC6U, 0x40U, 0xBAU, 0xD0U, 0x0AU, 0x66U,
0xEBU, 0x68U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U,
0x68U, 0x28U, 0x28U, 0x75U, 0x69U, 0x6EU, 0x74U, 0x33U, 0x32U, 0x5FU,
0x74U, 0x29U, 0x65U, 0x78U, 0x70U, 0x65U, 0x63U, 0x74U, 0x65U, 0x64U,
0x5FU, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U, 0x68U,
-1
View File
@@ -980,7 +980,6 @@ struct URIToken_test : public beast::unit_test::suite
// setup env
env.fund(XRP(1000), alice, bob, gw);
env.close();
env.trust(USD(100000), alice, bob);
env.close();
env(pay(gw, alice, USD(1000)));
+31 -31
View File
@@ -37,36 +37,20 @@ namespace test {
static char const* bobs_account_objects[] = {
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 4,
"TakerGets" : {
"currency" : "USD",
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "A984D036A0E562433A8377CA57D1A1E056E58C0D04818F8DFD3A1AA3F217DD82"
})json",
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 5,
"TakerGets" : {
"currency" : "USD",
"issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "CAFE32332D752387B01083B60CC63069BA4A969C9730836929F841450F6A718E"
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 4,
"TakerGets" : {
"currency" : "USD",
"issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "A984D036A0E562433A8377CA57D1A1E056E58C0D04818F8DFD3A1AA3F217DD82"
})json",
R"json({
"Balance" : {
@@ -111,6 +95,22 @@ static char const* bobs_account_objects[] = {
},
"LowNode" : "0",
"index" : "D89BC239086183EB9458C396E643795C1134963E6550E682A190A5F021766D43"
})json",
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 3,
"TakerGets" : {
"currency" : "USD",
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "E11029302EE744401427793A4F37BCB18F698D55C96851BEC5ABBD6242CF03D7"
})json"};
class AccountObjects_test : public beast::unit_test::suite
@@ -332,7 +332,7 @@ public:
auto& aobj = resp[jss::result][jss::account_objects][i];
aobj.removeMember("PreviousTxnID");
aobj.removeMember("PreviousTxnLgrSeq");
BEAST_EXPECT(aobj == bobj[i + 2]);
BEAST_EXPECT(aobj == bobj[i + 1]);
}
}
// test stepped one-at-a-time with limit=1, resume from prev marker
+6 -6
View File
@@ -117,9 +117,9 @@ public:
BEAST_EXPECT(jroOuter[0u][jss::quality] == "100000000");
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::currency] == "USD");
BEAST_EXPECT(
jroOuter[0u][jss::taker_gets][jss::issuer] == gw.human());
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::value] == "2");
BEAST_EXPECT(jroOuter[0u][jss::taker_pays] == "200000000");
jroOuter[0u][jss::taker_gets][jss::issuer] == bob.human());
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::value] == "1");
BEAST_EXPECT(jroOuter[0u][jss::taker_pays] == "100000000");
BEAST_EXPECT(jroOuter[1u][jss::quality] == "5000000");
BEAST_EXPECT(jroOuter[1u][jss::taker_gets][jss::currency] == "USD");
@@ -131,9 +131,9 @@ public:
BEAST_EXPECT(jroOuter[2u][jss::quality] == "100000000");
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::currency] == "USD");
BEAST_EXPECT(
jroOuter[2u][jss::taker_gets][jss::issuer] == bob.human());
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::value] == "1");
BEAST_EXPECT(jroOuter[2u][jss::taker_pays] == "100000000");
jroOuter[2u][jss::taker_gets][jss::issuer] == gw.human());
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::value] == "2");
BEAST_EXPECT(jroOuter[2u][jss::taker_pays] == "200000000");
}
{
+8 -128
View File
@@ -163,7 +163,7 @@ public:
void
testNoParams(FeatureBitset features)
{
testcase("Default Env: config-forced, none on-ledger");
testcase("No Params, None Enabled");
using namespace test::jtx;
Env env{*this};
@@ -178,6 +178,8 @@ public:
{
if (!BEAST_EXPECT(feature.isMember(jss::name)))
return;
// default config - so all should be disabled, and
// supported. Some may be vetoed.
bool expectVeto =
(votes.at(feature[jss::name].asString()) ==
VoteBehavior::DefaultNo);
@@ -186,12 +188,8 @@ public:
VoteBehavior::Obsolete);
BEAST_EXPECTS(
feature.isMember(jss::enabled) &&
feature[jss::enabled].asBool(),
!feature[jss::enabled].asBool(),
feature[jss::name].asString() + " enabled");
BEAST_EXPECTS(
feature.isMember(jss::ledger_enabled) &&
!feature[jss::ledger_enabled].asBool(),
feature[jss::name].asString() + " ledger_enabled");
BEAST_EXPECTS(
feature.isMember(jss::vetoed) &&
feature[jss::vetoed].isBool() == !expectObsolete &&
@@ -210,7 +208,7 @@ public:
void
testSomeEnabled(FeatureBitset features)
{
testcase("Two config-forced, none on-ledger");
testcase("No Params, Some Enabled");
using namespace test::jtx;
Env env{
@@ -230,10 +228,7 @@ public:
(void)id.parseHex(it.key().asString().c_str());
if (!BEAST_EXPECT((*it).isMember(jss::name)))
return;
bool const expectOnLedger =
env.app().getAmendmentTable().isEnabled(id);
bool const expectForced =
id == featureDepositAuth || id == featureDepositPreauth;
bool expectEnabled = env.app().getAmendmentTable().isEnabled(id);
bool expectSupported =
env.app().getAmendmentTable().isSupported(id);
bool expectVeto =
@@ -244,14 +239,9 @@ public:
VoteBehavior::Obsolete);
BEAST_EXPECTS(
(*it).isMember(jss::enabled) &&
(*it)[jss::enabled].asBool() ==
(expectOnLedger || expectForced),
(*it)[jss::enabled].asBool() == expectEnabled,
(*it)[jss::name].asString() + " enabled");
BEAST_EXPECTS(
(*it).isMember(jss::ledger_enabled) &&
(*it)[jss::ledger_enabled].asBool() == expectOnLedger,
(*it)[jss::name].asString() + " ledger_enabled");
if (expectOnLedger)
if (expectEnabled)
BEAST_EXPECTS(
!(*it).isMember(jss::vetoed),
(*it)[jss::name].asString() + " vetoed");
@@ -370,122 +360,12 @@ public:
}
}
void
testConfigForced(FeatureBitset features)
{
testcase("Config-forced features ([features] stanza)");
using namespace test::jtx;
auto const forced = featurePriceOracle;
auto const forcedHex = to_string(forced);
Env env{*this, FeatureBitset(forced)};
auto jrr = env.rpc("server_definitions")[jss::result];
if (!BEAST_EXPECT(jrr.isMember(jss::features)))
return;
bool sawForced = false;
for (auto it = jrr[jss::features].begin();
it != jrr[jss::features].end();
++it)
{
auto const& f = *it;
auto const name = f[jss::name].asString();
if (!BEAST_EXPECTS(
f.isMember(jss::enabled) && f.isMember(jss::ledger_enabled),
name + " enabled/ledger_enabled"))
return;
BEAST_EXPECTS(
!f[jss::ledger_enabled].asBool(), name + " ledger_enabled");
if (it.key().asString() == forcedHex)
{
sawForced = true;
BEAST_EXPECTS(f[jss::enabled].asBool(), name + " enabled");
}
else
{
BEAST_EXPECTS(!f[jss::enabled].asBool(), name + " enabled");
}
}
BEAST_EXPECT(sawForced);
}
void
testOnLedger(FeatureBitset features)
{
testcase("On-ledger plus one config-forced");
using namespace test::jtx;
// Veto XahauGenesis: FRESH would otherwise enable it via pseudo-tx.
auto const forced = featurePriceOracle;
auto const forcedHex = to_string(forced);
Env env{
*this,
envconfig([](std::unique_ptr<Config> cfg) {
cfg->START_UP = Config::FRESH;
cfg->section(SECTION_VETO_AMENDMENTS)
.append(to_string(featureXahauGenesis) + " XahauGenesis");
return cfg;
}),
FeatureBitset(forced)};
auto jrr = env.rpc("server_definitions")[jss::result];
if (!BEAST_EXPECT(jrr.isMember(jss::features)))
return;
bool sawOnLedger = false;
bool sawForced = false;
for (auto it = jrr[jss::features].begin();
it != jrr[jss::features].end();
++it)
{
uint256 id;
(void)id.parseHex(it.key().asString().c_str());
auto const& f = *it;
auto const name = f[jss::name].asString();
if (!BEAST_EXPECTS(
f.isMember(jss::enabled) && f.isMember(jss::ledger_enabled),
name + " enabled/ledger_enabled"))
return;
bool const onLedger = env.app().getAmendmentTable().isEnabled(id);
bool const isForced = it.key().asString() == forcedHex;
BEAST_EXPECTS(
f[jss::ledger_enabled].asBool() == onLedger,
name + " ledger_enabled");
BEAST_EXPECTS(
f[jss::enabled].asBool() == (onLedger || isForced),
name + " enabled");
if (onLedger)
sawOnLedger = true;
if (isForced)
{
sawForced = true;
BEAST_EXPECTS(!onLedger, name + " forced not on-ledger");
}
}
BEAST_EXPECT(sawOnLedger);
BEAST_EXPECT(sawForced);
}
void
testServerFeatures(FeatureBitset features)
{
testNoParams(features);
testSomeEnabled(features);
testWithMajorities(features);
testConfigForced(features);
testOnLedger(features);
}
void
+6 -1
View File
@@ -252,7 +252,12 @@ applyCreate(
auto sleAMMRoot = std::make_shared<SLE>(keylet::account(*ammAccount));
sleAMMRoot->setAccountID(sfAccount, *ammAccount);
sleAMMRoot->setFieldAmount(sfBalance, STAmount{});
std::uint32_t const seqno = newAccountSeqNo(ctx_.view());
std::uint32_t const seqno{
ctx_.view().rules().enabled(featureXahauGenesis)
? ctx_.view().info().parentCloseTime.time_since_epoch().count()
: ctx_.view().rules().enabled(featureDeletableAccounts)
? ctx_.view().seq()
: 1};
sleAMMRoot->setFieldU32(sfSequence, seqno);
// Ignore reserves requirement, disable the master key, allow default
// rippling (AMM LPToken can be used in payments and offer crossing but
+2 -1
View File
@@ -554,7 +554,8 @@ Change::activateXahauGenesis()
{
sle = std::make_shared<SLE>(kl);
sle->setAccountID(sfAccount, accid);
std::uint32_t const seqno = newAccountSeqNo(sb);
std::uint32_t const seqno{
sb.info().parentCloseTime.time_since_epoch().count()};
sle->setFieldU32(sfSequence, seqno);
}
+2 -1
View File
@@ -243,7 +243,8 @@ GenesisMint::doApply()
if (created)
{
// Create the account.
std::uint32_t const seqno = newAccountSeqNo(view());
std::uint32_t const seqno{
view().info().parentCloseTime.time_since_epoch().count()};
sle = std::make_shared<SLE>(k);
sle->setAccountID(sfAccount, id);
+6 -1
View File
@@ -1307,7 +1307,12 @@ Import::doApply()
if (create)
{
// Create the account.
std::uint32_t const seqno = newAccountSeqNo(view());
std::uint32_t const seqno{
view().rules().enabled(featureXahauGenesis)
? view().info().parentCloseTime.time_since_epoch().count()
: view().rules().enabled(featureDeletableAccounts)
? view().seq()
: 1};
sle = std::make_shared<SLE>(keylet::account(id));
sle->setAccountID(sfAccount, id);
+5 -1
View File
@@ -1041,7 +1041,11 @@ ValidNewAccountRoot::finalize(
tt == ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION) &&
isTesSuccess(result))
{
std::uint32_t const startingSeq = newAccountSeqNo(view);
std::uint32_t const startingSeq{
view.rules().enabled(featureXahauGenesis)
? view.info().parentCloseTime.time_since_epoch().count()
: view.rules().enabled(featureDeletableAccounts) ? view.seq()
: 1};
if (accountSeq_ != startingSeq)
{
+6 -1
View File
@@ -354,7 +354,12 @@ Payment::doApply()
if (!sleDst)
{
std::uint32_t const seqno = newAccountSeqNo(view());
std::uint32_t const seqno{
view().rules().enabled(featureXahauGenesis)
? view().info().parentCloseTime.time_since_epoch().count()
: view().rules().enabled(featureDeletableAccounts)
? view().seq()
: 1};
// Create the account.
sleDst = std::make_shared<SLE>(k);
+5 -1
View File
@@ -328,7 +328,11 @@ Remit::doApply()
nativeRemit += accountReserve;
// Create the account.
std::uint32_t const seqno = newAccountSeqNo(sb);
std::uint32_t const seqno{
sb.rules().enabled(featureXahauGenesis)
? sb.info().parentCloseTime.time_since_epoch().count()
: sb.rules().enabled(featureDeletableAccounts) ? sb.seq()
: 1};
sleDstAcc = std::make_shared<SLE>(keylet::account(dstAccID));
sleDstAcc->setAccountID(sfAccount, dstAccID);
+56 -113
View File
@@ -207,7 +207,7 @@ validateHookParams(SetHookCtx& ctx, STArray const& hookParams)
// infer which operation the user is attempting to execute from the present and
// absent fields
HookSetOperation
SetHook::inferOperation(SetHookCtx& ctx, STObject const& hookSetObj)
SetHook::inferOperation(STObject const& hookSetObj)
{
uint64_t wasmByteCount = hookSetObj.isFieldPresent(sfCreateCode)
? hookSetObj.getFieldVL(sfCreateCode).size()
@@ -216,12 +216,7 @@ SetHook::inferOperation(SetHookCtx& ctx, STObject const& hookSetObj)
bool hasHash = hookSetObj.isFieldPresent(sfHookHash);
bool hasCode = hookSetObj.isFieldPresent(sfCreateCode);
bool invalidHookOn = ctx.rules.enabled(featureHookOnV2_1) &&
hookSetObj.isFieldPresent(sfHookOnOutgoing) !=
hookSetObj.isFieldPresent(sfHookOnIncoming);
if ((hasHash && hasCode) || invalidHookOn) // Both HookHash and CreateCode
// or invalid HookOn: invalid
if (hasHash && hasCode) // Both HookHash and CreateCode: invalid
return hsoINVALID;
else if (hasHash) // Hookhash only: install
return hsoINSTALL;
@@ -249,62 +244,6 @@ SetHook::inferOperation(SetHookCtx& ctx, STObject const& hookSetObj)
: hsoUPDATE;
}
bool
validateHookOn(SetHookCtx& ctx, STObject const& hookSetObj)
{
if (!hookSetObj.isFieldPresent(sfHookOn))
{
if (!ctx.rules.enabled(featureHookOnV2) &&
!ctx.rules.enabled(featureHookOnV2_1))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOn before featureHookOnV2_1 is enabled.";
return false;
}
if (!hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
!hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook without sfHookOn.";
return false;
}
auto const outgoing = hookSetObj.getFieldH256(sfHookOnOutgoing);
auto const incoming = hookSetObj.getFieldH256(sfHookOnIncoming);
if (outgoing == incoming)
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook outgoing and "
"incoming hookon must be different.";
return false;
}
return true;
}
else
{
if (hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook must no"
"include sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook with sfHookOn.";
return false;
}
}
return true;
}
// This is a context-free validation, it does not take into account the current
// state of the ledger returns < valid, instruction count > may throw
// overflow_error
@@ -315,7 +254,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
? hookSetObj.getFieldU32(sfFlags)
: 0;
switch (inferOperation(ctx, hookSetObj))
switch (inferOperation(hookSetObj))
{
case hsoNOOP: {
return true;
@@ -424,13 +363,6 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
// hookon may be present if the user so chooses
// flags may be present if the user so chooses
if (ctx.rules.enabled(featureHookOnV2_1) &&
(hookSetObj.isFieldPresent(sfHookOn) ||
hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming)) &&
!validateHookOn(ctx, hookSetObj))
return false;
return true;
}
@@ -475,13 +407,6 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
// hookon may be present if the user so chooses
// flags may be present if the user so chooses
if (ctx.rules.enabled(featureHookOnV2_1) &&
(hookSetObj.isFieldPresent(sfHookOn) ||
hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming)) &&
!validateHookOn(ctx, hookSetObj))
return false;
return true;
}
@@ -531,8 +456,56 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
}
// validate sfHookOn
if (!validateHookOn(ctx, hookSetObj))
return false;
if (!hookSetObj.isFieldPresent(sfHookOn))
{
if (!ctx.rules.enabled(featureHookOnV2))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOn before featureHookOnV2 is enabled.";
return false;
}
if (!hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
!hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook must include "
"sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook without sfHookOn.";
return false;
}
auto const outgoing = hookSetObj.getFieldH256(sfHookOnOutgoing);
auto const incoming = hookSetObj.getFieldH256(sfHookOnIncoming);
if (outgoing == incoming)
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook outgoing and "
"incoming hookon must be different.";
return false;
}
}
else
{
if (hookSetObj.isFieldPresent(sfHookOnOutgoing) ||
hookSetObj.isFieldPresent(sfHookOnIncoming))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOKON_MISSING << ")["
<< HS_ACC()
<< "]: Malformed transaction: SetHook must no"
"include sfHookOnOutgoing and sfHookOnIncoming "
"when creating a new hook with sfHookOn.";
return false;
}
}
// validate sfHookCanEmit
// HookCanEmit field is an optional field for backward compatibility
@@ -1432,7 +1405,7 @@ SetHook::setHook()
HookSetOperation op = hsoNOOP;
if (hookSetObj)
op = inferOperation(ctx, hookSetObj->get());
op = inferOperation(hookSetObj->get());
// these flags are not able to be passed onto the ledger object
int newFlags = 0;
@@ -1654,23 +1627,10 @@ SetHook::setHook()
newHook.setFieldH256(sfHookNamespace, *newNamespace);
}
if (ctx.rules.enabled(featureHookOnV2_1))
{
// sanity check
if (newHookOn && (newHookOnOutgoing || newHookOnIncoming))
return tecINTERNAL; // LCOV_EXCL_LINE
if ((!newHookOnOutgoing && newHookOnIncoming) ||
(newHookOnOutgoing && !newHookOnIncoming))
return tecINTERNAL; // LCOV_EXCL_LINE
}
// set the hookon field if it differs from definition
if (newHookOn)
{
if ((!view().rules().enabled(featureHookOnV2_1) ||
defHookOn.has_value()) &&
*defHookOn == *newHookOn)
if (*defHookOn == *newHookOn)
{
if (newHook.isFieldPresent(sfHookOn))
newHook.makeFieldAbsent(sfHookOn);
@@ -1705,22 +1665,6 @@ SetHook::setHook()
sfHookOnIncoming, *newHookOnIncoming);
}
if (ctx.rules.enabled(featureHookOnV2_1))
{
if (newHookOn)
{
if (newHook.isFieldPresent(sfHookOnIncoming))
newHook.makeFieldAbsent(sfHookOnIncoming);
if (newHook.isFieldPresent(sfHookOnOutgoing))
newHook.makeFieldAbsent(sfHookOnOutgoing);
}
if (newHookOnOutgoing || newHookOnIncoming)
{
if (newHook.isFieldPresent(sfHookOn))
newHook.makeFieldAbsent(sfHookOn);
}
}
// set the hookcanemit field if it differs from definition
if (newHookCanEmit)
{
@@ -1899,8 +1843,7 @@ SetHook::setHook()
newHookDef->setFieldH256(sfHookHash, *createHookHash);
// only HookOn or (HookOnOutgoing and HookOnIncoming)
if ((!view().rules().enabled(featureHookOnV2) &&
!view().rules().enabled(featureHookOnV2_1)) ||
if (!view().rules().enabled(featureHookOnV2) ||
(!newHookOnOutgoing && !newHookOnIncoming))
newHookDef->setFieldH256(sfHookOn, *newHookOn);
else
+1 -1
View File
@@ -86,7 +86,7 @@ public:
calculateBaseFee(ReadView const& view, STTx const& tx);
static HookSetOperation
inferOperation(SetHookCtx& ctx, STObject const& hookSetObj);
inferOperation(STObject const& hookSetObj);
static HookSetValidation
validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj);
+5 -1
View File
@@ -481,7 +481,11 @@ transferHelper(
}
// Create the account.
std::uint32_t const seqno = newAccountSeqNo(psb);
std::uint32_t const seqno{
psb.rules().enabled(featureXahauGenesis)
? psb.info().parentCloseTime.time_since_epoch().count()
: psb.rules().enabled(featureDeletableAccounts) ? psb.seq()
: 1};
sleDst = std::make_shared<SLE>(dstK);
sleDst->setAccountID(sfAccount, dst);
-18
View File
@@ -1266,24 +1266,6 @@ deleteAMMTrustLine(
std::optional<AccountID> const& ammAccountID,
beast::Journal j);
[[nodiscard]] inline std::uint32_t
newAccountSeqNo(ReadView const& view)
{
auto const time = view.info().parentCloseTime.time_since_epoch().count();
return view.rules().enabled(featureXahauGenesis)
// TEQU:
// When creating accounts in GenesisLedger, we previously set
// the account Sequence to 0. However, since this conflicts with
// the PseudoAccount requirements, we are changing it to 1.
// There will be no impact on networks that are already running,
// and for future networks, there won't be any impact unless you
// create accounts via GenesisLedger.
// This specifically addresses an issue that comes up during
// unittests.
? (time == 0 ? 1 : time)
: view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1;
}
} // namespace ripple
#endif
+91 -31
View File
@@ -22,10 +22,12 @@
#include <xrpld/app/main/Application.h>
#include <xrpld/app/misc/AmendmentTable.h>
#include <xrpld/app/misc/NetworkOPs.h>
#include <xrpld/core/Config.h>
#include <xrpld/rpc/detail/TransactionSign.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/RPCErr.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
@@ -33,6 +35,14 @@
#include <magic_enum.hpp>
#include <sstream>
#define MAGIC_ENUM(x, _min, _max) \
template <> \
struct magic_enum::customize::enum_range<x> \
{ \
static constexpr int min = _min; \
static constexpr int max = _max; \
};
#define MAGIC_ENUM_16(x) \
template <> \
struct magic_enum::customize::enum_range<x> \
@@ -48,6 +58,15 @@
static constexpr bool is_flags = true; \
};
MAGIC_ENUM(ripple::SerializedTypeID, -2, 10004);
MAGIC_ENUM(ripple::LedgerEntryType, 0, 255);
MAGIC_ENUM(ripple::TELcodes, -399, 300);
MAGIC_ENUM(ripple::TEMcodes, -299, -200);
MAGIC_ENUM(ripple::TEFcodes, -199, -100);
MAGIC_ENUM(ripple::TERcodes, -99, -1);
MAGIC_ENUM(ripple::TEScodes, 0, 1);
MAGIC_ENUM(ripple::TECcodes, 100, 255);
MAGIC_ENUM_16(ripple::TxType);
MAGIC_ENUM_FLAG(ripple::UniversalFlags);
MAGIC_ENUM_FLAG(ripple::AccountSetFlags);
MAGIC_ENUM_FLAG(ripple::OfferCreateFlags);
@@ -173,19 +192,24 @@ private:
ret[jss::TYPES]["Done"] = -1;
std::map<int32_t, std::string> type_map{{-1, "Done"}};
for (auto const& [rawName, typeValue] : sTypeMap)
for (auto const& entry : magic_enum::enum_entries<SerializedTypeID>())
{
std::string typeName =
translate(std::string(rawName).substr(4) /* remove STI_ */);
ret[jss::TYPES][typeName] = typeValue;
type_map[typeValue] = typeName;
const auto name = entry.second;
std::string type_name =
translate(name.data() + 4 /* remove STI_ */);
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::TYPES][type_name] = type_value;
type_map[type_value] = type_name;
}
ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
for (auto const& f : LedgerFormats::getInstance())
for (auto const& entry : magic_enum::enum_entries<LedgerEntryType>())
{
ret[jss::LEDGER_ENTRY_TYPES][f.getName()] = f.getType();
const auto name = entry.second;
std::string type_name = translate(name.data() + 2 /* remove lt_ */);
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::LEDGER_ENTRY_TYPES][type_name] = type_value;
}
ret[jss::FIELDS] = Json::arrayValue;
@@ -302,16 +326,71 @@ private:
}
ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
for (auto const& [code, terInfo] : transResults())
for (auto const& entry : magic_enum::enum_entries<TELcodes>())
{
ret[jss::TRANSACTION_RESULTS][terInfo.first] = code;
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TEMcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TEFcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TERcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TEScodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TECcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
auto const translate_tt = [](std::string inp) -> std::string {
if (inp == "Amendment")
return "EnableAmendment";
if (inp == "Fee")
return "SetFee";
if (inp == "PaychanClaim")
return "PaymentChannelClaim";
if (inp == "PaychanCreate")
return "PaymentChannelCreate";
if (inp == "PaychanFund")
return "PaymentChannelFund";
if (inp == "RegularKeySet")
return "SetRegularKey";
if (inp == "HookSet")
return "SetHook";
if (inp == "RemarksSet")
return "SetRemarks";
return inp;
};
ret[jss::TRANSACTION_TYPES] = Json::objectValue;
ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
for (auto const& f : TxFormats::getInstance())
for (auto const& entry : magic_enum::enum_entries<TxType>())
{
ret[jss::TRANSACTION_TYPES][f.getName()] = f.getType();
const auto name = entry.second;
std::string type_name = translate_tt(translate(name.data() + 2));
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::TRANSACTION_TYPES][type_name] = type_value;
}
// Transaction Flags:
@@ -466,25 +545,6 @@ doServerDefinitions(RPC::JsonContext& context)
features[to_string(h)][jss::majority] =
t.time_since_epoch().count();
// getJson's enabled is on-ledger; [features] also apply here.
for (auto const& name : features.getMemberNames())
{
Json::Value& entry = features[name];
entry[jss::ledger_enabled] = entry[jss::enabled].asBool();
}
for (auto const& h : context.app.config().features)
{
Json::Value& entry = features[to_string(h)];
if (!entry.isMember(jss::name))
{
if (auto const fname = featureToName(h); !fname.empty())
entry[jss::name] = fname;
}
if (!entry.isMember(jss::ledger_enabled))
entry[jss::ledger_enabled] = false;
entry[jss::enabled] = true;
}
lastFeatures = features;
{
const std::string out = Json::FastWriter().write(features);