Compare commits

..

43 Commits

Author SHA1 Message Date
Nicholas Dudfield
e633922226 Merge remote-tracking branch 'origin/dev' into add-clang-matrix 2025-10-14 16:44:37 +07:00
tequ
15c7ad6f78 Fix Invalid Tx flags (#514) 2025-10-14 15:35:48 +10:00
Niq Dudfield
1f12b9ec5a feat(logs): add -DBEAST_ENHANCED_LOGGING with file:line numbers for JLOG macro (#552) 2025-10-14 10:44:03 +10:00
Nicholas Dudfield
1a3210f3ab chore: remove unused Conan patching action
- Conan 2 natively supports Clang 18, no patching needed
- Action was only required for Conan 1.x settings.yml modification
2025-10-13 10:09:11 +07:00
Nicholas Dudfield
44b9dcfaee Merge branch 'dev' into add-clang-matrix - migrate to Conan 2
- Upgrade from Conan v1 to Conan v2 (>=2.0,<3)
- Convert profile configuration to Conan 2 heredoc syntax
- Add libc++ support to Conan 2 profile configuration
- Remove Conan settings patching action (Conan 2 supports Clang 18 natively!)
- Keep full compiler matrix implementation and GCC hiding logic
- Incorporate dev changes (cfg renames, database updates, etc.)
2025-10-13 10:01:20 +07:00
Niq Dudfield
ad0531ad6c chore: fix warnings (#509)
Co-authored-by: Denis Angell <dangell@transia.co>
Co-authored-by: RichardAH <richard.holland@starstone.co.nz>
2025-10-11 11:47:13 +10:00
tequ
e580f7cfc0 chore(vscode): enable format on save in settings.json (#578) 2025-10-11 11:43:50 +10:00
tequ
094f011006 Fix emit Hook API testcase name (#580) 2025-10-11 11:43:09 +10:00
Nicholas Dudfield
ecf0d68a64 fix: remove clang-14-gcc13 config - confirmed incompatible
- Testing confirmed Clang 14 cannot compile GCC 13 headers
- consteval usage in <chrono> causes compilation failure
- GCC 11 workaround is necessary for Clang 14
- Back to 6 working configurations
2025-08-20 08:22:47 +07:00
Nicholas Dudfield
775b6c14a0 test: add clang-14 with gcc-13 configuration
- Test if Clang 14 actually fails with GCC 13 headers
- This will validate if our GCC 11 workaround is necessary
- Total of 7 compiler configurations now

[ci-nix-full-matrix]
2025-08-20 08:10:51 +07:00
Nicholas Dudfield
7ea6f432c9 fix: add minimal safety improvements
- Use check=True for conan config init to catch failures immediately
- Add safety check to prevent clobbering when renaming GCC directories
- Document why Clang 16 is the cutoff for --gcc-install-dir support
2025-08-19 15:55:00 +07:00
Nicholas Dudfield
22a81a050f docs: improve comments about Conan initialization
- Explain that Conan 1 uses embedded defaults until config init
- Document that settings.yml is created on-demand
- Clarify that conan config init is safe (won't overwrite)
- Remove pr-description.md from repo (moved to .niqs-ignored)
2025-08-19 14:49:28 +07:00
Nicholas Dudfield
cd252504ad fix: use renamed action and add Conan init fallback
- Update workflow to use renamed xahau-patch-conan-post-setup action
- Use 'conan config home' to dynamically find settings.yml location
- Add debug logging to see Conan home contents
- Try 'conan config init' if settings.yml doesn't exist
- This should handle fresh Conan installations
2025-08-19 14:35:52 +07:00
Nicholas Dudfield
d8ccb1db81 refactor: move Conan patching to separate action
- Create new xahau-patch-conan-clang-versions action
- Remove patching logic from dependencies action
- Call patch action after Conan initialization in main workflow
- Fixes sequencing issue where settings.yml didn't exist yet
2025-08-19 14:30:16 +07:00
Nicholas Dudfield
27ec068050 fix: initialize Conan before patching settings.yml
- Check if settings.yml exists before trying to patch it
- Run 'conan profile new default --detect' if needed
- Fixes FileNotFoundError for fresh runners without cache
2025-08-19 14:20:09 +07:00
Nicholas Dudfield
2b6c92ecb1 debug: add logging to matrix generation 2025-08-19 14:15:49 +07:00
Nicholas Dudfield
e559008fc8 fix: use Python shell for Conan settings patch
- Switch to shell: python since PyYAML is pre-installed on Ubuntu
- Move export statement before Python heredoc so env var is available
- Adjust PR matrix logic: only release/candidate PRs get full matrix
- PRs to dev now use minimal matrix (most are feature branches)

[ci-nix-full-matrix]
2025-08-19 14:05:25 +07:00
Nicholas Dudfield
7ced4de6f2 feat: add clang-18 support with generic version detection
- Add clang-18-libcxx configuration (6 compiler configs total)
- Generic Conan settings patcher: auto-detects Clang version from compiler-id
- Works for any future Clang version without code changes
- Change minimal matrix to use clang-14 instead of clang-17 (faster)
- Sort Conan version list numerically when adding new versions

[ci-nix-full-matrix]
2025-08-19 13:54:46 +07:00
Nicholas Dudfield
3571a403f6 perf: use python:3-slim container for matrix-setup job
- Reduces total job time from ~26s to ~7s (72% faster)
- Bypasses ubuntu-latest's 25s toolchain setup overhead
- Container init only takes 3-5s with GitHub's layer caching
- Based on research showing containerized jobs skip host env config
2025-08-19 12:09:12 +07:00
Nicholas Dudfield
87dab64e0c perf: use self-hosted runner for matrix-setup job
- Reduces matrix generation time from ~30s to ~3s
- No VM provisioning overhead
- Perfect for tiny, safe, read-only jobs like JSON generation
2025-08-19 11:50:20 +07:00
Nicholas Dudfield
c730e1d5f0 fix: gcc stdlib flag, add dynamic matrix with [ci-nix-full-matrix]
- GCC doesn't recognize -stdlib flag (it's Clang-specific)
- Added dynamic matrix generation for branch-based CI control
- Main branches (dev/release/candidate) run all 5 compiler configs
- Feature branches and PRs run only gcc-13 and clang-17
- [ci-nix-full-matrix] tag forces full matrix on any branch
- Uses inline Python script in matrix-setup job
2025-08-19 11:38:14 +07:00
Nicholas Dudfield
9bf2bc0420 ci: add clang-17 with native libc++ to build matrix 2025-08-19 11:07:58 +07:00
Nicholas Dudfield
be1a34c7e9 ci: add gcc-11 and reinstate gcc-13 in build matrix 2025-08-19 11:06:19 +07:00
Nicholas Dudfield
06b4344cc0 fix: use -lt 16 check for gcc hiding, update compiler-id docs 2025-08-19 10:46:55 +07:00
Nicholas Dudfield
7a11eb6c15 docs: clarify gcc hiding trick is for clang < 16 2025-08-18 21:24:57 +07:00
Nicholas Dudfield
c1a35510f4 fix: install gcc before hiding versions, add verification 2025-08-18 21:19:27 +07:00
Nicholas Dudfield
7c4b1bafeb fix: install target gcc version before hiding others 2025-08-18 21:12:19 +07:00
Nicholas Dudfield
ccfa6da70a fix: use integer renaming for hidden gcc versions 2025-08-18 21:09:49 +07:00
Nicholas Dudfield
139f1bd32b ci: use directory hiding for clang-14 and --gcc-install-dir for clang-16+ 2025-08-18 21:05:58 +07:00
Nicholas Dudfield
a3b00d57a2 docs: clarify compiler_id includes gcc version for clang builds 2025-08-18 20:44:40 +07:00
Nicholas Dudfield
b822b66825 ci: simplify gcc toolchain logic - no update-alternatives needed 2025-08-18 20:34:49 +07:00
Nicholas Dudfield
6684075a28 ci: remove gcc-14-base to prevent header conflicts 2025-08-18 20:27:49 +07:00
Nicholas Dudfield
cc28fcf190 ci: remove gcc-14 and add clang-14 with gcc-11 test
- Add clang-14-libstdcxx-gcc11 matrix entry for testing
- Remove gcc-14 to prevent Clang from finding it
- Add diagnostic to show which headers Clang will use
- Test both gcc-11 and gcc-13 with clang-14
2025-08-18 20:19:26 +07:00
Nicholas Dudfield
ddca64815d ci: simplify by using update-alternatives instead of gcc-install-dir
- Keep clang_gcc_toolchain to control which GCC to install
- Use update-alternatives to set system default GCC
- Remove complex --gcc-install-dir flag from CMAKE_CXX_FLAGS
- Clang automatically uses the default GCC's headers
2025-08-18 20:15:30 +07:00
Nicholas Dudfield
60df462bc2 fix: properly quote CMAKE_CXX_FLAGS with spaces 2025-08-18 19:41:21 +07:00
Nicholas Dudfield
4755a37cad ci: add clang_gcc_toolchain support for header compatibility
- Clang 14 uses GCC 11 toolchain to avoid GCC 14 header issues
- Clang 16 uses GCC 13 toolchain for better compatibility
- Use --gcc-install-dir to specify exact GCC version
- Fixes consteval and ranges concept errors with newer libstdc++
2025-08-18 18:51:38 +07:00
Nicholas Dudfield
df652b457b ci: include stdlib in compiler_id for cache keys
- compiler_id now includes stdlib (e.g. clang-14-libstdcxx)
- Cache keys are self-documenting without redundant stdlib param
- Actions still receive stdlib for Conan/CMake configuration
- Added documentation explaining matrix key purposes
2025-08-18 18:37:20 +07:00
Nicholas Dudfield
0651332bb3 refactor: use clear stdlib naming instead of force_libstdcpp
- Rename force_libstdcpp to stdlib with values libstdcxx/libcxx
- Make stdlib a required enum field with validation
- Update cache keys to be self-documenting (e.g. clang-14-libstdcxx)
- Cleaner, more intuitive configuration flow
2025-08-18 18:21:42 +07:00
Nicholas Dudfield
5286bae753 ci: fix cache key to include stdlib choice 2025-08-18 18:12:11 +07:00
Nicholas Dudfield
4b9ef8db22 ci: move force_libstdcpp to matrix-level property 2025-08-18 18:07:39 +07:00
Nicholas Dudfield
360546d555 ci: add clang-14 to matrix, temporarily disable gcc 2025-08-18 18:04:31 +07:00
Nicholas Dudfield
4efcbd1eaa ci: add option to use libstdc++ with clang-16 2025-08-18 18:00:51 +07:00
Nicholas Dudfield
4c90598462 ci: add clang to build matrix 2025-08-18 16:58:09 +07:00
25 changed files with 808 additions and 75 deletions

View File

@@ -21,7 +21,7 @@ inputs:
required: false
default: ''
compiler-id:
description: 'Unique identifier for compiler/version combination used for cache keys'
description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. clang-14-libstdcxx-gcc11, gcc-13-libstdcxx)'
required: false
default: ''
cache_version:
@@ -36,6 +36,17 @@ inputs:
description: 'Main branch name for restore keys'
required: false
default: 'dev'
stdlib:
description: 'C++ standard library to use'
required: true
type: choice
options:
- libstdcxx
- libcxx
clang_gcc_toolchain:
description: 'GCC version to use for Clang toolchain (e.g. 11, 13)'
required: false
default: ''
runs:
using: 'composite'
@@ -93,6 +104,38 @@ runs:
CCACHE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
fi
# Configure C++ standard library if specified
# libstdcxx used for clang-14/16 to work around missing lexicographical_compare_three_way in libc++
# libcxx can be used with clang-17+ which has full C++20 support
# Note: -stdlib flag is Clang-specific, GCC always uses libstdc++
CMAKE_CXX_FLAGS=""
if [[ "${{ inputs.cxx }}" == clang* ]]; then
# Only Clang needs the -stdlib flag
if [ "${{ inputs.stdlib }}" = "libstdcxx" ]; then
CMAKE_CXX_FLAGS="-stdlib=libstdc++"
elif [ "${{ inputs.stdlib }}" = "libcxx" ]; then
CMAKE_CXX_FLAGS="-stdlib=libc++"
fi
fi
# GCC always uses libstdc++ and doesn't need/support the -stdlib flag
# Configure GCC toolchain for Clang if specified
if [ -n "${{ inputs.clang_gcc_toolchain }}" ] && [[ "${{ inputs.cxx }}" == clang* ]]; then
# Extract Clang version from compiler executable name (e.g., clang++-14 -> 14)
clang_version=$(echo "${{ inputs.cxx }}" | grep -oE '[0-9]+$')
# Clang 16+ supports --gcc-install-dir (precise path specification)
# Clang <16 only has --gcc-toolchain (uses discovery heuristics)
if [ -n "$clang_version" ] && [ "$clang_version" -ge "16" ]; then
# Clang 16+ uses --gcc-install-dir (canonical, precise)
CMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS --gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/${{ inputs.clang_gcc_toolchain }}"
else
# Clang 14-15 uses --gcc-toolchain (deprecated but necessary)
# Note: This still uses discovery, so we hide newer GCC versions in the workflow
CMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS --gcc-toolchain=/usr"
fi
fi
# Run CMake configure
# Note: conanfile.py hardcodes 'build/generators' as the output path.
# If we're in a 'build' folder, Conan detects this and uses just 'generators/'
@@ -101,6 +144,7 @@ runs:
cmake .. \
-G "${{ inputs.generator }}" \
$CCACHE_ARGS \
${CMAKE_CXX_FLAGS:+-DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS"} \
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=${{ inputs.configuration }}

View File

@@ -10,7 +10,7 @@ inputs:
required: false
default: '.build'
compiler-id:
description: 'Unique identifier for compiler/version combination used for cache keys'
description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. clang-14-libstdcxx-gcc11, gcc-13-libstdcxx)'
required: false
default: ''
cache_version:
@@ -25,6 +25,13 @@ inputs:
description: 'Main branch name for restore keys'
required: false
default: 'dev'
stdlib:
description: 'C++ standard library for Conan configuration (note: also in compiler-id)'
required: true
type: choice
options:
- libstdcxx
- libcxx
outputs:
cache-hit:
@@ -70,6 +77,7 @@ runs:
path: |
~/.conan
~/.conan2
# Note: compiler-id format is compiler-version-stdlib[-gccversion]
key: ${{ runner.os }}-conan-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ hashFiles('**/conanfile.txt', '**/conanfile.py') }}-${{ inputs.configuration }}
restore-keys: |
${{ runner.os }}-conan-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ hashFiles('**/conanfile.txt', '**/conanfile.py') }}-
@@ -84,6 +92,8 @@ runs:
- name: Install dependencies
shell: bash
env:
CONAN_REQUEST_TIMEOUT: 180 # Increase timeout to 3 minutes for slow mirrors
run: |
# Create build directory
mkdir -p ${{ inputs.build_dir }}

View File

@@ -13,21 +13,146 @@ concurrency:
cancel-in-progress: true
jobs:
build-job:
matrix-setup:
runs-on: ubuntu-latest
container: python:3-slim
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Generate build matrix
id: set-matrix
shell: python
run: |
import json
import os
# Full matrix with all 6 compiler configurations
# Each configuration includes all parameters needed by the build job
full_matrix = [
{
"compiler_id": "gcc-11-libstdcxx",
"compiler": "gcc",
"cc": "gcc-11",
"cxx": "g++-11",
"compiler_version": 11,
"stdlib": "libstdcxx",
"configuration": "Debug"
},
{
"compiler_id": "gcc-13-libstdcxx",
"compiler": "gcc",
"cc": "gcc-13",
"cxx": "g++-13",
"compiler_version": 13,
"stdlib": "libstdcxx",
"configuration": "Debug"
},
{
"compiler_id": "clang-14-libstdcxx-gcc11",
"compiler": "clang",
"cc": "clang-14",
"cxx": "clang++-14",
"compiler_version": 14,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 11,
"configuration": "Debug"
},
{
"compiler_id": "clang-16-libstdcxx-gcc13",
"compiler": "clang",
"cc": "clang-16",
"cxx": "clang++-16",
"compiler_version": 16,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 13,
"configuration": "Debug"
},
{
"compiler_id": "clang-17-libcxx",
"compiler": "clang",
"cc": "clang-17",
"cxx": "clang++-17",
"compiler_version": 17,
"stdlib": "libcxx",
"configuration": "Debug"
},
{
# Clang 18 - testing if it's faster than Clang 17 with libc++
# Requires patching Conan v1 settings.yml to add version 18
"compiler_id": "clang-18-libcxx",
"compiler": "clang",
"cc": "clang-18",
"cxx": "clang++-18",
"compiler_version": 18,
"stdlib": "libcxx",
"configuration": "Debug"
}
]
# Minimal matrix for PRs and feature branches
minimal_matrix = [
full_matrix[1], # gcc-13 (middle-ground gcc)
full_matrix[2] # clang-14 (mature, stable clang)
]
# Determine which matrix to use based on the target branch
ref = "${{ github.ref }}"
base_ref = "${{ github.base_ref }}" # For PRs, this is the target branch
event_name = "${{ github.event_name }}"
commit_message = """${{ github.event.head_commit.message }}"""
pr_title = """${{ github.event.pull_request.title }}"""
# Debug logging
print(f"Event: {event_name}")
print(f"Ref: {ref}")
print(f"Base ref: {base_ref}")
print(f"PR title: {pr_title}")
print(f"Commit message: {commit_message}")
# Check for override tags in commit message or PR title
force_full = "[ci-nix-full-matrix]" in commit_message or "[ci-nix-full-matrix]" in pr_title
print(f"Force full matrix: {force_full}")
# Check if this is targeting a main branch
# For PRs: check base_ref (target branch)
# For pushes: check ref (current branch)
main_branches = ["refs/heads/dev", "refs/heads/release", "refs/heads/candidate"]
if force_full:
# Override: always use full matrix if tag is present
use_full = True
elif event_name == "pull_request":
# For PRs, base_ref is just the branch name (e.g., "dev", not "refs/heads/dev")
# Check if the PR targets release or candidate (more critical branches)
use_full = base_ref in ["release", "candidate"]
else:
# For pushes, ref is the full reference (e.g., "refs/heads/dev")
use_full = ref in main_branches
# Select the appropriate matrix
if use_full:
if force_full:
print(f"Using FULL matrix (6 configs) - forced by [ci-nix-full-matrix] tag")
else:
print(f"Using FULL matrix (6 configs) - targeting main branch")
matrix = full_matrix
else:
print(f"Using MINIMAL matrix (2 configs) - feature branch/PR")
matrix = minimal_matrix
# Output the matrix as JSON
output = json.dumps({"include": matrix})
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f"matrix={output}\n")
build:
needs: matrix-setup
runs-on: ubuntu-latest
outputs:
artifact_name: ${{ steps.set-artifact-name.outputs.artifact_name }}
strategy:
fail-fast: false
matrix:
compiler: [gcc]
configuration: [Debug]
include:
- compiler: gcc
cc: gcc-13
cxx: g++-13
compiler_id: gcc-13
compiler_version: 13
matrix: ${{ fromJSON(needs.matrix-setup.outputs.matrix) }}
env:
build_dir: .build
# Bump this number to invalidate all caches globally.
@@ -41,8 +166,70 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y ninja-build ${{ matrix.cc }} ${{ matrix.cxx }} ccache
# Install the specific GCC version needed for Clang
if [ -n "${{ matrix.clang_gcc_toolchain }}" ]; then
echo "=== Installing GCC ${{ matrix.clang_gcc_toolchain }} for Clang ==="
sudo apt-get install -y gcc-${{ matrix.clang_gcc_toolchain }} g++-${{ matrix.clang_gcc_toolchain }} libstdc++-${{ matrix.clang_gcc_toolchain }}-dev
echo "=== GCC versions available after installation ==="
ls -la /usr/lib/gcc/x86_64-linux-gnu/ | grep -E "^d"
fi
# For Clang < 16 with --gcc-toolchain, hide newer GCC versions
# This is needed because --gcc-toolchain still picks the highest version
#
# THE GREAT GCC HIDING TRICK (for Clang < 16):
# Clang versions before 16 don't have --gcc-install-dir, only --gcc-toolchain
# which is deprecated and still uses discovery heuristics that ALWAYS pick
# the highest version number. So we play a sneaky game...
#
# We rename newer GCC versions to very low integers (1, 2, 3...) which makes
# Clang think they're ancient GCC versions. Since 11 > 3 > 2 > 1, Clang will
# pick GCC 11 over our renamed versions. It's dumb but it works!
#
# Example: GCC 12→1, GCC 13→2, GCC 14→3, so Clang picks 11 (highest number)
if [ -n "${{ matrix.clang_gcc_toolchain }}" ] && [ "${{ matrix.compiler_version }}" -lt "16" ]; then
echo "=== Hiding GCC versions newer than ${{ matrix.clang_gcc_toolchain }} for Clang < 16 ==="
target_version=${{ matrix.clang_gcc_toolchain }}
counter=1 # Start with 1 - these will be seen as "GCC version 1, 2, 3" etc
for dir in /usr/lib/gcc/x86_64-linux-gnu/*/; do
if [ -d "$dir" ]; then
version=$(basename "$dir")
# Check if version is numeric and greater than target
if [[ "$version" =~ ^[0-9]+$ ]] && [ "$version" -gt "$target_version" ]; then
echo "Hiding GCC $version -> renaming to $counter (will be seen as GCC version $counter)"
# Safety check: ensure target doesn't already exist
if [ ! -e "/usr/lib/gcc/x86_64-linux-gnu/$counter" ]; then
sudo mv "$dir" "/usr/lib/gcc/x86_64-linux-gnu/$counter"
else
echo "ERROR: Cannot rename GCC $version - /usr/lib/gcc/x86_64-linux-gnu/$counter already exists"
exit 1
fi
counter=$((counter + 1))
fi
fi
done
fi
# Verify what Clang will use
if [ -n "${{ matrix.clang_gcc_toolchain }}" ]; then
echo "=== Verifying GCC toolchain selection ==="
echo "Available GCC versions:"
ls -la /usr/lib/gcc/x86_64-linux-gnu/ | grep -E "^d.*[0-9]+$" || true
echo ""
echo "Clang's detected GCC installation:"
${{ matrix.cxx }} -v -E -x c++ /dev/null -o /dev/null 2>&1 | grep "Found candidate GCC installation" || true
fi
# Install libc++ dev packages if using libc++ (not needed for libstdc++)
if [ "${{ matrix.stdlib }}" = "libcxx" ]; then
sudo apt-get install -y libc++-${{ matrix.compiler_version }}-dev libc++abi-${{ matrix.compiler_version }}-dev
fi
# Install Conan 2
pip install --upgrade "conan>=2.0"
pip install --upgrade "conan>=2.0,<3"
- name: Configure ccache
uses: ./.github/actions/xahau-configure-ccache
@@ -57,14 +244,21 @@ jobs:
# Create the default profile directory if it doesn't exist
mkdir -p ~/.conan2/profiles
# Determine the correct libcxx based on stdlib parameter
if [ "${{ matrix.stdlib }}" = "libcxx" ]; then
LIBCXX="libc++"
else
LIBCXX="libstdc++11"
fi
# Create profile with our specific settings
cat > ~/.conan2/profiles/default <<EOF
[settings]
arch=x86_64
build_type=Release
build_type=${{ matrix.configuration }}
compiler=${{ matrix.compiler }}
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.libcxx=${LIBCXX}
compiler.version=${{ matrix.compiler_version }}
os=Linux
@@ -99,6 +293,7 @@ jobs:
compiler-id: ${{ matrix.compiler_id }}
cache_version: ${{ env.CACHE_VERSION }}
main_branch: ${{ env.MAIN_BRANCH_NAME }}
stdlib: ${{ matrix.stdlib }}
- name: Build
uses: ./.github/actions/xahau-ga-build
@@ -111,6 +306,8 @@ jobs:
compiler-id: ${{ matrix.compiler_id }}
cache_version: ${{ env.CACHE_VERSION }}
main_branch: ${{ env.MAIN_BRANCH_NAME }}
stdlib: ${{ matrix.stdlib }}
clang_gcc_toolchain: ${{ matrix.clang_gcc_toolchain || '' }}
- name: Set artifact name
id: set-artifact-name

View File

@@ -8,6 +8,6 @@
"editor.semanticHighlighting.enabled": true,
"editor.tabSize": 4,
"editor.defaultFormatter": "xaver.clang-format",
"editor.formatOnSave": false
"editor.formatOnSave": true
}
}

View File

@@ -50,6 +50,12 @@ target_sources (xrpl_core PRIVATE
src/ripple/beast/utility/src/beast_Journal.cpp
src/ripple/beast/utility/src/beast_PropertyStream.cpp)
# Conditionally add enhanced logging source when BEAST_ENHANCED_LOGGING is enabled
if(DEFINED BEAST_ENHANCED_LOGGING AND BEAST_ENHANCED_LOGGING)
target_sources(xrpl_core PRIVATE
src/ripple/beast/utility/src/beast_EnhancedLogging.cpp)
endif()
#[===============================[
core sources
#]===============================]
@@ -155,6 +161,13 @@ target_link_libraries (xrpl_core
ed25519::ed25519
date::date
Ripple::opts)
# Link date-tz library when enhanced logging is enabled
if(DEFINED BEAST_ENHANCED_LOGGING AND BEAST_ENHANCED_LOGGING)
if(TARGET date::date-tz)
target_link_libraries(xrpl_core PUBLIC date::date-tz)
endif()
endif()
#[=================================[
main/core headers installation
#]=================================]

View File

@@ -33,6 +33,25 @@ if(Git_FOUND)
endif()
endif() #git
# make SOURCE_ROOT_PATH define available for logging
set(SOURCE_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/")
add_definitions(-DSOURCE_ROOT_PATH="${SOURCE_ROOT_PATH}")
# BEAST_ENHANCED_LOGGING option - adds file:line numbers and formatting to logs
# Default to ON for Debug builds, OFF for Release
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
option(BEAST_ENHANCED_LOGGING "Include file and line numbers in log messages" ON)
else()
option(BEAST_ENHANCED_LOGGING "Include file and line numbers in log messages" OFF)
endif()
if(BEAST_ENHANCED_LOGGING)
add_definitions(-DBEAST_ENHANCED_LOGGING=1)
message(STATUS "Log line numbers enabled")
else()
message(STATUS "Log line numbers disabled")
endif()
if(thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options("-stdlib=libc++")

View File

@@ -26,7 +26,7 @@ class Xrpl(ConanFile):
}
requires = [
'date/3.0.1',
'date/3.0.3',
'libarchive/3.6.0',
'lz4/1.9.4',
'grpc/1.50.1',
@@ -52,7 +52,7 @@ class Xrpl(ConanFile):
'tool_requires_b2': False,
'cassandra-cpp-driver/*:shared': False,
'date/*:header_only': True,
'date/*:header_only': False,
'grpc/*:shared': False,
'grpc/*:secure': True,
'libarchive/*:shared': False,

View File

@@ -156,34 +156,22 @@ deserializeManifest(Slice s, beast::Journal journal)
}
}
template <class Stream>
Stream&
logMftAct(
Stream& s,
std::string const& action,
PublicKey const& pk,
std::uint32_t seq)
{
s << "Manifest: " << action
<< ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
<< ";";
return s;
}
// Helper macros to format manifest log messages while preserving line numbers
#define LOG_MANIFEST_ACTION(stream, action, pk, seq) \
do \
{ \
JLOG(stream) << "Manifest: " << action \
<< ";Pk: " << toBase58(TokenType::NodePublic, pk) \
<< ";Seq: " << seq << ";"; \
} while (0)
template <class Stream>
Stream&
logMftAct(
Stream& s,
std::string const& action,
PublicKey const& pk,
std::uint32_t seq,
std::uint32_t oldSeq)
{
s << "Manifest: " << action
<< ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
<< ";OldSeq: " << oldSeq << ";";
return s;
}
#define LOG_MANIFEST_ACTION_WITH_OLD(stream, action, pk, seq, oldSeq) \
do \
{ \
JLOG(stream) << "Manifest: " << action \
<< ";Pk: " << toBase58(TokenType::NodePublic, pk) \
<< ";Seq: " << seq << ";OldSeq: " << oldSeq << ";"; \
} while (0)
bool
Manifest::verify() const
@@ -381,7 +369,7 @@ ManifestCache::applyManifest(Manifest m)
// several cases including when we receive manifests from a peer who
// doesn't have the latest data.
if (auto stream = j_.debug())
logMftAct(
LOG_MANIFEST_ACTION_WITH_OLD(
stream,
"Stale",
m.masterKey,
@@ -393,7 +381,7 @@ ManifestCache::applyManifest(Manifest m)
if (checkSignature && !m.verify())
{
if (auto stream = j_.warn())
logMftAct(stream, "Invalid", m.masterKey, m.sequence);
LOG_MANIFEST_ACTION(stream, "Invalid", m.masterKey, m.sequence);
return ManifestDisposition::invalid;
}
@@ -407,7 +395,7 @@ ManifestCache::applyManifest(Manifest m)
bool const revoked = m.revoked();
if (auto stream = j_.warn(); stream && revoked)
logMftAct(stream, "Revoked", m.masterKey, m.sequence);
LOG_MANIFEST_ACTION(stream, "Revoked", m.masterKey, m.sequence);
// Sanity check: the master key of this manifest should not be used as
// the ephemeral key of another manifest:
@@ -476,7 +464,7 @@ ManifestCache::applyManifest(Manifest m)
if (iter == map_.end())
{
if (auto stream = j_.info())
logMftAct(stream, "AcceptedNew", m.masterKey, m.sequence);
LOG_MANIFEST_ACTION(stream, "AcceptedNew", m.masterKey, m.sequence);
if (!revoked)
signingToMasterKeys_[m.signingKey] = m.masterKey;
@@ -489,7 +477,7 @@ ManifestCache::applyManifest(Manifest m)
// An ephemeral key was revoked and superseded by a new key. This is
// expected, but should happen infrequently.
if (auto stream = j_.info())
logMftAct(
LOG_MANIFEST_ACTION_WITH_OLD(
stream,
"AcceptedUpdate",
m.masterKey,
@@ -584,4 +572,9 @@ ManifestCache::save(
saveManifests(*db, dbTable, isTrusted, map_, j_);
}
// Clean up macros to avoid namespace pollution
#undef LOG_MANIFEST_ACTION
#undef LOG_MANIFEST_ACTION_WITH_OLD
} // namespace ripple

View File

@@ -221,7 +221,8 @@ public:
if (!ledger->info().accountHash.isNonZero())
{
JLOG(j.fatal()) << "AH is zero: " << getJson({*ledger, {}});
JLOG(j.fatal())
<< "AH is zero: " << getJson({*ledger, {}}).asString();
assert(false);
}

View File

@@ -33,6 +33,7 @@
#include <ripple/protocol/STArray.h>
#include <ripple/protocol/STObject.h>
#include <ripple/protocol/STTx.h>
#include <ripple/protocol/TxFlags.h>
#include <algorithm>
#include <cstdint>
#include <exception>
@@ -665,6 +666,13 @@ SetHook::preflight(PreflightContext const& ctx)
if (!isTesSuccess(ret))
return ret;
if (ctx.rules.enabled(fixInvalidTxFlags) &&
ctx.tx.getFlags() & tfUniversalMask)
{
JLOG(ctx.j.trace()) << "SetHook: Invalid flags set.";
return temINVALID_FLAG;
}
if (!ctx.tx.isFieldPresent(sfHooks))
{
JLOG(ctx.j.trace())

View File

@@ -27,6 +27,7 @@
#include <ripple/protocol/STArray.h>
#include <ripple/protocol/STObject.h>
#include <ripple/protocol/STTx.h>
#include <ripple/protocol/TxFlags.h>
#include <algorithm>
#include <cstdint>
@@ -81,6 +82,13 @@ SetSignerList::preflight(PreflightContext const& ctx)
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
if (ctx.rules.enabled(fixInvalidTxFlags) &&
(ctx.tx.getFlags() & tfUniversalMask))
{
JLOG(ctx.j.trace()) << "SetSignerList: invalid flags.";
return temINVALID_FLAG;
}
auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
if (!isTesSuccess(std::get<0>(result)))

View File

@@ -249,13 +249,22 @@ private:
// Wraps a Journal::Stream to skip evaluation of
// expensive argument lists if the stream is not active.
#ifndef JLOG
#ifdef BEAST_ENHANCED_LOGGING
#define JLOG(x) \
if (!x) \
if (!(x)) \
{ \
} \
else \
(x).withLocation(__FILE__, __LINE__)
#else
#define JLOG(x) \
if (!(x)) \
{ \
} \
else \
x
#endif
#endif
//------------------------------------------------------------------------------
// Debug logging:

View File

@@ -17,11 +17,19 @@
*/
//==============================================================================
#include <date/date.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/chrono.h>
#include <ripple/basics/contract.h>
#ifdef BEAST_ENHANCED_LOGGING
#include <ripple/beast/utility/EnhancedLogging.h>
#include <date/tz.h>
#endif
#include <boost/algorithm/string.hpp>
#include <cassert>
#include <cstring>
#include <ctime>
#include <fstream>
#include <functional>
#include <iostream>
@@ -316,11 +324,46 @@ Logs::format(
{
output.reserve(message.size() + partition.size() + 100);
output = to_string(std::chrono::system_clock::now());
#ifdef BEAST_ENHANCED_LOGGING
// Environment variables are used instead of config file because:
// 1. Logging starts before config parsing (needed to debug config issues)
// 2. This is a developer feature - devs can easily set env vars
// 3. Allows per-run overrides without editing config files
static const char* fmt = []() {
const char* env = std::getenv("LOG_DATE_FORMAT");
return env ? env : "%Y-%b-%d %T %Z"; // Default format
}();
// Check if we should use local time
static const bool useLocalTime = []() {
const char* env = std::getenv("LOG_DATE_LOCAL");
return env && std::strcmp(env, "1") == 0;
}();
if (useLocalTime)
{
auto now = std::chrono::system_clock::now();
auto local = date::make_zoned(date::current_zone(), now);
output = date::format(fmt, local);
}
else
{
output = date::format(fmt, std::chrono::system_clock::now());
}
#else
output = to_string(std::chrono::system_clock::now());
#endif
if (!output.empty()) // Allow setting date format to an empty string
output += " ";
output += " ";
if (!partition.empty())
{
#ifdef BEAST_ENHANCED_LOGGING
output += beast::detail::get_log_highlight_color();
#endif
output += partition + ":";
}
using namespace beast::severities;
switch (severity)
@@ -348,6 +391,10 @@ Logs::format(
break;
}
#ifdef BEAST_ENHANCED_LOGGING
output += "\033[0m";
#endif
output += message;
// Limit the maximum length of the output

View File

@@ -0,0 +1,85 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_UTILITY_ENHANCEDLOGGING_H_INCLUDED
#define BEAST_UTILITY_ENHANCEDLOGGING_H_INCLUDED
#include <cstddef> // for size_t
#include <iosfwd> // for std::ostream
namespace beast {
namespace detail {
// Check if we should use colors - cached at startup
bool
should_log_use_colors();
// Get the log highlight color - can be overridden via
// LOG_HIGHLIGHT_COLOR
const char*
get_log_highlight_color();
// Strip source root path from __FILE__ at compile time
// IMPORTANT: This MUST stay in the header as constexpr for compile-time
// evaluation!
constexpr const char*
strip_source_root(const char* file)
{
#ifdef SOURCE_ROOT_PATH
constexpr const char* sourceRoot = SOURCE_ROOT_PATH;
constexpr auto strlen_constexpr = [](const char* s) constexpr
{
const char* p = s;
while (*p)
++p;
return p - s;
};
constexpr auto strncmp_constexpr =
[](const char* a, const char* b, size_t n) constexpr
{
for (size_t i = 0; i < n; ++i)
{
if (a[i] != b[i])
return a[i] - b[i];
if (a[i] == '\0')
break;
}
return 0;
};
constexpr size_t sourceRootLen = strlen_constexpr(sourceRoot);
return (strncmp_constexpr(file, sourceRoot, sourceRootLen) == 0)
? file + sourceRootLen
: file;
#else
return file;
#endif
}
// Check if location info should be shown - cached at startup
bool
should_show_location();
// Helper to write location string (no leading/trailing space)
void
log_write_location_string(std::ostream& os, const char* file, int line);
} // namespace detail
} // namespace beast
#endif

View File

@@ -146,6 +146,10 @@ private:
ScopedStream(Sink& sink, Severity level);
#ifdef BEAST_ENHANCED_LOGGING
ScopedStream(Sink& sink, Severity level, const char* file, int line);
#endif
template <typename T>
ScopedStream(Stream const& stream, T const& t);
@@ -173,6 +177,10 @@ private:
Sink& m_sink;
Severity const m_level;
std::ostringstream mutable m_ostream;
#ifdef BEAST_ENHANCED_LOGGING
const char* file_ = nullptr;
int line_ = 0;
#endif
};
#ifndef __INTELLISENSE__
@@ -191,6 +199,33 @@ private:
//--------------------------------------------------------------------------
public:
/** Provide a light-weight way to check active() before string formatting */
#ifdef BEAST_ENHANCED_LOGGING
/** Stream with location information that prepends file:line to the first
* message */
class StreamWithLocation
{
public:
StreamWithLocation(Stream const& stream, const char* file, int line)
: file_(file), line_(line), stream_(stream)
{
}
/** Override to inject file:line before the first output */
template <typename T>
ScopedStream
operator<<(T const& t) const;
ScopedStream
operator<<(std::ostream& manip(std::ostream&)) const;
private:
const char* file_;
int line_;
const Stream& stream_;
};
#endif
class Stream
{
public:
@@ -255,6 +290,15 @@ public:
operator<<(T const& t) const;
/** @} */
#ifdef BEAST_ENHANCED_LOGGING
/** Create a StreamWithLocation that prepends file:line info */
StreamWithLocation
withLocation(const char* file, int line) const
{
return StreamWithLocation(*this, file, line);
}
#endif
private:
Sink& m_sink;
Severity m_level;
@@ -354,6 +398,8 @@ static_assert(std::is_nothrow_destructible<Journal>::value == true, "");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
template <typename T>
Journal::ScopedStream::ScopedStream(Journal::Stream const& stream, T const& t)
: ScopedStream(stream.sink(), stream.level())
@@ -378,6 +424,21 @@ Journal::Stream::operator<<(T const& t) const
return ScopedStream(*this, t);
}
#ifdef BEAST_ENHANCED_LOGGING
//------------------------------------------------------------------------------
template <typename T>
Journal::ScopedStream
Journal::StreamWithLocation::operator<<(T const& t) const
{
// Create a ScopedStream with location info
ScopedStream scoped(stream_.sink(), stream_.level(), file_, line_);
scoped.ostream() << t;
return scoped;
}
#endif
namespace detail {
template <class CharT, class Traits = std::char_traits<CharT>>

View File

@@ -0,0 +1,114 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/beast/utility/EnhancedLogging.h>
#include <cstdlib>
#include <cstring>
#include <ostream>
#include <unistd.h>
namespace beast {
namespace detail {
// Check if we should use colors - cached at startup
bool
should_log_use_colors()
{
static const bool use_colors = []() {
// Honor NO_COLOR environment variable (standard)
if (std::getenv("NO_COLOR"))
return false;
// Honor FORCE_COLOR to override terminal detection
if (std::getenv("FORCE_COLOR"))
return true;
// Check if stderr is a terminal
return isatty(STDERR_FILENO) != 0;
}();
return use_colors;
}
// Get the log highlight color - can be overridden via
// LOG_HIGHLIGHT_COLOR
const char*
get_log_highlight_color()
{
static const char* escape = []() {
const char* env = std::getenv("LOG_HIGHLIGHT_COLOR");
if (!env)
return "\033[36m"; // Default: cyan
// Simple map of color names to escape sequences
if (std::strcmp(env, "red") == 0)
return "\033[31m";
if (std::strcmp(env, "green") == 0)
return "\033[32m";
if (std::strcmp(env, "yellow") == 0)
return "\033[33m";
if (std::strcmp(env, "blue") == 0)
return "\033[34m";
if (std::strcmp(env, "magenta") == 0)
return "\033[35m";
if (std::strcmp(env, "cyan") == 0)
return "\033[36m";
if (std::strcmp(env, "white") == 0)
return "\033[37m";
if (std::strcmp(env, "gray") == 0 || std::strcmp(env, "grey") == 0)
return "\033[90m"; // Bright black (gray)
if (std::strcmp(env, "orange") == 0)
return "\033[93m"; // Bright yellow (appears orange-ish)
if (std::strcmp(env, "none") == 0)
return "";
// Default to cyan if unknown color name
return "\033[36m";
}();
return escape;
}
// Check if location info should be shown - cached at startup
bool
should_show_location()
{
static const bool show = []() {
const char* env = std::getenv("LOG_DISABLE");
// Show location by default, hide if LOG_DISABLE=1
return !env || std::strcmp(env, "1") != 0;
}();
return show;
}
// Helper to write location string (no leading/trailing space)
void
log_write_location_string(std::ostream& os, const char* file, int line)
{
if (detail::should_log_use_colors())
{
os << detail::get_log_highlight_color() << "["
<< detail::strip_source_root(file) << ":" << line << "]\033[0m";
}
else
{
os << "[" << detail::strip_source_root(file) << ":" << line << "]";
}
}
} // namespace detail
} // namespace beast

View File

@@ -19,6 +19,11 @@
#include <ripple/beast/utility/Journal.h>
#include <cassert>
#ifdef BEAST_ENHANCED_LOGGING
#include <ripple/beast/utility/EnhancedLogging.h>
#include <cstdlib>
#include <cstring>
#endif
namespace beast {
@@ -131,9 +136,36 @@ Journal::ScopedStream::ScopedStream(
m_ostream << manip;
}
#ifdef BEAST_ENHANCED_LOGGING
Journal::ScopedStream::ScopedStream(
Sink& sink,
Severity level,
const char* file,
int line)
: m_sink(sink), m_level(level), file_(file), line_(line)
{
// Modifiers applied from all ctors
m_ostream << std::boolalpha << std::showbase;
}
#endif
Journal::ScopedStream::~ScopedStream()
{
std::string const& s(m_ostream.str());
std::string s(m_ostream.str());
#ifdef BEAST_ENHANCED_LOGGING
// Add suffix if location is enabled
if (file_ && detail::should_show_location() && !s.empty() && s != "\n")
{
std::ostringstream combined;
combined << s;
if (!s.empty() && s.back() != ' ')
combined << " ";
detail::log_write_location_string(combined, file_, line_);
s = combined.str();
}
#endif
if (!s.empty())
{
if (s == "\n")
@@ -157,4 +189,18 @@ Journal::Stream::operator<<(std::ostream& manip(std::ostream&)) const
return ScopedStream(*this, manip);
}
#ifdef BEAST_ENHANCED_LOGGING
// Implementation moved to use new constructor
Journal::ScopedStream
Journal::StreamWithLocation::operator<<(
std::ostream& manip(std::ostream&)) const
{
// Create a ScopedStream with location info
ScopedStream scoped(stream_.sink(), stream_.level(), file_, line_);
scoped.ostream() << manip;
return scoped;
}
#endif
} // namespace beast

View File

@@ -239,19 +239,17 @@ verifyHandshake(
throw std::runtime_error("Invalid server domain");
}
// Check the network. Omitting Network-ID (on either side ours, or theirs)
// means NID=0
// Check network ID, treating absent/empty as default network 0
{
uint32_t peer_nid = 0;
std::uint32_t nid{0};
if (auto const iter = headers.find("Network-ID"); iter != headers.end())
{
if (!beast::lexicalCastChecked(
peer_nid, std::string(iter->value())))
if (!beast::lexicalCastChecked(nid, std::string(iter->value())))
throw std::runtime_error("Invalid peer network identifier");
}
uint32_t our_nid = networkID ? *networkID : 0;
if (peer_nid != our_nid)
if (networkID.value_or(0) != nid)
throw std::runtime_error("Peer is on a different network");
}

View File

@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 85;
static constexpr std::size_t numFeatures = 86;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -373,6 +373,7 @@ extern uint256 const fixProvisionalDoubleThreading;
extern uint256 const featureClawback;
extern uint256 const featureDeepFreeze;
extern uint256 const featureIOUIssuerWeakTSH;
extern uint256 const fixInvalidTxFlags;
} // namespace ripple

View File

@@ -479,6 +479,7 @@ REGISTER_FEATURE(Clawback, Supported::yes, VoteBehavior::De
REGISTER_FIX (fixProvisionalDoubleThreading, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(IOUIssuerWeakTSH, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixInvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes);
// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.

View File

@@ -5203,8 +5203,8 @@ class Import_test : public beast::unit_test::suite
std::string ns_str =
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECA"
"FE";
Json::Value jv = ripple::test::jtx::hook(
issuer, {{hso(createCodeHex)}}, hsfOVERRIDE | hsfCOLLECT);
Json::Value jv =
ripple::test::jtx::hook(issuer, {{hso(createCodeHex)}}, 0);
jv[jss::Hooks][0U][jss::Hook][jss::HookNamespace] = ns_str;
jv[jss::Hooks][0U][jss::Hook][jss::HookOn] =
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFFFFFBFFF"

View File

@@ -1659,6 +1659,36 @@ public:
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
}
void
test_signerListSetFlags(FeatureBitset features)
{
using namespace test::jtx;
for (bool const withFixInvalidTxFlags : {false, true})
{
Env env{
*this,
withFixInvalidTxFlags ? features
: features - fixInvalidTxFlags};
Account const alice{"alice"};
env.fund(XRP(1000), alice);
env.close();
bool const enabled = features[fixInvalidTxFlags];
testcase(
std::string("SignerListSet flag, fix ") +
(withFixInvalidTxFlags ? "enabled" : "disabled"));
ter const expected(
withFixInvalidTxFlags ? TER(temINVALID_FLAG) : TER(tesSUCCESS));
env(signers(alice, 2, {{bogie, 1}, {ghost, 1}}),
expected,
txflags(tfPassive));
env.close();
}
}
void
testAll(FeatureBitset features)
{
@@ -1695,6 +1725,9 @@ public:
testAll(all - featureMultiSignReserve - featureExpandedSignerList);
testAll(all - featureExpandedSignerList);
testAll(all);
test_signerListSetFlags(all);
test_amendmentTransition();
}
};

View File

@@ -364,6 +364,35 @@ public:
}
}
void
testInvalidTxFlags(FeatureBitset features)
{
testcase("Checks invalid tx flags");
using namespace jtx;
for (bool const withFixInvalidTxFlags : {false, true})
{
Env env{
*this,
withFixInvalidTxFlags ? features
: features - fixInvalidTxFlags};
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
env.close();
Json::Value jv =
ripple::test::jtx::hook(alice, {{{hso_delete()}}}, 0);
jv[jss::Flags] = tfUniversalMask;
env(jv,
M("Invalid SetHook flags"),
HSFEE,
withFixInvalidTxFlags ? ter(temINVALID_FLAG) : ter(tesSUCCESS));
env.close();
}
}
void
testGrants(FeatureBitset features)
{
@@ -12737,6 +12766,7 @@ public:
testHooksOwnerDir(features);
testHooksDisabled(features);
testTxStructure(features);
testInvalidTxFlags(features);
testInferHookSetOperation();
testParams(features);
testGrants(features);

View File

@@ -23,6 +23,7 @@
#include <ripple/beast/utility/Journal.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <boost/algorithm/string/predicate.hpp>
#include <regex>
#include <test/jtx.h>
#include <test/jtx/Env.h>
@@ -90,10 +91,26 @@ class Invariants_test : public beast::unit_test::suite
{
terActual = ac.checkInvariants(terActual, fee);
BEAST_EXPECT(terExpect == terActual);
BEAST_EXPECT(
sink.messages().str().starts_with("Invariant failed:") ||
sink.messages().str().starts_with(
"Transaction caused an exception"));
// Handle both with and without BEAST_ENHANCED_LOGGING
auto const msg = sink.messages().str();
bool hasExpectedPrefix = false;
#ifdef BEAST_ENHANCED_LOGGING
// When BEAST_ENHANCED_LOGGING is enabled, messages may include ANSI
// color codes and start with [file:line]. Just search for the
// message content.
hasExpectedPrefix =
msg.find("Invariant failed:") != std::string::npos ||
msg.find("Transaction caused an exception") !=
std::string::npos;
#else
// Without BEAST_ENHANCED_LOGGING, messages start directly with the
// text
hasExpectedPrefix = msg.starts_with("Invariant failed:") ||
msg.starts_with("Transaction caused an exception");
#endif
BEAST_EXPECT(hasExpectedPrefix);
for (auto const& m : expect_logs)
{
if (sink.messages().str().find(m) == std::string::npos)

View File

@@ -523,8 +523,7 @@ class AccountTx_test : public beast::unit_test::suite
"0B";
Json::Value jhv = hso(updateHookHex);
jhv[jss::Flags] = hsfOVERRIDE;
Json::Value jv =
ripple::test::jtx::hook(account, {{jhv}}, hsfOVERRIDE);
Json::Value jv = ripple::test::jtx::hook(account, {{jhv}}, 0);
return jv;
};
env(updateHook(alice), HSFEE, sig(alie));
@@ -553,8 +552,7 @@ class AccountTx_test : public beast::unit_test::suite
"000000";
jhv[jss::HookNamespace] = to_string(uint256{beast::zero});
jhv[jss::HookHash] = to_string(hookHash);
Json::Value jv =
ripple::test::jtx::hook(account, {{jhv}}, hsfOVERRIDE);
Json::Value jv = ripple::test::jtx::hook(account, {{jhv}}, 0);
return jv;
};
uint256 const hid = hh(env, alice);
@@ -563,8 +561,8 @@ class AccountTx_test : public beast::unit_test::suite
// Delete Hook
auto deleteHook = [](test::jtx::Account const& account) {
Json::Value jv = ripple::test::jtx::hook(
account, {{hso_delete()}}, hsfOVERRIDE);
Json::Value jv =
ripple::test::jtx::hook(account, {{hso_delete()}}, 0);
return jv;
};
env(deleteHook(alice), HSFEE, sig(alie));