Compare commits

..

18 Commits

Author SHA1 Message Date
Nicholas Dudfield
31044a8929 fix: replace detached threads in sleepFor(), fix tx-priority leak and rescan
- Replace std:🧵:detach() in Coro::sleepFor() with
  boost::asio::steady_timer on the existing io_service thread pool.
  Previously each 50ms poll spawned a new detached thread (~600 per
  missing node over 30s timeout).

- Clear expired TX-priority ranges in InboundLedgers::sweep() for
  sequences at or below the current validated ledger, preventing
  unbounded growth of txPriorityRange_.

- Track lastCheckedSeq in submit_and_wait to only scan new validated
  ledgers each poll iteration, eliminating O(n*polls) rescan overhead.
2026-02-26 17:11:06 +07:00
Nicholas Dudfield
ab4fb6501d chore: update levelization ordering 2026-02-26 15:33:27 +07:00
Nicholas Dudfield
f629836482 chore: format RCLValidations.cpp and SubmitAndWait.cpp 2026-02-26 15:29:16 +07:00
Nicholas Dudfield
1f0e1e0ecf Merge remote-tracking branch 'origin/dev' into partial-sync-mode 2026-02-26 14:11:23 +07:00
Nicholas Dudfield
44881c6255 merge: sync with origin/dev (rippled 2.4.0 repo restructure)
Merges 299 commits from origin/dev including the major rippled 2.4.0
repository restructure (src/ripple/ -> src/xrpld/ + include/xrpl/,
Builds/CMake/ -> cmake/, shards removal).

Resolved conflicts in 10 files, preserving all partial sync additions:
- Coro.ipp: kept our #include <thread> with new paths
- SHAMap.cpp: kept LocalValue.h, JobQueue.h, chrono includes
- NodeFamily.cpp: kept enhanced logging and addPriorityNode call
- InboundLedgers.cpp: kept RangeSet.h include with new paths
- NetworkOPs.cpp: updated beginConsensus to include clog param
- RPCHelpers.cpp: kept InboundLedgers.h, LocalValue.h includes
  and commented-out sync validation (updated for removed reporting())
- Handler.cpp: added submit_and_wait to new alphabetical handler list
- Handler.h: kept @@markers, updated to NO_CONDITION check
- SHAMapInnerNode.h: kept @@markers, added getBranchCount()
- ordering.txt: accepted upstream restructured dependency graph
2026-02-24 16:46:42 +07:00
Nicholas Dudfield
95bc06d4ea fix: remove debug log in onTimer() that causes use-after-free crash 2026-02-20 17:45:01 +07:00
Nicholas Dudfield
2ce3b3f1fd fix: make partial sync poll-wait opt-in via partialSyncWait flag
Previously finishFetch() entered the poll-wait loop for any coroutine
context, causing unit tests to spin for 30s on missing nodes with no
network to deliver them. Now requires explicit setPartialSyncWait(true)
from partial sync code paths (RPCHelpers, SubmitAndWait).
2026-02-20 16:54:06 +07:00
Nicholas Dudfield
fe56844871 chore: update levelization ordering 2026-02-20 11:17:33 +07:00
Nicholas Dudfield
c351266134 style: fix clang-format for Coro.ipp 2026-02-20 11:13:18 +07:00
Nicholas Dudfield
ef01ac6977 fix: remove leftover conflict marker text in RippledCore.cmake 2026-02-20 11:12:02 +07:00
Nicholas Dudfield
11cce2eac8 Merge remote-tracking branch 'origin/dev' into partial-sync-mode
# Conflicts:
#	Builds/CMake/RippledCore.cmake
#	src/ripple/app/misc/impl/Manifest.cpp
2026-02-20 11:05:29 +07:00
Nicholas Dudfield
b3c4e56c26 feat: check LedgerMaster in submit_and_wait for synced mode
When the node is synced and receiving transactions via gossip,
ledgers are built locally and won't be in InboundLedgers. Now
checks both:
- InboundLedgers (partial sync mode - ledgers from peers)
- LedgerMaster (synced mode - ledgers built from gossip)
2025-12-02 08:21:37 +07:00
Nicholas Dudfield
9ddf649e2a feat: add ledger range-based TX priority for faster tx detection
Simplify TX priority mechanism using RangeSet instead of per-TX hash
tracking. When submit_and_wait is called, it registers a ledger range
where TX nodes should be fetched before state nodes.

Key changes:
- Add prioritizeTxForLedgers(start, end) and isTxPrioritized(seq)
  to InboundLedgers using RangeSet<uint32_t>
- InboundLedger::trigger() checks range to decide TX-before-state order
- Remove complex per-TX hash tracking that couldn't help due to
  Merkle tree structure (need parent hashes to request children)
- Format CMake and source files
2025-12-01 15:31:15 +07:00
Nicholas Dudfield
aeb2888fe9 feat: add submit_and_wait RPC for partial sync mode
Adds a new RPC handler that submits transactions and waits for
validated results, designed for nodes still syncing:

- Broadcasts raw tx to network without local state validation
- Indexes tx hashes from incoming txMap leaf nodes for fast lookup
- Polls for tx in partial ledgers, then waits for validation quorum
- Only returns when numTrustedForLedger >= quorum (truly validated)

Supporting changes:
- Add hasTx()/knownTxHashes_ to InboundLedger for tx tracking
- Add findTxLedger() to InboundLedgers to search across ledgers
- Add broadcastRawTransaction() to NetworkOPs for blind relay
- Add coroutine-local fetchTimeout to LocalValue.h
- SHAMap::finishFetch() now uses configurable timeout
2025-12-01 10:53:54 +07:00
Nicholas Dudfield
8263f39e3e feat: add Coro::sleepFor() for non-blocking poll-waits
Yields the coroutine and schedules resume after delay, freeing up
the job queue thread instead of blocking it with sleep_for().

Updated SHAMap::finishFetch() and RPCHelpers getLedger() to use
coro->sleepFor() for partial sync poll-wait loops.
2025-11-30 12:21:00 +07:00
Nicholas Dudfield
dc5ec93207 feat: trigger early quorum calculation when VL is fetched
Call updateTrusted() immediately when all publisher lists become
available in applyListsAndBroadcast(), rather than waiting for
beginConsensus(). This allows validations to be trusted within
milliseconds of VL fetch instead of waiting 14+ seconds for
consensus to start.

Also adds debugging logs:
- PartialSync journal: untrusted validations during startup
- PartialSync journal: checkAccept quorum details
- ValidatorSite journal: VL fetch timing
2025-11-30 11:56:22 +07:00
Nicholas Dudfield
5d85d2df4b feat: add priority node fetching and network-observed ledger tracking
Partial sync mode improvements for faster RPC queries during sync:

- Track network-observed ledger from any validation (not just trusted)
  to allow queries before trusted validators are configured
- Add priority node fetching: queries can request specific nodes be
  fetched immediately via addPriorityNode/addPriorityHash
- Store state/transaction nodes directly to node store (not fetch pack)
  so partial sync queries find them immediately
- Add poll-wait loops in RPCHelpers for ledger header acquisition
- Replace postAndYield with sleep_for in SHAMap finishFetch
- Implement linear backoff for re-requests (50ms increments, max 2s)
2025-11-30 11:29:26 +07:00
Nicholas Dudfield
c20c948183 feat: partial sync mode proof of concept
Allows RPC handlers to query ledgers that are still being acquired,
enabling faster node startup for read queries.

Key changes:
- Add coroutine detection via getCurrentCoroPtr() in LocalValue.h
- Add postAndYield() to Coro for safe poll-wait synchronization
- Modify SHAMap::finishFetch() to poll-wait for missing nodes when
  in coroutine context (30s timeout, re-requests missing nodes)
- Add getPartialLedger() to InboundLedgers for accessing incomplete
  ledgers that have headers
- Add getLastValidatedLedger() to LedgerMaster to get validated
  ledger hash even when not fully synced
- Update RPCHelpers getLedger() to fall back to partial ledgers
- Fix Manifest seq_++ bug for new manifest entries
- Add zero hash guard in NetworkOPs::checkLastClosedLedger()

Note: This is a proof of concept. Production use would require
fetch prioritization to make queries fast enough to be practical.
2025-11-30 09:45:32 +07:00
353 changed files with 5919 additions and 47151 deletions

View File

@@ -1,37 +0,0 @@
codecov:
require_ci_to_pass: true
comment:
behavior: default
layout: reach,diff,flags,tree,reach
show_carryforward_flags: false
coverage:
range: "60..80"
precision: 1
round: nearest
status:
project:
default:
target: 60%
threshold: 2%
patch:
default:
target: auto
threshold: 2%
changes: false
github_checks:
annotations: true
parsers:
cobertura:
partials_as_hits: true
handle_missing_conditions : true
slack_app: false
ignore:
- "src/test/"
- "include/xrpl/beast/test/"
- "include/xrpl/beast/unit_test/"

View File

@@ -1,25 +1,8 @@
# This feature requires Git >= 2.24
# To use it by default in git blame:
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Format first-party source according to .clang-format
50760c693510894ca368e90369b0cc2dabfd07f3
# Reintroduce Clang-Format & Levelization
da1d20d6d5d862716125d60899b80fab5302954a
# Consolidate external libraries
da1d20d6d5d862716125d60899b80fab5302954a
# Rename .hpp to .h
0345a2645d0f5ad900f4fbbcaff96040d3a887fc
# Format formerly .hpp files
5a227dc719016e10045e17c9396ad401118044f1
# Rewrite includes
e61880699997398f5a746e6c4034edc7632661f5
# Move CMake directory (#4997)
e47b1c1b3b97c3f6d11858ee02f463596e29e7f0
# Rearrange sources (#4997)
bfafa2bb39e562901736d656806bd700c3699a2f
# Rewrite includes (#4997)
e61880699997398f5a746e6c4034edc7632661f5
# Recompute loops (#4997)
d25b5dcd568bb96c18e347d55fac10fe901a1bfb
# Reformat code with clang-format-18
02749feea88ce61c1f7eeb2d61a57d8ecf07ab11
e2384885f5f630c8f0ffe4bf21a169b433a16858
241b9ddde9e11beb7480600fd5ed90e1ef109b21
760f16f56835663d9286bd29294d074de26a7ba6
0eebe6a5f4246fced516d52b83ec4e7f47373edd

View File

@@ -2,14 +2,6 @@ name: build
description: 'Builds the project with ccache integration'
inputs:
cmake-target:
description: 'CMake target to build'
required: false
default: all
cmake-args:
description: 'Additional CMake arguments'
required: false
default: null
generator:
description: 'CMake generator to use'
required: true
@@ -28,10 +20,6 @@ inputs:
description: 'C++ compiler to use'
required: false
default: ''
gcov:
description: 'Gcov to use'
required: false
default: ''
compiler-id:
description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. clang-14-libstdcxx-gcc11, gcc-13-libstdcxx)'
required: false
@@ -53,11 +41,10 @@ inputs:
required: false
default: 'dev'
stdlib:
description: 'C++ standard library to use (default = compiler default, e.g. GCC always uses libstdc++)'
description: 'C++ standard library to use'
required: true
type: choice
options:
- default
- libstdcxx
- libcxx
clang_gcc_toolchain:
@@ -100,6 +87,11 @@ runs:
export CCACHE_CONFIGPATH="$HOME/.config/ccache/ccache.conf"
echo "CCACHE_CONFIGPATH=$CCACHE_CONFIGPATH" >> $GITHUB_ENV
# Keep config separate from cache_dir so configs aren't swapped when CCACHE_DIR changes between steps
mkdir -p ~/.config/ccache
export CCACHE_CONFIGPATH="$HOME/.config/ccache/ccache.conf"
echo "CCACHE_CONFIGPATH=$CCACHE_CONFIGPATH" >> $GITHUB_ENV
# Configure ccache settings AFTER cache restore (prevents stale cached config)
ccache --set-config=max_size=${{ inputs.ccache_max_size }}
ccache --set-config=hash_dir=${{ inputs.ccache_hash_dir }}
@@ -130,10 +122,6 @@ runs:
export CXX="${{ inputs.cxx }}"
fi
if [ -n "${{ inputs.gcov }}" ]; then
ln -sf /usr/bin/${{ inputs.gcov }} /usr/local/bin/gcov
fi
# Create wrapper toolchain that overlays ccache on top of Conan's toolchain
# This enables ccache for the main app build without affecting Conan dependency builds
if [ "${{ inputs.ccache_enabled }}" = "true" ]; then
@@ -197,8 +185,7 @@ runs:
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${TOOLCHAIN_FILE} \
-DCMAKE_BUILD_TYPE=${{ inputs.configuration }} \
-Dtests=TRUE \
-Dxrpld=TRUE \
${{ inputs.cmake-args }}
-Dxrpld=TRUE
- name: Show ccache config before build
if: inputs.ccache_enabled == 'true'
@@ -222,7 +209,7 @@ runs:
VERBOSE_FLAG="-- -v"
fi
cmake --build . --config ${{ inputs.configuration }} --parallel $(nproc) --target ${{ inputs.cmake-target }} ${VERBOSE_FLAG}
cmake --build . --config ${{ inputs.configuration }} --parallel $(nproc) ${VERBOSE_FLAG}
- name: Show ccache statistics
if: inputs.ccache_enabled == 'true'

View File

@@ -1,107 +0,0 @@
name: Check Genesis Hooks
on:
push:
pull_request:
jobs:
check-genesis-hooks:
runs-on: ubuntu-24.04
env:
CLANG_VERSION: 18
name: Verify xahau.h is in sync with genesis hooks
steps:
- name: Checkout repository
uses: actions/checkout@v6
# Install binaryen from GitHub Releases (pinned to version 100)
- name: Install binaryen (version 100)
run: |
curl -LO https://github.com/WebAssembly/binaryen/releases/download/version_100/binaryen-version_100-x86_64-linux.tar.gz
tar -xzf binaryen-version_100-x86_64-linux.tar.gz
sudo cp binaryen-version_100/bin/* /usr/local/bin/
wasm-opt --version
- name: Install clang-format
run: |
codename=$( lsb_release --codename --short )
sudo tee /etc/apt/sources.list.d/llvm.list >/dev/null <<EOF
deb http://apt.llvm.org/${codename}/ llvm-toolchain-${codename}-${CLANG_VERSION} main
deb-src http://apt.llvm.org/${codename}/ llvm-toolchain-${codename}-${CLANG_VERSION} main
EOF
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add
sudo apt-get update
sudo apt-get install clang-format-${CLANG_VERSION}
clang-format --version
# Install wasienv (WebAssembly SDK)
- name: Install wasienv
run: |
# Download install.sh
curl -o /tmp/wasienv-install.sh https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh
# Replace /bin to /local/bin
sed -i 's|/bin|/local/bin|g' /tmp/wasienv-install.sh
# Execute the installed script
bash /tmp/wasienv-install.sh
# Add wasienv to PATH for subsequent steps
- name: Setup wasienv
run: |
echo "$HOME/.wasienv/bin" >> $GITHUB_PATH
wasmcc -v || true
# Build and install hook-cleaner tool
- name: Build and install hook-cleaner
run: |
git clone https://github.com/richardah/hook-cleaner-c.git /tmp/hook-cleaner
cd /tmp/hook-cleaner
make
cp hook-cleaner /usr/local/bin/
chmod +x /usr/local/bin/hook-cleaner
# Build and install guard_checker tool
- name: Build and install guard_checker
run: |
cd include/xrpl/hook
make
cp guard_checker /usr/local/bin/
chmod +x /usr/local/bin/guard_checker
# Verify all required tools are available
- name: Verify required tools
run: |
echo "Checking tool availability..."
command -v wasmcc || (echo "Error: wasmcc not found" && exit 1)
command -v wasm-opt || (echo "Error: wasm-opt not found" && exit 1)
command -v hook-cleaner || (echo "Error: hook-cleaner not found" && exit 1)
command -v guard_checker || (echo "Error: guard_checker not found" && exit 1)
command -v xxd || (echo "Error: xxd not found" && exit 1)
command -v clang-format || (echo "Error: clang-format not found" && exit 1)
echo "All tools verified successfully"
# Execute build script to regenerate xahau.h
- name: Run build_xahau_h.sh
run: |
cd hook/genesis
./build_xahau_h.sh
# Check if xahau.h has changed (fail if out of sync)
- name: Verify xahau.h is in sync
run: |
if ! git diff --exit-code include/xrpl/hook/xahau.h; then
echo ""
echo "❌ ERROR: xahau.h is out of sync with genesis hooks"
echo ""
echo "The generated xahau.h differs from the committed version."
echo "Please run the following command and commit the changes:"
echo ""
echo " cd hook/genesis && ./build_xahau_h.sh"
echo ""
echo "Diff:"
git diff include/xrpl/hook/xahau.h
exit 1
fi
echo "✅ xahau.h is in sync with genesis hooks"

View File

@@ -20,7 +20,7 @@ jobs:
sudo apt-get update
sudo apt-get install clang-format-${CLANG_VERSION}
- name: Format first-party sources
run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format-${CLANG_VERSION} -i {} +
run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -not -path "src/magic/magic_enum.h" -exec clang-format-${CLANG_VERSION} -i {} +
- name: Check for differences
id: assert
run: |

View File

@@ -1,127 +0,0 @@
name: Formal Verification (Lean)
on:
push:
branches: ["feature-export-rng-lean"]
pull_request:
branches: ["**"]
types: [opened, synchronize, reopened]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lean-consensus:
name: Lean/C++ drift checks
runs-on: [self-hosted, macOS]
env:
BUILD_DIR: .build-formal
CMAKE_BUILD_DIR: .build-formal-cmake
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Add Homebrew to PATH
run: |
echo "/opt/homebrew/bin" >> "$GITHUB_PATH"
echo "/opt/homebrew/sbin" >> "$GITHUB_PATH"
- name: Install core tools
run: |
brew install coreutils
echo "Num proc: $(nproc)"
- name: Setup toolchain (mise)
uses: jdx/mise-action@v3.6.1
with:
cache: false
install: true
mise_toml: |
[tools]
cmake = "3.25.3"
python = "3.12"
pipx = "latest"
conan = "2"
ninja = "latest"
- name: Install tools via mise
run: |
mise install
mise reshim
echo "$HOME/.local/share/mise/shims" >> "$GITHUB_PATH"
- name: Install Lean toolchain
run: |
toolchain="$(cat formal_verification/lean-toolchain)"
curl -sSfL https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh \
| sh -s -- -y --default-toolchain "$toolchain"
echo "$HOME/.elan/bin" >> "$GITHUB_PATH"
"$HOME/.elan/bin/lake" --version
"$HOME/.elan/bin/lean" --version
- name: Build Lean proofs
run: |
cd formal_verification
"$HOME/.elan/bin/lake" build XahauConsensus:static
- name: Detect compiler version
id: detect-compiler
run: |
compiler_version=$(clang --version | grep -oE 'version [0-9]+' | grep -oE '[0-9]+')
echo "compiler_version=${compiler_version}" >> "$GITHUB_OUTPUT"
echo "Detected Apple Clang version: ${compiler_version}"
- name: Configure Conan profile
run: |
mkdir -p ~/.conan2/profiles
cat > ~/.conan2/profiles/default <<EOF
[settings]
arch=armv8
build_type=Debug
compiler=apple-clang
compiler.cppstd=20
compiler.libcxx=libc++
compiler.version=${{ steps.detect-compiler.outputs.compiler_version }}
os=Macos
[conf]
tools.build:cxxflags=["-Wno-missing-template-arg-list-after-template-kw"]
EOF
conan profile show
- name: Export custom Conan recipes
run: |
conan export external/snappy --version 1.1.10 --user xahaud --channel stable
conan export external/soci --version 4.0.3 --user xahaud --channel stable
conan export external/wasmedge --version 0.11.2 --user xahaud --channel stable
- name: Install Conan dependencies
env:
CONAN_REQUEST_TIMEOUT: 180
run: |
conan install . \
--output-folder "$BUILD_DIR" \
--build missing \
--settings build_type=Debug \
-o '&:tests=True' \
-o '&:xrpld=True' \
-o '&:formal_verification=True'
- name: Configure formal build
run: |
cmake -S . -B "$CMAKE_BUILD_DIR" -G Ninja \
-DCMAKE_TOOLCHAIN_FILE="$PWD/$BUILD_DIR/build/generators/conan_toolchain.cmake" \
-DCMAKE_BUILD_TYPE=Debug \
-Dtests=ON \
-Dxrpld=ON \
-Dformal_verification=ON
- name: Build formal-enabled rippled
run: |
cmake --build "$CMAKE_BUILD_DIR" --target rippled --parallel "$(nproc)"
- name: Run Lean/C++ drift checks
run: |
"$CMAKE_BUILD_DIR/rippled" --unittest=LeanConsensus --unittest-log

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Check levelization
run: python Builds/levelization/levelization.py
run: Builds/levelization/levelization.sh
- name: Check for differences
id: assert
run: |
@@ -40,7 +40,7 @@ jobs:
To fix it, you can do one of two things:
1. Download and apply the patch generated as an artifact of this
job to your repo, commit, and push.
2. Run 'python Builds/levelization/levelization.py' in your repo,
2. Run './Builds/levelization/levelization.sh' in your repo,
commit, and push.
See Builds/levelization/README.md for more info.

View File

@@ -18,10 +18,6 @@ jobs:
generator: bash ./hook/generate_sfcodes.sh
- target: hook/tts.h
generator: ./hook/generate_tts.sh
- target: hook/ls_flags.h
generator: ./hook/generate_lsflags.sh
- target: hook/tx_flags.h
generator: ./hook/generate_txflags.sh
runs-on: ubuntu-24.04
env:
CLANG_VERSION: 18

View File

@@ -57,9 +57,8 @@ jobs:
"cc": "gcc-11",
"cxx": "g++-11",
"compiler_version": 11,
"stdlib": "default",
"configuration": "Debug",
"job_type": "build"
"stdlib": "libstdcxx",
"configuration": "Debug"
},
{
"compiler_id": "gcc-13-libstdcxx",
@@ -67,20 +66,8 @@ jobs:
"cc": "gcc-13",
"cxx": "g++-13",
"compiler_version": 13,
"stdlib": "default",
"configuration": "Debug",
"job_type": "build"
},
{
"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"
"stdlib": "libstdcxx",
"configuration": "Debug"
},
{
"compiler_id": "clang-14-libstdcxx-gcc11",
@@ -90,8 +77,7 @@ jobs:
"compiler_version": 14,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 11,
"configuration": "Debug",
"job_type": "build"
"configuration": "Debug"
},
{
"compiler_id": "clang-16-libstdcxx-gcc13",
@@ -101,8 +87,7 @@ jobs:
"compiler_version": 16,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 13,
"configuration": "Debug",
"job_type": "build"
"configuration": "Debug"
},
{
"compiler_id": "clang-17-libcxx",
@@ -111,8 +96,7 @@ jobs:
"cxx": "clang++-17",
"compiler_version": 17,
"stdlib": "libcxx",
"configuration": "Debug",
"job_type": "build"
"configuration": "Debug"
},
{
# Clang 18 - testing if it's faster than Clang 17 with libc++
@@ -123,16 +107,14 @@ jobs:
"cxx": "clang++-18",
"compiler_version": 18,
"stdlib": "libcxx",
"configuration": "Debug",
"job_type": "build"
"configuration": "Debug"
}
]
# Minimal matrix for PRs and feature branches
minimal_matrix = [
full_matrix[1], # gcc-13 (middle-ground gcc)
full_matrix[2], # gcc-13 coverage
full_matrix[3] # clang-14 (mature, stable clang)
full_matrix[2] # clang-14 (mature, stable clang)
]
# Determine which matrix to use based on the target branch
@@ -207,21 +189,14 @@ jobs:
# Select the appropriate matrix
if use_full:
if force_full:
print(f"Using FULL matrix (7 configs) - forced by [ci-nix-full-matrix] tag")
print(f"Using FULL matrix (6 configs) - forced by [ci-nix-full-matrix] tag")
else:
print(f"Using FULL matrix (7 configs) - targeting main branch")
print(f"Using FULL matrix (6 configs) - targeting main branch")
matrix = full_matrix
else:
print(f"Using MINIMAL matrix (3 configs) - feature branch/PR")
print(f"Using MINIMAL matrix (2 configs) - feature branch/PR")
matrix = minimal_matrix
# Add runs_on based on job_type
for entry in matrix:
if entry.get("job_type") == "coverage":
entry["runs_on"] = '["self-hosted", "generic", 24.04]'
else:
entry["runs_on"] = '["self-hosted", "generic", 20.04]'
# Output the matrix as JSON
output = json.dumps({"include": matrix})
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
@@ -229,10 +204,7 @@ jobs:
build:
needs: matrix-setup
runs-on: ${{ fromJSON(matrix.runs_on) }}
permissions:
id-token: write
contents: read
runs-on: [self-hosted, generic, 20.04]
container:
image: ubuntu:24.04
volumes:
@@ -261,7 +233,7 @@ jobs:
apt-get install -y software-properties-common
add-apt-repository ppa:ubuntu-toolchain-r/test -y
apt-get update
apt-get install -y git python3 python-is-python3 pipx
apt-get install -y python3 python-is-python3 pipx
pipx ensurepath
apt-get install -y cmake ninja-build ${{ matrix.cc }} ${{ matrix.cxx }} ccache
apt-get install -y perl # for openssl build
@@ -332,12 +304,6 @@ jobs:
pipx install "conan>=2.0,<3"
echo "$HOME/.local/bin" >> $GITHUB_PATH
# Install gcovr for coverage jobs
if [ "${{ matrix.job_type }}" = "coverage" ]; then
pipx install "gcovr>=7,<9"
apt-get install -y curl lcov
fi
- name: Check environment
run: |
echo "PATH:"
@@ -347,13 +313,6 @@ jobs:
which ${{ matrix.cc }} && ${{ matrix.cc }} --version || echo "${{ matrix.cc }} not found"
which ${{ matrix.cxx }} && ${{ matrix.cxx }} --version || echo "${{ matrix.cxx }} not found"
which ccache && ccache --version || echo "ccache not found"
# Check gcovr for coverage jobs
if [ "${{ matrix.job_type }}" = "coverage" ]; then
which gcov && gcov --version || echo "gcov not found"
which gcovr && gcovr --version || echo "gcovr not found"
fi
echo "---- Full Environment ----"
env
@@ -381,7 +340,6 @@ jobs:
gha_cache_enabled: 'false' # Disable caching for self hosted runner
- name: Build
if: matrix.job_type == 'build'
uses: ./.github/actions/xahau-ga-build
with:
generator: Ninja
@@ -396,27 +354,7 @@ jobs:
clang_gcc_toolchain: ${{ matrix.clang_gcc_toolchain || '' }}
ccache_max_size: '100G'
- name: Build (Coverage)
if: matrix.job_type == 'coverage'
uses: ./.github/actions/xahau-ga-build
with:
generator: Ninja
configuration: ${{ matrix.configuration }}
build_dir: ${{ env.build_dir }}
cc: ${{ matrix.cc }}
cxx: ${{ matrix.cxx }}
gcov: ${{ matrix.gcov }}
compiler-id: ${{ matrix.compiler_id }}
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
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'
- name: Set artifact name
if: matrix.job_type == 'build'
id: set-artifact-name
run: |
ARTIFACT_NAME="build-output-nix-${{ github.run_id }}-${{ matrix.compiler }}-${{ matrix.configuration }}"
@@ -429,7 +367,6 @@ jobs:
ls -la ${{ env.build_dir }} || echo "Build directory not found or empty"
- name: Run tests
if: matrix.job_type == 'build'
run: |
# Ensure the binary exists before trying to run
if [ -f "${{ env.build_dir }}/rippled" ]; then
@@ -438,42 +375,3 @@ jobs:
echo "Error: rippled executable not found in ${{ env.build_dir }}"
exit 1
fi
# Coverage-specific steps
- name: Move coverage report
if: matrix.job_type == 'coverage'
shell: bash
run: |
mv "${{ env.build_dir }}/coverage.xml" ./
- name: Archive coverage report
if: matrix.job_type == 'coverage'
uses: actions/upload-artifact@v4
with:
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: coverage.xml
fail_ci_if_error: true
disable_search: true
verbose: true
plugins: noop
use_oidc: true
- name: Export server definitions
if: matrix.job_type == 'build' && matrix.compiler_id == 'gcc-13-libstdcxx'
run: |
${{ env.build_dir }}/rippled --definitions | python3 -m json.tool > server_definitions.json
- name: Upload server definitions
if: matrix.job_type == 'build' && matrix.compiler_id == 'gcc-13-libstdcxx'
uses: actions/upload-artifact@v7
with:
name: server-definitions
path: server_definitions.json
archive: false

10
.gitignore vendored
View File

@@ -53,9 +53,6 @@ Builds/levelization/results/paths.txt
Builds/levelization/results/includes/
Builds/levelization/results/includedby/
# Python
__pycache__
# Ignore tmp directory.
tmp
@@ -127,12 +124,5 @@ bld.rippled/
generated
.vscode
# AI docs (local working documents)
.ai-docs/
# Local formal-methods workspace; kept as a separate repository and optionally
# symlinked here for navigation.
formal/lean/xahau_consensus
# Suggested in-tree build directory
/.build/

4
.testnet/.gitignore vendored
View File

@@ -1,4 +0,0 @@
output/
__pycache__/
scenarios/odd-cases/
scenarios/suite-experiments.yml

View File

@@ -1,29 +0,0 @@
"""Scenario: ConsensusEntropy amendment crashes non-supporting node.
Votes ConsensusEntropy accept on all nodes except n4, then waits for n4
to crash as the amendment activates without its support.
x-testnet run --scenario-script consensus_entropy_crash.py
"""
from helpers import CONSENSUS_ENTROPY_FEATURE
async def scenario(ctx, log):
await ctx.wait_for_ledger_close()
ctx.feature(CONSENSUS_ENTROPY_FEATURE, vetoed=False, exclude_nodes=[4])
log("Waiting for ConsensusEntropy to be voted for...")
await ctx.wait_for_feature(
CONSENSUS_ENTROPY_FEATURE,
check=lambda s: not s.get("vetoed"),
exclude_nodes=[4],
timeout=60,
)
log("Waiting for n4 to crash...")
op = await ctx.wait_for_nodes_down(nodes=[4], timeout=600)
ctx.assert_log("unsupported amendments activated", since=op.started, nodes=[4])
ctx.assert_exit_status(0, nodes=[4])
log("PASS: n4 shut down due to unsupported amendment")

View File

@@ -1,52 +0,0 @@
""":descr: entropy stays valid under transaction load"""
from __future__ import annotations
from helpers import require_entropy, get_entropy_tx, assert_valid_entropy
variants = [
{"label": "light", "min_txns": 5, "max_txns": 10},
{"label": "heavy", "min_txns": 50, "max_txns": 60},
{"label": "super_heavy", "min_txns": 90, "max_txns": 120},
]
async def scenario(ctx, log, *, min_txns=5, max_txns=10, **_):
await require_entropy(ctx, log)
gen = ctx.txn_generator(min_txns=min_txns, max_txns=max_txns)
await gen.start()
await gen.wait_until_ready()
log(f"Transaction generator ready ({min_txns}-{max_txns} txns/ledger)")
# Wait for pipeline warmup + a few txn-bearing ledgers.
await ctx.wait_for_ledgers(3, node_id=0, timeout=60)
start_seq = ctx.validated_ledger_index(0)
await ctx.wait_for_ledgers(10, node_id=0, timeout=120)
end_seq = ctx.validated_ledger_index(0)
log(f"Inspecting ledgers {start_seq + 1}{end_seq}")
digests = set()
total_user_txns = 0
for seq in range(start_seq + 1, end_seq + 1):
ce, user_txns = get_entropy_tx(ctx, seq)
digest, count = assert_valid_entropy(ce, seq, seen_digests=digests)
total_user_txns += len(user_txns)
log(
f" Ledger {seq}: EntropyCount={count} "
f"user_txns={len(user_txns)} Digest={digest[:16]}..."
)
await gen.stop()
log(
f"Verified {end_seq - start_seq} ledgers: {total_user_txns} user txns, "
f"all entropy valid and unique"
)
if total_user_txns == 0:
raise AssertionError("No user transactions were included in any ledger")
log("PASS")

View File

@@ -1,28 +0,0 @@
""":descr: healthy non-standalone testnet without UNLReport mints Tier 1 fallback"""
from __future__ import annotations
from helpers import require_entropy, get_entropy_tx, assert_consensus_fallback
async def scenario(ctx, log):
await require_entropy(ctx, log)
# Non-standalone nodes require a ledger-anchored UNLReport before assigning
# validator_quorum / participant_aligned labels. Without it, the RNG pipeline
# may still collect commits/reveals, but injection must remain Tier 1.
await ctx.wait_for_ledgers(3, node_id=0, timeout=60)
log("Pipeline warmed up without UNLReport")
start_seq = ctx.validated_ledger_index(0)
await ctx.wait_for_ledgers(5, node_id=0, timeout=90)
end_seq = ctx.validated_ledger_index(0)
log(f"Inspecting ledgers {start_seq + 1} -> {end_seq}")
for seq in range(start_seq + 1, end_seq + 1):
ce, _ = get_entropy_tx(ctx, seq)
digest, count = assert_consensus_fallback(ce, seq)
log(f" Ledger {seq}: EntropyCount={count} Digest={digest[:16]}...")
log(f"Verified {end_seq - start_seq} ledgers: all consensus_fallback")
log("PASS")

View File

@@ -1,160 +0,0 @@
""":descr: 5/6 validator_quorum, 4/6 participant_aligned (tier 2), recovery
Requires node_count: 6 (see suite.yml) — the smallest NON-degenerate Tier 2
size. At n=6: tier2 floor = 4, validator quorum = 5, validation quorum = 5. So
6/6, 5/6 present -> validator_quorum (EntropyTier=3)
4/6 present -> participant_aligned (EntropyTier=2, count 4) <-- the band
3/6 present -> consensus_fallback (EntropyTier=1)
n=5 has NO tier-2 band (tier2 == quorum == 4), which is why the existing
degradation smoke at 5 nodes only ever sees tier 3 / fallback.
KEY: the 4/6 window is BELOW the 80% validation quorum (5). The 4 survivors
keep CLOSING ledgers that carry tier-2 entropy, but those ledgers do NOT
validate until the network recovers — exactly the transition window Tier 2
serves. So validated_ledger_index() stalls; we instead inspect a surviving
node's CLOSED ledger (its LCL) directly, and cross-check the injection from the
cohort's logs.
"""
from __future__ import annotations
from helpers import (
require_entropy,
get_entropy_tx,
assert_participant_aligned,
assert_validator_quorum,
)
def _closed_entropy(result):
"""(seq, ConsensusEntropy tx) from a ctx.ledger('closed', transactions=True)
result, or (None, None) if the fetch returned no usable ledger.
Enforces the per-ledger invariant that an entropy-enabled closed ledger
carries EXACTLY ONE ConsensusEntropy pseudo-tx (mirroring get_entropy_tx):
a duplicate or missing injection raises here with a clear error instead of
being silently skipped and resurfacing later as a generic 'no tier-2 ledger'.
"""
if not result or not isinstance(result.get("ledger"), dict):
return None, None
led = result["ledger"]
try:
seq = int(led.get("ledger_index"))
except (TypeError, ValueError):
return None, None
ce = [
t
for t in led.get("transactions", [])
if isinstance(t, dict) and t.get("TransactionType") == "ConsensusEntropy"
]
if len(ce) != 1:
raise AssertionError(
f"Closed ledger {seq}: expected 1 ConsensusEntropy txn, got {len(ce)}"
)
return seq, ce[0]
async def scenario(ctx, log):
await require_entropy(ctx, log)
# Baseline: healthy 6/6 produces validator_quorum entropy.
await ctx.wait_for_ledgers(1, node_id=0, timeout=30)
# --- 5/6: settles back to validator_quorum (5 present >= quorum 5) ---
val_before_drop = ctx.validated_ledger_index(0)
ctx.stop_node(5)
await ctx.wait_for_nodes_down(nodes=[5], timeout=30)
# Settle a few ledgers past the membership change. The ledger right at a
# validator drop can carry a transient consensus_fallback (tier 1, count 0,
# deterministic and by design) before the commit/reveal pipeline re-primes,
# so we do NOT assume any single post-drop ledger is already tier 3.
await ctx.wait_for_ledgers(4, node_id=0, timeout=90)
# 5/6 is at/above the 80% quorum (5), so steady state is validator_quorum.
# Scan the post-drop validated ledgers (all carry the 5-node cohort, so a
# tier-3 here has count == 5) and require at least one clean validator_quorum
# — EntropyTier=3, count >= quorum, non-zero digest — tolerating the
# transition fallback instead of depending on where the tip happened to land.
val_5of6 = ctx.validated_ledger_index(0)
t3_seq = None
for seq in range(val_5of6, val_before_drop, -1):
ce, _ = get_entropy_tx(ctx, seq)
tier = ce.get("EntropyTier")
log(f" 5/6 ledger {seq}: tier={tier} count={ce.get('EntropyCount')}")
if tier == 3:
assert_validator_quorum(ce, seq, min_count=5)
t3_seq = seq
break
if t3_seq is None:
raise AssertionError(
f"5/6: no validator_quorum (tier 3) entropy in post-drop validated "
f"ledgers {val_before_drop + 1}..{val_5of6}"
)
log(f"5/6: validator_quorum at validated seq {t3_seq}")
#@@start test-participant-aligned-window
# --- 4/6: participant_aligned (Tier 2) degraded window ---
ctx.stop_node(4)
await ctx.wait_for_nodes_down(nodes=[4], timeout=30)
# ~12s window: confirm tier-2 INJECTION from the cohort's logs, and that the
# round is NOT the impossible/fallback path (which is what distinguishes the
# tier-2 band from the tier-1 fallback regime).
op = await ctx.sleep(12, name="tier2_window")
selected_t2 = ctx.search_logs(
r"RNG: entropy selected seq=\d+ tier=2 count=4",
within=op.window,
nodes=[0, 1, 2, 3],
)
log(f"4/6: 'entropy selected tier=2 count=4' logs: {selected_t2.count}")
if selected_t2.count == 0:
raise AssertionError(
"4/6 window injected no participant_aligned (tier 2) entropy: no "
"'RNG: entropy selected ... tier=2 count=4' on the surviving cohort"
)
ctx.assert_not_log(
r"reason=impossible-entropy-gate", within=op.window, nodes=[0, 1, 2, 3]
)
# Verify the on-ledger EntropyTier=2 DIRECTLY: validation is stalled (4 < 5),
# so sample the surviving cohort's CLOSED ledger (its LCL — built but not yet
# validated). At least one must be participant_aligned with EntropyCount=4.
tier2_on_ledger = 0
last_seq = None
for _ in range(5):
seq, ce = _closed_entropy(
ctx.ledger("closed", transactions=True, node_id=0)
)
if ce is not None and seq is not None and seq != last_seq:
last_seq = seq
tier = ce.get("EntropyTier")
count = ce.get("EntropyCount", -1)
log(f" closed ledger {seq}: tier={tier} count={count}")
if tier == 2:
assert_participant_aligned(ce, seq, expected_count=4)
tier2_on_ledger += 1
await ctx.sleep(3)
if tier2_on_ledger == 0:
raise AssertionError(
"no closed participant_aligned (tier 2) ledger observed during the "
"4/6 window (tier 2 was injected per logs, but not seen on a closed "
"ledger)"
)
log(f"4/6: {tier2_on_ledger} participant_aligned closed ledger(s) verified")
#@@end test-participant-aligned-window
# --- Recovery: liveness — validation resumes once quorum is restored ---
ctx.start_node(4)
ctx.start_node(5)
await ctx.wait_for_ledgers(1, node_id=0, timeout=120)
val_recovered = ctx.validated_ledger_index(0)
if not val_recovered or val_recovered <= val_5of6:
raise AssertionError(
f"Validated ledger did not advance after recovery "
f"({val_5of6} -> {val_recovered})"
)
log(f"Recovered: validated seq {val_5of6} -> {val_recovered}")
log("PASS")

View File

@@ -1,148 +0,0 @@
""":descr: 4/5 liveness, 3/5 fallback-entropy (consensus_fallback), recovery"""
from __future__ import annotations
from helpers import ZERO_DIGEST, require_entropy, get_entropy_tx, entropy_fields
async def scenario(ctx, log):
await require_entropy(ctx, log)
# Baseline: wait 1 ledger to confirm network is healthy.
await ctx.wait_for_ledgers(1, node_id=0, timeout=30)
# --- 4/5 liveness ---
ctx.stop_node(4)
await ctx.wait_for_nodes_down(nodes=[4], timeout=30)
await ctx.wait_for_ledgers(1, node_id=0, timeout=30)
log("4/5: liveness OK")
# Snapshot validated seq before dropping to 3/5.
val_before = ctx.validated_ledger_index(0)
# --- 3/5 degraded window ---
ctx.stop_node(3)
await ctx.wait_for_nodes_down(nodes=[3], timeout=30)
# 10s ≈ 3 rounds at 3s cadence.
await ctx.sleep(10)
val_after = ctx.validated_ledger_index(0)
log(f"3/5: validated ledger {val_before}{val_after}")
# Accepted/built ledgers may still later appear as validated once the full
# network rejoins. For ConsensusEntropy the key invariant is that every
# ledger created during this sub-quorum window carries FALLBACK entropy
# (consensus_fallback: non-zero consensus-bound digest, count 0) — never
# validator-tier entropy.
degraded_fallback = 0
degraded_end = val_after or val_before
if val_before and degraded_end and degraded_end > val_before:
for seq in range(val_before + 1, degraded_end + 1):
ce, _ = get_entropy_tx(ctx, seq)
digest, entropy_count, is_fallback = entropy_fields(ce)
tier = ce.get("EntropyTier")
# consensus_fallback (EntropyTier=1): explicit tier, count 0,
# deterministic NON-zero digest.
if tier != 1:
raise AssertionError(
f"Ledger {seq}: expected EntropyTier==1 "
f"(consensus_fallback) during 3/5 window, got {tier} "
f"(EntropyCount={entropy_count})"
)
if entropy_count != 0:
raise AssertionError(
f"Ledger {seq}: fallback EntropyCount must be 0, got "
f"{entropy_count}"
)
if not digest or digest == ZERO_DIGEST:
raise AssertionError(
f"Ledger {seq}: fallback digest must be non-zero "
f"(consensus_fallback), got {digest[:16]}..."
)
assert is_fallback # tier==1 implies fallback
degraded_fallback += 1
log(
f" Degraded ledger {seq}: EntropyCount={entropy_count} "
f"FALLBACK"
)
log(f"3/5 entropy summary: {degraded_fallback} fallback")
# Log checks tied to current transition mechanics:
# - commit-set SHAMap publication is the observable output of entering the
# commit sidecar phase
# - ConvergingCommit transition is the gateway out of seq=0-only behavior
# - reason=impossible-entropy-gate is the explicit degraded-window fallback path
ctx.log_level("LedgerConsensus", "trace")
ctx.log_level("ConsensusExtensions", "trace")
op = await ctx.sleep(6, name="stall_window")
ctx.assert_not_log(
r"RNG: transitioned to ConvergingCommit", within=op.window, nodes=[0, 1, 2]
)
ctx.assert_not_log(
r"RNG: built commitSet SHAMap", within=op.window, nodes=[0, 1, 2]
)
gate_blocked = ctx.search_logs(
r"STALLDIAG: establish gate blocked reason=(pause|no-tx-consensus)",
within=op.window,
nodes=[0, 1, 2],
)
log(f"3/5: establish gate-blocked logs in 6s: {gate_blocked.count}")
impossible = ctx.search_logs(
r"RNG: skipping commit wait reason=impossible-entropy-gate",
within=op.window,
nodes=[0, 1, 2],
)
log(f"3/5: RNG impossible-entropy-gate skips in 6s: {impossible.count}")
# --- Recovery: restart nodes, verify ledger advancement ---
ctx.start_node(3)
ctx.start_node(4)
await ctx.wait_for_ledgers(1, node_id=0, timeout=120)
val_recovered = ctx.validated_ledger_index(0)
pre_recovery = max(v for v in [val_before, val_after] if v is not None)
log(f"Recovered: validated seq {pre_recovery}{val_recovered}")
if not val_recovered or val_recovered <= pre_recovery:
raise AssertionError(
f"Validated ledger did not advance after recovery "
f"({pre_recovery}{val_recovered})"
)
# Inspect post-recovery ledgers separately from the degraded window above.
# Once the network is back at quorum, validator-tier entropy is expected
# again (transitional fallback ledgers are fine) and must be quorum-met.
fallback_count = 0
validator_count = 0
for seq in range(pre_recovery + 1, val_recovered + 1):
ce, _ = get_entropy_tx(ctx, seq)
digest, entropy_count, is_fallback = entropy_fields(ce)
if is_fallback:
fallback_count += 1
else:
validator_count += 1
if entropy_count < 4:
raise AssertionError(
f"Ledger {seq}: validator entropy with sub-quorum "
f"EntropyCount={entropy_count} (need >= 4)"
)
log(
f" Ledger {seq}: EntropyCount={entropy_count} "
f"{'FALLBACK' if is_fallback else 'VALIDATOR'}"
)
log(
f"Entropy summary: {fallback_count} fallback, "
f"{validator_count} validator"
)
log("PASS")

View File

@@ -1,44 +0,0 @@
""":descr: drop 2 nodes (3/5 stall), restart both, verify recovery"""
from __future__ import annotations
from helpers import require_entropy
async def scenario(ctx, log):
await require_entropy(ctx, log)
await ctx.wait_for_ledgers(1, node_id=0, timeout=60)
log("Baseline OK")
# Drop 2 nodes → validation stall.
ctx.stop_node(3)
ctx.stop_node(4)
await ctx.wait_for_nodes_down(nodes=[3, 4], timeout=30)
info = ctx.rpc.server_info(node_id=0)
val_before = info.get("info", {}).get("validated_ledger", {}).get("seq", 0)
log(f"Stalled at validated seq {val_before}")
# Let it sit for a few rounds in degraded state.
await ctx.sleep(6)
# Bring both nodes back.
ctx.start_node(3)
ctx.start_node(4)
log("Restarted n3 and n4, waiting for recovery...")
# Recovery: wait for ANY validated ledger advance on n0.
await ctx.wait_for_ledger_close(node_id=0, timeout=60)
info = ctx.rpc.server_info(node_id=0)
val_after = info.get("info", {}).get("validated_ledger", {}).get("seq", 0)
log(f"Recovered: validated seq {val_before}{val_after}")
if val_after <= val_before:
raise AssertionError(
f"Validated ledger did not advance after recovery "
f"({val_before}{val_after})"
)
log("PASS")

View File

@@ -1,27 +0,0 @@
""":descr: all 5 nodes healthy, every ledger has valid unique quorum-met entropy"""
from __future__ import annotations
from helpers import require_entropy, get_entropy_tx, assert_valid_entropy
async def scenario(ctx, log):
await require_entropy(ctx, log)
# Wait for RNG pipeline to warm up past bootstrap skip.
await ctx.wait_for_ledgers(3, node_id=0, timeout=60)
log("Pipeline warmed up")
start_seq = ctx.validated_ledger_index(0)
await ctx.wait_for_ledgers(10, node_id=0, timeout=120)
end_seq = ctx.validated_ledger_index(0)
log(f"Inspecting ledgers {start_seq + 1}{end_seq}")
digests = set()
for seq in range(start_seq + 1, end_seq + 1):
ce, _ = get_entropy_tx(ctx, seq)
digest, count = assert_valid_entropy(ce, seq, seen_digests=digests)
log(f" Ledger {seq}: EntropyCount={count} Digest={digest[:16]}...")
log(f"Verified {end_seq - start_seq} ledgers: all quorum entropy, all unique")
log("PASS")

View File

@@ -1,104 +0,0 @@
defaults:
network:
node_count: 5
launcher: tmux
find_ports: true
slave_delay: 0.2
features:
- ConsensusEntropy
- Export
track_features:
- ConsensusEntropy
- Export
unl_report: true
log_levels:
TxQ: info
Protocol: debug
Peer: debug
LedgerConsensus: debug
ConsensusExtensions: debug
NetworkOPs: info
env:
XAHAU_RESOURCE_PER_PORT: "1"
rc:
- rng_poll_ms=333
tests:
# --- CE + Export (80% quorum, SHAMap convergence) ---
- name: steady_state_export_ce
script: .testnet/scenarios/export/steady_state_export.py
- name: retriable_export_ce
script: .testnet/scenarios/export/retriable_export.py
- name: export_degradation_ce
script: .testnet/scenarios/export/export_degradation.py
network:
rc:
- rng_poll_ms=333
- n3:no_export_sig=true
- n4:no_export_sig=true
- name: export_without_unl_report
script: .testnet/scenarios/export/export_without_unl_report.py
network:
features:
- Export
track_features:
- Export
unl_report: false
- name: export_no_veto_missing_observation
script: .testnet/scenarios/export/export_no_veto_missing_observation.py
network:
rc:
- rng_poll_ms=333
- n4:no_export_sig_hash=true
# CE + Export: 1 node suppressed, 4/5 = 80% quorum, should succeed
- name: export_ce_one_node_down
script: .testnet/scenarios/export/export_quorum.py
params:
expect_success: true
network:
rc:
- rng_poll_ms=333
- n4:no_export_sig=true
# --- Export only, no CE (80% active-view quorum) ---
- name: export_only_all_up
script: .testnet/scenarios/export/export_quorum.py
params:
expect_success: true
network:
features:
- Export
track_features:
- Export
- name: export_only_one_node_down
script: .testnet/scenarios/export/export_quorum.py
params:
expect_success: true
network:
features:
- Export
track_features:
- Export
rc:
- rng_poll_ms=333
- n4:no_export_sig=true
- name: export_only_two_nodes_down
script: .testnet/scenarios/export/export_quorum.py
params:
expect_success: false
network:
features:
- Export
track_features:
- Export
rc:
- rng_poll_ms=333
- n3:no_export_sig=true
- n4:no_export_sig=true

View File

@@ -1,123 +0,0 @@
""":descr: Submit ttEXPORT with 2 nodes suppressing export sigs, verify it
retries via terRETRY_EXPORT until LLS expiry (insufficient signatures).
Nodes 3 and 4 have runtime_config no_export_sig=true, so only 3/5 nodes
provide export signatures. With 80% quorum = ceil(5*0.8) = 4 required,
the export cannot reach quorum and should expire via tecEXPORT_EXPIRED.
Flow:
1. Fund alice and bob
2. alice submits ttEXPORT with tight LLS
3. Export retries (only 3/5 sigs available, need 4)
4. Verify export expires with tecEXPORT_EXPIRED
5. Verify subsequent payment still works (sequence not permanently blocked)
"""
from __future__ import annotations
from export_helpers import require_export, assert_shadow_ticket
async def scenario(ctx, log):
await require_export(ctx, log)
# --- Setup ---
await ctx.fund_accounts({"alice": 10000, "bob": 1000})
log("Accounts funded")
alice = ctx.account("alice")
bob = ctx.account("bob")
current_seq = ctx.validated_ledger_index(0)
log(f"Current ledger: {current_seq}")
log("Nodes 3,4 have runtime_config no_export_sig=true (3/5 sigs, need 4)")
#@@start test-export-below-quorum-expiry
# --- Submit ttEXPORT (should retry then expire -- only 3/5 sigs) ---
export_start = ctx.mark("export-degradation-submit-start")
result = await ctx.submit_and_wait(
{
"TransactionType": "Export",
"LastLedgerSequence": current_seq + 8,
"Fee": "1000000",
"ExportedTxn": {
"TransactionType": "Payment",
"Account": alice.address,
"Destination": bob.address,
"Amount": "1000000",
"Fee": "10",
"Sequence": 0,
"TicketSequence": 1,
"FirstLedgerSequence": current_seq + 1,
"LastLedgerSequence": current_seq + 6,
"Flags": 2147483648,
"SigningPubKey": "",
},
},
alice.wallet,
timeout=60,
)
export_end = ctx.mark("export-degradation-submit-end")
final_seq = ctx.validated_ledger_index(0)
engine_result = result.get("engine_result", "")
log(f"Export completed at ledger {final_seq}, result: {engine_result}")
# With only 3/5 sigs and 80% quorum (4 required), export MUST fail
if engine_result == "tesSUCCESS":
raise AssertionError(
"Export should NOT have succeeded with only 3/5 sigs "
"(need 4 for 80% quorum) -- check runtime_config no_export_sig"
)
# Should be tecEXPORT_EXPIRED (LLS reached without quorum). Be exact here:
# any other non-success means the retry/expiry boundary regressed.
if engine_result != "tecEXPORT_EXPIRED":
raise AssertionError(
f"Expected tecEXPORT_EXPIRED below quorum, got {engine_result}"
)
log(f"Export failed as expected ({engine_result})")
retry_logs = ctx.assert_log(
r"Export: insufficient signatures .*result=terRETRY_EXPORT",
since=export_start,
until=export_end,
)
log(f"Export insufficient-signature retries: {retry_logs.count}")
expired_logs = ctx.assert_log(
r"Export: last ledger expired .*result=tecEXPORT_EXPIRED",
since=export_start,
until=export_end,
)
log(f"Export LLS expiry logs: {expired_logs.count}")
# No shadow ticket should exist (export never reached quorum)
assert_shadow_ticket(ctx, alice.address, log, expect_exists=False)
#@@end test-export-below-quorum-expiry
# --- Verify subsequent payment works regardless ---
log("Submitting payment from alice to bob...")
pay_result = await ctx.submit_and_wait(
{
"TransactionType": "Payment",
"Destination": bob.address,
"Amount": "1000000",
"Fee": "12",
},
alice.wallet,
timeout=30,
)
pay_engine = pay_result.get("engine_result", "")
log(f"Payment result: {pay_engine}")
if pay_engine != "tesSUCCESS":
raise AssertionError(
f"Payment failed after expired export: {pay_engine} "
f"-- sequence may be blocked"
)
log("Payment succeeded -- account not permanently blocked")
log("PASS")

View File

@@ -1,181 +0,0 @@
"""Shared helpers for Export scenario tests."""
from __future__ import annotations
from xahaud_scripts.testnet.config import _unl_report_index, feature_name_to_hash
async def require_export(
ctx, log, *, require_unl_report=True, require_runtime_config=True
):
"""Wait for first ledger and assert Export is enabled.
Network-mode Export success requires a parent-ledger UNLReport-backed
active validator view. Most export scenarios seed that report in genesis;
assert it here so a success-path test cannot accidentally pass setup
without the condition Export::doApply requires. The no-UNLReport retry
scenario opts out deliberately.
The tracked export suite also uses XAHAUD_RUNTIME_TEST_CONFIG for polling
and fault-injection knobs. Default binaries reject the runtime_config RPC,
so check it up front rather than silently running without those knobs.
"""
await ctx.wait_for_ledger_close(timeout=120)
if require_runtime_config:
result = ctx.rpc.runtime_config(0)
if not result or result.get("error"):
raise AssertionError(
"Export suite requires a binary built with "
"xahaud_runtime_test_config=ON; runtime_config RPC returned "
f"{result}"
)
log("RuntimeConfig RPC active")
feature = ctx.feature_check(feature_name_to_hash("Export"), node_id=0)
if not feature or not feature.get("enabled", False):
raise AssertionError(f"Export not enabled: {feature}")
log("Export enabled")
if require_unl_report:
result = ctx.rpc.ledger_entry(0, _unl_report_index())
node = (result or {}).get("node", {})
active = node.get("ActiveValidators", [])
if node.get("LedgerEntryType") != "UNLReport" or not active:
raise AssertionError(
"Export success scenario requires a ledger UNLReport with "
f"ActiveValidators, got: {result}"
)
log(f"UNLReport active validators: {len(active)}")
def find_export_txns(ctx, seq):
"""Find Export transactions in a ledger.
Returns list of Export transaction dicts.
"""
result = ctx.ledger(seq, transactions=True)
if not result:
return []
txns = result.get("ledger", {}).get("transactions", [])
return [tx for tx in txns if tx.get("TransactionType") == "Export"]
def dst_param(address):
"""Encode an address as a HookParameter entry for the DST param."""
from xrpl.core.addresscodec import decode_classic_address
dst_hex = decode_classic_address(address).hex().upper()
return {
"HookParameter": {
"HookParameterName": "445354", # "DST"
"HookParameterValue": dst_hex,
}
}
def assert_hook_accepted(meta, log, *, expected_emits=1):
"""Assert hook executed with ACCEPT and the expected emit count.
Checks sfHookExecutions in transaction metadata.
Returns the hook execution entry for further inspection.
"""
hook_execs = meta.get("HookExecutions", [])
if not hook_execs:
raise AssertionError("No HookExecutions in metadata")
exec_entry = hook_execs[0].get("HookExecution", {})
hook_result = exec_entry.get("HookResult", -1)
emit_count = exec_entry.get("HookEmitCount", -1)
return_code = exec_entry.get("HookReturnCode", "")
log(f" HookResult={hook_result} EmitCount={emit_count} ReturnCode={return_code}")
# HookResult 3 = ExitType::ACCEPT
if hook_result != 3:
raise AssertionError(
f"Hook did not ACCEPT: HookResult={hook_result} "
f"ReturnCode={return_code}"
)
if emit_count != expected_emits:
raise AssertionError(
f"Expected {expected_emits} emits, got {emit_count}"
)
# ReturnCode 0 = success; non-zero = ASSERT line number in hook
if return_code and str(return_code) != "0":
raise AssertionError(
f"Hook returned error code {return_code} "
f"(likely ASSERT failure at that line)"
)
return exec_entry
def assert_export_result(meta, log, *, require_signers=True):
"""Assert ExportResult is present and well-formed in metadata.
Returns the ExportResult dict.
"""
export_result = meta.get("ExportResult", {})
if not export_result:
raise AssertionError("ExportResult not found in metadata")
# Must have LedgerSequence and TransactionHash
if "LedgerSequence" not in export_result:
raise AssertionError("ExportResult missing LedgerSequence")
if "TransactionHash" not in export_result:
raise AssertionError("ExportResult missing TransactionHash")
# Must have the inner ExportedTxn object
inner = export_result.get("ExportedTxn", {})
if not inner:
raise AssertionError("ExportResult missing ExportedTxn (multisigned blob)")
log(f" ExportResult: seq={export_result['LedgerSequence']} "
f"hash={export_result['TransactionHash'][:16]}...")
# Inner tx should have Account, Destination, TransactionType
if "Account" not in inner:
raise AssertionError("ExportedTxn missing Account")
if "TransactionType" not in inner:
raise AssertionError("ExportedTxn missing TransactionType")
# Should have empty SigningPubKey (multisigned)
if inner.get("SigningPubKey", "NOT_EMPTY") != "":
raise AssertionError(
f"ExportedTxn SigningPubKey should be empty, "
f"got '{inner.get('SigningPubKey')}'"
)
if require_signers:
signers = inner.get("Signers", [])
if not signers:
raise AssertionError("ExportedTxn has no Signers (multisig not applied)")
log(f" Signers: {len(signers)} validator(s)")
return export_result
def assert_shadow_ticket(ctx, account_address, log, *, expect_exists=True):
"""Assert shadow ticket exists (or doesn't) for the account."""
obj_result = ctx.rpc.request(
0, "account_objects", {"account": account_address}
)
all_objects = (obj_result or {}).get("account_objects", [])
shadow_tickets = [
obj for obj in all_objects
if obj.get("LedgerEntryType") == "ShadowTicket"
]
log(f" Shadow tickets: {len(shadow_tickets)}")
if expect_exists and not shadow_tickets:
raise AssertionError("Expected shadow ticket but none found")
if not expect_exists and shadow_tickets:
raise AssertionError(
f"Expected no shadow tickets but found {len(shadow_tickets)}"
)
return shadow_tickets

View File

@@ -1,87 +0,0 @@
""":descr: Export succeeds when quorum sidecar material exists but one active
validator withholds exportSigSetHash observation.
Node 4 has runtime_config no_export_sig_hash=true. It still attaches export
signatures, but it does not publish its exportSigSetHash in proposals. The
remaining 4/5 active validators can still align on the same export sidecar
hash, so the round must not retry/expire just because fullObservation is false.
"""
from __future__ import annotations
from export_helpers import (
require_export,
assert_export_result,
assert_shadow_ticket,
)
async def scenario(ctx, log):
await require_export(ctx, log)
await ctx.fund_accounts({"alice": 10000, "bob": 1000})
log("Accounts funded")
alice = ctx.account("alice")
bob = ctx.account("bob")
current_seq = ctx.validated_ledger_index(0)
log(f"Current ledger: {current_seq}")
log("Node 4 withholds exportSigSetHash but still attaches export signatures")
export_start = ctx.mark("export-no-veto-submit-start")
result = await ctx.submit_and_wait(
{
"TransactionType": "Export",
"LastLedgerSequence": current_seq + 10,
"Fee": "1000000",
"ExportedTxn": {
"TransactionType": "Payment",
"Account": alice.address,
"Destination": bob.address,
"Amount": "1000000",
"Fee": "10",
"Sequence": 0,
"TicketSequence": 1,
"FirstLedgerSequence": current_seq + 1,
"LastLedgerSequence": current_seq + 8,
"Flags": 2147483648,
"SigningPubKey": "",
},
},
alice.wallet,
timeout=60,
)
export_end = ctx.mark("export-no-veto-submit-end")
final_seq = ctx.validated_ledger_index(0)
engine_result = result.get("engine_result", "")
meta = result.get("meta", {})
log(f"Export completed at ledger {final_seq}, result: {engine_result}")
if engine_result != "tesSUCCESS":
raise AssertionError(f"Expected tesSUCCESS, got {engine_result}")
export_result = assert_export_result(meta, log, require_signers=True)
signers = export_result.get("ExportedTxn", {}).get("Signers", [])
if len(signers) < 4:
raise AssertionError(f"Expected at least 4 signers, got {len(signers)}")
log(f"Export signer count: {len(signers)}")
no_veto_logs = ctx.assert_log(
r"Export: missing exportSigSetHash observation ignored",
since=export_start,
until=export_end,
)
log(f"Export no-veto missing-observation logs: {no_veto_logs.count}")
withhold_logs = ctx.assert_log(
r"Export: withholding exportSigSetHash",
since=export_start,
until=export_end,
)
log(f"Export sidecar hash withholding logs: {withhold_logs.count}")
assert_shadow_ticket(ctx, alice.address, log, expect_exists=True)
log("PASS")

View File

@@ -1,117 +0,0 @@
""":descr: Test Export quorum behavior. When enough active validators sign,
the export should succeed whether or not CE is enabled. When fewer than the
active-view quorum sign, the export should expire.
Parameterized via `expect_success` kwarg from suite.yml.
Flow:
1. Fund alice and bob
2. alice submits ttEXPORT
3. Verify result matches expectation (tesSUCCESS or tecEXPORT_EXPIRED)
4. Verify ExportResult + shadow ticket on success, absence on failure
5. Verify subsequent payment works regardless
"""
from __future__ import annotations
from export_helpers import (
require_export,
assert_export_result,
assert_shadow_ticket,
)
async def scenario(ctx, log, expect_success=True):
await require_export(ctx, log)
# --- Setup ---
await ctx.fund_accounts({"alice": 10000, "bob": 1000})
log("Accounts funded")
alice = ctx.account("alice")
bob = ctx.account("bob")
current_seq = ctx.validated_ledger_index(0)
log(f"Current ledger: {current_seq}")
outcome = "success" if expect_success else "failure (below quorum)"
log(f"Expecting export {outcome}")
# --- Submit ttEXPORT ---
result = await ctx.submit_and_wait(
{
"TransactionType": "Export",
"LastLedgerSequence": current_seq + 10,
"Fee": "1000000",
"ExportedTxn": {
"TransactionType": "Payment",
"Account": alice.address,
"Destination": bob.address,
"Amount": "1000000",
"Fee": "10",
"Sequence": 0,
"TicketSequence": 1,
"FirstLedgerSequence": current_seq + 1,
"LastLedgerSequence": current_seq + 8,
"Flags": 2147483648,
"SigningPubKey": "",
},
},
alice.wallet,
timeout=60,
)
final_seq = ctx.validated_ledger_index(0)
engine_result = result.get("engine_result", "")
meta = result.get("meta", {})
log(f"Export at ledger {final_seq}, result: {engine_result}")
if expect_success:
if engine_result != "tesSUCCESS":
raise AssertionError(
f"Expected tesSUCCESS, got {engine_result}"
)
# Assert ExportResult is well-formed with signers
assert_export_result(meta, log, require_signers=True)
# Assert shadow ticket was created
assert_shadow_ticket(ctx, alice.address, log, expect_exists=True)
log("Export succeeded as expected (active-view quorum reached)")
else:
if engine_result == "tesSUCCESS":
raise AssertionError(
"Export should NOT have succeeded below active-view quorum"
)
if engine_result != "tecEXPORT_EXPIRED":
raise AssertionError(
"Expected tecEXPORT_EXPIRED below active-view quorum, "
f"got {engine_result}"
)
log(f"Export failed as expected ({engine_result})")
# No shadow ticket should exist
assert_shadow_ticket(ctx, alice.address, log, expect_exists=False)
# --- Verify subsequent payment works ---
log("Submitting payment from alice to bob...")
pay_result = await ctx.submit_and_wait(
{
"TransactionType": "Payment",
"Destination": bob.address,
"Amount": "1000000",
"Fee": "12",
},
alice.wallet,
timeout=30,
)
pay_engine = pay_result.get("engine_result", "")
log(f"Payment result: {pay_engine}")
if pay_engine != "tesSUCCESS":
raise AssertionError(f"Payment failed: {pay_engine}")
log("Payment succeeded -- account not blocked")
log("PASS")

View File

@@ -1,92 +0,0 @@
""":descr: Export retries/expires without a ledger-anchored UNLReport view.
All validators may sign, but network-mode Export must not assemble quorum
material from a node-local trusted-config view. Without UNLReport, the export
should retry until LastLedgerSequence and expire without creating a shadow
ticket.
"""
from __future__ import annotations
from export_helpers import require_export, assert_shadow_ticket
async def scenario(ctx, log):
await require_export(ctx, log, require_unl_report=False)
await ctx.fund_accounts({"alice": 10000, "bob": 1000})
log("Accounts funded")
alice = ctx.account("alice")
bob = ctx.account("bob")
current_seq = ctx.validated_ledger_index(0)
log(f"Current ledger: {current_seq}")
log("UNLReport intentionally absent; export must not use local config view")
export_start = ctx.mark("export-without-unlreport-submit-start")
result = await ctx.submit_and_wait(
{
"TransactionType": "Export",
"LastLedgerSequence": current_seq + 8,
"Fee": "1000000",
"ExportedTxn": {
"TransactionType": "Payment",
"Account": alice.address,
"Destination": bob.address,
"Amount": "1000000",
"Fee": "10",
"Sequence": 0,
"TicketSequence": 1,
"FirstLedgerSequence": current_seq + 1,
"LastLedgerSequence": current_seq + 6,
"Flags": 2147483648,
"SigningPubKey": "",
},
},
alice.wallet,
timeout=60,
)
export_end = ctx.mark("export-without-unlreport-submit-end")
final_seq = ctx.validated_ledger_index(0)
engine_result = result.get("engine_result", "")
log(f"Export completed at ledger {final_seq}, result: {engine_result}")
if engine_result == "tesSUCCESS":
raise AssertionError(
"Export should not succeed without a ledger-anchored UNLReport view"
)
# Be exact: without a UNLReport view the export should retry until LLS and
# expire, not fail by some unrelated terminal code.
if engine_result != "tecEXPORT_EXPIRED":
raise AssertionError(
"Expected tecEXPORT_EXPIRED without UNLReport view, "
f"got {engine_result}"
)
warning_logs = ctx.assert_log(
r"Export: retrying without ledger-anchored validator view",
since=export_start,
until=export_end,
)
log(f"Export no-UNLReport retry warnings: {warning_logs.count}")
retry_logs = ctx.assert_log(
r"Export: insufficient signatures .*result=terRETRY_EXPORT",
since=export_start,
until=export_end,
)
log(f"Export retry logs: {retry_logs.count}")
expired_logs = ctx.assert_log(
r"Export: last ledger expired .*result=tecEXPORT_EXPIRED",
since=export_start,
until=export_end,
)
log(f"Export expiry logs: {expired_logs.count}")
assert_shadow_ticket(ctx, alice.address, log, expect_exists=False)
log("PASS")

View File

@@ -1,94 +0,0 @@
""":descr: Submit ttEXPORT directly (no hook), verify it succeeds with
ExportResult in metadata. Then submit a payment from the same account
to verify sequence handling doesn't block subsequent transactions.
Flow:
1. Fund alice and bob
2. alice submits ttEXPORT with inner payment -> tesSUCCESS (provisional)
3. Validators attach sigs via proposals -> quorum -> ExportResult in metadata
4. alice submits a Payment to bob -> should succeed (sequence not blocked)
"""
from __future__ import annotations
from export_helpers import require_export, assert_export_result, assert_shadow_ticket
async def scenario(ctx, log):
await require_export(ctx, log)
# --- Setup ---
await ctx.fund_accounts({"alice": 10000, "bob": 1000})
log("Accounts funded")
alice = ctx.account("alice")
bob = ctx.account("bob")
current_seq = ctx.validated_ledger_index(0)
log(f"Current ledger: {current_seq}")
# --- 1. Submit ttEXPORT ---
result = await ctx.submit_and_wait(
{
"TransactionType": "Export",
"LastLedgerSequence": current_seq + 15,
"Fee": "1000000",
"ExportedTxn": {
"TransactionType": "Payment",
"Account": alice.address,
"Destination": bob.address,
"Amount": "1000000",
"Fee": "10",
"Sequence": 0,
"TicketSequence": 1,
"FirstLedgerSequence": current_seq + 1,
"LastLedgerSequence": current_seq + 10,
"Flags": 2147483648,
"SigningPubKey": "",
},
},
alice.wallet,
timeout=60,
)
export_seq = ctx.validated_ledger_index(0)
engine_result = result.get("engine_result", "")
log(f"Export completed at ledger {export_seq}, result: {engine_result}")
if engine_result != "tesSUCCESS":
raise AssertionError(
f"Expected tesSUCCESS for export, got {engine_result}"
)
# Assert ExportResult is well-formed with signers
meta = result.get("meta", {})
assert_export_result(meta, log, require_signers=True)
# Assert shadow ticket was created
assert_shadow_ticket(ctx, alice.address, log, expect_exists=True)
# --- 2. Submit Payment from same account ---
log("Submitting payment from alice to bob...")
pay_result = await ctx.submit_and_wait(
{
"TransactionType": "Payment",
"Destination": bob.address,
"Amount": "1000000",
"Fee": "12",
},
alice.wallet,
timeout=30,
)
pay_engine = pay_result.get("engine_result", "")
log(f"Payment result: {pay_engine}")
if pay_engine != "tesSUCCESS":
raise AssertionError(f"Payment failed: {pay_engine}")
log(
f"Both transactions succeeded: "
f"Export at ledger {export_seq}, Payment at ledger {ctx.validated_ledger_index(0)}"
)
log("Sequence handling OK - export didn't block subsequent txns")
log("PASS")

View File

@@ -1,211 +0,0 @@
""":descr: install xport hook, trigger export, verify emitted ttEXPORT lifecycle
1. Fund alice (hook holder), bob (trigger), carol (export destination)
2. Install xport hook on alice
3. bob pays alice with DST=carol → hook calls xport() → emits ttEXPORT
4. Emitted ttEXPORT enters open ledger, validators attach sigs via proposals
5. Verify Export transaction appears in a subsequent ledger
"""
from __future__ import annotations
from export_helpers import (
require_export,
find_export_txns,
dst_param,
assert_hook_accepted,
assert_export_result,
assert_shadow_ticket,
)
# C source for the xport hook — verbatim from src/test/app/Export_test_hooks.h
# On Payment to the hook account, exports a 1 XAH payment to the DST param.
XPORT_HOOK_C = r"""
#include <stdint.h>
extern int32_t _g(uint32_t id, uint32_t maxiter);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t xport(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
extern int64_t xport_reserve(uint32_t count);
extern int64_t hook_account(uint32_t write_ptr, uint32_t write_len);
extern int64_t otxn_param(uint32_t write_ptr, uint32_t write_len, uint32_t name_ptr, uint32_t name_len);
extern int64_t otxn_type(void);
extern int64_t ledger_seq(void);
#define SBUF(x) (uint32_t)(x), sizeof(x)
#define ASSERT(x) if (!(x)) rollback((uint32_t)#x, sizeof(#x), __LINE__)
#define ttPAYMENT 0
#define tfCANONICAL 0x80000000UL
#define amAMOUNT 1
#define amFEE 8
#define atACCOUNT 1
#define atDESTINATION 3
#define ENCODE_TT(buf_out, tt) \
buf_out[0] = 0x12U; buf_out[1] = (tt >> 8) & 0xFFU; buf_out[2] = tt & 0xFFU; buf_out += 3;
#define ENCODE_FLAGS(buf_out, flags) \
buf_out[0] = 0x22U; buf_out[1] = (flags >> 24) & 0xFFU; buf_out[2] = (flags >> 16) & 0xFFU; \
buf_out[3] = (flags >> 8) & 0xFFU; buf_out[4] = flags & 0xFFU; buf_out += 5;
#define ENCODE_SEQUENCE(buf_out, seq) \
buf_out[0] = 0x24U; buf_out[1] = (seq >> 24) & 0xFFU; buf_out[2] = (seq >> 16) & 0xFFU; \
buf_out[3] = (seq >> 8) & 0xFFU; buf_out[4] = seq & 0xFFU; buf_out += 5;
#define ENCODE_FLS(buf_out, fls) \
buf_out[0] = 0x20U; buf_out[1] = 0x1AU; buf_out[2] = (fls >> 24) & 0xFFU; \
buf_out[3] = (fls >> 16) & 0xFFU; buf_out[4] = (fls >> 8) & 0xFFU; \
buf_out[5] = fls & 0xFFU; buf_out += 6;
#define ENCODE_LLS(buf_out, lls) \
buf_out[0] = 0x20U; buf_out[1] = 0x1BU; buf_out[2] = (lls >> 24) & 0xFFU; \
buf_out[3] = (lls >> 16) & 0xFFU; buf_out[4] = (lls >> 8) & 0xFFU; \
buf_out[5] = lls & 0xFFU; buf_out += 6;
#define ENCODE_DROPS(buf_out, drops, amt_type) \
buf_out[0] = 0x60U + amt_type; buf_out[1] = 0x40U + ((drops >> 56) & 0x3FU); \
buf_out[2] = (drops >> 48) & 0xFFU; buf_out[3] = (drops >> 40) & 0xFFU; \
buf_out[4] = (drops >> 32) & 0xFFU; buf_out[5] = (drops >> 24) & 0xFFU; \
buf_out[6] = (drops >> 16) & 0xFFU; buf_out[7] = (drops >> 8) & 0xFFU; \
buf_out[8] = drops & 0xFFU; buf_out += 9;
#define ENCODE_SIGNING_PUBKEY_EMPTY(buf_out) \
buf_out[0] = 0x73U; buf_out[1] = 0x00U; buf_out += 2;
#define ENCODE_ACCOUNT(buf_out, acc, acc_type) \
buf_out[0] = 0x80U + acc_type; buf_out[1] = 0x14U; \
for (int i = 0; i < 20; ++i) buf_out[2+i] = acc[i]; buf_out += 22;
#define PREPARE_PAYMENT_SIMPLE_SIZE 270U
int64_t hook(uint32_t reserved) {
_g(1, 1);
if (otxn_type() != ttPAYMENT)
return accept(0, 0, 0);
ASSERT(xport_reserve(1) == 1);
uint8_t dst[20];
int64_t dst_len = otxn_param(SBUF(dst), "DST", 3);
ASSERT(dst_len == 20);
uint8_t acc[20];
ASSERT(hook_account(SBUF(acc)) == 20);
uint32_t cls = (uint32_t)ledger_seq();
uint8_t tx[PREPARE_PAYMENT_SIMPLE_SIZE];
uint8_t* buf = tx;
ENCODE_TT(buf, ttPAYMENT);
ENCODE_FLAGS(buf, tfCANONICAL);
ENCODE_SEQUENCE(buf, 0);
ENCODE_FLS(buf, cls + 1);
ENCODE_LLS(buf, cls + 5);
// sfTicketSequence = UINT32 field 41 = 0x20 0x29
buf[0] = 0x20U; buf[1] = 0x29U;
buf[2] = 0; buf[3] = 0; buf[4] = 0; buf[5] = 1;
buf += 6;
uint64_t drops = 1000000;
ENCODE_DROPS(buf, drops, amAMOUNT);
ENCODE_DROPS(buf, 10, amFEE);
ENCODE_SIGNING_PUBKEY_EMPTY(buf);
ENCODE_ACCOUNT(buf, acc, atACCOUNT);
ENCODE_ACCOUNT(buf, dst, atDESTINATION);
uint8_t hash[32];
int64_t xport_result = xport(SBUF(hash), (uint32_t)tx, buf - tx);
ASSERT(xport_result == 32);
return accept(0, 0, 0);
}
"""
async def scenario(ctx, log):
# Wait for network to start and amendments to activate
await require_export(ctx, log)
# --- Setup ---
await ctx.fund_accounts({"alice": 10000, "bob": 10000, "carol": 1000})
log("Accounts funded")
alice = ctx.account("alice")
carol = ctx.account("carol")
# Compile and install xport hook on alice
wasm = ctx.compile_hook(XPORT_HOOK_C, label="xport")
await ctx.submit_and_wait(
{
"TransactionType": "SetHook",
"Hooks": [
{
"Hook": {
"CreateCode": wasm.hex().upper(),
"HookOn": "0" * 64,
"HookNamespace": "0" * 64,
"HookApiVersion": 0,
"Flags": 1, # hsfOVERRIDE
}
}
],
"Fee": "100000000",
},
alice.wallet,
)
log(
f"Hook installed on alice ({alice.address[:12]}...) "
f"ledger {ctx.validated_ledger_index(0)}"
)
# --- Trigger ---
# bob pays alice → hook calls xport() → emits ttEXPORT
trigger_result = await ctx.submit_and_wait(
{
"TransactionType": "Payment",
"Destination": alice.address,
"Amount": "100000000",
"Fee": "1000000",
"HookParameters": [dst_param(carol.address)],
},
ctx.account("bob").wallet,
)
trigger_seq = ctx.validated_ledger_index(0)
log(f"Export triggered at ledger {trigger_seq}")
# Assert hook fired with ACCEPT and emitted 1 tx
trigger_meta = trigger_result.get("meta", {})
assert_hook_accepted(trigger_meta, log, expected_emits=1)
# --- Verify: check each ledger close for the Export transaction ---
max_ledgers = 10
for i in range(max_ledgers):
await ctx.wait_for_ledgers(1, node_id=0, timeout=30)
seq = ctx.validated_ledger_index(0)
exports = find_export_txns(ctx, seq)
if exports:
export_tx = exports[0]
meta = export_tx.get("meta", export_tx.get("metaData", {}))
result = meta.get("TransactionResult", "")
log(f"Ledger {seq}: Export txn found, result={result}")
if result != "tesSUCCESS":
raise AssertionError(f"Export did not succeed: {result}")
# Assert ExportResult is well-formed with signers and inner tx
assert_export_result(meta, log, require_signers=True)
# Assert shadow ticket was created
assert_shadow_ticket(ctx, alice.address, log, expect_exists=True)
log("PASS")
return
log(f"Ledger {seq}: no Export txn yet")
raise AssertionError(
f"No Export transaction found after {max_ledgers} ledger closes"
)

View File

@@ -1,180 +0,0 @@
"""Shared helpers for ConsensusEntropy scenario tests."""
from __future__ import annotations
from xahaud_scripts.testnet.config import feature_name_to_hash
ZERO_DIGEST = "0" * 64
CONSENSUS_ENTROPY_FEATURE = feature_name_to_hash("ConsensusEntropy")
def feature_hash(name: str) -> str:
"""Return the amendment hash accepted by feature RPC."""
return feature_name_to_hash(name)
def feature_status(ctx, name: str, node_id=0):
"""Query a feature by amendment hash; feature RPC names are ambiguous."""
return ctx.feature_check(feature_hash(name), node_id=node_id)
def consensus_entropy_feature(ctx, node_id=0):
"""Query ConsensusEntropy by amendment hash."""
return feature_status(ctx, "ConsensusEntropy", node_id=node_id)
async def require_entropy(ctx, log):
"""Wait for first ledger and assert ConsensusEntropy is enabled."""
await ctx.wait_for_ledger_close(timeout=120)
feature = consensus_entropy_feature(ctx, node_id=0)
if not feature or not feature.get("enabled", False):
raise AssertionError(f"ConsensusEntropy not enabled: {feature}")
log("ConsensusEntropy enabled")
def get_entropy_tx(ctx, seq):
"""Fetch ledger and return (ce_tx, user_txns) or raise."""
result = ctx.ledger(seq, transactions=True)
if not result:
raise AssertionError(f"Ledger {seq}: fetch failed")
ledger = result.get("ledger")
if not isinstance(ledger, dict):
raise AssertionError(f"Ledger {seq}: fetch returned no ledger: {result}")
txns = ledger.get("transactions", [])
ce = [tx for tx in txns if tx.get("TransactionType") == "ConsensusEntropy"]
user = [tx for tx in txns if tx.get("TransactionType") != "ConsensusEntropy"]
if len(ce) != 1:
raise AssertionError(
f"Ledger {seq}: expected 1 ConsensusEntropy txn, got {len(ce)}"
)
return ce[0], user
def entropy_fields(ce_tx):
"""Return (digest, entropy_count, is_fallback) from a ConsensusEntropy tx.
consensus_fallback rounds carry a deterministic non-zero consensus-bound
digest with EntropyCount=0 and EntropyTier=1 (consensus_fallback).
Validator entropy has EntropyTier=3 (validator_quorum).
WARNING: is_fallback is ``tier != 3``, so it lumps participant_aligned
(Tier 2) in with fallback. It is only safe where no Tier 2 band exists
(e.g. 5-node networks, where tier2 == quorum). For band-aware scenarios use
the explicit assert_consensus_fallback / assert_participant_aligned /
assert_validator_quorum helpers, which check EntropyTier directly.
"""
digest = ce_tx.get("Digest", "")
entropy_count = ce_tx.get("EntropyCount", -1)
tier = ce_tx.get("EntropyTier", None)
if tier is not None:
is_fallback = tier != 3
else:
is_fallback = entropy_count == 0
return digest, entropy_count, is_fallback
def assert_participant_aligned(ce_tx, seq, expected_count=None):
"""Assert participant_aligned (Tier 2) entropy on a ConsensusEntropy tx.
Tier 2 is the sub-quorum band: the agreed reveal cohort is >= the
participant floor but < the 80% validator quorum, so it carries
EntropyTier=2 with a deterministic non-zero digest. NOTE entropy_fields()'s
is_fallback lumps tier 2 in with fallback (is_fallback = tier != 3), so the
tier must be checked EXPLICITLY here.
"""
digest = ce_tx.get("Digest", "")
count = ce_tx.get("EntropyCount", -1)
tier = ce_tx.get("EntropyTier", None)
if tier != 2:
raise AssertionError(
f"Ledger {seq}: expected EntropyTier==2 (participant_aligned), "
f"got {tier} (EntropyCount={count})"
)
if not digest or digest == ZERO_DIGEST:
raise AssertionError(
f"Ledger {seq}: participant_aligned digest must be non-zero, got "
f"{digest[:16]}..."
)
if expected_count is not None and count != expected_count:
raise AssertionError(
f"Ledger {seq}: participant_aligned EntropyCount must be "
f"{expected_count} (the surviving cohort), got {count}"
)
return digest, count
def assert_validator_quorum(ce_tx, seq, min_count=None):
"""Assert validator_quorum (Tier 3) entropy on a ConsensusEntropy tx:
EntropyTier=3, a deterministic non-zero digest, and (optionally)
EntropyCount >= min_count (the active quorum). The count can EXCEED the
quorum (e.g. a still-full 6/6 ledger caught at a 6->5 transition), so check
>=, not ==.
"""
digest = ce_tx.get("Digest", "")
count = ce_tx.get("EntropyCount", -1)
tier = ce_tx.get("EntropyTier", None)
if tier != 3:
raise AssertionError(
f"Ledger {seq}: expected EntropyTier==3 (validator_quorum), got "
f"{tier} (EntropyCount={count})"
)
if not digest or digest == ZERO_DIGEST:
raise AssertionError(
f"Ledger {seq}: validator_quorum digest must be non-zero, got "
f"{digest[:16]}..."
)
if min_count is not None and count < min_count:
raise AssertionError(
f"Ledger {seq}: validator_quorum EntropyCount={count} < quorum "
f"{min_count}"
)
return digest, count
def assert_consensus_fallback(ce_tx, seq):
"""Assert consensus_fallback (Tier 1) entropy on a ConsensusEntropy tx:
EntropyTier=1, EntropyCount=0, and a deterministic NON-zero digest.
"""
digest = ce_tx.get("Digest", "")
count = ce_tx.get("EntropyCount", -1)
tier = ce_tx.get("EntropyTier", None)
if tier != 1:
raise AssertionError(
f"Ledger {seq}: expected EntropyTier==1 (consensus_fallback), got "
f"{tier} (EntropyCount={count})"
)
if count != 0:
raise AssertionError(
f"Ledger {seq}: consensus_fallback EntropyCount must be 0, got "
f"{count}"
)
if not digest or digest == ZERO_DIGEST:
raise AssertionError(
f"Ledger {seq}: consensus_fallback digest must be non-zero, got "
f"{digest[:16]}..."
)
return digest, count
def assert_valid_entropy(ce_tx, seq, seen_digests=None):
"""Assert quorum-met validator entropy. Optionally check uniqueness."""
digest, entropy_count, is_fallback = entropy_fields(ce_tx)
if is_fallback or not digest or digest == ZERO_DIGEST:
raise AssertionError(f"Ledger {seq}: fallback/empty Digest")
if entropy_count < 4:
raise AssertionError(
f"Ledger {seq}: EntropyCount={entropy_count} < 4 (sub-quorum)"
)
if seen_digests is not None:
if digest in seen_digests:
raise AssertionError(f"Ledger {seq}: duplicate Digest {digest[:16]}...")
seen_digests.add(digest)
return digest, entropy_count

View File

@@ -1,92 +0,0 @@
defaults:
network:
node_count: 5
launcher: tmux
find_ports: true
slave_delay: 0.2
features:
- ConsensusEntropy
- Export
track_features:
- ConsensusEntropy
- Export
unl_report: true
log_levels:
TxQ: info
Protocol: debug
Peer: debug
LedgerConsensus: debug
ConsensusExtensions: debug
NetworkOPs: info
env:
XAHAU_RESOURCE_PER_PORT: "1"
rc:
- rng_poll_ms=250
tests:
- name: latency_baseline_ce
script: .testnet/scenarios/perf/ce_export_latency_probe.py
params:
warmup_ledgers: 3
ledgers: 8
submit_export: false
- name: latency_baseline_export
script: .testnet/scenarios/perf/ce_export_latency_probe.py
params:
warmup_ledgers: 3
ledgers: 8
submit_export: true
- name: latency_proposal_delay_export
script: .testnet/scenarios/perf/ce_export_latency_probe.py
params:
warmup_ledgers: 3
ledgers: 8
submit_export: true
network:
rc:
- rng_poll_ms=250
- delay=100,jitter=25,msg=proposal
- name: latency_directed_pair_delay_export
script: .testnet/scenarios/perf/ce_export_latency_probe.py
params:
warmup_ledgers: 3
ledgers: 8
submit_export: true
network:
rc:
- rng_poll_ms=250
- n0->n2:delay=750,jitter=100,msg=proposal
- n2->n0:delay=750,jitter=100,msg=proposal
- name: latency_slow_minority_export
script: .testnet/scenarios/perf/ce_export_latency_probe.py
params:
warmup_ledgers: 3
ledgers: 8
submit_export: true
export_timeout: 120
network:
rc:
- rng_poll_ms=250
- n3->n0:delay=500,jitter=100,msg=proposal
- n3->n1:delay=500,jitter=100,msg=proposal
- n3->n2:delay=500,jitter=100,msg=proposal
- n4->n0:delay=500,jitter=100,msg=proposal
- n4->n1:delay=500,jitter=100,msg=proposal
- n4->n2:delay=500,jitter=100,msg=proposal
- n0->n3:delay=500,jitter=100,msg=proposal
- n1->n3:delay=500,jitter=100,msg=proposal
- n2->n3:delay=500,jitter=100,msg=proposal
- n0->n4:delay=500,jitter=100,msg=proposal
- n1->n4:delay=500,jitter=100,msg=proposal
- n2->n4:delay=500,jitter=100,msg=proposal
- name: latency_export_no_veto_with_delay
script: .testnet/scenarios/export/export_no_veto_missing_observation.py
network:
rc:
- rng_poll_ms=250
- delay=300,jitter=100,msg=proposal
- n4:no_export_sig_hash=true

View File

@@ -1,196 +0,0 @@
""":descr: measure CE/export behavior while RuntimeConfig injects latency/drop.
The suite supplies runtime fault injection through network.rc. This scenario
does not mutate RuntimeConfig itself; it observes what the launched network does
under that condition and logs enough counters to compare variants.
"""
from __future__ import annotations
from collections import Counter
import json
from export.export_helpers import assert_export_result, require_export
from helpers import consensus_entropy_feature, get_entropy_tx
async def _require_runtime_config(ctx, log):
result = ctx.rpc.runtime_config(0)
if not result or result.get("error"):
raise AssertionError(
"Latency probe requires a binary built with "
"xahaud_runtime_test_config=ON; runtime_config RPC returned "
f"{result}"
)
log("RuntimeConfig RPC active")
async def _require_consensus_entropy(ctx, log):
feature = consensus_entropy_feature(ctx, node_id=0)
if not feature or not feature.get("enabled", False):
raise AssertionError(f"ConsensusEntropy not enabled: {feature}")
log("ConsensusEntropy enabled")
def _log_runtime_config(ctx, log):
for node_id in range(ctx.node_count):
cfg = ctx.rpc.runtime_config(node_id)
if cfg is None:
raise AssertionError(f"runtime_config RPC failed on node {node_id}")
log(
f"runtime_config n{node_id}: "
f"{json.dumps(cfg, sort_keys=True, separators=(',', ':'))}"
)
async def _submit_direct_export(ctx, log, *, timeout):
await ctx.fund_accounts({"alice": 10000, "bob": 1000})
alice = ctx.account("alice")
bob = ctx.account("bob")
current_seq = ctx.validated_ledger_index(0)
if current_seq is None:
raise AssertionError("validated ledger is not available before Export")
log(f"Submitting direct Export at validated ledger {current_seq}")
started = ctx.mark("latency-export-submit-start")
result = await ctx.submit_and_wait(
{
"TransactionType": "Export",
"LastLedgerSequence": current_seq + 12,
"Fee": "1000000",
"ExportedTxn": {
"TransactionType": "Payment",
"Account": alice.address,
"Destination": bob.address,
"Amount": "1000000",
"Fee": "10",
"Sequence": 0,
"TicketSequence": 1,
"FirstLedgerSequence": current_seq + 1,
"LastLedgerSequence": current_seq + 10,
"Flags": 2147483648,
"SigningPubKey": "",
},
},
alice.wallet,
timeout=timeout,
)
ended = ctx.mark("latency-export-submit-end")
elapsed = (ended.monotonic_ns - started.monotonic_ns) / 1_000_000_000
engine_result = result.get("engine_result", "")
log(f"Export result={engine_result} elapsed={elapsed:.3f}s")
if engine_result != "tesSUCCESS":
raise AssertionError(f"Expected Export tesSUCCESS, got {engine_result}")
export_result = assert_export_result(result.get("meta", {}), log)
signers = export_result.get("ExportedTxn", {}).get("Signers", [])
log(f"Export signer count={len(signers)}")
return started, ended
def _summarize_logs(ctx, log, *, label, started, ended):
patterns = {
"rng_selected": r"RNG: entropy selected",
"rng_fallback": r"tier=1",
"rng_participant_aligned": r"tier=2",
"rng_validator_quorum": r"tier=3",
"export_retry": r"terRETRY_EXPORT",
"export_quorum_timeout": r"Export: exportSigSet quorum alignment timeout",
"export_missing_observation_ignored": (
r"Export: missing exportSigSetHash observation ignored"
),
}
for name, pattern in patterns.items():
result = ctx.search_logs(pattern, since=started, until=ended, limit=500)
log(f"log_count {label}.{name}={result.count}")
async def scenario(
ctx,
log,
*,
warmup_ledgers=3,
ledgers=8,
submit_export=False,
export_timeout=90,
):
await ctx.wait_for_ledger_close(timeout=120)
await _require_runtime_config(ctx, log)
_log_runtime_config(ctx, log)
await _require_consensus_entropy(ctx, log)
if submit_export:
# require_export also asserts the UNLReport precondition for successful
# network-mode Export. Keep that explicit in perf runs so a missing
# report does not masquerade as a latency failure.
await require_export(ctx, log, require_runtime_config=False)
await ctx.wait_for_ledgers(warmup_ledgers, node_id=0, timeout=120)
warm_seq = ctx.validated_ledger_index(0)
log(f"Warmup complete at validated ledger {warm_seq}")
export_window = None
if submit_export:
export_window = await _submit_direct_export(
ctx, log, timeout=export_timeout
)
started = ctx.mark("latency-probe-start")
start_seq = ctx.validated_ledger_index(0)
await ctx.wait_for_ledgers(ledgers, node_id=0, timeout=max(120, ledgers * 30))
ended = ctx.mark("latency-probe-end")
end_seq = ctx.validated_ledger_index(0)
if start_seq is None or end_seq is None:
raise AssertionError("validated ledger index unavailable during probe")
elapsed = (ended.monotonic_ns - started.monotonic_ns) / 1_000_000_000
closed = max(0, end_seq - start_seq)
cadence = elapsed / closed if closed else 0.0
log(
f"Observed validated ledgers {start_seq + 1}..{end_seq} "
f"closed={closed} elapsed={elapsed:.3f}s cadence={cadence:.3f}s/ledger"
)
tiers: Counter[int] = Counter()
counts: Counter[int] = Counter()
missing_entropy = 0
for seq in range(start_seq + 1, end_seq + 1):
try:
ce, user_txns = get_entropy_tx(ctx, seq)
except AssertionError as exc:
missing_entropy += 1
log(f" Ledger {seq}: no ConsensusEntropy tx ({exc})")
continue
tier = ce.get("EntropyTier", -1)
count = ce.get("EntropyCount", -1)
tiers[tier] += 1
counts[count] += 1
log(
f" Ledger {seq}: tier={tier} count={count} "
f"user_txns={len(user_txns)} digest={ce.get('Digest', '')[:16]}..."
)
log(
"SUMMARY "
f"closed={closed} elapsed_s={elapsed:.3f} cadence_s={cadence:.3f} "
f"tiers={dict(sorted(tiers.items()))} "
f"counts={dict(sorted(counts.items()))} "
f"missing_entropy={missing_entropy}"
)
_summarize_logs(ctx, log, label="probe", started=started, ended=ended)
if export_window is not None:
_summarize_logs(
ctx,
log,
label="export",
started=export_window[0],
ended=export_window[1],
)
log("PASS")

View File

@@ -1,62 +0,0 @@
defaults:
network:
node_count: 5
launcher: tmux
find_ports: true
slave_delay: 0.2
features:
- ConsensusEntropy
track_features:
- ConsensusEntropy
unl_report: true
log_levels:
TxQ: info
Protocol: debug
Peer: debug
LedgerConsensus: debug
ConsensusExtensions: debug
NetworkOPs: info
env:
XAHAU_RESOURCE_PER_PORT: "1"
rc:
- rng_poll_ms=333
tests:
- name: steady_state_entropy
script: .testnet/scenarios/entropy/steady_state_entropy.py
- name: fallback_without_unl_report
script: .testnet/scenarios/entropy/fallback_without_unl_report.py
network:
unl_report: false
- name: steady_state_entropy_fast_start
script: .testnet/scenarios/entropy/steady_state_entropy.py
network:
env:
XAHAUD_RUNTIME_TEST_CONFIG: '{"set":{"global":{"rng_poll_ms":333,"bootstrap_fast_start":true}}}'
- name: entropy_with_transactions
script: .testnet/scenarios/entropy/entropy_with_transactions.py
- name: quorum_recovery_smoke
script: .testnet/scenarios/entropy/quorum_recovery_smoke.py
- name: quorum_degradation_smoke
script: .testnet/scenarios/entropy/quorum_degradation_smoke.py
network:
log_levels:
LedgerConsensus: trace
ConsensusExtensions: trace
# Tier 2 (participant_aligned) needs 6 nodes: n=5 has no band (tier2 ==
# quorum). At 6, the 4/6 window is the participant_aligned band.
- name: participant_aligned_smoke
script: .testnet/scenarios/entropy/participant_aligned_smoke.py
network:
node_count: 6
log_levels:
LedgerConsensus: trace
ConsensusExtensions: trace
# Export scenarios: see export-suite.yml

View File

@@ -50,7 +50,7 @@ that `test` code should *never* be included in `ripple` code.)
## Validation
The [levelization.py](levelization.py) script takes no parameters,
The [levelization.sh](levelization.sh) script takes no parameters,
reads no environment variables, and can be run from any directory,
as long as it is in the expected location in the rippled repo.
It can be run at any time from within a checked out repo, and will
@@ -84,7 +84,7 @@ It generates many files of [results](results):
Github Actions workflow to test that levelization loops haven't
changed. Unfortunately, if changes are detected, it can't tell if
they are improvements or not, so if you have resolved any issues or
done anything else to improve levelization, run `levelization.py`,
done anything else to improve levelization, run `levelization.sh`,
and commit the updated results.
The `loops.txt` and `ordering.txt` files relate the modules
@@ -108,7 +108,7 @@ The committed files hide the detailed values intentionally, to
prevent false alarms and merging issues, and because it's easy to
get those details locally.
1. Run `levelization.py`
1. Run `levelization.sh`
2. Grep the modules in `paths.txt`.
* For example, if a cycle is found `A ~= B`, simply `grep -w
A Builds/levelization/results/paths.txt | grep -w B`

View File

@@ -1,283 +0,0 @@
#!/usr/bin/env python3
"""
Usage: levelization.py
This script takes no parameters, and can be called from any directory in the file system.
"""
import os
import re
import sys
from collections import defaultdict
from pathlib import Path
# Compile regex patterns once at module level
INCLUDE_PATTERN = re.compile(r"^\s*#include.*/.*\.h")
INCLUDE_PATH_PATTERN = re.compile(r'[<"]([^>"]+)[>"]')
def dictionary_sort_key(s):
"""
Create a sort key that mimics 'sort -d' (dictionary order).
Dictionary order only considers blanks and alphanumeric characters.
"""
return "".join(c for c in s if c.isalnum() or c.isspace())
def get_level(file_path):
"""
Extract the level from a file path (second and third directory components).
Equivalent to bash: cut -d/ -f 2,3
Examples:
src/ripple/app/main.cpp -> ripple.app
src/test/app/Import_test.cpp -> test.app
"""
parts = file_path.split("/")
if len(parts) >= 3:
level = f"{parts[1]}/{parts[2]}"
elif len(parts) >= 2:
level = f"{parts[1]}/toplevel"
else:
level = file_path
# If the "level" indicates a file, cut off the filename
if "." in level.split("/")[-1]:
# Use the "toplevel" label as a workaround for `sort`
# inconsistencies between different utility versions
level = level.rsplit("/", 1)[0] + "/toplevel"
return level.replace("/", ".")
def extract_include_level(include_line):
"""
Extract the include path from an #include directive.
Gets the first two directory components from the include path.
Equivalent to bash: cut -d/ -f 1,2
Examples:
#include <ripple/basics/base_uint.h> -> ripple.basics
#include "ripple/app/main/Application.h" -> ripple.app
"""
match = INCLUDE_PATH_PATTERN.search(include_line)
if not match:
return None
include_path = match.group(1)
parts = include_path.split("/")
if len(parts) >= 2:
include_level = f"{parts[0]}/{parts[1]}"
else:
include_level = include_path
# If the "includelevel" indicates a file, cut off the filename
if "." in include_level.split("/")[-1]:
include_level = include_level.rsplit("/", 1)[0] + "/toplevel"
return include_level.replace("/", ".")
def find_repository_directories(start_path, depth_limit=10):
"""
Find the repository root by looking for src or include folders.
Walks up the directory tree from the start path.
"""
current = start_path.resolve()
for _ in range(depth_limit):
src_path = current / "src"
include_path = current / "include"
has_src = src_path.exists()
has_include = include_path.exists()
if has_src or has_include:
dirs = []
if has_src:
dirs.append(src_path)
if has_include:
dirs.append(include_path)
return current, dirs
parent = current.parent
if parent == current:
break
current = parent
raise RuntimeError(
"Could not find repository root. "
"Expected to find a directory containing 'src' and/or 'include' folders."
)
def main():
script_dir = Path(__file__).parent.resolve()
os.chdir(script_dir)
# Clean up and create results directory.
results_dir = script_dir / "results"
if results_dir.exists():
import shutil
shutil.rmtree(results_dir)
results_dir.mkdir()
# Find the repository root.
try:
repo_root, scan_dirs = find_repository_directories(script_dir)
print(f"Found repository root: {repo_root}")
for scan_dir in scan_dirs:
print(f" Scanning: {scan_dir.relative_to(repo_root)}")
except RuntimeError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
# Find all #include directives.
print("\nScanning for raw includes...")
raw_includes = []
rawincludes_file = results_dir / "rawincludes.txt"
with open(rawincludes_file, "w", buffering=8192) as raw_f:
for dir_path in scan_dirs:
for file_path in dir_path.rglob("*"):
if not file_path.is_file():
continue
try:
rel_path_str = str(file_path.relative_to(repo_root))
with open(
file_path, "r", encoding="utf-8", errors="ignore", buffering=8192
) as f:
for line in f:
if "#include" not in line or "boost" in line:
continue
if INCLUDE_PATTERN.match(line):
line_stripped = line.strip()
entry = f"{rel_path_str}:{line_stripped}\n"
print(entry, end="")
raw_f.write(entry)
raw_includes.append((rel_path_str, line_stripped))
except Exception as e:
print(f"Error reading {file_path}: {e}", file=sys.stderr)
# Build levelization paths and count directly.
print("Build levelization paths")
path_counts = defaultdict(int)
for file_path, include_line in raw_includes:
include_level = extract_include_level(include_line)
if not include_level:
continue
level = get_level(file_path)
if level != include_level:
path_counts[(level, include_level)] += 1
# Sort and deduplicate paths.
print("Sort and deduplicate paths")
sorted_items = sorted(
path_counts.items(),
key=lambda x: (dictionary_sort_key(x[0][0]), dictionary_sort_key(x[0][1])),
)
paths_file = results_dir / "paths.txt"
with open(paths_file, "w") as f:
for (level, include_level), count in sorted_items:
line = f"{count:7} {level} {include_level}\n"
print(line.rstrip())
f.write(line)
# Split into flat-file database.
print("Split into flat-file database")
includes_dir = results_dir / "includes"
includedby_dir = results_dir / "includedby"
includes_dir.mkdir()
includedby_dir.mkdir()
includes_data = defaultdict(list)
includedby_data = defaultdict(list)
for (level, include_level), count in sorted_items:
includes_data[level].append((include_level, count))
includedby_data[include_level].append((level, count))
for level in sorted(includes_data.keys(), key=dictionary_sort_key):
with open(includes_dir / level, "w") as f:
for include_level, count in includes_data[level]:
line = f"{include_level} {count}\n"
print(line.rstrip())
f.write(line)
for include_level in sorted(includedby_data.keys(), key=dictionary_sort_key):
with open(includedby_dir / include_level, "w") as f:
for level, count in includedby_data[include_level]:
line = f"{level} {count}\n"
print(line.rstrip())
f.write(line)
# Search for loops.
print("Search for loops")
loops_file = results_dir / "loops.txt"
ordering_file = results_dir / "ordering.txt"
# Pre-load all include files into memory for fast lookup.
includes_cache = {}
includes_lookup = {}
for include_file in sorted(includes_dir.iterdir(), key=lambda p: p.name):
if not include_file.is_file():
continue
includes_cache[include_file.name] = []
includes_lookup[include_file.name] = {}
with open(include_file, "r") as f:
for line in f:
parts = line.strip().split()
if len(parts) >= 2:
name, count = parts[0], int(parts[1])
includes_cache[include_file.name].append((name, count))
includes_lookup[include_file.name][name] = count
loops_found = set()
with open(loops_file, "w", buffering=8192) as loops_f, open(
ordering_file, "w", buffering=8192
) as ordering_f:
for source in sorted(includes_cache.keys()):
for include, include_freq in includes_cache[source]:
if include not in includes_lookup:
continue
source_freq = includes_lookup[include].get(source)
if source_freq is not None:
loop_key = tuple(sorted([source, include]))
if loop_key in loops_found:
continue
loops_found.add(loop_key)
loops_f.write(f"Loop: {source} {include}\n")
diff = include_freq - source_freq
if diff > 3:
loops_f.write(f" {source} > {include}\n\n")
elif diff < -3:
loops_f.write(f" {include} > {source}\n\n")
elif source_freq == include_freq:
loops_f.write(f" {include} == {source}\n\n")
else:
loops_f.write(f" {include} ~= {source}\n\n")
else:
ordering_f.write(f"{source} > {include}\n")
# Print results.
print("\nOrdering:")
with open(ordering_file, "r") as f:
print(f.read(), end="")
print("\nLoops:")
with open(loops_file, "r") as f:
print(f.read(), end="")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,130 @@
#!/bin/bash
# Usage: levelization.sh
# This script takes no parameters, reads no environment variables,
# and can be run from any directory, as long as it is in the expected
# location in the repo.
pushd $( dirname $0 )
if [ -v PS1 ]
then
# if the shell is interactive, clean up any flotsam before analyzing
git clean -ix
fi
# Ensure all sorting is ASCII-order consistently across platforms.
export LANG=C
rm -rfv results
mkdir results
includes="$( pwd )/results/rawincludes.txt"
pushd ../..
echo Raw includes:
grep -r '^[ ]*#include.*/.*\.h' include src | \
grep -v boost | tee ${includes}
popd
pushd results
oldifs=${IFS}
IFS=:
mkdir includes
mkdir includedby
echo Build levelization paths
exec 3< ${includes} # open rawincludes.txt for input
while read -r -u 3 file include
do
level=$( echo ${file} | cut -d/ -f 2,3 )
# If the "level" indicates a file, cut off the filename
if [[ "${level##*.}" != "${level}" ]]
then
# Use the "toplevel" label as a workaround for `sort`
# inconsistencies between different utility versions
level="$( dirname ${level} )/toplevel"
fi
level=$( echo ${level} | tr '/' '.' )
includelevel=$( echo ${include} | sed 's/.*["<]//; s/[">].*//' | \
cut -d/ -f 1,2 )
if [[ "${includelevel##*.}" != "${includelevel}" ]]
then
# Use the "toplevel" label as a workaround for `sort`
# inconsistencies between different utility versions
includelevel="$( dirname ${includelevel} )/toplevel"
fi
includelevel=$( echo ${includelevel} | tr '/' '.' )
if [[ "$level" != "$includelevel" ]]
then
echo $level $includelevel | tee -a paths.txt
fi
done
echo Sort and dedup paths
sort -ds paths.txt | uniq -c | tee sortedpaths.txt
mv sortedpaths.txt paths.txt
exec 3>&- #close fd 3
IFS=${oldifs}
unset oldifs
echo Split into flat-file database
exec 4<paths.txt # open paths.txt for input
while read -r -u 4 count level include
do
echo ${include} ${count} | tee -a includes/${level}
echo ${level} ${count} | tee -a includedby/${include}
done
exec 4>&- #close fd 4
loops="$( pwd )/loops.txt"
ordering="$( pwd )/ordering.txt"
pushd includes
echo Search for loops
# Redirect stdout to a file
exec 4>&1
exec 1>"${loops}"
for source in *
do
if [[ -f "$source" ]]
then
exec 5<"${source}" # open for input
while read -r -u 5 include includefreq
do
if [[ -f $include ]]
then
if grep -q -w $source $include
then
if grep -q -w "Loop: $include $source" "${loops}"
then
continue
fi
sourcefreq=$( grep -w $source $include | cut -d\ -f2 )
echo "Loop: $source $include"
# If the counts are close, indicate that the two modules are
# on the same level, though they shouldn't be
if [[ $(( $includefreq - $sourcefreq )) -gt 3 ]]
then
echo -e " $source > $include\n"
elif [[ $(( $sourcefreq - $includefreq )) -gt 3 ]]
then
echo -e " $include > $source\n"
elif [[ $sourcefreq -eq $includefreq ]]
then
echo -e " $include == $source\n"
else
echo -e " $include ~= $source\n"
fi
else
echo "$source > $include" >> "${ordering}"
fi
fi
done
exec 5>&- #close fd 5
fi
done
exec 1>&4 #close fd 1
exec 4>&- #close fd 4
cat "${ordering}"
cat "${loops}"
popd
popd
popd

View File

@@ -26,7 +26,7 @@ Loop: xrpld.app xrpld.nodestore
xrpld.app > xrpld.nodestore
Loop: xrpld.app xrpld.overlay
xrpld.overlay == xrpld.app
xrpld.overlay ~= xrpld.app
Loop: xrpld.app xrpld.peerfinder
xrpld.app > xrpld.peerfinder

View File

@@ -12,7 +12,6 @@ libxrpl.server > xrpl.basics
libxrpl.server > xrpl.json
libxrpl.server > xrpl.protocol
libxrpl.server > xrpl.server
test.app > test.shamap
test.app > test.toplevel
test.app > test.unit_test
test.app > xrpl.basics
@@ -22,7 +21,6 @@ test.app > xrpld.ledger
test.app > xrpld.nodestore
test.app > xrpld.overlay
test.app > xrpld.rpc
test.app > xrpld.shamap
test.app > xrpl.hook
test.app > xrpl.json
test.app > xrpl.protocol
@@ -45,7 +43,6 @@ test.consensus > xrpld.app
test.consensus > xrpld.consensus
test.consensus > xrpld.core
test.consensus > xrpld.ledger
test.consensus > xrpl.json
test.consensus > xrpl.protocol
test.core > test.jtx
test.core > test.toplevel
@@ -59,8 +56,6 @@ test.csf > xrpl.basics
test.csf > xrpld.consensus
test.csf > xrpl.json
test.csf > xrpl.protocol
test.formal_verification > xrpld.app
test.formal_verification > xrpld.consensus
test.json > test.jtx
test.json > xrpl.json
test.jtx > xrpl.basics
@@ -89,7 +84,6 @@ test.nodestore > xrpl.basics
test.nodestore > xrpld.core
test.nodestore > xrpld.nodestore
test.nodestore > xrpld.unity
test.nodestore > xrpl.protocol
test.overlay > test.jtx
test.overlay > test.toplevel
test.overlay > test.unit_test
@@ -124,7 +118,6 @@ test.rpc > xrpld.core
test.rpc > xrpld.net
test.rpc > xrpld.overlay
test.rpc > xrpld.rpc
test.rpc > xrpld.shamap
test.rpc > xrpl.hook
test.rpc > xrpl.json
test.rpc > xrpl.protocol
@@ -210,5 +203,6 @@ xrpld.rpc > xrpl.protocol
xrpld.rpc > xrpl.resource
xrpld.rpc > xrpl.server
xrpld.shamap > xrpl.basics
xrpld.shamap > xrpld.core
xrpld.shamap > xrpld.nodestore
xrpld.shamap > xrpl.protocol

View File

@@ -122,7 +122,6 @@ endif()
find_package(nudb REQUIRED)
find_package(date REQUIRED)
find_package(xxHash REQUIRED)
find_package(magic_enum REQUIRED)
include(deps/WasmEdge)
if(TARGET nudb::core)

View File

@@ -12,7 +12,7 @@ The server software that powers Xahau is called `xahaud` and is available in thi
### Build from Source
* [Read the build instructions in our documentation](https://xahau.network/docs/infrastructure/build-xahaud/)
* [Read the build instructions in our documentation](https://xahau.network/infrastructure/building-xahau)
* If you encounter any issues, please [open an issue](https://github.com/xahau/xahaud/issues)
## Highlights of Xahau

View File

@@ -71,7 +71,6 @@ cmake .. -G Ninja \
-Dxrpld=TRUE \
-Dtests=TRUE &&
ccache -z &&
ccache -p &&
ninja -j $3 && echo "=== Re-running final link with verbose output ===" && rm -f rippled && ninja -v rippled &&
ccache -s &&
strip -s rippled &&
@@ -95,16 +94,8 @@ if [[ "$4" == "" ]]; then
echo "Non GH, local building, no Action runner magic"
else
# GH Action, runner
if [[ "$(git rev-parse --abbrev-ref HEAD)" == "release" ]]; then
echo "building on the release branch... placing it in builds/candidate"
mkdir /data/builds/candidate
cp /io/release-build/xahaud /data/builds/candidate/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
cp /io/release-build/release.info /data/builds/candidate/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4.releaseinfo
else
echo "building non-release branch, placing it in builds root"
cp /io/release-build/xahaud /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
cp /io/release-build/release.info /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4.releaseinfo
fi
cp /io/release-build/xahaud /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
cp /io/release-build/release.info /data/builds/$(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4.releaseinfo
echo "Published build to: http://build.xahau.tech/"
echo $(date +%Y).$(date +%-m).$(date +%-d)-$(git rev-parse --abbrev-ref HEAD)+$4
fi

View File

@@ -12,16 +12,17 @@ echo "-- GITHUB_REPOSITORY: $1"
echo "-- GITHUB_SHA: $2"
echo "-- GITHUB_RUN_NUMBER: $4"
umask 0000
umask 0000;
####
cd /io
mkdir -p src/certs
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o src/certs/certbundle.h
if [ "$(grep certbundle.h src/xrpld/net/detail/RegisterSSLCerts.cpp | wc -l)" -eq "0" ]; then
cp src/xrpld/net/detail/RegisterSSLCerts.cpp src/xrpld/net/detail/RegisterSSLCerts.cpp.old
perl -i -pe "s/^{/{
cd /io;
mkdir -p src/certs;
curl --silent -k https://raw.githubusercontent.com/RichardAH/rippled-release-builder/main/ca-bundle/certbundle.h -o src/certs/certbundle.h;
if [ "`grep certbundle.h src/xrpld/net/detail/RegisterSSLCerts.cpp | wc -l`" -eq "0" ]
then
cp src/xrpld/net/detail/RegisterSSLCerts.cpp src/xrpld/net/detail/RegisterSSLCerts.cpp.old
perl -i -pe "s/^{/{
#ifdef EMBEDDED_CA_BUNDLE
BIO *cbio = BIO_new_mem_buf(ca_bundle.data(), ca_bundle.size());
X509_STORE *cts = SSL_CTX_get_cert_store(ctx.native_handle());
@@ -67,14 +68,15 @@ fi
source /opt/rh/gcc-toolset-11/enable
export PATH=/usr/local/bin:$PATH
export CC='/usr/lib64/ccache/gcc' &&
export CXX='/usr/lib64/ccache/g++' &&
echo "-- Build Rippled --" &&
pwd &&
echo "MOVING TO [ build-core.sh ]"
export CXX='/usr/lib64/ccache/g++' &&
echo "-- Build Rippled --" &&
pwd &&
printenv >.env.temp
cat .env.temp | grep '=' | sed s/\\\(^[^=]\\+=\\\)/\\1\\\"/g | sed s/\$/\\\"/g >.env
rm .env.temp
echo "MOVING TO [ build-core.sh ]";
printenv > .env.temp;
cat .env.temp | grep '=' | sed s/\\\(^[^=]\\+=\\\)/\\1\\\"/g|sed s/\$/\\\"/g > .env;
rm .env.temp;
echo "Persisting ENV:"
cat .env

View File

@@ -95,9 +95,6 @@
# - replace both functions setup_target_for_coverage_gcovr_* with a single setup_target_for_coverage_gcovr
# - add support for all gcovr output formats
#
# 2024-04-03, Bronek Kozicki
# - add support for output formats: jacoco, clover, lcov
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
@@ -259,10 +256,10 @@ endif()
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# FORMAT "cobertura" # Output format, one of:
# # xml cobertura sonarqube jacoco clover
# # json-summary json-details coveralls csv
# # txt html-single html-nested html-details
# # lcov (xml is an alias to cobertura;
# # xml cobertura sonarqube json-summary
# # json-details coveralls csv txt
# # html-single html-nested html-details
# # (xml is an alias to cobertura;
# # if no format is set, defaults to xml)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
@@ -311,8 +308,6 @@ function(setup_target_for_coverage_gcovr)
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.txt)
elseif(Coverage_FORMAT STREQUAL "csv")
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.csv)
elseif(Coverage_FORMAT STREQUAL "lcov")
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.lcov)
else()
set(GCOVR_OUTPUT_FILE ${Coverage_NAME}.xml)
endif()
@@ -325,14 +320,6 @@ function(setup_target_for_coverage_gcovr)
set(Coverage_FORMAT cobertura) # overwrite xml
elseif(Coverage_FORMAT STREQUAL "sonarqube")
list(APPEND GCOVR_ADDITIONAL_ARGS --sonarqube "${GCOVR_OUTPUT_FILE}" )
elseif(Coverage_FORMAT STREQUAL "jacoco")
list(APPEND GCOVR_ADDITIONAL_ARGS --jacoco "${GCOVR_OUTPUT_FILE}" )
list(APPEND GCOVR_ADDITIONAL_ARGS --jacoco-pretty )
elseif(Coverage_FORMAT STREQUAL "clover")
list(APPEND GCOVR_ADDITIONAL_ARGS --clover "${GCOVR_OUTPUT_FILE}" )
list(APPEND GCOVR_ADDITIONAL_ARGS --clover-pretty )
elseif(Coverage_FORMAT STREQUAL "lcov")
list(APPEND GCOVR_ADDITIONAL_ARGS --lcov "${GCOVR_OUTPUT_FILE}" )
elseif(Coverage_FORMAT STREQUAL "json-summary")
list(APPEND GCOVR_ADDITIONAL_ARGS --json-summary "${GCOVR_OUTPUT_FILE}" )
list(APPEND GCOVR_ADDITIONAL_ARGS --json-summary-pretty)
@@ -393,7 +380,6 @@ function(setup_target_for_coverage_gcovr)
${GCOVR_PATH}
--gcov-executable ${GCOV_TOOL}
--gcov-ignore-parse-errors=negative_hits.warn_once_per_file
--gcov-ignore-parse-errors=suspicious_hits.warn_once_per_file
-r ${BASEDIR}
${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS}

View File

@@ -54,7 +54,6 @@ add_library(xrpl.imports.main INTERFACE)
target_link_libraries(xrpl.imports.main
INTERFACE
LibArchive::LibArchive
magic_enum::magic_enum
OpenSSL::Crypto
Ripple::boost
wasmedge::wasmedge
@@ -69,17 +68,6 @@ target_link_libraries(xrpl.imports.main
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>
)
# date-tz for enhanced logging (always linked, code is #ifdef guarded)
if(TARGET date::date-tz)
target_link_libraries(xrpl.imports.main INTERFACE date::date-tz)
endif()
# BEAST_ENHANCED_LOGGING: enable for Debug builds OR when explicitly requested
# Uses generator expression so it works with multi-config generators (Xcode, VS, Ninja Multi-Config)
target_compile_definitions(xrpl.imports.main INTERFACE
$<$<OR:$<CONFIG:Debug>,$<BOOL:${BEAST_ENHANCED_LOGGING}>>:BEAST_ENHANCED_LOGGING=1>
)
include(add_module)
include(target_link_modules)
@@ -160,18 +148,11 @@ target_link_modules(xrpl PUBLIC
# $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
# $<INSTALL_INTERFACE:include>)
if(formal_verification AND NOT xrpld)
message(FATAL_ERROR "formal_verification requires xrpld=ON")
endif()
if(xrpld)
add_executable(rippled)
if(tests)
target_compile_definitions(rippled PUBLIC ENABLE_TESTS)
endif()
if(xahaud_runtime_test_config)
target_compile_definitions(rippled PUBLIC XAHAUD_ENABLE_RUNTIME_TEST_CONFIG=1)
endif()
target_include_directories(rippled
PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
@@ -187,21 +168,6 @@ if(xrpld)
"${CMAKE_CURRENT_SOURCE_DIR}/src/test/*.cpp"
)
target_sources(rippled PRIVATE ${sources})
set(HOOKS_TEST_DIR "" CACHE PATH "External hook Env-test directory")
if(NOT HOOKS_TEST_DIR AND DEFINED ENV{HOOKS_TEST_DIR})
set(HOOKS_TEST_DIR "$ENV{HOOKS_TEST_DIR}")
endif()
if(HOOKS_TEST_DIR)
file(GLOB_RECURSE hook_test_sources CONFIGURE_DEPENDS
"${HOOKS_TEST_DIR}/*_test.cpp"
)
if(hook_test_sources)
message(STATUS "Including external hook Env tests from ${HOOKS_TEST_DIR}")
target_sources(rippled PRIVATE ${hook_test_sources})
target_include_directories(rippled PRIVATE "${HOOKS_TEST_DIR}")
endif()
endif()
endif()
target_link_libraries(rippled
@@ -215,7 +181,6 @@ if(xrpld)
# This is likely not strictly necessary, but listed explicitly as a good practice.
m
)
include(XahaudFormalVerification)
exclude_if_included(rippled)
# define a macro for tests that might need to
# be exluded or run differently in CI environment

View File

@@ -22,9 +22,6 @@ target_compile_definitions (opts
$<$<BOOL:${beast_no_unit_test_inline}>:BEAST_NO_UNIT_TEST_INLINE=1>
$<$<BOOL:${beast_disable_autolink}>:BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES=1>
$<$<BOOL:${single_io_service_thread}>:RIPPLE_SINGLE_IO_SERVICE_THREAD=1>
# Enhanced logging is enabled for Debug builds, or explicitly via
# -DBEAST_ENHANCED_LOGGING=ON for other build types.
$<$<OR:$<CONFIG:Debug>,$<BOOL:${BEAST_ENHANCED_LOGGING}>>:BEAST_ENHANCED_LOGGING=1>
$<$<BOOL:${voidstar}>:ENABLE_VOIDSTAR>)
target_compile_options (opts
INTERFACE

View File

@@ -12,21 +12,6 @@ option(xrpld "Build xrpld" ON)
option(tests "Build tests" ON)
option(xahaud_runtime_test_config
"Enable XAHAUD_RUNTIME_TEST_CONFIG env and runtime_config RPC fault-injection controls"
OFF)
# Conan 2 local opt-in:
# [conf]
# tools.cmake.cmaketoolchain:extra_variables={"xahaud_runtime_test_config":"ON"}
option(formal_verification
"Enable Lean-backed formal-verification cross-check tests"
OFF)
# Default off: this pulls the Lean runtime and the vendored formal model into
# the test binary. Conan/local opt-in mirrors the runtime-test-config pattern:
# [conf]
# tools.cmake.cmaketoolchain:extra_variables={"formal_verification":"ON"}
option(unity "Creates a build using UNITY support in cmake. This is the default" ON)
if(unity)
if(NOT is_ci)

View File

@@ -1,65 +0,0 @@
if(NOT formal_verification)
return()
endif()
if(NOT xrpld)
message(FATAL_ERROR "formal_verification requires xrpld=ON")
endif()
if(NOT tests)
message(FATAL_ERROR "formal_verification requires tests=ON")
endif()
if(CMAKE_CROSSCOMPILING)
message(FATAL_ERROR "formal_verification currently supports native builds only")
endif()
if(WIN32)
message(FATAL_ERROR "formal_verification currently supports Unix-like native builds only")
endif()
set(XAHAU_FORMAL_VERIFICATION_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/formal_verification"
CACHE PATH
"Lean formal-verification project used by formal_verification=ON")
include(XahaudLean)
xahaud_require_lean_toolchain("${XAHAU_FORMAL_VERIFICATION_DIR}")
set(XAHAU_FORMAL_ARCHIVE
"${XAHAU_FORMAL_VERIFICATION_DIR}/.lake/build/lib/libxahau__consensus_XahauConsensus.a")
file(GLOB_RECURSE XAHAU_FORMAL_SOURCES CONFIGURE_DEPENDS
"${XAHAU_FORMAL_VERIFICATION_DIR}/*.lean")
# Lake currently writes package artifacts under the Lean workspace's .lake/
# directory. Keep this option native/test-only until the build is moved to a
# copied CMake-binary-dir workspace or Lake grows a stable external build-dir
# interface we can rely on here.
#
# This target deliberately invokes Lake whenever the formal-enabled `rippled`
# target is built. Lake still performs its own incremental rebuild, but CMake
# must not trust a source-tree `.lake` archive purely by timestamp.
add_custom_target(xahaud_formal_verification_lean
COMMAND "${LAKE_EXECUTABLE}" build XahauConsensus:static
WORKING_DIRECTORY "${XAHAU_FORMAL_VERIFICATION_DIR}"
DEPENDS
"${XAHAU_FORMAL_VERIFICATION_DIR}/lakefile.toml"
"${XAHAU_FORMAL_VERIFICATION_DIR}/lean-toolchain"
"${XAHAU_FORMAL_VERIFICATION_DIR}/lake-manifest.json"
${XAHAU_FORMAL_SOURCES}
BYPRODUCTS "${XAHAU_FORMAL_ARCHIVE}"
COMMENT "Building Lean formal-verification archive"
VERBATIM)
add_dependencies(rippled xahaud_formal_verification_lean)
target_compile_definitions(rippled PRIVATE XAHAUD_ENABLE_FORMAL_VERIFICATION=1)
target_include_directories(rippled PRIVATE "${LEAN_INCLUDE_DIR}")
target_link_libraries(rippled "${XAHAU_FORMAL_ARCHIVE}" "${LEAN_SHARED_LIBRARY}")
if(UNIX)
set_property(TARGET rippled APPEND PROPERTY BUILD_RPATH "${LEAN_SYSROOT}/lib/lean")
endif()
message(STATUS "Formal verification enabled: ${XAHAU_FORMAL_VERIFICATION_DIR}")
message(STATUS "Lean ${LEAN_EXPECTED_VERSION} sysroot: ${LEAN_SYSROOT}")

View File

@@ -1,113 +0,0 @@
include_guard(GLOBAL)
function(xahaud_require_lean_toolchain project_dir)
if(NOT EXISTS "${project_dir}/lean-toolchain")
message(FATAL_ERROR "Lean project is missing lean-toolchain: ${project_dir}")
endif()
file(READ "${project_dir}/lean-toolchain" lean_toolchain)
string(STRIP "${lean_toolchain}" lean_toolchain)
if(NOT lean_toolchain MATCHES "^leanprover/lean4:v([0-9]+\\.[0-9]+\\.[0-9]+([-+._A-Za-z0-9]+)?)$")
message(FATAL_ERROR
"Unsupported lean-toolchain format `${lean_toolchain}` in ${project_dir}")
endif()
set(expected_lean_version "${CMAKE_MATCH_1}")
find_program(LAKE_EXECUTABLE
NAMES lake
HINTS "$ENV{HOME}/.elan/bin")
if(NOT LAKE_EXECUTABLE)
message(FATAL_ERROR
"formal_verification=ON requires Lake on PATH or in ~/.elan/bin. "
"Install elan, then run `lake build` once in ${project_dir}.")
endif()
execute_process(
COMMAND "${LAKE_EXECUTABLE}" env lean --version
WORKING_DIRECTORY "${project_dir}"
OUTPUT_VARIABLE lean_version_output
ERROR_VARIABLE lean_version_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE lean_version_result)
if(NOT lean_version_result EQUAL 0)
message(FATAL_ERROR
"Could not run `${LAKE_EXECUTABLE} env lean --version`: "
"${lean_version_error}")
endif()
if(NOT lean_version_output MATCHES "^Lean \\(version ([^,)]+)[,)]")
message(FATAL_ERROR
"Could not parse Lean version from `${lean_version_output}`")
endif()
set(actual_lean_version "${CMAKE_MATCH_1}")
if(NOT actual_lean_version STREQUAL expected_lean_version)
message(FATAL_ERROR
"Lean version mismatch for formal_verification=ON. "
"Expected ${expected_lean_version} from ${project_dir}/lean-toolchain, "
"but `${LAKE_EXECUTABLE} env lean --version` returned "
"`${lean_version_output}`")
endif()
execute_process(
COMMAND "${LAKE_EXECUTABLE}" --version
WORKING_DIRECTORY "${project_dir}"
OUTPUT_VARIABLE lake_version_output
ERROR_VARIABLE lake_version_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE lake_version_result)
if(NOT lake_version_result EQUAL 0)
message(FATAL_ERROR
"Could not run `${LAKE_EXECUTABLE} --version`: ${lake_version_error}")
endif()
if(NOT lake_version_output MATCHES "Lean version ([^)]+)\\)")
message(FATAL_ERROR
"Could not parse Lake's Lean version from `${lake_version_output}`")
endif()
set(lake_lean_version "${CMAKE_MATCH_1}")
if(NOT lake_lean_version STREQUAL expected_lean_version)
message(FATAL_ERROR
"Lake version mismatch for formal_verification=ON. "
"Expected Lean ${expected_lean_version} from ${project_dir}/lean-toolchain, "
"but `${LAKE_EXECUTABLE} --version` returned `${lake_version_output}`")
endif()
if(NOT EXISTS "${project_dir}/lakefile.toml")
message(FATAL_ERROR
"formal_verification=ON requires ${project_dir}/lakefile.toml")
endif()
execute_process(
COMMAND "${LAKE_EXECUTABLE}" env printenv LEAN_SYSROOT
WORKING_DIRECTORY "${project_dir}"
OUTPUT_VARIABLE lean_sysroot
ERROR_VARIABLE lean_sysroot_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE lean_sysroot_result)
if(NOT lean_sysroot_result EQUAL 0 OR NOT lean_sysroot)
message(FATAL_ERROR
"Could not determine Lean sysroot via "
"`${LAKE_EXECUTABLE} env printenv LEAN_SYSROOT`: ${lean_sysroot_error}")
endif()
set(lean_include_dir "${lean_sysroot}/include")
if(NOT EXISTS "${lean_include_dir}/lean/lean.h")
message(FATAL_ERROR "Lean header not found: ${lean_include_dir}/lean/lean.h")
endif()
find_library(lean_shared_library
NAMES leanshared libleanshared
PATHS "${lean_sysroot}/lib/lean"
NO_DEFAULT_PATH)
if(NOT lean_shared_library)
message(FATAL_ERROR
"Lean shared runtime not found under ${lean_sysroot}/lib/lean")
endif()
set(LAKE_EXECUTABLE "${LAKE_EXECUTABLE}" PARENT_SCOPE)
set(LEAN_SYSROOT "${lean_sysroot}" PARENT_SCOPE)
set(LEAN_INCLUDE_DIR "${lean_include_dir}" PARENT_SCOPE)
set(LEAN_SHARED_LIBRARY "${lean_shared_library}" PARENT_SCOPE)
set(LEAN_EXPECTED_VERSION "${expected_lean_version}" PARENT_SCOPE)
endfunction()

View File

@@ -1,5 +1,4 @@
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
import re
@@ -15,7 +14,6 @@ class Xrpl(ConanFile):
'assertions': [True, False],
'coverage': [True, False],
'fPIC': [True, False],
'formal_verification': [True, False],
'jemalloc': [True, False],
'rocksdb': [True, False],
'shared': [True, False],
@@ -31,7 +29,6 @@ class Xrpl(ConanFile):
'date/3.0.3',
'grpc/1.50.1',
'libarchive/3.7.6',
'magic_enum/0.9.5',
'nudb/2.0.8',
'openssl/3.6.0',
'soci/4.0.3@xahaud/stable',
@@ -47,7 +44,6 @@ class Xrpl(ConanFile):
'assertions': False,
'coverage': False,
'fPIC': True,
'formal_verification': False,
'jemalloc': False,
'rocksdb': True,
'shared': False,
@@ -113,14 +109,6 @@ class Xrpl(ConanFile):
if self.settings.compiler == 'apple-clang':
self.options['boost/*'].visibility = 'global'
def validate(self):
if self.options.formal_verification and (
not self.options.tests or not self.options.xrpld
):
raise ConanInvalidConfiguration(
'formal_verification=True requires tests=True and xrpld=True'
)
def requirements(self):
# Force sqlite3 version to avoid conflicts with soci
self.requires('sqlite3/3.47.0', override=True)
@@ -143,18 +131,6 @@ class Xrpl(ConanFile):
'cfg/*',
'cmake/*',
'external/*',
'formal_verification/*.json',
'formal_verification/*.lean',
'formal_verification/*.md',
'formal_verification/*.toml',
'formal_verification/lean-toolchain',
'formal_verification/XahauConsensus/*.lean',
'!formal_verification/.lake',
'!formal_verification/.lake/*',
'!formal_verification/.lake/**',
'!formal_verification/**/.lake',
'!formal_verification/**/.lake/*',
'!formal_verification/**/.lake/**',
'include/*',
'src/*',
)
@@ -171,7 +147,6 @@ class Xrpl(ConanFile):
tc.variables['tests'] = self.options.tests
tc.variables['assert'] = self.options.assertions
tc.variables['coverage'] = self.options.coverage
tc.variables['formal_verification'] = self.options.formal_verification
tc.variables['jemalloc'] = self.options.jemalloc
tc.variables['rocksdb'] = self.options.rocksdb
tc.variables['BUILD_SHARED_LIBS'] = self.options.shared
@@ -187,11 +162,6 @@ class Xrpl(ConanFile):
cmake.build()
def package(self):
if self.options.formal_verification:
raise ConanInvalidConfiguration(
'formal_verification=True is a local/CI test build option and '
'is not supported for Conan packages'
)
cmake = CMake(self)
cmake.verbose = True
cmake.install()

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
/.lake

View File

@@ -1,166 +0,0 @@
# xahau_consensus
Lean proofs for small Xahau consensus invariants.
This package is intentionally narrow. It does **not** try to verify the C++
implementation directly. It mirrors small formulas and decision ladders from
the consensus-extension code so the safety arguments can be checked as theorems
instead of repeatedly re-derived in review notes.
Current modules:
- `XahauConsensus.Threshold`
- mirrors `calculateParticipantThreshold`
- proves the Tier-2 intersection inequality:
`count + floor(count / 5) < 2 * participantThreshold count`
- proves the threshold is minimal for that strict inequality
- proves the original-view threshold remains safe when nUNL shrinks the
effective view
- includes the `original=10`, `effective=8` regression example showing why
using the effective view for the Tier-2 floor is forkable
- proves `participantThreshold count <= quorumThreshold count` for
non-empty views
- distinguishes raw formula helpers from the live safety-wrapped gate
thresholds used by `ConsensusExtensions`
- `XahauConsensus.ThresholdFacts`
- records small-network values and band-empty/band-present examples
- proves exact multiple-of-five behavior
- proves threshold monotonicity facts
- `XahauConsensus.SixtyPercent`
- defines a naive `ceil(60%)` threshold
- proves naive 60% is unsafe at exact multiples of five
- proves the live derived floor is one higher there and restores strict
intersection safety
- `XahauConsensus.Intersection`
- proves the abstract cardinality argument behind quorum intersection
- shows two threshold-sized cohorts must overlap above the fault bound
whenever `n + f < 2t`
- specializes that argument to the live participant threshold, including
nUNL-shrunk effective views
- `XahauConsensus.HonestOverlap`
- bridges overlap arithmetic to the consensus claim that two cohorts share at
least one honest validator
- specializes that bridge to the participant threshold and `floor(n/5)` fault
bound
- `XahauConsensus.ViewUniverse`
- proves original-view anchoring remains safe under nUNL shrink
- separates strict safety from threshold reachability
- defines cross-view participant-band presence/absence
- shows effective-view thresholds can be unsafe against the original fault
bound
- shows trusted-superset counting universes erode the intersection margin
- `XahauConsensus.NunlCap`
- models the protocol's ceil-25% nUNL disablement cap
- proves 8/6 and 10/8 band collapse examples
- records that 10 at max cap has effective view 7, below the original
participant floor
- records the important counterexample: original `20`, effective `15` does
**not** make validator quorum meet the original participant floor
- `XahauConsensus.SidecarAlignment`
- models aligned participant counting for sidecar hashes
- proves non-active peers and non-active local publication cannot pad the
alignment count
- proves changing nonmember reports cannot change quorum alignment
- `XahauConsensus.EntropySelector`
- models the tier-label ladder from `ConsensusExtensions::selectEntropy`
- proves non-UNLReport views select fallback
- proves the quorum / participant / fallback bands select the expected tier
- `XahauConsensus.SelectorDeterminism`
- models labeled digest output
- proves digest payload bytes do not affect the label when consensus metadata
is fixed
- records examples where changing view provenance or view sizes changes labels
- `XahauConsensus.ExportGate`
- models export's quorum-aligned success rule
- models export's sidecar-gate outcome as `proceed` or `retryOrExpire`, with
no deterministic fallback signature set
- proves missing minority observation does not block a quorum-aligned export
- proves `fullObservation` alone cannot change the export decision
- `XahauConsensus.ExportQuorum`
- proves two 80% export quorums overlap above the standard Byzantine bound
in nonempty active universes
- proves export quorum overlap remains above the original-view Byzantine
bound when nUNL shrinkage is within the protocol cap
- proves Byzantine validators at the standard bound cannot veto quorum
- records concrete overlap margins for 5/10/20-validator universes
- `XahauConsensus.FinsetIntersection`
- uses Mathlib finite sets to prove the cardinality premise behind the
arithmetic intersection theorems
- specializes that bridge for Tier-2 cohorts, nUNL-shrunk cohorts, and export
80% quorums
- `XahauConsensus.Invariants`
- restates cross-module design contracts in one place
- pins the live safety-wrapped threshold relationship
- proves the cross-view entropy gate is exactly the selector's non-fallback
boundary
- pins non-UNLReport fallback and export full-observation independence
Run:
```sh
~/.elan/bin/lake build
```
## Optional C++ cross-checks
The xahaud CMake build can also compile a Lean-backed unit-test path, but it is
off by default and is not part of normal release builds:
Install Lean through `elan` first. The CMake integration intentionally keeps the
tooling rule simple: when `formal_verification=ON`, it looks for `lake` on
`PATH` or in `~/.elan/bin`, asks that Lake environment to run `lean --version`,
verifies the exact version specified by this package's `lean-toolchain`, then
asks Lake for `LEAN_SYSROOT` and checks that `lean.h` and `libleanshared`
exist.
```sh
conan install . --output-folder=build-formal --build=missing \
-s build_type=Release \
-o '&:tests=True' \
-o '&:xrpld=True' \
-o '&:formal_verification=True'
cmake -S . -B build-formal-cmake \
-DCMAKE_TOOLCHAIN_FILE=$PWD/build-formal/build/generators/conan_toolchain.cmake \
-Dtests=ON \
-Dxrpld=ON \
-Dformal_verification=ON
cmake --build build-formal-cmake --target rippled
./build-formal-cmake/rippled --unittest=LeanConsensus
```
This path currently supports native test builds only. It builds
`XahauConsensus:static`, links the resulting Lean archive and runtime into the
test binary, and runs C++ drift tests over selected scalar formulas and helper
predicates. Some checks compare directly to named production helpers; others are
review-oriented safety predicates computed from those helpers. The exported
surface is intentionally scalar and reviewable:
- Byzantine bound, participant threshold, and validator quorum threshold.
- The safety-wrapped zero-view thresholds used by the live gates.
- The cross-view entropy gate threshold, with effective and original view
denominators kept separate.
- The entropy tier selector policy for `(fromUNLReport, participantCount,
effectiveView, originalView)`.
- Sidecar aligned-participant counting, full-observation, quorum-aligned
predicates, and active-view mask-counting samples.
- Export's quorum-only sidecar-gate proceed predicate, where `fullObservation`
is diagnostic rather than success-gating; a small final-apply snapshot model
makes explicit that gate proceed is not the same as closed-ledger
`Export::doApply` success.
- NegativeUNL cap/effective-view arithmetic.
- View-universe safety predicates and naive-60% regression anchors.
This is still a model-to-code cross-check, not a proof that the C++ implements
the Lean model. Its value is narrower and practical: if a production formula,
decision ladder, or helper predicate changes without the formal model changing
too, the gated unit test fails. The formal CMake target invokes Lake on each
formal-enabled `rippled` build and lets Lake decide whether its own artifacts
are current; CMake does not trust an existing source-tree archive by timestamp.
Lake still writes build artifacts under the Lean workspace's `.lake/`
directory, and the Conan recipe intentionally excludes that directory from
exported sources, so keep this option as a local/CI confidence build rather
than a release packaging input. The Conan recipe rejects
`formal_verification=True` unless `tests=True` and `xrpld=True`, and refuses to
package formal-enabled builds.

View File

@@ -1,32 +0,0 @@
# Xahau Lean Roadmap
This package should stay focused on invariants that are compact enough to be
reviewable and stable enough to mirror from C++.
Good targets:
1. Threshold arithmetic
- Tier-2 participant threshold formula
- quorum threshold relation
- nUNL original-view anchoring
- small-network boundary examples
2. Sidecar alignment
- active-view-only counting
- quorum-aligned predicate
- full-observation as diagnostic vs success precondition where applicable
3. Entropy selector
- non-UNLReport fallback
- tier ladder from agreed participant count
- no local pending-state dependency in the tier decision
4. Export gate
- quorum-aligned success without full observation
- no deterministic fallback value
- retry/expire as liveness behavior, not ledger-content substitution
Poor targets for this package:
- direct verification of C++ implementation details
- wall-clock timing and network scheduling liveness
- full ledger execution semantics
Those belong in C++ tests, CSF/testnet scenarios, or a dedicated temporal model.

View File

@@ -1,17 +0,0 @@
-- This module serves as the root of the `XahauConsensus` library.
-- Import modules here that should be built as part of the library.
import XahauConsensus.Threshold
import XahauConsensus.ThresholdFacts
import XahauConsensus.SixtyPercent
import XahauConsensus.Intersection
import XahauConsensus.HonestOverlap
import XahauConsensus.ViewUniverse
import XahauConsensus.NunlCap
import XahauConsensus.SidecarAlignment
import XahauConsensus.EntropySelector
import XahauConsensus.SelectorDeterminism
import XahauConsensus.ExportGate
import XahauConsensus.ExportQuorum
import XahauConsensus.FinsetIntersection
import XahauConsensus.Invariants
import XahauConsensus.FFI

View File

@@ -1,74 +0,0 @@
import XahauConsensus.Threshold
namespace XahauConsensus
inductive EntropyTier where
| consensusFallback
| participantAligned
| validatorQuorum
deriving DecidableEq, Repr
/-- Minimal model of `ConsensusExtensions::selectEntropy`'s network,
non-failed, non-empty tier ladder.
The real C++ also computes a digest. This model deliberately focuses on the
part that can fork by labeling the same agreed set differently: the tier
decision from `(fromUNLReport, participantCount, effectiveView, originalView)`.
It does not model the standalone development shortcut, timeout-driven
`entropyFailed_` downgrade, or empty-map fallback; those paths all bypass or
downgrade this ladder rather than producing a stronger non-fallback label.
-/
def selectEntropyTier
(fromUNLReport : Bool)
(participantCount effectiveView originalView : Nat) : EntropyTier :=
if !fromUNLReport then
EntropyTier.consensusFallback
else if participantCount >= safeQuorumThreshold effectiveView then
EntropyTier.validatorQuorum
else if participantCount >= safeParticipantThreshold originalView then
EntropyTier.participantAligned
else
EntropyTier.consensusFallback
/-- Non-standalone nodes must fail closed to fallback until the validator view
is ledger-anchored by a UNLReport. -/
theorem no_unl_report_selects_fallback
(participantCount effectiveView originalView : Nat) :
selectEntropyTier false participantCount effectiveView originalView =
EntropyTier.consensusFallback := by
rfl
/-- At or above the effective-view quorum threshold, the ladder selects the
strongest entropy tier. -/
theorem quorum_count_selects_validator_quorum
{participantCount effectiveView originalView : Nat}
(hQuorum : safeQuorumThreshold effectiveView <= participantCount) :
selectEntropyTier true participantCount effectiveView originalView =
EntropyTier.validatorQuorum := by
unfold selectEntropyTier
simp [hQuorum]
/-- Below validator quorum but at or above the original-view participant floor,
the ladder selects Tier 2. -/
theorem participant_band_selects_tier2
{participantCount effectiveView originalView : Nat}
(hBelowQuorum : participantCount < safeQuorumThreshold effectiveView)
(hParticipant : safeParticipantThreshold originalView <= participantCount) :
selectEntropyTier true participantCount effectiveView originalView =
EntropyTier.participantAligned := by
unfold selectEntropyTier
simp [Nat.not_le_of_gt hBelowQuorum, hParticipant]
/-- Below both thresholds, the ladder falls back. -/
theorem below_participant_floor_selects_fallback
{participantCount effectiveView originalView : Nat}
(hBelowQuorum : participantCount < safeQuorumThreshold effectiveView)
(hBelowParticipant : participantCount < safeParticipantThreshold originalView) :
selectEntropyTier true participantCount effectiveView originalView =
EntropyTier.consensusFallback := by
unfold selectEntropyTier
simp [
Nat.not_le_of_gt hBelowQuorum,
Nat.not_le_of_gt hBelowParticipant]
end XahauConsensus

View File

@@ -1,139 +0,0 @@
namespace XahauConsensus
/-- Minimal model of the sidecar export gate.
`alignedParticipants` is the number of participants observed on the export
sidecar, `quorumThreshold` is the required aligned count, and
`fullObservation` records whether every participant was observed. The C++ gate
must use quorum alignment for success; full observation is only diagnostic.
-/
structure ExportGate where
alignedParticipants : Nat
quorumThreshold : Nat
fullObservation : Bool
deriving DecidableEq, Repr
/-- Export sidecar-gate outcome. This is not the final `Export::doApply`
result: closed-ledger apply re-validates the frozen agreed signature snapshot
before it can create a shadow ticket. -/
inductive ExportOutcome where
| proceed
| retryOrExpire
deriving DecidableEq, Repr
/-- The success predicate used by export: enough participants are aligned. -/
def ExportGate.quorumAligned (gate : ExportGate) : Bool :=
decide (gate.quorumThreshold <= gate.alignedParticipants)
/-- Export proceeds exactly when quorum alignment is met. -/
def ExportGate.proceed (gate : ExportGate) : Bool :=
gate.quorumAligned
/-- Export's externally visible decision shape. -/
def ExportGate.outcome (gate : ExportGate) : ExportOutcome :=
if gate.proceed then ExportOutcome.proceed else ExportOutcome.retryOrExpire
/-- Minimal model of the additional closed-ledger apply preconditions.
The sidecar gate only proves that one `exportSigSetHash` had quorum alignment.
Network-mode `Export::doApply` then independently requires a ledger-anchored
validator view, no convergence failure for the round, a frozen agreed sidecar
map, a parseable/valid signature set, and enough verified signers in that map.
The model intentionally excludes cryptography and metadata construction; it
exists to prevent reading `ExportGate.proceed` as final apply success.
-/
structure ExportApplySnapshot where
fromUNLReport : Bool
convergenceFailed : Bool
agreedSetPresent : Bool
agreedSetValid : Bool
signerCount : Nat
quorumThreshold : Nat
deriving DecidableEq, Repr
/-- Closed-ledger apply can use only a valid, frozen agreed sidecar snapshot. -/
def ExportApplySnapshot.validAgreedSnapshot
(snapshot : ExportApplySnapshot) : Bool :=
snapshot.fromUNLReport &&
!snapshot.convergenceFailed &&
snapshot.agreedSetPresent &&
snapshot.agreedSetValid &&
decide (snapshot.quorumThreshold <= snapshot.signerCount)
/-- Minimal network-mode apply decision: valid agreed snapshot applies; all
other cases retry or expire. -/
def ExportApplySnapshot.outcome
(snapshot : ExportApplySnapshot) : ExportOutcome :=
if snapshot.validAgreedSnapshot then
ExportOutcome.proceed
else
ExportOutcome.retryOrExpire
theorem apply_success_iff_valid_agreed_snapshot
(snapshot : ExportApplySnapshot) :
snapshot.outcome = ExportOutcome.proceed
snapshot.validAgreedSnapshot = true := by
unfold ExportApplySnapshot.outcome
by_cases h : snapshot.validAgreedSnapshot <;> simp [h]
/-- Gate success alone is not final apply success. For example, the sidecar
gate may have quorum alignment while the final apply path has no frozen agreed
sidecar map available and therefore retries. -/
theorem gate_proceed_does_not_imply_apply_success :
gate : ExportGate, snapshot : ExportApplySnapshot,
ExportGate.proceed gate = true
ExportApplySnapshot.outcome snapshot =
ExportOutcome.retryOrExpire := by
refine
ExportGate.mk 4 4 false,
ExportApplySnapshot.mk true false false true 4 4,
?_,
?_ <;> rfl
/-- A missing minority, represented by `fullObservation = false`, does not
prevent export when the quorum threshold is met. -/
theorem missing_minority_does_not_prevent_proceed
{alignedParticipants quorumThreshold : Nat}
(hQuorum : quorumThreshold <= alignedParticipants) :
(ExportGate.mk alignedParticipants quorumThreshold false).proceed = true := by
unfold ExportGate.proceed ExportGate.quorumAligned
simp [hQuorum]
theorem missing_minority_proceeds
{alignedParticipants quorumThreshold : Nat}
(hQuorum : quorumThreshold <= alignedParticipants) :
(ExportGate.mk alignedParticipants quorumThreshold false).outcome =
ExportOutcome.proceed := by
unfold ExportGate.outcome
simp [missing_minority_does_not_prevent_proceed hQuorum]
/-- Export must not proceed below the aligned-participant quorum threshold. -/
theorem below_quorum_does_not_proceed
{alignedParticipants quorumThreshold : Nat}
(fullObservation : Bool)
(hBelow : alignedParticipants < quorumThreshold) :
(ExportGate.mk alignedParticipants quorumThreshold fullObservation).proceed =
false := by
unfold ExportGate.proceed ExportGate.quorumAligned
simp [Nat.not_le_of_gt hBelow]
/-- Below quorum, export retries or expires. There is no deterministic fallback
signature set analogous to RNG's Tier 1 fallback digest. -/
theorem below_quorum_retries_or_expires
{alignedParticipants quorumThreshold : Nat}
(fullObservation : Bool)
(hBelow : alignedParticipants < quorumThreshold) :
(ExportGate.mk alignedParticipants quorumThreshold fullObservation).outcome =
ExportOutcome.retryOrExpire := by
unfold ExportGate.outcome
simp [below_quorum_does_not_proceed fullObservation hBelow]
/-- Flipping only the diagnostic `fullObservation` field cannot change the
export decision. -/
theorem changing_fullObservation_alone_does_not_change_proceed
(alignedParticipants quorumThreshold : Nat) :
(ExportGate.mk alignedParticipants quorumThreshold true).proceed =
(ExportGate.mk alignedParticipants quorumThreshold false).proceed := by
rfl
end XahauConsensus

View File

@@ -1,254 +0,0 @@
import XahauConsensus.Intersection
import XahauConsensus.NunlCap
import XahauConsensus.ThresholdFacts
namespace XahauConsensus
/-!
Nat-cardinality arithmetic for export sidecar quorum uniqueness.
The model deliberately stays at the level used by `Intersection.lean`:
* `n` is the active validator universe size.
* `a` and `b` are the numbers of validators supporting two export sidecar
hashes in that same universe.
* `overlap` is the size of the intersection between those two support sets.
* `faultyOverlap + honestOverlap = overlap` splits that intersection.
No `Finset` structure is needed here; callers supply the usual
inclusion-exclusion cardinality inequality `a + b <= n + overlap`.
-/
theorem disabled_le_cap_mul_four_le
{originalView disabled : Nat}
(hCap : disabled <= disabledCap originalView) :
disabled * 4 <= originalView + 3 := by
unfold disabledCap ceilDiv at hCap
have hFour : 0 < 4 := by decide
simp at hCap
have hMul :=
(Nat.le_div_iff_mul_le hFour).mp hCap
omega
theorem quorumThreshold_mul_five_ge_four_mul (n : Nat) :
4 * n <= 5 * quorumThreshold n := by
unfold quorumThreshold
have hHundred : 0 < 100 := by decide
have hDiv :
(n * 80 + 99) / 100 <= (n * 80 + 99) / 100 :=
Nat.le_refl _
have hBound :=
(Nat.div_le_iff_le_mul hHundred).mp hDiv
omega
theorem byzantineBound_mul_five_le (n : Nat) :
byzantineBound n * 5 <= n := by
unfold byzantineBound
exact Nat.div_mul_le_self n 5
/-- Two 80% export quorums in one active universe overlap by at least
`2 * quorumThreshold n - n`. -/
theorem two_export_quorums_overlap_lower_bound
{n a b overlap : Nat}
(hCardinality : a + b <= n + overlap)
(hA : quorumThreshold n <= a)
(hB : quorumThreshold n <= b) :
2 * quorumThreshold n - n <= overlap := by
omega
/-- The 80% quorum threshold is intersection-safe against the standard
`floor(n / 5)` fault bound for every nonempty active universe. -/
theorem quorumThreshold_intersection_safe
{n : Nat} (hPositive : 0 < n) :
n + byzantineBound n < 2 * quorumThreshold n := by
unfold quorumThreshold byzantineBound
omega
/-- The unconditional version is false: the empty active universe has raw
quorum threshold zero, so there is no strict intersection margin. -/
theorem quorumThreshold_empty_not_intersection_safe :
¬ 0 + byzantineBound 0 < 2 * quorumThreshold 0 := by
native_decide
/-- Two export sidecar hashes both clearing 80% quorum in the same nonempty
active universe must have overlap larger than the standard fault bound. -/
theorem export_hash_quorums_overlap_gt_byzantine
{n a b overlap : Nat}
(hPositive : 0 < n)
(hCardinality : a + b <= n + overlap)
(hA : quorumThreshold n <= a)
(hB : quorumThreshold n <= b) :
byzantineBound n < overlap := by
exact overlap_gt_fault_of_two_threshold_cohorts
hCardinality
hA
hB
(quorumThreshold_intersection_safe hPositive)
/-- If the overlap between two quorum-clearing export hashes is split into
faulty and honest validators, and at most `floor(n / 5)` validators in that
overlap are faulty, then the overlap contains an honest validator. -/
theorem export_hash_quorums_force_honest_overlap
{n a b overlap faultyOverlap honestOverlap : Nat}
(hPositive : 0 < n)
(hCardinality : a + b <= n + overlap)
(hA : quorumThreshold n <= a)
(hB : quorumThreshold n <= b)
(hSplit : overlap = faultyOverlap + honestOverlap)
(hFaulty : faultyOverlap <= byzantineBound n) :
0 < honestOverlap := by
have hOverlap :
byzantineBound n < overlap :=
export_hash_quorums_overlap_gt_byzantine
hPositive
hCardinality
hA
hB
omega
/-- Export quorum intersection remains above the original-view Byzantine bound
when nUNL shrinkage is within the protocol's ceil-25% cap. -/
theorem export_quorum_intersection_safe_under_nunl_cap
{originalView effectiveView disabled : Nat}
(hEffective : effectiveView = originalView - disabled)
(hCap : disabled <= disabledCap originalView)
(hPositive : 0 < effectiveView) :
effectiveView + byzantineBound originalView <
2 * quorumThreshold effectiveView := by
have hCapBound :
disabled * 4 <= originalView + 3 :=
disabled_le_cap_mul_four_le hCap
have hQuorumBound :
4 * effectiveView <= 5 * quorumThreshold effectiveView :=
quorumThreshold_mul_five_ge_four_mul effectiveView
have hByzBound :
byzantineBound originalView * 5 <= originalView :=
byzantineBound_mul_five_le originalView
omega
/-- Two export sidecar hashes both clearing 80% quorum in an nUNL-shrunk
effective view must still overlap above the original-view Byzantine bound,
provided the shrinkage stays within the protocol cap. -/
theorem export_hash_quorums_overlap_gt_original_byzantine_under_nunl_cap
{originalView effectiveView disabled a b overlap : Nat}
(hEffective : effectiveView = originalView - disabled)
(hCap : disabled <= disabledCap originalView)
(hPositive : 0 < effectiveView)
(hCardinality : a + b <= effectiveView + overlap)
(hA : quorumThreshold effectiveView <= a)
(hB : quorumThreshold effectiveView <= b) :
byzantineBound originalView < overlap := by
exact overlap_gt_fault_of_two_threshold_cohorts
hCardinality
hA
hB
(export_quorum_intersection_safe_under_nunl_cap
hEffective
hCap
hPositive)
/-- A Byzantine minority at the standard bound cannot veto export quorum:
after removing `floor(n / 5)` validators, enough validators remain to meet the
80% quorum threshold. -/
theorem byzantineBound_cannot_veto_quorum (n : Nat) :
byzantineBound n + quorumThreshold n <= n := by
unfold byzantineBound quorumThreshold
omega
/-- Equivalent no-veto form using subtraction. -/
theorem quorumThreshold_le_universe_minus_byzantineBound (n : Nat) :
quorumThreshold n <= n - byzantineBound n := by
have hNoVeto := byzantineBound_cannot_veto_quorum n
omega
/-- Concrete regression anchor: in a 5-validator active universe, two 80%
export quorums overlap in at least three validators. -/
theorem export_quorum_five_overlap_at_least_three
{a b overlap : Nat}
(hCardinality : a + b <= 5 + overlap)
(hA : quorumThreshold 5 <= a)
(hB : quorumThreshold 5 <= b) :
3 <= overlap := by
have hLower :
2 * quorumThreshold 5 - 5 <= overlap :=
two_export_quorums_overlap_lower_bound
hCardinality
hA
hB
have hExact : 2 * quorumThreshold 5 - 5 = 3 := by
native_decide
omega
/-- Concrete regression anchor: in a 10-validator active universe, two 80%
export quorums overlap in at least six validators. -/
theorem export_quorum_ten_overlap_at_least_six
{a b overlap : Nat}
(hCardinality : a + b <= 10 + overlap)
(hA : quorumThreshold 10 <= a)
(hB : quorumThreshold 10 <= b) :
6 <= overlap := by
have hLower :
2 * quorumThreshold 10 - 10 <= overlap :=
two_export_quorums_overlap_lower_bound
hCardinality
hA
hB
have hExact : 2 * quorumThreshold 10 - 10 = 6 := by
native_decide
omega
/-- Concrete regression anchor: in a 20-validator active universe, two 80%
export quorums overlap in at least twelve validators. -/
theorem export_quorum_twenty_overlap_at_least_twelve
{a b overlap : Nat}
(hCardinality : a + b <= 20 + overlap)
(hA : quorumThreshold 20 <= a)
(hB : quorumThreshold 20 <= b) :
12 <= overlap := by
have hLower :
2 * quorumThreshold 20 - 20 <= overlap :=
two_export_quorums_overlap_lower_bound
hCardinality
hA
hB
have hExact : 2 * quorumThreshold 20 - 20 = 12 := by
native_decide
omega
/-- On exact multiples of five, two 80% export quorums overlap in at least
`3 * k` validators. -/
theorem export_quorum_five_mul_overlap_at_least_three_mul
{k a b overlap : Nat}
(hCardinality : a + b <= 5 * k + overlap)
(hA : quorumThreshold (5 * k) <= a)
(hB : quorumThreshold (5 * k) <= b) :
3 * k <= overlap := by
have hLower :
2 * quorumThreshold (5 * k) - 5 * k <= overlap :=
two_export_quorums_overlap_lower_bound
hCardinality
hA
hB
rw [quorumThreshold_five_mul] at hLower
omega
/-- On exact multiples of five, quorum overlap strictly exceeds the standard
fault bound by at least `2 * k`. For `k = 0` this is only a non-strict
difference statement; strict safety is provided by
`export_hash_quorums_overlap_gt_byzantine` for nonempty universes. -/
theorem export_quorum_five_mul_overlap_margin
{k a b overlap : Nat}
(hCardinality : a + b <= 5 * k + overlap)
(hA : quorumThreshold (5 * k) <= a)
(hB : quorumThreshold (5 * k) <= b) :
byzantineBound (5 * k) + 2 * k <= overlap := by
have hOverlap :
3 * k <= overlap :=
export_quorum_five_mul_overlap_at_least_three_mul
hCardinality
hA
hB
rw [byzantineBound_five_mul]
omega
end XahauConsensus

View File

@@ -1,188 +0,0 @@
import XahauConsensus.Threshold
import XahauConsensus.Invariants
import XahauConsensus.NunlCap
import XahauConsensus.SidecarAlignment
import XahauConsensus.ViewUniverse
import XahauConsensus.ExportQuorum
import XahauConsensus.SixtyPercent
namespace XahauConsensus
/-! Scalar C ABI exports used by the optional C++ drift tests.
These functions intentionally expose only plain integer formulas. The broader
Lean project proves properties about these definitions; the C++ tests then
check that selected production formulas and helper predicates still compute the
same values.
-/
-- @@start ffi-scalar-export-surface
@[export xahau_byzantine_bound]
def xahauByzantineBound (count : UInt64) : UInt64 :=
(byzantineBound count.toNat).toUInt64
@[export xahau_participant_threshold]
def xahauParticipantThreshold (count : UInt64) : UInt64 :=
(participantThreshold count.toNat).toUInt64
@[export xahau_quorum_threshold]
def xahauQuorumThreshold (count : UInt64) : UInt64 :=
(quorumThreshold count.toNat).toUInt64
@[export xahau_safe_quorum_threshold]
def xahauSafeQuorumThreshold (count : UInt64) : UInt64 :=
(safeQuorumThreshold count.toNat).toUInt64
@[export xahau_safe_participant_threshold]
def xahauSafeParticipantThreshold (count : UInt64) : UInt64 :=
(safeParticipantThreshold count.toNat).toUInt64
@[export xahau_entropy_gate_threshold_for_view]
def xahauEntropyGateThresholdForView
(effectiveView originalView : UInt64) : UInt64 :=
(entropyGateThresholdForView effectiveView.toNat originalView.toNat).toUInt64
def entropyTierCode : EntropyTier UInt8
| EntropyTier.consensusFallback => 1
| EntropyTier.participantAligned => 2
| EntropyTier.validatorQuorum => 3
@[export xahau_select_entropy_tier]
def xahauSelectEntropyTier
(fromUNLReport participantCount effectiveView originalView : UInt64) : UInt8 :=
entropyTierCode <|
selectEntropyTier
(fromUNLReport != 0)
participantCount.toNat
effectiveView.toNat
originalView.toNat
@[export xahau_aligned_participants]
def xahauAlignedParticipants
(aligned localIsMember localPublished : UInt64) : UInt64 :=
(alignedParticipants
aligned.toNat
(localIsMember != 0)
(localPublished != 0)).toUInt64
@[export xahau_quorum_aligned]
def xahauQuorumAligned
(threshold aligned localIsMember localPublished : UInt64) : UInt8 :=
if quorumAligned
threshold.toNat
aligned.toNat
(localIsMember != 0)
(localPublished != 0) then
1
else
0
@[export xahau_full_observation]
def xahauFullObservation (peersSeen txConverged : UInt64) : UInt8 :=
if fullObservation peersSeen.toNat txConverged.toNat then 1 else 0
@[export xahau_export_gate_proceed]
def xahauExportGateProceed
(alignedParticipants quorumThreshold fullObservation : UInt64) : UInt8 :=
if (ExportGate.mk
alignedParticipants.toNat
quorumThreshold.toNat
(fullObservation != 0)).proceed then
1
else
0
@[export xahau_strict_intersection_safe]
def xahauStrictIntersectionSafe
(activeView byzantineUniverse threshold : UInt64) : UInt8 :=
if activeView.toNat + byzantineBound byzantineUniverse.toNat <
2 * threshold.toNat then
1
else
0
@[export xahau_nonvacuous_strict_intersection_safe]
def xahauNonvacuousStrictIntersectionSafe
(activeView byzantineUniverse threshold : UInt64) : UInt8 :=
if threshold.toNat <= activeView.toNat
activeView.toNat + byzantineBound byzantineUniverse.toNat <
2 * threshold.toNat then
1
else
0
@[export xahau_participant_band_nonempty]
def xahauParticipantBandNonempty
(effectiveView originalView : UInt64) : UInt8 :=
if participantThreshold originalView.toNat < quorumThreshold effectiveView.toNat then
1
else
0
@[export xahau_export_quorum_overlap_lower_bound]
def xahauExportQuorumOverlapLowerBound (activeView : UInt64) : UInt64 :=
(2 * quorumThreshold activeView.toNat - activeView.toNat).toUInt64
@[export xahau_export_quorum_safe_under_nunl_cap]
def xahauExportQuorumSafeUnderNunlCap
(originalView effectiveView disabled : UInt64) : UInt8 :=
if effectiveView.toNat = originalView.toNat - disabled.toNat
disabled.toNat <= disabledCap originalView.toNat
0 < effectiveView.toNat
effectiveView.toNat + byzantineBound originalView.toNat <
2 * quorumThreshold effectiveView.toNat then
1
else
0
private def maskBit (mask : UInt64) (peer : Nat) : Bool :=
((mask.toNat / (2 ^ peer)) % 2) == 1
@[export xahau_active_aligned_count_mask]
def xahauActiveAlignedCountMask
(count activeMask alignedMask : UInt64) : UInt64 :=
(activeAlignedCount
(maskBit activeMask)
(maskBit alignedMask)
count.toNat).toUInt64
@[export xahau_quorum_aligned_mask]
def xahauQuorumAlignedMask
(threshold count activeMask alignedMask localIsMember localPublished : UInt64) : UInt8 :=
let aligned :=
activeAlignedCount
(maskBit activeMask)
(maskBit alignedMask)
count.toNat
if quorumAligned
threshold.toNat
aligned
(localIsMember != 0)
(localPublished != 0) then
1
else
0
@[export xahau_naive_sixty_percent_threshold]
def xahauNaiveSixtyPercentThreshold (count : UInt64) : UInt64 :=
(naiveSixtyPercentThreshold count.toNat).toUInt64
@[export xahau_naive_sixty_percent_is_safe]
def xahauNaiveSixtyPercentIsSafe (count : UInt64) : UInt8 :=
if count.toNat + byzantineBound count.toNat <
2 * naiveSixtyPercentThreshold count.toNat then
1
else
0
@[export xahau_disabled_cap]
def xahauDisabledCap (originalView : UInt64) : UInt64 :=
(disabledCap originalView.toNat).toUInt64
@[export xahau_effective_view]
def xahauEffectiveView (originalView disabled : UInt64) : UInt64 :=
(effectiveView originalView.toNat disabled.toNat).toUInt64
-- @@end ffi-scalar-export-surface
end XahauConsensus

View File

@@ -1,88 +0,0 @@
import Mathlib.Data.Finset.Card
import XahauConsensus.ExportQuorum
import XahauConsensus.Intersection
namespace XahauConsensus
/-!
Finite-set bridge for the quorum-intersection arithmetic.
The arithmetic modules prove useful facts from the premise
`a + b <= n + overlap`. This module discharges that premise for actual finite
cohorts `A` and `B` that are both subsets of a common validator universe `U`.
-/
open Finset
/-- Inclusion-exclusion bridge: two finite cohorts inside one universe satisfy
the cardinality premise used by `Intersection.lean`. -/
theorem finset_cardinality_bound
[DecidableEq α]
{U A B : Finset α}
(hA : A U)
(hB : B U) :
A.card + B.card <= U.card + (A B).card := by
have hUnionSubset : A B U := by
intro x hx
rcases Finset.mem_union.mp hx with hxA | hxB
· exact hA hxA
· exact hB hxB
have hUnionCard : (A B).card <= U.card :=
Finset.card_le_card hUnionSubset
have hInclusion :
(A B).card + (A B).card = A.card + B.card :=
Finset.card_union_add_card_inter A B
omega
/-- Set-level Tier-2 form: two participant-threshold cohorts in the same
validator universe overlap above the Byzantine bound. -/
theorem finset_participant_threshold_cohorts_overlap_gt_byzantine
[DecidableEq α]
{U A B : Finset α}
(hAUniverse : A U)
(hBUniverse : B U)
(hAThreshold : participantThreshold U.card <= A.card)
(hBThreshold : participantThreshold U.card <= B.card) :
byzantineBound U.card < (A B).card := by
exact participant_threshold_cohorts_overlap_gt_byzantine
(finset_cardinality_bound hAUniverse hBUniverse)
hAThreshold
hBThreshold
/-- nUNL/set-level form: two original-view participant-threshold cohorts in a
shrunk effective universe still overlap above the original Byzantine bound. -/
theorem finset_participant_threshold_cohorts_overlap_gt_byzantine_under_shrink
[DecidableEq α]
{Original Effective A B : Finset α}
(hEffectiveSubset : Effective Original)
(hAUniverse : A Effective)
(hBUniverse : B Effective)
(hAThreshold : participantThreshold Original.card <= A.card)
(hBThreshold : participantThreshold Original.card <= B.card) :
byzantineBound Original.card < (A B).card := by
have hShrink : Effective.card <= Original.card :=
Finset.card_le_card hEffectiveSubset
exact participant_threshold_cohorts_overlap_gt_byzantine_under_shrink
hShrink
(finset_cardinality_bound hAUniverse hBUniverse)
hAThreshold
hBThreshold
/-- Set-level export form: two 80% export sidecar quorums in the same nonempty
active universe overlap above the standard Byzantine bound. -/
theorem finset_export_hash_quorums_overlap_gt_byzantine
[DecidableEq α]
{U A B : Finset α}
(hNonempty : 0 < U.card)
(hAUniverse : A U)
(hBUniverse : B U)
(hAThreshold : quorumThreshold U.card <= A.card)
(hBThreshold : quorumThreshold U.card <= B.card) :
byzantineBound U.card < (A B).card := by
exact export_hash_quorums_overlap_gt_byzantine
hNonempty
(finset_cardinality_bound hAUniverse hBUniverse)
hAThreshold
hBThreshold
end XahauConsensus

View File

@@ -1,70 +0,0 @@
import XahauConsensus.Intersection
namespace XahauConsensus
/-!
Bridge from cardinality arithmetic to the consensus-language statement:
if cohort overlap is larger than the maximum faulty overlap, then the overlap
contains at least one honest validator.
-/
/-- If the overlap is larger than the number of faulty validators in it, then
some honest validator remains in the overlap. -/
theorem honest_overlap_exists
{overlap faultyInOverlap : Nat}
(hFaultyLtOverlap : faultyInOverlap < overlap) :
0 < overlap - faultyInOverlap := by
omega
/-- If total faulty validators are bounded by `faultBound`, and the overlap is
larger than `faultBound`, then the overlap contains an honest validator. -/
theorem honest_overlap_exists_of_fault_bound
{overlap faultyInOverlap faultBound : Nat}
(hFaultyBound : faultyInOverlap <= faultBound)
(hOverlapGtFaultBound : faultBound < overlap) :
0 < overlap - faultyInOverlap := by
omega
/-- Direct bridge from the abstract two-cohort intersection theorem: two
threshold-sized cohorts under the strict safety inequality have honest overlap,
provided faulty validators in the overlap are bounded by `f`.
-/
theorem honest_overlap_of_two_threshold_cohorts
{n a b overlap threshold faultBound faultyInOverlap : Nat}
(hCardinality : a + b <= n + overlap)
(hA : threshold <= a)
(hB : threshold <= b)
(hSafety : n + faultBound < 2 * threshold)
(hFaultyBound : faultyInOverlap <= faultBound) :
0 < overlap - faultyInOverlap := by
have hOverlapGtFaultBound :
faultBound < overlap :=
overlap_gt_fault_of_two_threshold_cohorts
hCardinality
hA
hB
hSafety
exact honest_overlap_exists_of_fault_bound
hFaultyBound
hOverlapGtFaultBound
/-- Direct participant-threshold form: two Tier-2-sized cohorts in the same
view have honest overlap under the `floor(n/5)` Byzantine bound. -/
theorem honest_overlap_of_participant_threshold_cohorts
{count a b overlap faultyInOverlap : Nat}
(hCardinality : a + b <= count + overlap)
(hA : participantThreshold count <= a)
(hB : participantThreshold count <= b)
(hFaultyBound : faultyInOverlap <= byzantineBound count) :
0 < overlap - faultyInOverlap := by
have hOverlapGtBound :
byzantineBound count < overlap :=
participant_threshold_cohorts_overlap_gt_byzantine
hCardinality
hA
hB
exact honest_overlap_exists_of_fault_bound
hFaultyBound
hOverlapGtBound
end XahauConsensus

View File

@@ -1,96 +0,0 @@
import XahauConsensus.Threshold
namespace XahauConsensus
/-!
Abstract cardinality arithmetic for quorum intersection arguments.
The variables are plain natural-number cardinalities:
* `n`: universe size
* `a`, `b`: cohort sizes
* `o`: overlap size
* `t`: quorum threshold
* `f`: tolerated faulty overlap
The shape `a + b <= n + o` captures the inclusion-exclusion upper bound
without committing to a concrete `Finset` model.
-/
/-- If two threshold-sized cohorts fit in an `n`-sized universe only by
overlapping by `o`, and `n + f < 2 * t`, then the overlap is larger than the
fault bound `f`. -/
theorem overlap_gt_fault_of_two_threshold_cohorts
{n a b o t f : Nat}
(hCardinality : a + b <= n + o)
(hA : t <= a)
(hB : t <= b)
(hSafety : n + f < 2 * t) :
f < o := by
omega
/-- Reviewer-facing contrapositive form: if the overlap is no larger than the
fault bound, then under the strict safety inequality the two cohorts cannot
both meet threshold. -/
theorem not_both_threshold_cohorts_of_overlap_le_fault
{n a b o t f : Nat}
(hOverlap : o <= f)
(hCardinality : a + b <= n + o)
(hSafety : n + f < 2 * t) :
¬ (t <= a t <= b) := by
intro hBoth
have hStrict :
f < o :=
overlap_gt_fault_of_two_threshold_cohorts
hCardinality hBoth.1 hBoth.2 hSafety
omega
/-- Equivalent disjunctive form of the reviewer fact: with insufficient
overlap, at least one candidate cohort must be below threshold. -/
theorem overlap_le_fault_forces_cohort_below_threshold
{n a b o t f : Nat}
(hOverlap : o <= f)
(hCardinality : a + b <= n + o)
(hSafety : n + f < 2 * t) :
a < t b < t := by
have hNotBoth :
¬ (t <= a t <= b) :=
not_both_threshold_cohorts_of_overlap_le_fault
hOverlap hCardinality hSafety
omega
/-- Direct Tier-2 form: two cohorts at the participant threshold in the same
original-view universe must overlap by more than the tolerated Byzantine bound.
-/
theorem participant_threshold_cohorts_overlap_gt_byzantine
{count a b overlap : Nat}
(hCardinality : a + b <= count + overlap)
(hA : participantThreshold count <= a)
(hB : participantThreshold count <= b) :
byzantineBound count < overlap := by
exact overlap_gt_fault_of_two_threshold_cohorts
hCardinality
hA
hB
(participantThreshold_intersection_safe count)
/-- nUNL form: when the effective universe shrinks, the original-view
participant threshold still forces overlap above the original Byzantine bound.
-/
theorem participant_threshold_cohorts_overlap_gt_byzantine_under_shrink
{originalView effectiveView a b overlap : Nat}
(hShrink : effectiveView <= originalView)
(hCardinality : a + b <= effectiveView + overlap)
(hA : participantThreshold originalView <= a)
(hB : participantThreshold originalView <= b) :
byzantineBound originalView < overlap := by
exact overlap_gt_fault_of_two_threshold_cohorts
hCardinality
hA
hB
(participantThreshold_safe_under_effective_shrink
originalView
effectiveView
hShrink)
end XahauConsensus

View File

@@ -1,112 +0,0 @@
import XahauConsensus.Threshold
import XahauConsensus.EntropySelector
import XahauConsensus.ExportGate
namespace XahauConsensus
/-!
Small cross-module invariants that state the design contract in one place.
These do not verify C++ directly. They pin the consensus arguments that the C++
is intended to implement.
-/
/-- Same-count band fact: with both thresholds computed from one view size,
Tier 2 is never stricter than validator quorum. Production nUNL rounds use
cross-view thresholds instead; see `entropyGateThresholdForView`. -/
theorem same_count_tier2_not_stricter_than_validator_quorum (count : Nat) :
safeParticipantThreshold count <= safeQuorumThreshold count :=
safeParticipantThreshold_le_safeQuorumThreshold count
/-- Same-view shorthand: the live entropy gate is the weaker of Tier 2 and
validator quorum, so it is never above validator quorum. -/
def entropyGateThresholdModel (count : Nat) : Nat :=
min (safeQuorumThreshold count) (safeParticipantThreshold count)
theorem entropy_gate_le_validator_quorum (count : Nat) :
entropyGateThresholdModel count <= safeQuorumThreshold count := by
unfold entropyGateThresholdModel
exact Nat.min_le_left _ _
theorem entropy_gate_le_participant_threshold (count : Nat) :
entropyGateThresholdModel count <= safeParticipantThreshold count := by
unfold entropyGateThresholdModel
exact Nat.min_le_right _ _
/-- Production shape: validator quorum is over the effective post-nUNL view,
while Tier 2 is over the original pre-nUNL view. -/
def entropyGateThresholdForView (effectiveView originalView : Nat) : Nat :=
min (safeQuorumThreshold effectiveView) (safeParticipantThreshold originalView)
theorem entropy_gate_for_view_le_validator_quorum
(effectiveView originalView : Nat) :
entropyGateThresholdForView effectiveView originalView <=
safeQuorumThreshold effectiveView := by
unfold entropyGateThresholdForView
exact Nat.min_le_left _ _
theorem entropy_gate_for_view_le_participant_threshold
(effectiveView originalView : Nat) :
entropyGateThresholdForView effectiveView originalView <=
safeParticipantThreshold originalView := by
unfold entropyGateThresholdForView
exact Nat.min_le_right _ _
/-- The entropy gate is exactly the selector's non-fallback boundary: reaching
the lower of the validator-quorum and participant-aligned thresholds is enough
to select a non-fallback tier, and below it the selector falls back. -/
theorem selectEntropyTier_nonfallback_iff_entropy_gate
(participantCount effectiveView originalView : Nat) :
selectEntropyTier true participantCount effectiveView originalView
EntropyTier.consensusFallback
entropyGateThresholdForView effectiveView originalView <=
participantCount := by
unfold selectEntropyTier entropyGateThresholdForView
by_cases hQuorum : safeQuorumThreshold effectiveView <= participantCount
· constructor
· intro _
exact Nat.le_trans (Nat.min_le_left _ _) hQuorum
· intro _
simp [hQuorum]
· by_cases hParticipant :
safeParticipantThreshold originalView <= participantCount
· constructor
· intro _
exact Nat.le_trans (Nat.min_le_right _ _) hParticipant
· intro _
simp [hQuorum, hParticipant]
· constructor
· intro hNonfallback
simp [hQuorum, hParticipant] at hNonfallback
· intro hGate
have hBelowQuorum :
participantCount < safeQuorumThreshold effectiveView :=
Nat.lt_of_not_ge hQuorum
have hBelowParticipant :
participantCount < safeParticipantThreshold originalView :=
Nat.lt_of_not_ge hParticipant
have hBelowGate :
participantCount <
min (safeQuorumThreshold effectiveView)
(safeParticipantThreshold originalView) :=
(Nat.lt_min).mpr hBelowQuorum, hBelowParticipant
exact False.elim (Nat.not_lt_of_ge hGate hBelowGate)
/-- Until the view is ledger-anchored, entropy tier labeling fails closed. -/
theorem non_unl_report_cannot_mint_nonfallback
(participantCount effectiveView originalView : Nat) :
selectEntropyTier false participantCount effectiveView originalView =
EntropyTier.consensusFallback :=
no_unl_report_selects_fallback participantCount effectiveView originalView
/-- Export success is a quorum-alignment property, not a full-observation
property. -/
theorem export_success_independent_of_full_observation
(alignedParticipants quorumThreshold : Nat) :
(ExportGate.mk alignedParticipants quorumThreshold true).proceed =
(ExportGate.mk alignedParticipants quorumThreshold false).proceed :=
changing_fullObservation_alone_does_not_change_proceed
alignedParticipants
quorumThreshold
end XahauConsensus

View File

@@ -1,147 +0,0 @@
import XahauConsensus.Threshold
namespace XahauConsensus
/-!
Arithmetic facts for nUNL-capped view shrinkage.
The examples here intentionally use the original view for the participant
floor and the effective post-nUNL view for validator quorum. That is the
cross-view comparison that matters when disabled validators collapse the space
between the Tier-2 participant floor and the Tier-3 validator-quorum floor.
-/
/-- Integer ceiling division, defined defensively for `d = 0`. -/
def ceilDiv (n d : Nat) : Nat :=
if d = 0 then 0 else (n + d - 1) / d
/-- The protocol's ceil-25% nUNL disablement cap for an original validator view. -/
def disabledCap (originalView : Nat) : Nat :=
ceilDiv originalView 4
/-- The post-nUNL effective validator view after `disabled` validators drop. -/
def effectiveView (originalView disabled : Nat) : Nat :=
originalView - disabled
theorem ceilDiv_zero_right (n : Nat) : ceilDiv n 0 = 0 := by
simp [ceilDiv]
theorem ceilDiv_four_eight : ceilDiv 8 4 = 2 := by
native_decide
theorem ceilDiv_four_ten : ceilDiv 10 4 = 3 := by
native_decide
theorem ceilDiv_four_twenty : ceilDiv 20 4 = 5 := by
native_decide
theorem disabledCap_eight : disabledCap 8 = 2 := by
native_decide
theorem disabledCap_ten : disabledCap 10 = 3 := by
native_decide
theorem disabledCap_twenty : disabledCap 20 = 5 := by
native_decide
theorem effectiveView_eight_at_disabledCap :
effectiveView 8 (disabledCap 8) = 6 := by
native_decide
theorem effectiveView_ten_at_disabledCap :
effectiveView 10 (disabledCap 10) = 7 := by
native_decide
theorem effectiveView_twenty_at_disabledCap :
effectiveView 20 (disabledCap 20) = 15 := by
native_decide
/-- Original 8 with two disabled validators collapses the participant/quorum band. -/
theorem band_collapse_original8_effective6 :
quorumThreshold 6 = participantThreshold 8 := by
native_decide
theorem quorum_original8_effective6_meets_participant_floor :
participantThreshold 8 <= quorumThreshold 6 := by
native_decide
/-- Original 10 with two disabled validators collapses the participant/quorum band. -/
theorem band_collapse_original10_effective8 :
quorumThreshold 8 = participantThreshold 10 := by
native_decide
theorem quorum_original10_effective8_meets_participant_floor :
participantThreshold 10 <= quorumThreshold 8 := by
native_decide
/-- Original 10 at the full ceil-25% cap leaves effective view 7, below the participant floor. -/
theorem quorum_original10_effective7_below_participant_floor :
quorumThreshold 7 < participantThreshold 10 := by
native_decide
theorem max_cap_original10_below_participant_floor :
quorumThreshold (effectiveView 10 (disabledCap 10)) <
participantThreshold 10 := by
native_decide
/-- At original 20, the full ceil-25% cap leaves effective view 15, which is too small. -/
theorem quorum_original20_effective15_below_participant_floor :
quorumThreshold 15 < participantThreshold 20 := by
native_decide
theorem quorum_original20_effective15_does_not_meet_participant_floor :
¬ participantThreshold 20 <= quorumThreshold 15 := by
native_decide
/-- Original 20 with four disabled validators collapses the participant/quorum band. -/
theorem band_collapse_original20_effective16 :
quorumThreshold 16 = participantThreshold 20 := by
native_decide
theorem quorum_original20_effective16_meets_participant_floor :
participantThreshold 20 <= quorumThreshold 16 := by
native_decide
/-- The ceil-25% cap does not by itself guarantee collapse at size 20. -/
theorem max_cap_original20_below_participant_floor :
quorumThreshold (effectiveView 20 (disabledCap 20)) <
participantThreshold 20 := by
native_decide
/--
General cross-view comparison: an effective-view quorum satisfies the
original-view participant floor whenever that quorum clears the original
intersection boundary.
-/
theorem quorumThreshold_meets_participantThreshold_of_intersection_premise
{originalView effectiveView : Nat}
(h :
originalView + byzantineBound originalView <
2 * quorumThreshold effectiveView) :
participantThreshold originalView <= quorumThreshold effectiveView := by
exact participantThreshold_minimal originalView (quorumThreshold effectiveView) h
/--
Once the effective-view quorum threshold meets the original-view participant
floor, any validator count meeting validator quorum also meets the participant
floor anchored to the original view.
-/
theorem validators_meet_participant_floor_of_meet_quorum
{originalView effectiveView validators : Nat}
(hBand : participantThreshold originalView <= quorumThreshold effectiveView)
(hQuorum : quorumThreshold effectiveView <= validators) :
participantThreshold originalView <= validators :=
Nat.le_trans hBand hQuorum
/-- If cross-view quorum is no higher than the participant floor, the in-between band is empty. -/
theorem cross_view_participant_band_empty
{originalView effectiveView : Nat}
(hCollapse : quorumThreshold effectiveView <= participantThreshold originalView) :
¬ participants,
participantThreshold originalView <= participants
participants < quorumThreshold effectiveView := by
intro hExists
rcases hExists with participants, hParticipant, hBelowQuorum
omega
end XahauConsensus

View File

@@ -1,64 +0,0 @@
import XahauConsensus.EntropySelector
namespace XahauConsensus
/-- A minimal digest model: the payload is opaque to the selector, while the
label is the entropy tier chosen from the consensus metadata. -/
structure LabeledDigest (α : Type) where
payload : α
label : EntropyTier
deriving Repr
def labelDigest
(fromUNLReport : Bool)
(participantCount effectiveView originalView : Nat)
(payload : α) : LabeledDigest α :=
{ payload
label :=
selectEntropyTier
fromUNLReport
participantCount
effectiveView
originalView }
/-- The digest payload itself does not affect the selected tier. The label is
entirely determined by the consensus metadata. -/
theorem payload_does_not_affect_tier
{α : Type}
{payloadA payloadB : α}
(fromUNLReport : Bool)
(participantCount effectiveView originalView : Nat) :
(labelDigest
fromUNLReport
participantCount
effectiveView
originalView
payloadA).label =
(labelDigest
fromUNLReport
participantCount
effectiveView
originalView
payloadB).label := by
rfl
/-- Without a UNLReport anchor the same count and views can receive a different
label. -/
theorem label_can_differ_when_fromUNLReport_differs :
(labelDigest true 8 10 10 0).label
(labelDigest false 8 10 10 0).label := by
native_decide
/-- Changing the effective validator view can change the digest label. -/
theorem label_can_differ_when_effective_view_differs :
(labelDigest true 7 8 10 0).label
(labelDigest true 7 10 10 0).label := by
native_decide
/-- Changing the original validator view can change the digest label. -/
theorem label_can_differ_when_original_view_differs :
(labelDigest true 6 10 8 0).label
(labelDigest true 6 10 10 0).label := by
native_decide
end XahauConsensus

View File

@@ -1,241 +0,0 @@
namespace XahauConsensus
/-- Count a local boolean contribution as the `Nat` value used in threshold
comparisons. -/
def localPublishedCount (localPublished : Bool) : Nat :=
if localPublished then 1 else 0
/-- The proof-level participant count behind sidecar alignment.
`aligned` is the count of aligned remote active-view participants; a local
publication contributes one more participant. -/
def alignedParticipants
(aligned : Nat)
(localIsMember localPublished : Bool) : Nat :=
aligned + localPublishedCount (localIsMember && localPublished)
/-- Sidecar quorum predicate, kept boolean to mirror the implementation check. -/
def quorumAligned
(threshold aligned : Nat)
(localIsMember localPublished : Bool) : Bool :=
decide (threshold <= alignedParticipants aligned localIsMember localPublished)
/-- Full sidecar observation means every converged transaction has been seen. -/
def fullObservation (peersSeen txConverged : Nat) : Bool :=
peersSeen == txConverged
/-- Count aligned peers from a finite peer prefix, filtering through the active
view before any alignment bit contributes. -/
def activeAlignedCount
(inActiveView peerAligned : Nat Bool) : Nat Nat
| 0 => 0
| peer + 1 =>
activeAlignedCount inActiveView peerAligned peer +
localPublishedCount (inActiveView peer && peerAligned peer)
theorem localPublishedCount_true :
localPublishedCount true = 1 := by
rfl
theorem localPublishedCount_false :
localPublishedCount false = 0 := by
rfl
theorem localPublishedCount_le_one (published : Bool) :
localPublishedCount published <= 1 := by
cases published <;> simp [localPublishedCount]
/-- Core participant-count equation: aligned remotes plus the local published
contribution. -/
theorem alignedParticipants_eq_aligned_plus_localPublished
(aligned : Nat) (localIsMember localPublished : Bool) :
alignedParticipants aligned localIsMember localPublished =
aligned + localPublishedCount (localIsMember && localPublished) := by
rfl
/-- A non-active local node cannot pad the participant count. -/
theorem alignedParticipants_local_nonmember
(aligned : Nat) (localPublished : Bool) :
alignedParticipants aligned false localPublished = aligned := by
cases localPublished <;> rfl
/-- An active local node contributes exactly when it published the sidecar hash. -/
theorem alignedParticipants_local_member
(aligned : Nat) (localPublished : Bool) :
alignedParticipants aligned true localPublished =
aligned + localPublishedCount localPublished := by
cases localPublished <;> rfl
/-- The local node can add at most one participant to the remote aligned count. -/
theorem alignedParticipants_le_aligned_succ
(aligned : Nat) (localIsMember localPublished : Bool) :
alignedParticipants aligned localIsMember localPublished <= aligned + 1 := by
cases localIsMember <;> cases localPublished <;>
simp [alignedParticipants, localPublishedCount]
/-- The boolean quorum predicate is exactly the threshold comparison over
`alignedParticipants`. -/
theorem quorumAligned_iff_threshold_le_alignedParticipants
(threshold aligned : Nat) (localIsMember localPublished : Bool) :
quorumAligned threshold aligned localIsMember localPublished = true
threshold <= alignedParticipants aligned localIsMember localPublished := by
unfold quorumAligned
simp
/-- The boolean full-observation predicate is exactly equality of the observed
and converged counts. -/
theorem fullObservation_iff_peersSeen_eq_txConverged
(peersSeen txConverged : Nat) :
fullObservation peersSeen txConverged = true
peersSeen = txConverged := by
unfold fullObservation
simp
/-- A peer outside the active view contributes zero, even if its sidecar
alignment bit is set. -/
theorem activeAlignedCount_succ_nonmember
{inActiveView peerAligned : Nat Bool} {peer : Nat}
(hNonmember : inActiveView peer = false) :
activeAlignedCount inActiveView peerAligned (peer + 1) =
activeAlignedCount inActiveView peerAligned peer := by
simp [activeAlignedCount, hNonmember, localPublishedCount]
/-- A prefix of `n` peer positions can contribute at most `n` aligned active
remote participants. -/
theorem activeAlignedCount_le_prefix
(inActiveView peerAligned : Nat Bool) (n : Nat) :
activeAlignedCount inActiveView peerAligned n <= n := by
induction n with
| zero =>
simp [activeAlignedCount]
| succ n ih =>
cases hAligned : inActiveView n && peerAligned n
· simp [activeAlignedCount, hAligned, localPublishedCount]
exact Nat.le_trans ih (Nat.le_succ n)
· simp [activeAlignedCount, hAligned, localPublishedCount]
exact ih
/-- With the optional local contribution included, the participant count is
bounded by the inspected remote prefix plus one. -/
theorem alignedParticipants_le_prefix_succ
(inActiveView peerAligned : Nat Bool)
(n : Nat)
(localIsMember localPublished : Bool) :
alignedParticipants
(activeAlignedCount inActiveView peerAligned n)
localIsMember
localPublished <= n + 1 := by
have hRemote := activeAlignedCount_le_prefix inActiveView peerAligned n
cases localIsMember <;> cases localPublished <;>
simp [alignedParticipants, localPublishedCount]
· exact Nat.le_trans hRemote (Nat.le_succ n)
· exact Nat.le_trans hRemote (Nat.le_succ n)
· exact Nat.le_trans hRemote (Nat.le_succ n)
· exact hRemote
/-- Adding a nonmember peer to the inspected prefix cannot increase
`alignedParticipants`. -/
theorem alignedParticipants_succ_nonmember
{inActiveView peerAligned : Nat Bool} {peer : Nat}
(localIsMember localPublished : Bool)
(hNonmember : inActiveView peer = false) :
alignedParticipants
(activeAlignedCount inActiveView peerAligned (peer + 1))
localIsMember
localPublished =
alignedParticipants
(activeAlignedCount inActiveView peerAligned peer)
localIsMember
localPublished := by
simp [alignedParticipants, activeAlignedCount_succ_nonmember hNonmember]
/-- Consequently, a nonmember peer cannot change the quorum-aligned result. -/
theorem quorumAligned_succ_nonmember
{inActiveView peerAligned : Nat Bool} {peer threshold : Nat}
(localIsMember localPublished : Bool)
(hNonmember : inActiveView peer = false) :
quorumAligned threshold
(activeAlignedCount inActiveView peerAligned (peer + 1))
localIsMember
localPublished =
quorumAligned threshold
(activeAlignedCount inActiveView peerAligned peer)
localIsMember
localPublished := by
simp [
quorumAligned,
alignedParticipants_succ_nonmember
localIsMember
localPublished
hNonmember]
/-- Active-view filtering: only member peers' alignment bits can affect the
aligned remote count. -/
theorem activeAlignedCount_ext_on_members
{n : Nat} {inActiveView alignedA alignedB : Nat Bool}
(hSameOnMembers :
peer, peer < n inActiveView peer = true
alignedA peer = alignedB peer) :
activeAlignedCount inActiveView alignedA n =
activeAlignedCount inActiveView alignedB n := by
induction n with
| zero =>
rfl
| succ n ih =>
have hPrefix :
peer, peer < n inActiveView peer = true
alignedA peer = alignedB peer := by
intro peer hLt hMember
exact hSameOnMembers peer (Nat.lt_trans hLt (Nat.lt_succ_self n)) hMember
have hAt :
localPublishedCount (inActiveView n && alignedA n) =
localPublishedCount (inActiveView n && alignedB n) := by
cases hMember : inActiveView n
· simp [localPublishedCount]
· have hEq := hSameOnMembers n (Nat.lt_succ_self n) hMember
simp [hEq, localPublishedCount]
simp [activeAlignedCount, ih hPrefix, hAt]
/-- Changing sidecar alignment reports for nonmembers cannot change the final
participant count. -/
theorem alignedParticipants_ext_on_members
{n : Nat} {inActiveView alignedA alignedB : Nat Bool}
{localIsMember : Bool}
{localPublished : Bool}
(hSameOnMembers :
peer, peer < n inActiveView peer = true
alignedA peer = alignedB peer) :
alignedParticipants
(activeAlignedCount inActiveView alignedA n)
localIsMember
localPublished =
alignedParticipants
(activeAlignedCount inActiveView alignedB n)
localIsMember
localPublished := by
simp [
alignedParticipants,
activeAlignedCount_ext_on_members hSameOnMembers]
/-- Changing sidecar alignment reports for nonmembers cannot turn quorum on or
off. -/
theorem quorumAligned_ext_on_members
{n threshold : Nat} {inActiveView alignedA alignedB : Nat Bool}
{localIsMember : Bool}
{localPublished : Bool}
(hSameOnMembers :
peer, peer < n inActiveView peer = true
alignedA peer = alignedB peer) :
quorumAligned threshold
(activeAlignedCount inActiveView alignedA n)
localIsMember
localPublished =
quorumAligned threshold
(activeAlignedCount inActiveView alignedB n)
localIsMember
localPublished := by
simp [
quorumAligned,
alignedParticipants_ext_on_members hSameOnMembers]
end XahauConsensus

View File

@@ -1,56 +0,0 @@
import XahauConsensus.Threshold
namespace XahauConsensus
/-!
Review-oriented facts about the tempting `ceil(60%)` participant threshold.
The live `participantThreshold` is one higher than naive 60% at exact
multiples of five. That extra vote is what turns equality at the
Byzantine-overlap boundary into strict intersection safety.
-/
/-- A naive `ceil(0.6 * count)` threshold. -/
def naiveSixtyPercentThreshold (count : Nat) : Nat :=
(count * 60 + 99) / 100
theorem naiveSixtyPercentThreshold_five_mul (k : Nat) :
naiveSixtyPercentThreshold (5 * k) = 3 * k := by
unfold naiveSixtyPercentThreshold
omega
theorem participantThreshold_five_mul_eq_naiveSixtyPercentThreshold_succ
(k : Nat) :
participantThreshold (5 * k) =
naiveSixtyPercentThreshold (5 * k) + 1 := by
unfold participantThreshold byzantineBound naiveSixtyPercentThreshold
omega
/-- At exact multiples of five, naive 60% only reaches the unsafe boundary. -/
theorem naiveSixtyPercentThreshold_five_mul_hits_intersection_boundary
(k : Nat) :
2 * naiveSixtyPercentThreshold (5 * k) =
5 * k + byzantineBound (5 * k) := by
unfold naiveSixtyPercentThreshold byzantineBound
omega
theorem naiveSixtyPercentThreshold_five_mul_not_intersection_safe
(k : Nat) :
¬ 5 * k + byzantineBound (5 * k) <
2 * naiveSixtyPercentThreshold (5 * k) := by
rw [naiveSixtyPercentThreshold_five_mul_hits_intersection_boundary k]
omega
theorem participantThreshold_five_mul_intersection_safe (k : Nat) :
5 * k + byzantineBound (5 * k) <
2 * participantThreshold (5 * k) := by
exact participantThreshold_intersection_safe (5 * k)
/-- At exact multiples of five, the live threshold clears the boundary by two. -/
theorem participantThreshold_five_mul_intersection_margin (k : Nat) :
2 * participantThreshold (5 * k) =
(5 * k + byzantineBound (5 * k)) + 2 := by
unfold participantThreshold byzantineBound
omega
end XahauConsensus

View File

@@ -1,124 +0,0 @@
namespace XahauConsensus
/-- C++: `count / 5`, the conservative Byzantine bound used by
`calculateParticipantThreshold`. -/
def byzantineBound (count : Nat) : Nat :=
count / 5
/-- C++: `calculateParticipantThreshold(count)`.
This is the smallest integer `t` satisfying `2 * t > count + floor(count / 5)`.
-/
def participantThreshold (count : Nat) : Nat :=
(count + byzantineBound count) / 2 + 1
/-- C++: `calculateQuorumThreshold(count)`, i.e. `ceil(0.8 * count)`. -/
def quorumThreshold (count : Nat) : Nat :=
(count * 80 + 99) / 100
/-- C++: `ConsensusExtensions::quorumThreshold()`.
The raw formula gives `0` for an empty view, but the live consensus-extension
gate requires at least one aligned participant for safety.
-/
def safeQuorumThreshold (count : Nat) : Nat :=
if count = 0 then 1 else quorumThreshold count
/-- C++: `ConsensusExtensions::tier2Threshold()`.
`participantThreshold 0` already returns `1`; this wrapper makes the
zero-view safety rule explicit and mirrors the C++ method shape.
-/
def safeParticipantThreshold (count : Nat) : Nat :=
if count = 0 then 1 else participantThreshold count
/-- The Tier-2 threshold strictly exceeds the Byzantine-overlap boundary.
This is the load-bearing equivocation invariant behind participant-aligned
entropy: two cohorts of this size in a `count`-sized universe overlap in more
than `floor(count / 5)` validators.
-/
theorem participantThreshold_intersection_safe (count : Nat) :
count + byzantineBound count < 2 * participantThreshold count := by
unfold participantThreshold byzantineBound
omega
/-- Anchoring the Tier-2 threshold to the original pre-nUNL view remains safe
when the effective post-nUNL view shrinks.
This is the arithmetic reason `originalViewSize` is the right denominator:
smaller effective universes only increase the intersection margin.
-/
theorem participantThreshold_safe_under_effective_shrink
(originalView effectiveView : Nat)
(hShrink : effectiveView <= originalView) :
effectiveView + byzantineBound originalView <
2 * participantThreshold originalView := by
have hSafe := participantThreshold_intersection_safe originalView
omega
/-- Concrete regression example: if `originalView = 10` and `effectiveView = 8`,
using the effective view's participant threshold (`5`) leaves the overlap equal
to the original-view Byzantine bound (`2`), not strictly greater than it.
This is why the C++ must not replace `originalViewSize` with `size()` for the
Tier-2 floor.
-/
theorem effective_threshold_regression_hits_boundary_example :
2 * participantThreshold 8 <= 8 + byzantineBound 10 := by
native_decide
theorem threshold_minimal_for_boundary (boundary threshold : Nat) :
boundary < 2 * threshold boundary / 2 + 1 <= threshold := by
omega
theorem below_threshold_not_safe_for_boundary (boundary threshold : Nat) :
threshold < boundary / 2 + 1 2 * threshold <= boundary := by
omega
/-- `participantThreshold` is the smallest threshold satisfying the strict
intersection-safety inequality. -/
theorem participantThreshold_minimal (count threshold : Nat) :
count + byzantineBound count < 2 * threshold
participantThreshold count <= threshold := by
intro hSafe
unfold participantThreshold
exact threshold_minimal_for_boundary
(count + byzantineBound count)
threshold
hSafe
/-- Anything below `participantThreshold` fails the strict intersection-safety
inequality. -/
theorem below_participantThreshold_not_safe (count threshold : Nat) :
threshold < participantThreshold count
2 * threshold <= count + byzantineBound count := by
intro hBelow
unfold participantThreshold at hBelow
exact below_threshold_not_safe_for_boundary
(count + byzantineBound count)
threshold
hBelow
/-- The participant threshold never exceeds the 80% validator-quorum threshold.
This is useful because Tier 2 should form a band below Tier 3, not a stricter
condition than validator quorum.
-/
theorem participantThreshold_le_quorumThreshold (count : Nat) :
0 < count participantThreshold count <= quorumThreshold count := by
intro hCount
unfold participantThreshold quorumThreshold byzantineBound
omega
/-- With the live safety wrappers, the participant threshold never exceeds the
validator-quorum threshold, including the empty-view edge case. -/
theorem safeParticipantThreshold_le_safeQuorumThreshold (count : Nat) :
safeParticipantThreshold count <= safeQuorumThreshold count := by
unfold safeParticipantThreshold safeQuorumThreshold
by_cases hZero : count = 0
· simp [hZero]
· have hPositive : 0 < count := Nat.pos_of_ne_zero hZero
simp [hZero, participantThreshold_le_quorumThreshold count hPositive]
end XahauConsensus

View File

@@ -1,223 +0,0 @@
import XahauConsensus.Threshold
namespace XahauConsensus
/-!
Additional arithmetic facts about the Xahau consensus thresholds.
These lemmas are deliberately small and review-oriented: they expose concrete
edge cases, exact multiples-of-five behavior, participant/quorum band facts,
and monotonicity of the threshold functions.
-/
theorem byzantineBound_zero : byzantineBound 0 = 0 := by
native_decide
theorem participantThreshold_zero : participantThreshold 0 = 1 := by
native_decide
theorem quorumThreshold_zero : quorumThreshold 0 = 0 := by
native_decide
theorem safeQuorumThreshold_zero : safeQuorumThreshold 0 = 1 := by
native_decide
theorem safeParticipantThreshold_zero : safeParticipantThreshold 0 = 1 := by
native_decide
theorem byzantineBound_one : byzantineBound 1 = 0 := by
native_decide
theorem participantThreshold_one : participantThreshold 1 = 1 := by
native_decide
theorem quorumThreshold_one : quorumThreshold 1 = 1 := by
native_decide
theorem safeQuorumThreshold_one : safeQuorumThreshold 1 = 1 := by
native_decide
theorem safeParticipantThreshold_one : safeParticipantThreshold 1 = 1 := by
native_decide
theorem participantThreshold_two : participantThreshold 2 = 2 := by
native_decide
theorem quorumThreshold_two : quorumThreshold 2 = 2 := by
native_decide
theorem participantThreshold_three : participantThreshold 3 = 2 := by
native_decide
theorem quorumThreshold_three : quorumThreshold 3 = 3 := by
native_decide
theorem participantThreshold_four : participantThreshold 4 = 3 := by
native_decide
theorem quorumThreshold_four : quorumThreshold 4 = 4 := by
native_decide
theorem byzantineBound_five : byzantineBound 5 = 1 := by
native_decide
theorem participantThreshold_five : participantThreshold 5 = 4 := by
native_decide
theorem quorumThreshold_five : quorumThreshold 5 = 4 := by
native_decide
theorem byzantineBound_ten : byzantineBound 10 = 2 := by
native_decide
theorem participantThreshold_ten : participantThreshold 10 = 7 := by
native_decide
theorem quorumThreshold_ten : quorumThreshold 10 = 8 := by
native_decide
theorem byzantineBound_twenty : byzantineBound 20 = 4 := by
native_decide
theorem participantThreshold_twenty : participantThreshold 20 = 13 := by
native_decide
theorem quorumThreshold_twenty : quorumThreshold 20 = 16 := by
native_decide
theorem byzantineBound_five_mul (k : Nat) :
byzantineBound (5 * k) = k := by
unfold byzantineBound
omega
theorem participantThreshold_five_mul (k : Nat) :
participantThreshold (5 * k) = 3 * k + 1 := by
unfold participantThreshold byzantineBound
omega
theorem quorumThreshold_five_mul (k : Nat) :
quorumThreshold (5 * k) = 4 * k := by
unfold quorumThreshold
omega
/-- On exact multiples of five, the strict safety margin is exactly two. -/
theorem participantThreshold_five_mul_margin (k : Nat) :
2 * participantThreshold (5 * k) =
(5 * k + byzantineBound (5 * k)) + 2 := by
rw [participantThreshold_five_mul, byzantineBound_five_mul]
omega
/-- One below the multiple-of-five participant threshold reaches only equality
with the unsafe boundary, so the strict safety inequality fails. -/
theorem below_participantThreshold_five_mul_hits_boundary (k : Nat) :
2 * (participantThreshold (5 * k) - 1) =
5 * k + byzantineBound (5 * k) := by
rw [participantThreshold_five_mul, byzantineBound_five_mul]
omega
theorem participantThreshold_five_mul_lt_quorumThreshold_five_mul
{k : Nat} (h : 1 < k) :
participantThreshold (5 * k) < quorumThreshold (5 * k) := by
rw [participantThreshold_five_mul, quorumThreshold_five_mul]
omega
theorem participantThreshold_five_eq_quorumThreshold_five :
participantThreshold 5 = quorumThreshold 5 := by
native_decide
theorem participantThreshold_ten_lt_quorumThreshold_ten :
participantThreshold 10 < quorumThreshold 10 := by
native_decide
theorem participant_band_nonempty {count : Nat}
(h : participantThreshold count < quorumThreshold count) :
participants,
participantThreshold count <= participants
participants < quorumThreshold count := by
exact participantThreshold count, Nat.le_refl _, h
theorem participant_band_empty {count : Nat}
(h : quorumThreshold count <= participantThreshold count) :
¬ participants,
participantThreshold count <= participants
participants < quorumThreshold count := by
intro hExists
rcases hExists with participants, hParticipant, hBelowQuorum
omega
theorem participant_band_empty_zero :
¬ participants,
participantThreshold 0 <= participants
participants < quorumThreshold 0 := by
apply participant_band_empty
native_decide
theorem participant_band_empty_one :
¬ participants,
participantThreshold 1 <= participants
participants < quorumThreshold 1 := by
apply participant_band_empty
native_decide
theorem participant_band_empty_two :
¬ participants,
participantThreshold 2 <= participants
participants < quorumThreshold 2 := by
apply participant_band_empty
native_decide
theorem participant_band_empty_five :
¬ participants,
participantThreshold 5 <= participants
participants < quorumThreshold 5 := by
apply participant_band_empty
native_decide
theorem participant_band_nonempty_three :
participants,
participantThreshold 3 <= participants
participants < quorumThreshold 3 := by
apply participant_band_nonempty
native_decide
theorem participant_band_nonempty_four :
participants,
participantThreshold 4 <= participants
participants < quorumThreshold 4 := by
apply participant_band_nonempty
native_decide
theorem participant_band_nonempty_ten :
participants,
participantThreshold 10 <= participants
participants < quorumThreshold 10 := by
apply participant_band_nonempty
native_decide
theorem participant_band_nonempty_five_mul {k : Nat} (h : 1 < k) :
participants,
participantThreshold (5 * k) <= participants
participants < quorumThreshold (5 * k) := by
exact participant_band_nonempty
(participantThreshold_five_mul_lt_quorumThreshold_five_mul h)
theorem byzantineBound_mono {a b : Nat} (h : a <= b) :
byzantineBound a <= byzantineBound b := by
unfold byzantineBound
exact Nat.div_le_div_right h
theorem participantThreshold_mono {a b : Nat} (h : a <= b) :
participantThreshold a <= participantThreshold b := by
unfold participantThreshold
apply Nat.succ_le_succ
apply Nat.div_le_div_right
have hByzantine := byzantineBound_mono h
omega
theorem quorumThreshold_mono {a b : Nat} (h : a <= b) :
quorumThreshold a <= quorumThreshold b := by
unfold quorumThreshold
apply Nat.div_le_div_right
omega
end XahauConsensus

View File

@@ -1,201 +0,0 @@
import XahauConsensus.ThresholdFacts
namespace XahauConsensus
/-!
Concrete arithmetic examples for the distinction between the active effective
view, the original pre-nUNL view, and any larger trusted counting universe.
The safety shape is deliberately Nat-only: two cohorts of size `threshold` in
an `activeView` overlap strictly beyond the Byzantine bound charged to
`byzantineUniverse` when
`activeView + byzantineBound byzantineUniverse < 2 * threshold`.
-/
def strictIntersectionSafe
(activeView byzantineUniverse threshold : Nat) : Prop :=
activeView + byzantineBound byzantineUniverse < 2 * threshold
/-- Strict intersection safety plus reachability of the threshold inside the
active view. This separates "safe if it happens" from "possible to happen". -/
def nonvacuousStrictIntersectionSafe
(activeView byzantineUniverse threshold : Nat) : Prop :=
threshold <= activeView strictIntersectionSafe activeView byzantineUniverse threshold
/-- Cross-view Tier-2 band: participant floor is anchored to the original view,
validator quorum to the effective view. -/
def participantBandNonempty
(effectiveView originalView : Nat) : Prop :=
participants,
participantThreshold originalView <= participants
participants < quorumThreshold effectiveView
theorem participantBandNonempty_iff
(effectiveView originalView : Nat) :
participantBandNonempty effectiveView originalView
participantThreshold originalView < quorumThreshold effectiveView := by
constructor
· intro h
rcases h with participants, hParticipant, hBelowQuorum
omega
· intro h
exact participantThreshold originalView, Nat.le_refl _, h
/-- The original-view participant threshold remains safe when nUNL shrinks the
active effective view. -/
theorem original_threshold_safe_under_nunl_shrink
{originalView effectiveView : Nat}
(hShrink : effectiveView <= originalView) :
strictIntersectionSafe
effectiveView
originalView
(participantThreshold originalView) := by
unfold strictIntersectionSafe
exact participantThreshold_safe_under_effective_shrink
originalView
effectiveView
hShrink
theorem original_threshold_nonvacuous_under_nunl_shrink
{originalView effectiveView : Nat}
(hShrink : effectiveView <= originalView)
(hReachable : participantThreshold originalView <= effectiveView) :
nonvacuousStrictIntersectionSafe
effectiveView
originalView
(participantThreshold originalView) := by
constructor
· exact hReachable
· exact original_threshold_safe_under_nunl_shrink hShrink
/-- The original-view threshold is also safe if the Byzantine counting universe
is no larger than the original view. -/
theorem original_threshold_safe_for_no_larger_counting_universe
{originalView effectiveView countingUniverse : Nat}
(hShrink : effectiveView <= originalView)
(hCounting : countingUniverse <= originalView) :
strictIntersectionSafe
effectiveView
countingUniverse
(participantThreshold originalView) := by
unfold strictIntersectionSafe
have hOriginal :=
participantThreshold_safe_under_effective_shrink
originalView
effectiveView
hShrink
have hBound := byzantineBound_mono hCounting
omega
/-- Any threshold at or below the overlap boundary is not strictly safe. -/
theorem not_strictIntersectionSafe_of_threshold_le_boundary
{activeView byzantineUniverse threshold : Nat}
(hBoundary : 2 * threshold <= activeView + byzantineBound byzantineUniverse) :
¬ strictIntersectionSafe activeView byzantineUniverse threshold := by
unfold strictIntersectionSafe
omega
/-- If the effective-view threshold is below what the original Byzantine bound
requires, it cannot prove strict intersection safety against that original
bound. -/
theorem effective_threshold_not_safe_against_original_bound
{originalView effectiveView : Nat}
(hBelow :
participantThreshold effectiveView <
(effectiveView + byzantineBound originalView) / 2 + 1) :
¬ strictIntersectionSafe
effectiveView
originalView
(participantThreshold effectiveView) := by
apply not_strictIntersectionSafe_of_threshold_le_boundary
exact below_threshold_not_safe_for_boundary
(effectiveView + byzantineBound originalView)
(participantThreshold effectiveView)
hBelow
/-- A larger trusted counting universe increases the Byzantine side of the
boundary, eroding the strict-intersection margin. -/
theorem original_boundary_le_trusted_superset_boundary
{originalView effectiveView trustedUniverse : Nat}
(hSuperset : originalView <= trustedUniverse) :
effectiveView + byzantineBound originalView <=
effectiveView + byzantineBound trustedUniverse := by
have hBound := byzantineBound_mono hSuperset
omega
/-- Concrete nUNL example: `originalView = 10`, `effectiveView = 8`, and the
original threshold still clears the original Byzantine bound. -/
theorem original_ten_effective_eight_original_threshold_safe :
strictIntersectionSafe 8 10 (participantThreshold 10) := by
unfold strictIntersectionSafe
native_decide
theorem original_ten_effective_eight_participant_band_empty :
¬ participantBandNonempty 8 10 := by
rw [participantBandNonempty_iff]
native_decide
theorem original_ten_effective_eight_original_threshold_reachable :
nonvacuousStrictIntersectionSafe 8 10 (participantThreshold 10) := by
apply original_threshold_nonvacuous_under_nunl_shrink
· native_decide
· native_decide
/-- Concrete regression: for `originalView = 10` and `effectiveView = 8`, the
effective threshold does not strictly clear the original Byzantine bound. -/
theorem original_ten_effective_eight_effective_threshold_not_safe :
¬ strictIntersectionSafe 8 10 (participantThreshold 8) := by
apply not_strictIntersectionSafe_of_threshold_le_boundary
native_decide
/-- The same failure as a direct boundary comparison, useful when reviewing the
raw arithmetic. -/
theorem original_ten_effective_eight_effective_threshold_hits_boundary :
2 * participantThreshold 8 <= 8 + byzantineBound 10 := by
native_decide
/-- Larger concrete nUNL example with the original threshold anchored at
`20`. -/
theorem original_twenty_effective_sixteen_original_threshold_safe :
strictIntersectionSafe 16 20 (participantThreshold 20) := by
unfold strictIntersectionSafe
native_decide
theorem original_twenty_effective_sixteen_participant_band_empty :
¬ participantBandNonempty 16 20 := by
rw [participantBandNonempty_iff]
native_decide
theorem original_twenty_effective_fifteen_participant_band_empty :
¬ participantBandNonempty 15 20 := by
rw [participantBandNonempty_iff]
native_decide
theorem original_twenty_effective_fifteen_original_threshold_reachable :
nonvacuousStrictIntersectionSafe 15 20 (participantThreshold 20) := by
apply original_threshold_nonvacuous_under_nunl_shrink
· native_decide
· native_decide
/-- With `originalView = 20` and `effectiveView = 16`, using the effective
threshold again reaches the unsafe boundary. -/
theorem original_twenty_effective_sixteen_effective_threshold_not_safe :
¬ strictIntersectionSafe 16 20 (participantThreshold 16) := by
apply not_strictIntersectionSafe_of_threshold_le_boundary
native_decide
/-- Counting Byzantine stake over a trusted universe of `20` instead of the
original view of `10` erodes the margin all the way to equality. -/
theorem trusted_superset_twenty_erodes_original_ten_margin_to_boundary :
2 * participantThreshold 10 = 10 + byzantineBound 20 := by
native_decide
/-- The equality above means the original threshold for `10` is not strictly
safe if Byzantine weight is counted over the larger trusted universe `20`. -/
theorem trusted_superset_twenty_original_ten_threshold_not_safe :
¬ strictIntersectionSafe 10 20 (participantThreshold 10) := by
apply not_strictIntersectionSafe_of_threshold_le_boundary
native_decide
end XahauConsensus

View File

@@ -1,96 +0,0 @@
{"version": "1.2.0",
"packagesDir": ".lake/packages",
"packages":
[{"url": "https://github.com/leanprover-community/mathlib4.git",
"type": "git",
"subDir": null,
"scope": "",
"rev": "fabf563a7c95a166b8d7b6efca11c8b4dc9d911f",
"name": "mathlib",
"manifestFile": "lake-manifest.json",
"inputRev": "v4.31.0",
"inherited": false,
"configFile": "lakefile.lean"},
{"url": "https://github.com/leanprover-community/plausible",
"type": "git",
"subDir": null,
"scope": "leanprover-community",
"rev": "63045536fe95024e6c18fc7b48e03f506701c5bc",
"name": "plausible",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
"inherited": true,
"configFile": "lakefile.toml"},
{"url": "https://github.com/leanprover-community/LeanSearchClient",
"type": "git",
"subDir": null,
"scope": "leanprover-community",
"rev": "c5d5b8fe6e5158def25cd28eb94e4141ad97c843",
"name": "LeanSearchClient",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
"inherited": true,
"configFile": "lakefile.toml"},
{"url": "https://github.com/leanprover-community/import-graph",
"type": "git",
"subDir": null,
"scope": "leanprover-community",
"rev": "5c7542ed018c78194f1e2b903eaf6a792b74c03d",
"name": "importGraph",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
"inherited": true,
"configFile": "lakefile.toml"},
{"url": "https://github.com/leanprover-community/ProofWidgets4",
"type": "git",
"subDir": null,
"scope": "leanprover-community",
"rev": "24b0d9dc081c5423f8eec7e866c441e5184f29d9",
"name": "proofwidgets",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
"inherited": true,
"configFile": "lakefile.lean"},
{"url": "https://github.com/leanprover-community/aesop",
"type": "git",
"subDir": null,
"scope": "leanprover-community",
"rev": "e3cb2f741431ce31bf73549fb52316a57368b06f",
"name": "aesop",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
"inherited": true,
"configFile": "lakefile.toml"},
{"url": "https://github.com/leanprover-community/quote4",
"type": "git",
"subDir": null,
"scope": "leanprover-community",
"rev": "f46324995fca5f0483b742e4eb4daec7f4ee50d2",
"name": "Qq",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
"inherited": true,
"configFile": "lakefile.toml"},
{"url": "https://github.com/leanprover-community/batteries",
"type": "git",
"subDir": null,
"scope": "leanprover-community",
"rev": "fa08db58b30eb033edcdab331bba000827f9f785",
"name": "batteries",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
"inherited": true,
"configFile": "lakefile.toml"},
{"url": "https://github.com/leanprover/lean4-cli",
"type": "git",
"subDir": null,
"scope": "leanprover",
"rev": "92564e5770e4d09f2d86dfbf8ada1e9c715b384c",
"name": "Cli",
"manifestFile": "lake-manifest.json",
"inputRev": "v4.31.0",
"inherited": true,
"configFile": "lakefile.toml"}],
"name": "xahau_consensus",
"lakeDir": ".lake",
"fixedToolchain": false}

View File

@@ -1,11 +0,0 @@
name = "xahau_consensus"
version = "0.1.0"
defaultTargets = ["XahauConsensus"]
[[require]]
name = "mathlib"
git = "https://github.com/leanprover-community/mathlib4.git"
rev = "v4.31.0"
[[lean_lib]]
name = "XahauConsensus"

View File

@@ -1 +0,0 @@
leanprover/lean4:v4.31.0

View File

@@ -47,8 +47,5 @@
#define MEM_OVERLAP -43
#define TOO_MANY_STATE_MODIFICATIONS -44
#define TOO_MANY_NAMESPACES -45
#define EXPORT_FAILURE -46
#define TOO_MANY_EXPORTED_TXN -47
#define TOO_LITTLE_ENTROPY -48
#define HOOK_ERROR_CODES
#endif //HOOK_ERROR_CODES

View File

@@ -2,9 +2,6 @@
// Generated using generate_extern.sh
#include <stdint.h>
#ifndef HOOK_EXTERN
#ifdef __cplusplus
extern "C" {
#endif
extern int32_t __attribute__((noduplicate))
_g(uint32_t guard_id, uint32_t maxiter);
@@ -339,43 +336,5 @@ prepare(
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
xport_reserve(uint32_t count);
extern int64_t
xport(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
xport_cancel(uint32_t ticket_seq);
/*
Consensus entropy APIs.
min_tier is a fail-closed floor:
1 = consensus_fallback, 2 = participant_aligned, 3 = validator_quorum.
min_count is the minimum validator/reveal count the caller accepts.
If the most recent finalized entropy object does not satisfy both floors,
these APIs return TOO_LITTLE_ENTROPY. Open-ledger and simulate execution
are provisional previews over the entropy currently visible to the node;
final ordered ledger execution may see a different entropy object.
*/
extern int64_t
dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t
random(
uint32_t write_ptr,
uint32_t write_len,
uint32_t min_tier,
uint32_t min_count);
#ifdef __cplusplus
}
#endif
#define HOOK_EXTERN
#endif // HOOK_EXTERN

View File

@@ -9,7 +9,7 @@ ENUM_FILE="$SCRIPT_DIR/../include/xrpl/hook/Enum.h"
echo '// For documentation please see: https://xrpl-hooks.readme.io/reference/'
echo '// Generated using generate_error.sh'
echo '#ifndef HOOK_ERROR_CODES'
sed -n '/enum class hook_return_code/,/};/p' "$ENUM_FILE" |
sed -n '/enum hook_return_code/,/};/p' "$ENUM_FILE" |
awk '
function ltrim(s) { sub(/^[[:space:]]+/, "", s); return s }
function rtrim(s) { sub(/[[:space:]]+$/, "", s); return s }
@@ -31,7 +31,7 @@ sed -n '/enum class hook_return_code/,/};/p' "$ENUM_FILE" |
{
line = $0
if (line ~ /enum[[:space:]]+class[[:space:]]+hook_return_code/)
if (line ~ /enum[[:space:]]+hook_return_code/)
next
if (line ~ /^[[:space:]]*\{/)
next

View File

@@ -11,9 +11,6 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
echo '// Generated using generate_extern.sh'
echo '#include <stdint.h>'
echo '#ifndef HOOK_EXTERN'
echo '#ifdef __cplusplus'
echo 'extern "C" {'
echo '#endif'
echo
awk '
function trim(s) {
@@ -41,21 +38,6 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
# Insert __attribute__((noduplicate)) before _g
sub(/[[:space:]]+_g/, " __attribute__((noduplicate)) _g", line);
}
if (line ~ /[[:space:]]+dice[[:space:]]*\(/) {
print "/*";
print " Consensus entropy APIs.";
print "";
print " min_tier is a fail-closed floor:";
print " 1 = consensus_fallback, 2 = participant_aligned, 3 = validator_quorum.";
print " min_count is the minimum validator/reveal count the caller accepts.";
print "";
print " If the most recent finalized entropy object does not satisfy both floors,";
print " these APIs return TOO_LITTLE_ENTROPY. Open-ledger and simulate execution";
print " are provisional previews over the entropy currently visible to the node;";
print " final ordered ledger execution may see a different entropy object.";
print "*/";
}
# printf("\n");
@@ -64,9 +46,6 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
}
' "$APPLY_HOOK"
echo '#ifdef __cplusplus'
echo '}'
echo '#endif'
echo '#define HOOK_EXTERN'
echo '#endif // HOOK_EXTERN'
} | (

View File

@@ -1,82 +0,0 @@
#!/bin/bash
set -eu
SCRIPT_DIR=$(dirname "$0")
SCRIPT_DIR=$(cd "$SCRIPT_DIR" && pwd)
RIPPLED_ROOT="$SCRIPT_DIR/../include/xrpl"
LEDGER_FORMATS="$RIPPLED_ROOT/protocol/LedgerFormats.h"
echo '// Generated using generate_lsflags.sh'
echo ''
echo '#ifndef HOOKLSFLAGS_INCLUDED'
echo '#define HOOKLSFLAGS_INCLUDED 1'
echo ''
awk '
function ltrim(s) { sub(/^[[:space:]]+/, "", s); return s }
function rtrim(s) { sub(/[[:space:]]+$/, "", s); return s }
function trim(s) { return rtrim(ltrim(s)) }
function flush_group() {
if (entry_count > 0 && group != "") {
printf "enum %s {\n", group
for (i = 1; i <= entry_count; i++) {
printf " %s,\n", entries[i]
}
printf "};\n"
}
delete entries
entry_count = 0
}
/enum LedgerSpecificFlags \{/ { inside = 1; next }
inside && /^\};/ { inside = 0; flush_group(); next }
!inside { next }
# Group header comments: // ltFOO or // remarks
/^[[:space:]]*\/\/[[:space:]]*(lt[A-Z_]+|remarks)[[:space:]]*$/ {
flush_group()
line = $0
sub(/.*\/\/[[:space:]]*/, "", line)
group = trim(line)
next
}
# Skip pure comment lines (not group headers)
/^[[:space:]]*\/\// { next }
# Skip blank lines
/^[[:space:]]*$/ { next }
# Accumulate flag lines (handle multi-line values)
{
line = $0
# Strip inline comments
sub(/\/\/.*/, "", line)
line = trim(line)
if (line == "") next
if (pending != "") {
pending = pending " " line
} else {
pending = line
}
# If line ends with comma, the entry is complete
if (pending ~ /,$/) {
# Remove trailing comma
sub(/,$/, "", pending)
entries[++entry_count] = pending
pending = ""
}
}
BEGIN {
inside = 0
group = ""
pending = ""
entry_count = 0
}
' "$LEDGER_FORMATS"
echo ''
echo '#endif // HOOKLSFLAGS_INCLUDED'

View File

@@ -1,25 +0,0 @@
#!/bin/bash
set -eu
SCRIPT_DIR=$(dirname "$0")
SCRIPT_DIR=$(cd "$SCRIPT_DIR" && pwd)
RIPPLED_ROOT="$SCRIPT_DIR/../include/xrpl"
TX_FLAGS="$RIPPLED_ROOT/protocol/TxFlags.h"
echo '// Generated using generate_txflags.sh'
echo '#include "ls_flags.h"'
echo '#include <stdint.h>'
echo ''
cat "$TX_FLAGS" |
awk '
/^[[:space:]]*enum / {
if (count > 0) print ""
inside = 1
count++
}
inside {
print
if (/};/) inside = 0
}
'

View File

@@ -1,203 +0,0 @@
#!/bin/bash
# build_xahau_h.sh
# Builds genesis hook WASMs and updates xahau.h with hex arrays
set -euo pipefail
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script directory and path constants
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
XAHAU_H="${SCRIPT_DIR}/../../include/xrpl/hook/xahau.h"
TEMP_DIR="${SCRIPT_DIR}/.temp"
# Hook file mappings (space-separated: name:file)
HOOK_FILES=(
"GovernanceHook:govern.wasm"
"RewardHook:reward.wasm"
# "MintHook:mint.wasm"
)
# Cleanup function
cleanup() {
local exit_code=$?
if [ ${exit_code} -eq 0 ] && [ -d "${TEMP_DIR}" ]; then
rm -rf "${TEMP_DIR}"
elif [ ${exit_code} -ne 0 ]; then
echo -e "${RED}Error: Script failed with exit code ${exit_code}${NC}" >&2
if [ -d "${TEMP_DIR}" ]; then
echo -e "${YELLOW}Temp files preserved at: ${TEMP_DIR}${NC}" >&2
fi
fi
exit ${exit_code}
}
trap cleanup EXIT INT TERM
# Tool verification
echo -e "${BLUE}==> Checking required tools...${NC}"
REQUIRED_TOOLS=("make" "xxd" "sed" "clang-format" "wasm-opt")
for tool in "${REQUIRED_TOOLS[@]}"; do
if ! command -v "${tool}" &> /dev/null; then
echo -e "${RED}Error: Required tool '${tool}' not found${NC}" >&2
exit 1
fi
echo -e "${GREEN}${tool}${NC}"
done
# Verify wasm-opt version is exactly 100
WASM_OPT_VERSION=$(wasm-opt --version | grep -oE '[0-9]+' | head -1)
if [ "${WASM_OPT_VERSION}" != "100" ]; then
echo -e "${RED}Error: wasm-opt version must be 100, but found ${WASM_OPT_VERSION}${NC}" >&2
exit 1
fi
echo -e "${GREEN} ✓ wasm-opt version 100${NC}"
# Verify xahau.h exists
if [ ! -f "${XAHAU_H}" ]; then
echo -e "${RED}Error: xahau.h not found at ${XAHAU_H}${NC}" >&2
exit 1
fi
# Create temp directory
mkdir -p "${TEMP_DIR}"
# Build all WASM files
echo -e "${BLUE}==> Building WASM files with 'make all'...${NC}"
cd "${SCRIPT_DIR}"
make all
echo -e "${GREEN} Build completed successfully${NC}"
# Function to convert WASM to hex array
wasm_to_hex_array() {
local wasm_file="$1"
local indent=" "
if [ ! -f "${wasm_file}" ]; then
echo -e "${RED}Error: WASM file not found: ${wasm_file}${NC}" >&2
return 1
fi
# Convert to hex with xxd, format with sed
xxd -p -u -c 10 "${wasm_file}" | \
sed 's/../0x&U,/g' | \
sed "s/^/${indent}/g" | \
sed '$ s/,$//'
}
# Function to update hook array in xahau.h
update_hook_array() {
local hook_name="$1"
local hex_array="$2"
local temp_file="${TEMP_DIR}/xahau.h.tmp"
echo -e "${BLUE}==> Updating ${hook_name}...${NC}"
# Check if hook already exists
if grep -q "static const std::vector<uint8_t> ${hook_name} = {" "${XAHAU_H}"; then
echo -e "${YELLOW} Replacing existing ${hook_name}${NC}"
# Use awk to replace the array content
awk -v hook="${hook_name}" -v hex="${hex_array}" '
BEGIN { in_array=0 }
{
if ($0 ~ "static const std::vector<uint8_t> " hook " = {") {
print $0
print hex
in_array=1
next
}
if (in_array && $0 ~ /};/) {
print "};"
in_array=0
next
}
if (!in_array) {
print $0
}
}
' "${XAHAU_H}" > "${temp_file}"
mv "${temp_file}" "${XAHAU_H}"
else
echo -e "${YELLOW} Adding new ${hook_name}${NC}"
# Find the position before #endif and add the new hook
awk -v hook="${hook_name}" -v hex="${hex_array}" '
{
if ($0 ~ /#endif.*XAHAU_GENESIS_HOOKS/) {
print ""
print "static const std::vector<uint8_t> " hook " = {"
print hex
print "};"
print ""
print $0
} else {
print $0
}
}
' "${XAHAU_H}" > "${temp_file}"
mv "${temp_file}" "${XAHAU_H}"
fi
echo -e "${GREEN}${hook_name} updated${NC}"
}
# Process each hook
for hook_entry in "${HOOK_FILES[@]}"; do
hook_name="${hook_entry%%:*}"
wasm_file="${SCRIPT_DIR}/${hook_entry##*:}"
echo -e "${BLUE}==> Converting ${wasm_file} to hex array...${NC}"
hex_array=$(wasm_to_hex_array "${wasm_file}")
if [ $? -ne 0 ]; then
echo -e "${RED}Error: Failed to convert ${wasm_file}${NC}" >&2
exit 1
fi
echo -e "${GREEN} Conversion successful ($(echo "${hex_array}" | wc -l) lines)${NC}"
update_hook_array "${hook_name}" "${hex_array}"
done
# Format with clang-format
echo -e "${BLUE}==> Formatting with clang-format...${NC}"
cp "${XAHAU_H}" "${TEMP_DIR}/xahau.h.before_format"
clang-format -i "${XAHAU_H}"
echo -e "${GREEN} Formatting completed${NC}"
# Verification
echo -e "${BLUE}==> Verifying changes...${NC}"
for hook_entry in "${HOOK_FILES[@]}"; do
hook_name="${hook_entry%%:*}"
if grep -q "static const std::vector<uint8_t> ${hook_name} = {" "${XAHAU_H}"; then
echo -e "${GREEN}${hook_name} found in xahau.h${NC}"
else
echo -e "${RED}${hook_name} NOT found in xahau.h${NC}" >&2
exit 1
fi
done
# Show summary
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}Successfully updated xahau.h${NC}"
echo -e "${GREEN}========================================${NC}"
echo -e "Updated hooks:"
for hook_entry in "${HOOK_FILES[@]}"; do
hook_name="${hook_entry%%:*}"
wasm_file="${SCRIPT_DIR}/${hook_entry##*:}"
size=$(wc -c < "${wasm_file}" | tr -d ' ')
echo -e " - ${hook_name}: ${size} bytes"
done
echo ""
echo -e "File location: ${XAHAU_H}"
echo ""

View File

@@ -1,46 +0,0 @@
// For documentation please see: https://xrpl-hooks.readme.io/reference/
// Generated using generate_error.sh
#ifndef HOOK_ERROR_CODES
#define SUCCESS 0
#define OUT_OF_BOUNDS -1
#define INTERNAL_ERROR -2
#define TOO_BIG -3
#define TOO_SMALL -4
#define DOESNT_EXIST -5
#define NO_FREE_SLOTS -6
#define INVALID_ARGUMENT -7
#define ALREADY_SET -8
#define PREREQUISITE_NOT_MET -9
#define FEE_TOO_LARGE -10
#define EMISSION_FAILURE -11
#define TOO_MANY_NONCES -12
#define TOO_MANY_EMITTED_TXN -13
#define NOT_IMPLEMENTED -14
#define INVALID_ACCOUNT -15
#define GUARD_VIOLATION -16
#define INVALID_FIELD -17
#define PARSE_ERROR -18
#define RC_ROLLBACK -19
#define RC_ACCEPT -20
#define NO_SUCH_KEYLET -21
#define NOT_AN_ARRAY -22
#define NOT_AN_OBJECT -23
#define INVALID_FLOAT -10024
#define DIVISION_BY_ZERO -25
#define MANTISSA_OVERSIZED -26
#define MANTISSA_UNDERSIZED -27
#define EXPONENT_OVERSIZED -28
#define EXPONENT_UNDERSIZED -29
#define OVERFLOW -30
#define NOT_IOU_AMOUNT -31
#define NOT_AN_AMOUNT -32
#define CANT_RETURN_NEGATIVE -33
#define NOT_AUTHORIZED -34
#define PREVIOUS_FAILURE_PREVENTS_RETRY -35
#define TOO_MANY_PARAMS -36
#define INVALID_TXN -37
#define RESERVE_INSUFFICIENT -38
#define COMPLEX_NOT_SUPPORTED -39
#define DOES_NOT_MATCH -40
#define HOOK_ERROR_CODES
#endif //HOOK_ERROR_CODES

View File

@@ -1,352 +0,0 @@
// For documentation please see: https://xrpl-hooks.readme.io/reference/
// Generated using generate_extern.sh
#include <stdint.h>
#ifndef HOOK_EXTERN
extern int32_t __attribute__((noduplicate))
_g(uint32_t guard_id, uint32_t maxiter);
extern int64_t
accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t
emit(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
etxn_burden(void);
extern int64_t
etxn_details(uint32_t write_ptr, uint32_t write_len);
extern int64_t
etxn_fee_base(uint32_t read_ptr, uint32_t read_len);
extern int64_t
etxn_generation(void);
extern int64_t
etxn_nonce(uint32_t write_ptr, uint32_t write_len);
extern int64_t
etxn_reserve(uint32_t count);
extern int64_t
fee_base(void);
extern int64_t
float_compare(int64_t float1, int64_t float2, uint32_t mode);
extern int64_t
float_divide(int64_t float1, int64_t float2);
extern int64_t
float_exponent(int64_t float1);
extern int64_t
float_exponent_set(int64_t float1, int32_t exponent);
extern int64_t
float_int(int64_t float1, uint32_t decimal_places, uint32_t abs);
extern int64_t
float_invert(int64_t float1);
extern int64_t
float_log(int64_t float1);
extern int64_t
float_mantissa(int64_t float1);
extern int64_t
float_mantissa_set(int64_t float1, int64_t mantissa);
extern int64_t
float_mulratio(
int64_t float1,
uint32_t round_up,
uint32_t numerator,
uint32_t denominator);
extern int64_t
float_multiply(int64_t float1, int64_t float2);
extern int64_t
float_negate(int64_t float1);
extern int64_t
float_one(void);
extern int64_t
float_root(int64_t float1, uint32_t n);
extern int64_t
float_set(int32_t exponent, int64_t mantissa);
extern int64_t
float_sign(int64_t float1);
extern int64_t
float_sign_set(int64_t float1, uint32_t negative);
extern int64_t
float_sto(
uint32_t write_ptr,
uint32_t write_len,
uint32_t cread_ptr,
uint32_t cread_len,
uint32_t iread_ptr,
uint32_t iread_len,
int64_t float1,
uint32_t field_code);
extern int64_t
float_sto_set(uint32_t read_ptr, uint32_t read_len);
extern int64_t
float_sum(int64_t float1, int64_t float2);
extern int64_t
hook_account(uint32_t write_ptr, uint32_t write_len);
extern int64_t
hook_again(void);
extern int64_t
hook_hash(uint32_t write_ptr, uint32_t write_len, int32_t hook_no);
extern int64_t
hook_param(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
otxn_param(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
hook_param_set(
uint32_t read_ptr,
uint32_t read_len,
uint32_t kread_ptr,
uint32_t kread_len,
uint32_t hread_ptr,
uint32_t hread_len);
extern int64_t
hook_pos(void);
extern int64_t
hook_skip(uint32_t read_ptr, uint32_t read_len, uint32_t flags);
extern int64_t
ledger_keylet(
uint32_t write_ptr,
uint32_t write_len,
uint32_t lread_ptr,
uint32_t lread_len,
uint32_t hread_ptr,
uint32_t hread_len);
extern int64_t
ledger_last_hash(uint32_t write_ptr, uint32_t write_len);
extern int64_t
ledger_last_time(void);
extern int64_t
ledger_nonce(uint32_t write_ptr, uint32_t write_len);
extern int64_t
ledger_seq(void);
extern int64_t
meta_slot(uint32_t slot_no);
extern int64_t
otxn_burden(void);
extern int64_t
otxn_field(uint32_t write_ptr, uint32_t write_len, uint32_t field_id);
extern int64_t
otxn_field_txt(uint32_t write_ptr, uint32_t write_len, uint32_t field_id);
extern int64_t
otxn_generation(void);
extern int64_t
otxn_id(uint32_t write_ptr, uint32_t write_len, uint32_t flags);
extern int64_t
otxn_slot(uint32_t slot_no);
extern int64_t
otxn_type(void);
extern int64_t
rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t
slot(uint32_t write_ptr, uint32_t write_len, uint32_t slot);
extern int64_t
slot_clear(uint32_t slot);
extern int64_t
slot_count(uint32_t slot);
extern int64_t
slot_float(uint32_t slot_no);
extern int64_t
slot_id(uint32_t write_ptr, uint32_t write_len, uint32_t slot);
extern int64_t
slot_set(uint32_t read_ptr, uint32_t read_len, uint32_t slot);
extern int64_t
slot_size(uint32_t slot);
extern int64_t
slot_subarray(uint32_t parent_slot, uint32_t array_id, uint32_t new_slot);
extern int64_t
slot_subfield(uint32_t parent_slot, uint32_t field_id, uint32_t new_slot);
extern int64_t
slot_type(uint32_t slot_no, uint32_t flags);
extern int64_t
state(
uint32_t write_ptr,
uint32_t write_len,
uint32_t kread_ptr,
uint32_t kread_len);
extern int64_t
state_foreign(
uint32_t write_ptr,
uint32_t write_len,
uint32_t kread_ptr,
uint32_t kread_len,
uint32_t nread_ptr,
uint32_t nread_len,
uint32_t aread_ptr,
uint32_t aread_len);
extern int64_t
state_foreign_set(
uint32_t read_ptr,
uint32_t read_len,
uint32_t kread_ptr,
uint32_t kread_len,
uint32_t nread_ptr,
uint32_t nread_len,
uint32_t aread_ptr,
uint32_t aread_len);
extern int64_t
state_set(
uint32_t read_ptr,
uint32_t read_len,
uint32_t kread_ptr,
uint32_t kread_len);
extern int64_t
sto_emplace(
uint32_t write_ptr,
uint32_t write_len,
uint32_t sread_ptr,
uint32_t sread_len,
uint32_t fread_ptr,
uint32_t fread_len,
uint32_t field_id);
extern int64_t
sto_erase(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len,
uint32_t field_id);
extern int64_t
sto_subarray(uint32_t read_ptr, uint32_t read_len, uint32_t array_id);
extern int64_t
sto_subfield(uint32_t read_ptr, uint32_t read_len, uint32_t field_id);
extern int64_t
sto_validate(uint32_t tread_ptr, uint32_t tread_len);
extern int64_t
trace(
uint32_t mread_ptr,
uint32_t mread_len,
uint32_t dread_ptr,
uint32_t dread_len,
uint32_t as_hex);
extern int64_t
trace_float(uint32_t read_ptr, uint32_t read_len, int64_t float1);
extern int64_t
trace_num(uint32_t read_ptr, uint32_t read_len, int64_t number);
extern int64_t
trace_slot(uint32_t read_ptr, uint32_t read_len, uint32_t slot);
extern int64_t
util_accid(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
util_keylet(
uint32_t write_ptr,
uint32_t write_len,
uint32_t keylet_type,
uint32_t a,
uint32_t b,
uint32_t c,
uint32_t d,
uint32_t e,
uint32_t f);
extern int64_t
util_raddr(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
util_sha512h(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len);
extern int64_t
util_verify(
uint32_t dread_ptr,
uint32_t dread_len,
uint32_t sread_ptr,
uint32_t sread_len,
uint32_t kread_ptr,
uint32_t kread_len);
extern int64_t xpop_slot(uint32_t, uint32_t);
#define HOOK_EXTERN
#endif // HOOK_EXTERN

View File

@@ -1,50 +0,0 @@
/**
* Hook API include file
*
* Note to the reader:
* This include defines two types of things: external functions and macros
* Functions are used sparingly because a non-inlining compiler may produce
* undesirable output.
*
* Find documentation here: https://xrpl-hooks.readme.io/reference/
*/
#ifndef HOOKAPI_INCLUDED
#define HOOKAPI_INCLUDED 1
#define KEYLET_HOOK 1
#define KEYLET_HOOK_STATE 2
#define KEYLET_ACCOUNT 3
#define KEYLET_AMENDMENTS 4
#define KEYLET_CHILD 5
#define KEYLET_SKIP 6
#define KEYLET_FEES 7
#define KEYLET_NEGATIVE_UNL 8
#define KEYLET_LINE 9
#define KEYLET_OFFER 10
#define KEYLET_QUALITY 11
#define KEYLET_EMITTED_DIR 12
#define KEYLET_TICKET 13
#define KEYLET_SIGNERS 14
#define KEYLET_CHECK 15
#define KEYLET_DEPOSIT_PREAUTH 16
#define KEYLET_UNCHECKED 17
#define KEYLET_OWNER_DIR 18
#define KEYLET_PAGE 19
#define KEYLET_ESCROW 20
#define KEYLET_PAYCHAN 21
#define KEYLET_EMITTED 22
#define KEYLET_NFT_OFFER 23
#define KEYLET_HOOK_DEFINITION 24
#define COMPARE_EQUAL 1U
#define COMPARE_LESS 2U
#define COMPARE_GREATER 4U
#include "error.h"
#include "extern.h"
#include "sfcodes.h"
#include "macro.h"
#include "types.h"
#endif

View File

@@ -1,679 +0,0 @@
/**
* These are helper macros for writing hooks, all of them are optional as is including hookmacro.h at all
*/
#include <stdint.h>
#include "hookapi.h"
#include "sfcodes.h"
#ifndef HOOKMACROS_INCLUDED
#define HOOKMACROS_INCLUDED 1
#ifdef NDEBUG
#define DEBUG 0
#else
#define DEBUG 1
#endif
#define TRACEVAR(v) if (DEBUG) trace_num((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (int64_t)v);
#define TRACEHEX(v) if (DEBUG) trace((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (uint32_t)(v), (uint32_t)(sizeof(v)), 1);
#define TRACEXFL(v) if (DEBUG) trace_float((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (int64_t)v);
#define TRACESTR(v) if (DEBUG) trace((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (uint32_t)(v), sizeof(v), 0);
// hook developers should use this guard macro, simply GUARD(<maximum iterations>)
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
#define GUARDM(maxiter, n) _g(( (1ULL << 31U) + (__LINE__ << 16) + n), (maxiter)+1)
#define SBUF(str) (uint32_t)(str), sizeof(str)
#define REQUIRE(cond, str)\
{\
if (!(cond))\
rollback(SBUF(str), __LINE__);\
}
// make a report buffer as a c-string
// provide a name for a buffer to declare (buf)
// provide a static string
// provide an integer to print after the string
#define RBUF(buf, out_len, str, num)\
unsigned char buf[sizeof(str) + 21];\
int out_len = 0;\
{\
int i = 0;\
for (; GUARDM(sizeof(str),1),i < sizeof(str); ++i)\
(buf)[i] = str[i];\
if ((buf)[sizeof(str)-1] == 0) i--;\
if ((num) < 0) (buf)[i++] = '-';\
uint64_t unsigned_num = (uint64_t)( (num) < 0 ? (num) * -1 : (num) );\
uint64_t j = 10000000000000000000ULL;\
int start = 1;\
for (; GUARDM(20,2), unsigned_num > 0 && j > 0; j /= 10)\
{\
unsigned char digit = ( unsigned_num / j ) % 10;\
if (digit == 0 && start)\
continue;\
start = 0;\
(buf)[i++] = '0' + digit;\
}\
(buf)[i] = '\0';\
out_len = i;\
}
#define RBUF2(buff, out_len, str, num, str2, num2)\
unsigned char buff[sizeof(str) + sizeof(str2) + 42];\
int out_len = 0;\
{\
unsigned char* buf = buff;\
int i = 0;\
for (; GUARDM(sizeof(str),1),i < sizeof(str); ++i)\
(buf)[i] = str[i];\
if ((buf)[sizeof(str)-1] == 0) i--;\
if ((num) < 0) (buf)[i++] = '-';\
uint64_t unsigned_num = (uint64_t)( (num) < 0 ? (num) * -1 : (num) );\
uint64_t j = 10000000000000000000ULL;\
int start = 1;\
for (; GUARDM(20,2), unsigned_num > 0 && j > 0; j /= 10)\
{\
unsigned char digit = ( unsigned_num / j ) % 10;\
if (digit == 0 && start)\
continue;\
start = 0;\
(buf)[i++] = '0' + digit;\
}\
buf += i;\
out_len += i;\
i = 0;\
for (; GUARDM(sizeof(str2),3),i < sizeof(str2); ++i)\
(buf)[i] = str2[i];\
if ((buf)[sizeof(str2)-1] == 0) i--;\
if ((num2) < 0) (buf)[i++] = '-';\
unsigned_num = (uint64_t)( (num2) < 0 ? (num2) * -1 : (num2) );\
j = 10000000000000000000ULL;\
start = 1;\
for (; GUARDM(20,4), unsigned_num > 0 && j > 0; j /= 10)\
{\
unsigned char digit = ( unsigned_num / j ) % 10;\
if (digit == 0 && start)\
continue;\
start = 0;\
(buf)[i++] = '0' + digit;\
}\
(buf)[i] = '\0';\
out_len += i;\
}
#define CLEARBUF(b)\
{\
for (int x = 0; GUARD(sizeof(b)), x < sizeof(b); ++x)\
b[x] = 0;\
}
// returns an in64_t, negative if error, non-negative if valid drops
#define AMOUNT_TO_DROPS(amount_buffer)\
(((amount_buffer)[0] >> 7) ? -2 : (\
((((uint64_t)((amount_buffer)[0])) & 0xb00111111) << 56) +\
(((uint64_t)((amount_buffer)[1])) << 48) +\
(((uint64_t)((amount_buffer)[2])) << 40) +\
(((uint64_t)((amount_buffer)[3])) << 32) +\
(((uint64_t)((amount_buffer)[4])) << 24) +\
(((uint64_t)((amount_buffer)[5])) << 16) +\
(((uint64_t)((amount_buffer)[6])) << 8) +\
(((uint64_t)((amount_buffer)[7])))))
#define SUB_OFFSET(x) ((int32_t)(x >> 32))
#define SUB_LENGTH(x) ((int32_t)(x & 0xFFFFFFFFULL))
#define BUFFER_EQUAL_20(buf1, buf2)\
(\
*(((uint64_t*)(buf1)) + 0) == *(((uint64_t*)(buf2)) + 0) &&\
*(((uint64_t*)(buf1)) + 1) == *(((uint64_t*)(buf2)) + 1) &&\
*(((uint32_t*)(buf1)) + 4) == *(((uint32_t*)(buf2)) + 4))
#define BUFFER_EQUAL_32(buf1, buf2)\
(\
*(((uint64_t*)(buf1)) + 0) == *(((uint64_t*)(buf2)) + 0) &&\
*(((uint64_t*)(buf1)) + 1) == *(((uint64_t*)(buf2)) + 1) &&\
*(((uint64_t*)(buf1)) + 2) == *(((uint64_t*)(buf2)) + 2) &&\
*(((uint64_t*)(buf1)) + 3) == *(((uint64_t*)(buf2)) + 3))
// when using this macro buf1len may be dynamic but buf2len must be static
// provide n >= 1 to indicate how many times the macro will be hit on the line of code
// e.g. if it is in a loop that loops 10 times n = 10
#define BUFFER_EQUAL_GUARD(output, buf1, buf1len, buf2, buf2len, n)\
{\
output = ((buf1len) == (buf2len) ? 1 : 0);\
for (int x = 0; GUARDM( (buf2len) * (n), 1 ), output && x < (buf2len);\
++x)\
output = *(((uint8_t*)(buf1)) + x) == *(((uint8_t*)(buf2)) + x);\
}
#define BUFFER_SWAP(x,y)\
{\
uint8_t* z = x;\
x = y;\
y = z;\
}
#define ACCOUNT_COMPARE(compare_result, buf1, buf2)\
{\
compare_result = 0;\
for (int i = 0; GUARD(20), i < 20; ++i)\
{\
if (buf1[i] > buf2[i])\
{\
compare_result = 1;\
break;\
}\
else if (buf1[i] < buf2[i])\
{\
compare_result = -1;\
break;\
}\
}\
}
#define BUFFER_EQUAL_STR_GUARD(output, buf1, buf1len, str, n)\
BUFFER_EQUAL_GUARD(output, buf1, buf1len, str, (sizeof(str)-1), n)
#define BUFFER_EQUAL_STR(output, buf1, buf1len, str)\
BUFFER_EQUAL_GUARD(output, buf1, buf1len, str, (sizeof(str)-1), 1)
#define BUFFER_EQUAL(output, buf1, buf2, compare_len)\
BUFFER_EQUAL_GUARD(output, buf1, compare_len, buf2, compare_len, 1)
#define UINT16_TO_BUF(buf_raw, i)\
{\
unsigned char* buf = (unsigned char*)buf_raw;\
buf[0] = (((uint64_t)i) >> 8) & 0xFFUL;\
buf[1] = (((uint64_t)i) >> 0) & 0xFFUL;\
}
#define UINT16_FROM_BUF(buf)\
(((uint64_t)((buf)[0]) << 8) +\
((uint64_t)((buf)[1]) << 0))
#define UINT32_TO_BUF(buf_raw, i)\
{\
unsigned char* buf = (unsigned char*)buf_raw;\
buf[0] = (((uint64_t)i) >> 24) & 0xFFUL;\
buf[1] = (((uint64_t)i) >> 16) & 0xFFUL;\
buf[2] = (((uint64_t)i) >> 8) & 0xFFUL;\
buf[3] = (((uint64_t)i) >> 0) & 0xFFUL;\
}
#define UINT32_FROM_BUF(buf)\
(((uint64_t)((buf)[0]) << 24) +\
((uint64_t)((buf)[1]) << 16) +\
((uint64_t)((buf)[2]) << 8) +\
((uint64_t)((buf)[3]) << 0))
#define UINT64_TO_BUF(buf_raw, i)\
{\
unsigned char* buf = (unsigned char*)buf_raw;\
buf[0] = (((uint64_t)i) >> 56) & 0xFFUL;\
buf[1] = (((uint64_t)i) >> 48) & 0xFFUL;\
buf[2] = (((uint64_t)i) >> 40) & 0xFFUL;\
buf[3] = (((uint64_t)i) >> 32) & 0xFFUL;\
buf[4] = (((uint64_t)i) >> 24) & 0xFFUL;\
buf[5] = (((uint64_t)i) >> 16) & 0xFFUL;\
buf[6] = (((uint64_t)i) >> 8) & 0xFFUL;\
buf[7] = (((uint64_t)i) >> 0) & 0xFFUL;\
}
#define UINT64_FROM_BUF(buf)\
(((uint64_t)((buf)[0]) << 56) +\
((uint64_t)((buf)[1]) << 48) +\
((uint64_t)((buf)[2]) << 40) +\
((uint64_t)((buf)[3]) << 32) +\
((uint64_t)((buf)[4]) << 24) +\
((uint64_t)((buf)[5]) << 16) +\
((uint64_t)((buf)[6]) << 8) +\
((uint64_t)((buf)[7]) << 0))
#define INT64_FROM_BUF(buf)\
((((uint64_t)((buf)[0] & 0x7FU) << 56) +\
((uint64_t)((buf)[1]) << 48) +\
((uint64_t)((buf)[2]) << 40) +\
((uint64_t)((buf)[3]) << 32) +\
((uint64_t)((buf)[4]) << 24) +\
((uint64_t)((buf)[5]) << 16) +\
((uint64_t)((buf)[6]) << 8) +\
((uint64_t)((buf)[7]) << 0)) * (buf[0] & 0x80U ? -1 : 1))
#define INT64_TO_BUF(buf_raw, i)\
{\
unsigned char* buf = (unsigned char*)buf_raw;\
buf[0] = (((uint64_t)i) >> 56) & 0x7FUL;\
buf[1] = (((uint64_t)i) >> 48) & 0xFFUL;\
buf[2] = (((uint64_t)i) >> 40) & 0xFFUL;\
buf[3] = (((uint64_t)i) >> 32) & 0xFFUL;\
buf[4] = (((uint64_t)i) >> 24) & 0xFFUL;\
buf[5] = (((uint64_t)i) >> 16) & 0xFFUL;\
buf[6] = (((uint64_t)i) >> 8) & 0xFFUL;\
buf[7] = (((uint64_t)i) >> 0) & 0xFFUL;\
if (i < 0) buf[0] |= 0x80U;\
}
#define ttPAYMENT 0
#define ttESCROW_CREATE 1
#define ttESCROW_FINISH 2
#define ttACCOUNT_SET 3
#define ttESCROW_CANCEL 4
#define ttREGULAR_KEY_SET 5
#define ttOFFER_CREATE 7
#define ttOFFER_CANCEL 8
#define ttTICKET_CREATE 10
#define ttSIGNER_LIST_SET 12
#define ttPAYCHAN_CREATE 13
#define ttPAYCHAN_FUND 14
#define ttPAYCHAN_CLAIM 15
#define ttCHECK_CREATE 16
#define ttCHECK_CASH 17
#define ttCHECK_CANCEL 18
#define ttDEPOSIT_PREAUTH 19
#define ttTRUST_SET 20
#define ttACCOUNT_DELETE 21
#define ttHOOK_SET 22
#define ttNFTOKEN_MINT 25
#define ttNFTOKEN_BURN 26
#define ttNFTOKEN_CREATE_OFFER 27
#define ttNFTOKEN_CANCEL_OFFER 28
#define ttNFTOKEN_ACCEPT_OFFER 29
#define ttURITOKEN_MINT 45
#define ttURITOKEN_BURN 46
#define ttURITOKEN_BUY 47
#define ttURITOKEN_CREATE_SELL_OFFER 48
#define ttURITOKEN_CANCEL_SELL_OFFER 49
#define ttCLAIM_REWARD 98
#define ttINVOKE 99
#define ttAMENDMENT 100
#define ttFEE 101
#define ttUNL_MODIFY 102
#define ttEMIT_FAILURE 103
#define tfCANONICAL 0x80000000UL
#define atACCOUNT 1U
#define atOWNER 2U
#define atDESTINATION 3U
#define atISSUER 4U
#define atAUTHORIZE 5U
#define atUNAUTHORIZE 6U
#define atTARGET 7U
#define atREGULARKEY 8U
#define atPSEUDOCALLBACK 9U
#define amAMOUNT 1U
#define amBALANCE 2U
#define amLIMITAMOUNT 3U
#define amTAKERPAYS 4U
#define amTAKERGETS 5U
#define amLOWLIMIT 6U
#define amHIGHLIMIT 7U
#define amFEE 8U
#define amSENDMAX 9U
#define amDELIVERMIN 10U
#define amMINIMUMOFFER 16U
#define amRIPPLEESCROW 17U
#define amDELIVEREDAMOUNT 18U
/**
* RH NOTE -- PAY ATTENTION
*
* ALL 'ENCODE' MACROS INCREMENT BUF_OUT
* THIS IS TO MAKE CHAINING EASY
* BUF_OUT IS A SACRIFICIAL POINTER
*
* 'ENCODE' MACROS WITH CONSTANTS HAVE
* ALIASING TO ASSIST YOU WITH ORDER
* _TYPECODE_FIELDCODE_ENCODE_MACRO
* TO PRODUCE A SERIALIZED OBJECT
* IN CANONICAL FORMAT YOU MUST ORDER
* FIRST BY TYPE CODE THEN BY FIELD CODE
*
* ALL 'PREPARE' MACROS PRESERVE POINTERS
*
**/
#define ENCODE_TL_SIZE 49
#define ENCODE_TL(buf_out, tlamt, amount_type)\
{\
uint8_t uat = amount_type; \
buf_out[0] = 0x60U +(uat & 0x0FU ); \
for (int i = 1; GUARDM(48, 1), i < 49; ++i)\
buf_out[i] = tlamt[i-1];\
buf_out += ENCODE_TL_SIZE;\
}
#define _06_XX_ENCODE_TL(buf_out, drops, amount_type )\
ENCODE_TL(buf_out, drops, amount_type );
#define ENCODE_TL_AMOUNT(buf_out, drops )\
ENCODE_TL(buf_out, drops, amAMOUNT );
#define _06_01_ENCODE_TL_AMOUNT(buf_out, drops )\
ENCODE_TL_AMOUNT(buf_out, drops );
// Encode drops to serialization format
// consumes 9 bytes
#define ENCODE_DROPS_SIZE 9
#define ENCODE_DROPS(buf_out, drops, amount_type ) \
{\
uint8_t uat = amount_type; \
uint64_t udrops = drops; \
buf_out[0] = 0x60U +(uat & 0x0FU ); \
buf_out[1] = 0b01000000 + (( udrops >> 56 ) & 0b00111111 ); \
buf_out[2] = (udrops >> 48) & 0xFFU; \
buf_out[3] = (udrops >> 40) & 0xFFU; \
buf_out[4] = (udrops >> 32) & 0xFFU; \
buf_out[5] = (udrops >> 24) & 0xFFU; \
buf_out[6] = (udrops >> 16) & 0xFFU; \
buf_out[7] = (udrops >> 8) & 0xFFU; \
buf_out[8] = (udrops >> 0) & 0xFFU; \
buf_out += ENCODE_DROPS_SIZE; \
}
#define _06_XX_ENCODE_DROPS(buf_out, drops, amount_type )\
ENCODE_DROPS(buf_out, drops, amount_type );
#define ENCODE_DROPS_AMOUNT(buf_out, drops )\
ENCODE_DROPS(buf_out, drops, amAMOUNT );
#define _06_01_ENCODE_DROPS_AMOUNT(buf_out, drops )\
ENCODE_DROPS_AMOUNT(buf_out, drops );
#define ENCODE_DROPS_FEE(buf_out, drops )\
ENCODE_DROPS(buf_out, drops, amFEE );
#define _06_08_ENCODE_DROPS_FEE(buf_out, drops )\
ENCODE_DROPS_FEE(buf_out, drops );
#define ENCODE_TT_SIZE 3
#define ENCODE_TT(buf_out, tt )\
{\
uint8_t utt = tt;\
buf_out[0] = 0x12U;\
buf_out[1] =(utt >> 8 ) & 0xFFU;\
buf_out[2] =(utt >> 0 ) & 0xFFU;\
buf_out += ENCODE_TT_SIZE; \
}
#define _01_02_ENCODE_TT(buf_out, tt)\
ENCODE_TT(buf_out, tt);
#define ENCODE_ACCOUNT_SIZE 22
#define ENCODE_ACCOUNT(buf_out, account_id, account_type)\
{\
uint8_t uat = account_type;\
buf_out[0] = 0x80U + uat;\
buf_out[1] = 0x14U;\
*(uint64_t*)(buf_out + 2) = *(uint64_t*)(account_id + 0);\
*(uint64_t*)(buf_out + 10) = *(uint64_t*)(account_id + 8);\
*(uint32_t*)(buf_out + 18) = *(uint32_t*)(account_id + 16);\
buf_out += ENCODE_ACCOUNT_SIZE;\
}
#define _08_XX_ENCODE_ACCOUNT(buf_out, account_id, account_type)\
ENCODE_ACCOUNT(buf_out, account_id, account_type);
#define ENCODE_ACCOUNT_SRC_SIZE 22
#define ENCODE_ACCOUNT_SRC(buf_out, account_id)\
ENCODE_ACCOUNT(buf_out, account_id, atACCOUNT);
#define _08_01_ENCODE_ACCOUNT_SRC(buf_out, account_id)\
ENCODE_ACCOUNT_SRC(buf_out, account_id);
#define ENCODE_ACCOUNT_DST_SIZE 22
#define ENCODE_ACCOUNT_DST(buf_out, account_id)\
ENCODE_ACCOUNT(buf_out, account_id, atDESTINATION);
#define _08_03_ENCODE_ACCOUNT_DST(buf_out, account_id)\
ENCODE_ACCOUNT_DST(buf_out, account_id);
#define ENCODE_ACCOUNT_OWNER_SIZE 22
#define ENCODE_ACCOUNT_OWNER(buf_out, account_id) \
ENCODE_ACCOUNT(buf_out, account_id, atOWNER);
#define _08_02_ENCODE_ACCOUNT_OWNER(buf_out, account_id) \
ENCODE_ACCOUNT_OWNER(buf_out, account_id);
#define ENCODE_UINT32_COMMON_SIZE 5U
#define ENCODE_UINT32_COMMON(buf_out, i, field)\
{\
uint32_t ui = i; \
uint8_t uf = field; \
buf_out[0] = 0x20U +(uf & 0x0FU); \
buf_out[1] =(ui >> 24 ) & 0xFFU; \
buf_out[2] =(ui >> 16 ) & 0xFFU; \
buf_out[3] =(ui >> 8 ) & 0xFFU; \
buf_out[4] =(ui >> 0 ) & 0xFFU; \
buf_out += ENCODE_UINT32_COMMON_SIZE; \
}
#define _02_XX_ENCODE_UINT32_COMMON(buf_out, i, field)\
ENCODE_UINT32_COMMON(buf_out, i, field)\
#define ENCODE_UINT32_UNCOMMON_SIZE 6U
#define ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
{\
uint32_t ui = i; \
uint8_t uf = field; \
buf_out[0] = 0x20U; \
buf_out[1] = uf; \
buf_out[2] =(ui >> 24 ) & 0xFFU; \
buf_out[3] =(ui >> 16 ) & 0xFFU; \
buf_out[4] =(ui >> 8 ) & 0xFFU; \
buf_out[5] =(ui >> 0 ) & 0xFFU; \
buf_out += ENCODE_UINT32_UNCOMMON_SIZE; \
}
#define _02_XX_ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
#define ENCODE_LLS_SIZE 6U
#define ENCODE_LLS(buf_out, lls )\
ENCODE_UINT32_UNCOMMON(buf_out, lls, 0x1B );
#define _02_27_ENCODE_LLS(buf_out, lls )\
ENCODE_LLS(buf_out, lls );
#define ENCODE_FLS_SIZE 6U
#define ENCODE_FLS(buf_out, fls )\
ENCODE_UINT32_UNCOMMON(buf_out, fls, 0x1A );
#define _02_26_ENCODE_FLS(buf_out, fls )\
ENCODE_FLS(buf_out, fls );
#define ENCODE_TAG_SRC_SIZE 5
#define ENCODE_TAG_SRC(buf_out, tag )\
ENCODE_UINT32_COMMON(buf_out, tag, 0x3U );
#define _02_03_ENCODE_TAG_SRC(buf_out, tag )\
ENCODE_TAG_SRC(buf_out, tag );
#define ENCODE_TAG_DST_SIZE 5
#define ENCODE_TAG_DST(buf_out, tag )\
ENCODE_UINT32_COMMON(buf_out, tag, 0xEU );
#define _02_14_ENCODE_TAG_DST(buf_out, tag )\
ENCODE_TAG_DST(buf_out, tag );
#define ENCODE_SEQUENCE_SIZE 5
#define ENCODE_SEQUENCE(buf_out, sequence )\
ENCODE_UINT32_COMMON(buf_out, sequence, 0x4U );
#define _02_04_ENCODE_SEQUENCE(buf_out, sequence )\
ENCODE_SEQUENCE(buf_out, sequence );
#define ENCODE_FLAGS_SIZE 5
#define ENCODE_FLAGS(buf_out, tag )\
ENCODE_UINT32_COMMON(buf_out, tag, 0x2U );
#define _02_02_ENCODE_FLAGS(buf_out, tag )\
ENCODE_FLAGS(buf_out, tag );
#define ENCODE_SIGNING_PUBKEY_SIZE 35
#define ENCODE_SIGNING_PUBKEY(buf_out, pkey )\
{\
buf_out[0] = 0x73U;\
buf_out[1] = 0x21U;\
*(uint64_t*)(buf_out + 2) = *(uint64_t*)(pkey + 0);\
*(uint64_t*)(buf_out + 10) = *(uint64_t*)(pkey + 8);\
*(uint64_t*)(buf_out + 18) = *(uint64_t*)(pkey + 16);\
*(uint64_t*)(buf_out + 26) = *(uint64_t*)(pkey + 24);\
buf[34] = pkey[32];\
buf_out += ENCODE_SIGNING_PUBKEY_SIZE;\
}
#define _07_03_ENCODE_SIGNING_PUBKEY(buf_out, pkey )\
ENCODE_SIGNING_PUBKEY(buf_out, pkey );
#define ENCODE_SIGNING_PUBKEY_NULL_SIZE 2
#define ENCODE_SIGNING_PUBKEY_NULL(buf_out )\
{\
*buf_out++ = 0x73U;\
*buf_out++ = 0x00U;\
}
#define _07_03_ENCODE_SIGNING_PUBKEY_NULL(buf_out )\
ENCODE_SIGNING_PUBKEY_NULL(buf_out );
#define _0E_0E_ENCODE_HOOKOBJ(buf_out, hhash)\
{\
uint8_t* hook0 = (hhash);\
*buf_out++ = 0xEEU; /* hook obj start */ \
if (hook0 == 0) /* noop */\
{\
/* do nothing */ \
}\
else\
{\
*buf_out++ = 0x22U; /* flags = override */\
*buf_out++ = 0x00U;\
*buf_out++ = 0x00U;\
*buf_out++ = 0x00U;\
*buf_out++ = 0x01U;\
if (hook0 == 0xFFFFFFFFUL) /* delete operation */ \
{\
*buf_out++ = 0x7BU; /* empty createcode */ \
*buf_out++ = 0x00U;\
}\
else\
{\
*buf_out++ = 0x50U; /* HookHash */\
*buf_out++ = 0x1FU;\
uint64_t* d = (uint64_t*)buf_out;\
uint64_t* s = (uint64_t*)hook0;\
*d++ = *s++;\
*d++ = *s++;\
*d++ = *s++;\
*d++ = *s++;\
buf_out+=32;\
}\
}\
*buf_out++ = 0xE1U;\
}
#define PREPARE_HOOKSET(buf_out_master, maxlen, h, sizeout)\
{\
uint8_t* buf_out = (buf_out_master); \
uint8_t acc[20]; \
uint32_t cls = (uint32_t)ledger_seq(); \
hook_account(SBUF(acc)); \
_01_02_ENCODE_TT (buf_out, ttHOOK_SET ); \
_02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); \
_02_04_ENCODE_SEQUENCE (buf_out, 0 ); \
_02_26_ENCODE_FLS (buf_out, cls + 1 ); \
_02_27_ENCODE_LLS (buf_out, cls + 5 ); \
uint8_t* fee_ptr = buf_out; \
_06_08_ENCODE_DROPS_FEE (buf_out, 0 ); \
_07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); \
_08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); \
uint32_t remaining_size = (maxlen) - (buf_out - (buf_out_master)); \
int64_t edlen = etxn_details((uint32_t)buf_out, remaining_size); \
buf_out += edlen; \
*buf_out++ = 0xFBU; /* hook array start */ \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[0]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[1]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[2]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[3]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[4]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[5]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[6]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[7]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[8]); \
_0E_0E_ENCODE_HOOKOBJ (buf_out, h[9]); \
*buf_out++ = 0xF1U; /* hook array end */ \
sizeout = (buf_out - (buf_out_master)); \
int64_t fee = etxn_fee_base(buf_out_master, sizeout); \
_06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \
}
#ifdef HAS_CALLBACK
#define PREPARE_PAYMENT_SIMPLE_SIZE 270U
#else
#define PREPARE_PAYMENT_SIMPLE_SIZE 248U
#endif
#define PREPARE_PAYMENT_SIMPLE( \
buf_out_master, drops_amount_raw, to_address, dest_tag_raw, src_tag_raw) \
{ \
uint8_t* buf_out = buf_out_master; \
uint8_t acc[20]; \
uint64_t drops_amount = (drops_amount_raw); \
uint32_t dest_tag = (dest_tag_raw); \
uint32_t src_tag = (src_tag_raw); \
uint32_t cls = (uint32_t)ledger_seq(); \
hook_account(SBUF(acc)); \
_01_02_ENCODE_TT(buf_out, ttPAYMENT); /* uint16 | size 3 */ \
_02_02_ENCODE_FLAGS(buf_out, tfCANONICAL); /* uint32 | size 5 */ \
_02_03_ENCODE_TAG_SRC(buf_out, src_tag); /* uint32 | size 5 */ \
_02_04_ENCODE_SEQUENCE(buf_out, 0); /* uint32 | size 5 */ \
_02_14_ENCODE_TAG_DST(buf_out, dest_tag); /* uint32 | size 5 */ \
_02_26_ENCODE_FLS(buf_out, cls + 1); /* uint32 | size 6 */ \
_02_27_ENCODE_LLS(buf_out, cls + 5); /* uint32 | size 6 */ \
_06_01_ENCODE_DROPS_AMOUNT( \
buf_out, drops_amount); /* amount | size 9 */ \
uint8_t* fee_ptr = buf_out; \
_06_08_ENCODE_DROPS_FEE(buf_out, 0); /* amount | size 9 */ \
_07_03_ENCODE_SIGNING_PUBKEY_NULL(buf_out); /* pk | size 35 */ \
_08_01_ENCODE_ACCOUNT_SRC(buf_out, acc); /* account | size 22 */ \
_08_03_ENCODE_ACCOUNT_DST( \
buf_out, to_address); /* account | size 22 */ \
int64_t edlen = etxn_details( \
(uint32_t)buf_out, \
PREPARE_PAYMENT_SIMPLE_SIZE); /* emitdet | size 1?? */ \
int64_t fee = \
etxn_fee_base(buf_out_master, PREPARE_PAYMENT_SIMPLE_SIZE); \
_06_08_ENCODE_DROPS_FEE(fee_ptr, fee); \
}
#ifdef HAS_CALLBACK
#define PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE 309
#else
#define PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE 287
#endif
#define PREPARE_PAYMENT_SIMPLE_TRUSTLINE( \
buf_out_master, tlamt, to_address, dest_tag_raw, src_tag_raw) \
{ \
uint8_t* buf_out = buf_out_master; \
uint8_t acc[20]; \
uint32_t dest_tag = (dest_tag_raw); \
uint32_t src_tag = (src_tag_raw); \
uint32_t cls = (uint32_t)ledger_seq(); \
hook_account(SBUF(acc)); \
_01_02_ENCODE_TT(buf_out, ttPAYMENT); /* uint16 | size 3 */ \
_02_02_ENCODE_FLAGS(buf_out, tfCANONICAL); /* uint32 | size 5 */ \
_02_03_ENCODE_TAG_SRC(buf_out, src_tag); /* uint32 | size 5 */ \
_02_04_ENCODE_SEQUENCE(buf_out, 0); /* uint32 | size 5 */ \
_02_14_ENCODE_TAG_DST(buf_out, dest_tag); /* uint32 | size 5 */ \
_02_26_ENCODE_FLS(buf_out, cls + 1); /* uint32 | size 6 */ \
_02_27_ENCODE_LLS(buf_out, cls + 5); /* uint32 | size 6 */ \
_06_01_ENCODE_TL_AMOUNT(buf_out, tlamt); /* amount | size 48 */ \
uint8_t* fee_ptr = buf_out; \
_06_08_ENCODE_DROPS_FEE(buf_out, 0); /* amount | size 9 */ \
_07_03_ENCODE_SIGNING_PUBKEY_NULL(buf_out); /* pk | size 35 */ \
_08_01_ENCODE_ACCOUNT_SRC(buf_out, acc); /* account | size 22 */ \
_08_03_ENCODE_ACCOUNT_DST( \
buf_out, to_address); /* account | size 22 */ \
etxn_details( \
(uint32_t)buf_out, \
PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE); /* emitdet | size 1?? */ \
int64_t fee = etxn_fee_base( \
buf_out_master, PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE); \
_06_08_ENCODE_DROPS_FEE(fee_ptr, fee); \
}
#endif

View File

@@ -1,215 +0,0 @@
// For documentation please see: https://xrpl-hooks.readme.io/reference/
// Generated using generate_sfcodes.sh
#define sfCloseResolution ((16U << 16U) + 1U)
#define sfMethod ((16U << 16U) + 2U)
#define sfTransactionResult ((16U << 16U) + 3U)
#define sfTickSize ((16U << 16U) + 16U)
#define sfUNLModifyDisabling ((16U << 16U) + 17U)
#define sfHookResult ((16U << 16U) + 18U)
#define sfLedgerEntryType ((1U << 16U) + 1U)
#define sfTransactionType ((1U << 16U) + 2U)
#define sfSignerWeight ((1U << 16U) + 3U)
#define sfTransferFee ((1U << 16U) + 4U)
#define sfVersion ((1U << 16U) + 16U)
#define sfHookStateChangeCount ((1U << 16U) + 17U)
#define sfHookEmitCount ((1U << 16U) + 18U)
#define sfHookExecutionIndex ((1U << 16U) + 19U)
#define sfHookApiVersion ((1U << 16U) + 20U)
#define sfNetworkID ((2U << 16U) + 1U)
#define sfFlags ((2U << 16U) + 2U)
#define sfSourceTag ((2U << 16U) + 3U)
#define sfSequence ((2U << 16U) + 4U)
#define sfPreviousTxnLgrSeq ((2U << 16U) + 5U)
#define sfLedgerSequence ((2U << 16U) + 6U)
#define sfCloseTime ((2U << 16U) + 7U)
#define sfParentCloseTime ((2U << 16U) + 8U)
#define sfSigningTime ((2U << 16U) + 9U)
#define sfExpiration ((2U << 16U) + 10U)
#define sfTransferRate ((2U << 16U) + 11U)
#define sfWalletSize ((2U << 16U) + 12U)
#define sfOwnerCount ((2U << 16U) + 13U)
#define sfDestinationTag ((2U << 16U) + 14U)
#define sfHighQualityIn ((2U << 16U) + 16U)
#define sfHighQualityOut ((2U << 16U) + 17U)
#define sfLowQualityIn ((2U << 16U) + 18U)
#define sfLowQualityOut ((2U << 16U) + 19U)
#define sfQualityIn ((2U << 16U) + 20U)
#define sfQualityOut ((2U << 16U) + 21U)
#define sfStampEscrow ((2U << 16U) + 22U)
#define sfBondAmount ((2U << 16U) + 23U)
#define sfLoadFee ((2U << 16U) + 24U)
#define sfOfferSequence ((2U << 16U) + 25U)
#define sfFirstLedgerSequence ((2U << 16U) + 26U)
#define sfLastLedgerSequence ((2U << 16U) + 27U)
#define sfTransactionIndex ((2U << 16U) + 28U)
#define sfOperationLimit ((2U << 16U) + 29U)
#define sfReferenceFeeUnits ((2U << 16U) + 30U)
#define sfReserveBase ((2U << 16U) + 31U)
#define sfReserveIncrement ((2U << 16U) + 32U)
#define sfSetFlag ((2U << 16U) + 33U)
#define sfClearFlag ((2U << 16U) + 34U)
#define sfSignerQuorum ((2U << 16U) + 35U)
#define sfCancelAfter ((2U << 16U) + 36U)
#define sfFinishAfter ((2U << 16U) + 37U)
#define sfSignerListID ((2U << 16U) + 38U)
#define sfSettleDelay ((2U << 16U) + 39U)
#define sfTicketCount ((2U << 16U) + 40U)
#define sfTicketSequence ((2U << 16U) + 41U)
#define sfNFTokenTaxon ((2U << 16U) + 42U)
#define sfMintedNFTokens ((2U << 16U) + 43U)
#define sfBurnedNFTokens ((2U << 16U) + 44U)
#define sfHookStateCount ((2U << 16U) + 45U)
#define sfEmitGeneration ((2U << 16U) + 46U)
#define sfLockCount ((2U << 16U) + 47U)
#define sfRewardTime ((2U << 16U) + 98U)
#define sfRewardLgrFirst ((2U << 16U) + 99U)
#define sfRewardLgrLast ((2U << 16U) + 100U)
#define sfIndexNext ((3U << 16U) + 1U)
#define sfIndexPrevious ((3U << 16U) + 2U)
#define sfBookNode ((3U << 16U) + 3U)
#define sfOwnerNode ((3U << 16U) + 4U)
#define sfBaseFee ((3U << 16U) + 5U)
#define sfExchangeRate ((3U << 16U) + 6U)
#define sfLowNode ((3U << 16U) + 7U)
#define sfHighNode ((3U << 16U) + 8U)
#define sfDestinationNode ((3U << 16U) + 9U)
#define sfCookie ((3U << 16U) + 10U)
#define sfServerVersion ((3U << 16U) + 11U)
#define sfNFTokenOfferNode ((3U << 16U) + 12U)
#define sfEmitBurden ((3U << 16U) + 13U)
#define sfHookInstructionCount ((3U << 16U) + 17U)
#define sfHookReturnCode ((3U << 16U) + 18U)
#define sfReferenceCount ((3U << 16U) + 19U)
#define sfRewardAccumulator ((3U << 16U) + 100U)
#define sfEmailHash ((4U << 16U) + 1U)
#define sfTakerPaysCurrency ((10U << 16U) + 1U)
#define sfTakerPaysIssuer ((10U << 16U) + 2U)
#define sfTakerGetsCurrency ((10U << 16U) + 3U)
#define sfTakerGetsIssuer ((10U << 16U) + 4U)
#define sfLedgerHash ((5U << 16U) + 1U)
#define sfParentHash ((5U << 16U) + 2U)
#define sfTransactionHash ((5U << 16U) + 3U)
#define sfAccountHash ((5U << 16U) + 4U)
#define sfPreviousTxnID ((5U << 16U) + 5U)
#define sfLedgerIndex ((5U << 16U) + 6U)
#define sfWalletLocator ((5U << 16U) + 7U)
#define sfRootIndex ((5U << 16U) + 8U)
#define sfAccountTxnID ((5U << 16U) + 9U)
#define sfNFTokenID ((5U << 16U) + 10U)
#define sfEmitParentTxnID ((5U << 16U) + 11U)
#define sfEmitNonce ((5U << 16U) + 12U)
#define sfEmitHookHash ((5U << 16U) + 13U)
#define sfBookDirectory ((5U << 16U) + 16U)
#define sfInvoiceID ((5U << 16U) + 17U)
#define sfNickname ((5U << 16U) + 18U)
#define sfAmendment ((5U << 16U) + 19U)
#define sfHookOn ((5U << 16U) + 20U)
#define sfDigest ((5U << 16U) + 21U)
#define sfChannel ((5U << 16U) + 22U)
#define sfConsensusHash ((5U << 16U) + 23U)
#define sfCheckID ((5U << 16U) + 24U)
#define sfValidatedHash ((5U << 16U) + 25U)
#define sfPreviousPageMin ((5U << 16U) + 26U)
#define sfNextPageMin ((5U << 16U) + 27U)
#define sfNFTokenBuyOffer ((5U << 16U) + 28U)
#define sfNFTokenSellOffer ((5U << 16U) + 29U)
#define sfHookStateKey ((5U << 16U) + 30U)
#define sfHookHash ((5U << 16U) + 31U)
#define sfHookNamespace ((5U << 16U) + 32U)
#define sfHookSetTxnID ((5U << 16U) + 33U)
#define sfOfferID ((5U << 16U) + 34U)
#define sfEscrowID ((5U << 16U) + 35U)
#define sfURITokenID ((5U << 16U) + 36U)
#define sfAmount ((6U << 16U) + 1U)
#define sfBalance ((6U << 16U) + 2U)
#define sfLimitAmount ((6U << 16U) + 3U)
#define sfTakerPays ((6U << 16U) + 4U)
#define sfTakerGets ((6U << 16U) + 5U)
#define sfLowLimit ((6U << 16U) + 6U)
#define sfHighLimit ((6U << 16U) + 7U)
#define sfFee ((6U << 16U) + 8U)
#define sfSendMax ((6U << 16U) + 9U)
#define sfDeliverMin ((6U << 16U) + 10U)
#define sfMinimumOffer ((6U << 16U) + 16U)
#define sfRippleEscrow ((6U << 16U) + 17U)
#define sfDeliveredAmount ((6U << 16U) + 18U)
#define sfNFTokenBrokerFee ((6U << 16U) + 19U)
#define sfHookCallbackFee ((6U << 16U) + 20U)
#define sfLockedBalance ((6U << 16U) + 21U)
#define sfPublicKey ((7U << 16U) + 1U)
#define sfMessageKey ((7U << 16U) + 2U)
#define sfSigningPubKey ((7U << 16U) + 3U)
#define sfTxnSignature ((7U << 16U) + 4U)
#define sfURI ((7U << 16U) + 5U)
#define sfSignature ((7U << 16U) + 6U)
#define sfDomain ((7U << 16U) + 7U)
#define sfFundCode ((7U << 16U) + 8U)
#define sfRemoveCode ((7U << 16U) + 9U)
#define sfExpireCode ((7U << 16U) + 10U)
#define sfCreateCode ((7U << 16U) + 11U)
#define sfMemoType ((7U << 16U) + 12U)
#define sfMemoData ((7U << 16U) + 13U)
#define sfMemoFormat ((7U << 16U) + 14U)
#define sfFulfillment ((7U << 16U) + 16U)
#define sfCondition ((7U << 16U) + 17U)
#define sfMasterSignature ((7U << 16U) + 18U)
#define sfUNLModifyValidator ((7U << 16U) + 19U)
#define sfValidatorToDisable ((7U << 16U) + 20U)
#define sfValidatorToReEnable ((7U << 16U) + 21U)
#define sfHookStateData ((7U << 16U) + 22U)
#define sfHookReturnString ((7U << 16U) + 23U)
#define sfHookParameterName ((7U << 16U) + 24U)
#define sfHookParameterValue ((7U << 16U) + 25U)
#define sfBlob ((7U << 16U) + 26U)
#define sfAccount ((8U << 16U) + 1U)
#define sfOwner ((8U << 16U) + 2U)
#define sfDestination ((8U << 16U) + 3U)
#define sfIssuer ((8U << 16U) + 4U)
#define sfAuthorize ((8U << 16U) + 5U)
#define sfUnauthorize ((8U << 16U) + 6U)
#define sfRegularKey ((8U << 16U) + 8U)
#define sfNFTokenMinter ((8U << 16U) + 9U)
#define sfEmitCallback ((8U << 16U) + 10U)
#define sfHookAccount ((8U << 16U) + 16U)
#define sfIndexes ((19U << 16U) + 1U)
#define sfHashes ((19U << 16U) + 2U)
#define sfAmendments ((19U << 16U) + 3U)
#define sfNFTokenOffers ((19U << 16U) + 4U)
#define sfHookNamespaces ((19U << 16U) + 5U)
#define sfPaths ((18U << 16U) + 1U)
#define sfTransactionMetaData ((14U << 16U) + 2U)
#define sfCreatedNode ((14U << 16U) + 3U)
#define sfDeletedNode ((14U << 16U) + 4U)
#define sfModifiedNode ((14U << 16U) + 5U)
#define sfPreviousFields ((14U << 16U) + 6U)
#define sfFinalFields ((14U << 16U) + 7U)
#define sfNewFields ((14U << 16U) + 8U)
#define sfTemplateEntry ((14U << 16U) + 9U)
#define sfMemo ((14U << 16U) + 10U)
#define sfSignerEntry ((14U << 16U) + 11U)
#define sfNFToken ((14U << 16U) + 12U)
#define sfEmitDetails ((14U << 16U) + 13U)
#define sfHook ((14U << 16U) + 14U)
#define sfSigner ((14U << 16U) + 16U)
#define sfMajority ((14U << 16U) + 18U)
#define sfDisabledValidator ((14U << 16U) + 19U)
#define sfEmittedTxn ((14U << 16U) + 20U)
#define sfHookExecution ((14U << 16U) + 21U)
#define sfHookDefinition ((14U << 16U) + 22U)
#define sfHookParameter ((14U << 16U) + 23U)
#define sfHookGrant ((14U << 16U) + 24U)
#define sfSigners ((15U << 16U) + 3U)
#define sfSignerEntries ((15U << 16U) + 4U)
#define sfTemplate ((15U << 16U) + 5U)
#define sfNecessary ((15U << 16U) + 6U)
#define sfSufficient ((15U << 16U) + 7U)
#define sfAffectedNodes ((15U << 16U) + 8U)
#define sfMemos ((15U << 16U) + 9U)
#define sfNFTokens ((15U << 16U) + 10U)
#define sfHooks ((15U << 16U) + 11U)
#define sfMajorities ((15U << 16U) + 16U)
#define sfDisabledValidators ((15U << 16U) + 17U)
#define sfHookExecutions ((15U << 16U) + 18U)
#define sfHookParameters ((15U << 16U) + 19U)
#define sfHookGrants ((15U << 16U) + 20U)
#define sfActiveValidators ((15U << 16U) + 95U)

View File

@@ -1,239 +0,0 @@
#include <stdint.h>
// 8 byte-int = 1 bytes
#define SFL_CLOSERESOLUTION 1
#define SFL_METHOD 1
#define SFL_TRANSACTIONRESULT 1
#define SFL_TICKSIZE 1
#define SFL_UNLMODIFYDISABLING 1
#define SFL_HOOKRESULT 1
// 16 byte-int = 2 bytes
#define SFL_LEDGERENTRYTYPE 2
#define SFL_TRANSACTIONTYPE 2
#define SFL_SIGNERWEIGHT 2
#define SFL_TRANSFERFEE 2
#define SFL_VERSION 2
#define SFL_HOOKSTATECHANGECOUNT 2
#define SFL_HOOKEMITCOUNT 2
#define SFL_HOOKEXECUTIONINDEX 2
#define SFL_HOOKAPIVERSION 2
// 32 byte-int = 4 bytes
#define SFL_NETWORKID 4
#define SFL_FLAGS 4
#define SFL_SOURCETAG 4
#define SFL_SEQUENCE 4
#define SFL_PREVIOUSTXNLGRSEQ 4
#define SFL_LEDGERSEQUENCE 4
#define SFL_CLOSETIME 4
#define SFL_PARENTCLOSETIME 4
#define SFL_SIGNINGTIME 4
#define SFL_EXPIRATION 4
#define SFL_TRANSFERRATE 4
#define SFL_WALLETSIZE 4
#define SFL_OWNERCOUNT 4
#define SFL_DESTINATIONTAG 4
#define SFL_HIGHQUALITYIN 4
#define SFL_HIGHQUALITYOUT 4
#define SFL_LOWQUALITYIN 4
#define SFL_LOWQUALITYOUT 4
#define SFL_QUALITYIN 4
#define SFL_QUALITYOUT 4
#define SFL_STAMPESCROW 4
#define SFL_BONDAMOUNT 4
#define SFL_LOADFEE 4
#define SFL_OFFERSEQUENCE 4
#define SFL_FIRSTLEDGERSEQUENCE 4
#define SFL_LASTLEDGERSEQUENCE 4
#define SFL_TRANSACTIONINDEX 4
#define SFL_OPERATIONLIMIT 4
#define SFL_REFERENCEFEEUNITS 4
#define SFL_RESERVEBASE 4
#define SFL_RESERVEINCREMENT 4
#define SFL_SETFLAG 4
#define SFL_CLEARFLAG 4
#define SFL_SIGNERQUORUM 4
#define SFL_CANCELAFTER 4
#define SFL_FINISHAFTER 4
#define SFL_SIGNERLISTID 4
#define SFL_SETTLEDELAY 4
#define SFL_TICKETCOUNT 4
#define SFL_TICKETSEQUENCE 4
#define SFL_NFTOKENTAXON 4
#define SFL_MINTEDNFTOKENS 4
#define SFL_BURNEDNFTOKENS 4
#define SFL_HOOKSTATECOUNT 4
#define SFL_EMITGENERATION 4
#define SFL_LOCKCOUNT 4
#define SFL_REWARDTIME 4
#define SFL_REWARDLGRFIRST 4
#define SFL_REWARDLGRLAST 4
#define SFL_FIRSTNFTOKENSEQUENCE 4
// 64 byte-int = 8 bytes
#define SFL_INDEX_NEXT 8
#define SFL_INDEX_PREVIOUS 8
#define SFL_BOOK_NODE 8
#define SFL_OWNER_NODE 8
#define SFL_BASE_FEE 8
#define SFL_EXCHANGE_RATE 8
#define SFL_LOW_NODE 8
#define SFL_HIGH_NODE 8
#define SFL_DESTINATION_NODE 8
#define SFL_COOKIE 8
#define SFL_SERVER_VERSION 8
#define SFL_EMIT_BURDEN 8
#define SFL_NFTOKEN_OFFER_NODE 8
#define SFL_HOOK_INSTRUCTION_COUNT 8
#define SFL_HOOK_RETURN_CODE 8
#define SFL_REFERENCE_COUNT 8
#define SFL_REWARD_ACCUMULATOR 8
// 128 byte-int = 4 bytes
#define SFL_EMAIL_HASH 128
// 160 byte-int = 4 bytes
#define SFL_TAKER_PAYS_CURRENCY 160
#define SFL_TAKER_PAYS_ISSUER 160
#define SFL_TAKER_GETS_CURRENCY 160
#define SFL_TAKER_GETS_ISSUER 160
// 256 byte-int = ??? bytes
#define SFL_LEDGER_HASH 256
#define SFL_PARENT_HASH 256
#define SFL_TRANSACTION_HASH 256
#define SFL_ACCOUNT_HASH 256
#define SFL_HOOK_ON 256
#define SFL_PREVIOUS_TXN_ID 256
#define SFL_LEDGER_INDEX 256
#define SFL_WALLET_LOCATOR 256
#define SFL_ROOT_INDEX 256
#define SFL_ACCOUNT_TXN_ID 256
#define SFL_NFTOKEN_ID 256
#define SFL_EMIT_PARENT_TXN_ID 256
#define SFL_EMIT_NONCE 256
#define SFL_EMIT_HOOK_HASH 256
// 256 byte-int = ??? bytes
#define SFL_BOOK_DIRECTORY 256
#define SFL_INVOICE_ID 256
#define SFL_NICKNAME 256
#define SFL_AMENDMENT 256
#define SFL_DIGEST 256
#define SFL_CHANNEL 256
#define SFL_CONSENSUS_HASH 256
#define SFL_CHECK_ID 256
#define SFL_VALIDATED_HASH 256
#define SFL_PREVIOUS_PAGE_MIN 256
#define SFL_NEXT_PAGE_MIN 256
#define SFL_NFTOKEN_BUY_OFFER 256
#define SFL_NFTOKEN_SELL_OFFER 256
#define SFL_HOOK_STATE_KEY 256
#define SFL_HOOK_HASH 256
#define SFL_HOOK_NAMESPACE 256
#define SFL_HOOK_SET_TXN_ID 256
#define SFL_OFFER_ID 256
#define SFL_ESCROW_ID 256
#define SFL_URITOKEN_ID 256
// 20 bytes
#define SFL_AMOUNT 20
#define SFL_BALANCE 20
#define SFL_LIMIT_AMOUNT 20
#define SFL_TAKER_PAYS 20
#define SFL_TAKER_GETS 20
#define SFL_LOW_LIMIT 20
#define SFL_HIGH_LIMIT 20
#define SFL_FEE 20
#define SFL_SEND_MAX 20
#define SFL_DELIVER_MIN 20
#define SFL_LOCKED_BALANCE 20
// Unimplemented
#define SFL_AMOUNT_MINIMUM_OFFER 8
#define SFL_AMOUNT_RIPPLE_ESCROW 8
#define SFL_AMOUNT_DELIVERED_AMOUNT 8
#define SFL_AMOUNT_NFTOKEN_BROKER_FEE 8
#define SFL_AMOUNT_HOOK_CALLBACK_FEE 8
#define SFL_AMOUNT_BASE_FEE_DROPS 8
#define SFL_AMOUNT_RESERVE_BASE_DROPS 8
#define SFL_AMOUNT_RESERVE_INCREMENT_DROPS 8
// Unimplemented
#define SFL_VL_PUBLIC_KEY 64
#define SFL_VL_MESSAGE_KEY 64
#define SFL_VL_SIGNING_PUB_KEY 64
// Unimplemented
#define SFL_VL_TXN_SIGNATURE 96
// Unimplemented
#define SFL_VL_URI 256
// Unimplemented
#define SFL_VL_SIGNATURE 96
// Unimplemented
#define SFL_VL_DOMAIN 256
#define SFL_VL_FUND_CODE 256
#define SFL_VL_REMOVE_CODE 256
#define SFL_VL_EXPIRE_CODE 256
#define SFL_VL_CREATE_CODE 256
#define SFL_VL_MEMO_TYPE 256
#define SFL_VL_MEMO_DATA 256
#define SFL_VL_MEMO_FORMAT 256
#define SFL_VL_FULFILLMENT 256
#define SFL_VL_CONDITION 256
// Unimplemented
#define SFL_VL_MASTER_SIGNATURE 96
// Unimplemented
#define SFL_VL_UNL_MODIFY_VALIDATOR 256
#define SFL_VL_VALIDATOR_TO_DISABLE 256
#define SFL_VL_VALIDATOR_TO_RE_ENABLE 256
#define SFL_VL_HOOK_STATE_DATA 256
#define SFL_VL_HOOK_RETURN_STRING 256
#define SFL_VL_HOOK_PARAMETER_NAME 256
#define SFL_VL_HOOK_PARAMETER_VALUE 256
#define SFL_VL_BLOB 256
// 20 bytes
#define SFL_ACCOUNT 20
#define SFL_OWNER 20
#define SFL_DESTINATION 20
#define SFL_ISSUER 20
#define SFL_AUTHORIZE 20
#define SFL_UNAUTHORIZE 20
#define SFL_REGULAR_KEY 20
#define SFL_NFTOKEN_MINTER 20
#define SFL_EMIT_CALLBACK 20
#define SFL_HOOK_ACCOUNT 20
#define SFL_NFTOKEN_MINTER 20
// Unimplemented
#define SFL_PATHS 1
// Unimplemented
#define SFL_VECTOR256_INDEXES 32
#define SFL_VECTOR256_HASHES 32
#define SFL_VECTOR256_AMENDMENTS 32
#define SFL_VECTOR256_NFTOKEN_OFFERS 32
#define SFL_VECTOR256_HOOK_NAMESPACES 32
// Unimplemented
#define SFL_TRANSACTION_META_DATA 1
#define SFL_CREATED_NODE 1
#define SFL_DELETED_NODE 1
#define SFL_MODIFIED_NODE 1
#define SFL_PREVIOUS_FIELDS 1
#define SFL_FINAL_FIELDS 1
#define SFL_NEW_FIELDS 1
#define SFL_TEMPLATE_ENTRY 1
#define SFL_MEMO 1
#define SFL_SIGNER_ENTRY 1
#define SFL_NFTOKEN 1
#define SFL_EMIT_DETAILS 1
#define SFL_HOOK 1
#define SFL_SIGNER 1
#define SFL_MAJORITY 1
#define SFL_DISABLED_VALIDATOR 1
#define SFL_EMITTED_TXN 1
#define SFL_HOOK_EXECUTION 1
#define SFL_HOOK_DEFINITION 1
#define SFL_HOOK_PARAMETER 1
#define SFL_HOOK_GRANT 1
#define SFL_SIGNERS 1
#define SFL_SIGNER_ENTRIES 1
#define SFL_TEMPLATE 1
#define SFL_NECESSARY 1
#define SFL_SUFFICIENT 1
#define SFL_AFFECTED_NODES 1
#define SFL_MEMOS 1
#define SFL_NFTOKENS 1
#define SFL_HOOKS 1
#define SFL_MAJORITIES 1
#define SFL_DISABLED_VALIDATORS 1
#define SFL_HOOK_EXECUTIONS 1
#define SFL_HOOK_EXECUTION 1

View File

@@ -1,9 +1,9 @@
all: reward govern mint
accept:
wasmcc accept.c -o accept.wasm -Oz -Wl,--allow-undefined -I./headers
wasmcc accept.c -o accept.wasm -Oz -Wl,--allow-undefined -I../
hook-cleaner accept.wasm
reward:
wasmcc reward.c -o reward.wasm -Oz -Wl,--allow-undefined -I./headers
wasmcc reward.c -o reward.wasm -Oz -Wl,--allow-undefined -I../
wasm-opt reward.wasm -o reward.wasm \
--shrink-level=100000000 \
--coalesce-locals-learning \
@@ -58,7 +58,7 @@ reward:
hook-cleaner reward.wasm
guard_checker reward.wasm
govern:
wasmcc govern.c -o govern.wasm -Oz -Wl,--allow-undefined -I./headers
wasmcc govern.c -o govern.wasm -Oz -Wl,--allow-undefined -I../
wasm-opt govern.wasm -o govern.wasm \
--shrink-level=100000000 \
--coalesce-locals-learning \
@@ -113,7 +113,7 @@ govern:
hook-cleaner govern.wasm
guard_checker govern.wasm
mint:
wasmcc mint.c -o mint.wasm -Oz -Wl,--allow-undefined -I./headers
wasmcc mint.c -o mint.wasm -Oz -Wl,--allow-undefined -I../
wasm-opt mint.wasm -o mint.wasm \
--shrink-level=100000000 \
--coalesce-locals-learning \
@@ -142,5 +142,5 @@ mint:
hook-cleaner mint.wasm
guard_checker mint.wasm
nftoken:
wasmcc nftoken.c -o nftoken.wasm -Oz -Wl,--allow-undefined -I./headers
wasmcc nftoken.c -o nftoken.wasm -Oz -Wl,--allow-undefined -I../
hook-cleaner nftoken.wasm

View File

@@ -49,7 +49,4 @@
#include "macro.h"
#include "tts.h"
#include "ls_flags.h"
#include "tx_flags.h"
#endif

View File

@@ -1,75 +0,0 @@
// Generated using generate_lsflags.sh
#ifndef HOOKLSFLAGS_INCLUDED
#define HOOKLSFLAGS_INCLUDED 1
enum ltACCOUNT_ROOT {
lsfPasswordSpent = 0x00010000,
lsfRequireDestTag = 0x00020000,
lsfRequireAuth = 0x00040000,
lsfDisallowXRP = 0x00080000,
lsfDisableMaster = 0x00100000,
lsfNoFreeze = 0x00200000,
lsfGlobalFreeze = 0x00400000,
lsfDefaultRipple = 0x00800000,
lsfDepositAuth = 0x01000000,
lsfTshCollect = 0x02000000,
lsfDisallowIncomingNFTokenOffer = 0x04000000,
lsfDisallowIncomingCheck = 0x08000000,
lsfDisallowIncomingPayChan = 0x10000000,
lsfDisallowIncomingTrustline = 0x20000000,
lsfURITokenIssuer = 0x40000000,
lsfDisallowIncomingRemit = 0x80000000,
lsfAllowTrustLineClawback = 0x00001000,
};
enum ltOFFER {
lsfPassive = 0x00010000,
lsfSell = 0x00020000,
};
enum ltRIPPLE_STATE {
lsfLowReserve = 0x00010000,
lsfHighReserve = 0x00020000,
lsfLowAuth = 0x00040000,
lsfHighAuth = 0x00080000,
lsfLowNoRipple = 0x00100000,
lsfHighNoRipple = 0x00200000,
lsfLowFreeze = 0x00400000,
lsfHighFreeze = 0x00800000,
lsfLowDeepFreeze = 0x02000000,
lsfHighDeepFreeze = 0x04000000,
lsfAMMNode = 0x01000000,
};
enum ltSIGNER_LIST {
lsfOneOwnerCount = 0x00010000,
};
enum ltDIR_NODE {
lsfNFTokenBuyOffers = 0x00000001,
lsfNFTokenSellOffers = 0x00000002,
lsfEmittedDir = 0x00000004,
};
enum ltNFTOKEN_OFFER {
lsfSellNFToken = 0x00000001,
};
enum ltURI_TOKEN {
lsfBurnable = 0x00000001,
};
enum remarks {
lsfImmutable = 1,
};
enum ltMPTOKEN_ISSUANCE {
lsfMPTLocked = 0x00000001,
lsfMPTCanLock = 0x00000002,
lsfMPTRequireAuth = 0x00000004,
lsfMPTCanEscrow = 0x00000008,
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
};
enum ltMPTOKEN {
lsfMPTAuthorized = 0x00000002,
};
enum ltCREDENTIAL {
lsfAccepted = 0x00010000,
};
#endif // HOOKLSFLAGS_INCLUDED

View File

@@ -9,8 +9,6 @@
#define sfUNLModifyDisabling ((16U << 16U) + 17U)
#define sfHookResult ((16U << 16U) + 18U)
#define sfWasLockingChainSend ((16U << 16U) + 19U)
#define sfSidecarType ((16U << 16U) + 20U)
#define sfEntropyTier ((16U << 16U) + 21U)
#define sfLedgerEntryType ((1U << 16U) + 1U)
#define sfTransactionType ((1U << 16U) + 2U)
#define sfSignerWeight ((1U << 16U) + 3U)
@@ -24,8 +22,6 @@
#define sfHookApiVersion ((1U << 16U) + 20U)
#define sfHookStateScale ((1U << 16U) + 21U)
#define sfLedgerFixType ((1U << 16U) + 22U)
#define sfHookExportCount ((1U << 16U) + 98U)
#define sfEntropyCount ((1U << 16U) + 99U)
#define sfNetworkID ((2U << 16U) + 1U)
#define sfFlags ((2U << 16U) + 2U)
#define sfSourceTag ((2U << 16U) + 3U)
@@ -84,7 +80,6 @@
#define sfRewardTime ((2U << 16U) + 98U)
#define sfRewardLgrFirst ((2U << 16U) + 99U)
#define sfRewardLgrLast ((2U << 16U) + 100U)
#define sfCancelTicketSequence ((2U << 16U) + 101U)
#define sfIndexNext ((3U << 16U) + 1U)
#define sfIndexPrevious ((3U << 16U) + 2U)
#define sfBookNode ((3U << 16U) + 3U)
@@ -164,7 +159,6 @@
#define sfEmittedTxnID ((5U << 16U) + 97U)
#define sfGovernanceMarks ((5U << 16U) + 98U)
#define sfGovernanceFlags ((5U << 16U) + 99U)
#define sfEntropyDigest ((5U << 16U) + 100U)
#define sfNumber ((9U << 16U) + 1U)
#define sfAmount ((6U << 16U) + 1U)
#define sfBalance ((6U << 16U) + 2U)
@@ -195,7 +189,6 @@
#define sfSignatureReward ((6U << 16U) + 29U)
#define sfMinAccountCreateAmount ((6U << 16U) + 30U)
#define sfLPTokenBalance ((6U << 16U) + 31U)
#define sfTrustLineRewardAccumulator ((6U << 16U) + 99U)
#define sfPublicKey ((7U << 16U) + 1U)
#define sfMessageKey ((7U << 16U) + 2U)
#define sfSigningPubKey ((7U << 16U) + 3U)
@@ -227,7 +220,6 @@
#define sfProvider ((7U << 16U) + 30U)
#define sfMPTokenMetadata ((7U << 16U) + 31U)
#define sfCredentialType ((7U << 16U) + 32U)
#define sfHookName ((7U << 16U) + 97U)
#define sfRemarkValue ((7U << 16U) + 98U)
#define sfRemarkName ((7U << 16U) + 99U)
#define sfAccount ((8U << 16U) + 1U)
@@ -263,7 +255,6 @@
#define sfIssuingChainIssue ((24U << 16U) + 2U)
#define sfAsset ((24U << 16U) + 3U)
#define sfAsset2 ((24U << 16U) + 4U)
#define sfClaimCurrency ((24U << 16U) + 5U)
#define sfXChainBridge ((25U << 16U) + 1U)
#define sfTransactionMetaData ((14U << 16U) + 2U)
#define sfCreatedNode ((14U << 16U) + 3U)
@@ -283,6 +274,7 @@
#define sfDisabledValidator ((14U << 16U) + 19U)
#define sfEmittedTxn ((14U << 16U) + 20U)
#define sfHookExecution ((14U << 16U) + 21U)
#define sfHookDefinition ((14U << 16U) + 22U)
#define sfHookParameter ((14U << 16U) + 23U)
#define sfHookGrant ((14U << 16U) + 24U)
#define sfVoteEntry ((14U << 16U) + 25U)
@@ -294,7 +286,6 @@
#define sfXChainCreateAccountAttestationCollectionElement ((14U << 16U) + 31U)
#define sfPriceData ((14U << 16U) + 32U)
#define sfCredential ((14U << 16U) + 33U)
#define sfExportedTxn ((14U << 16U) + 90U)
#define sfAmountEntry ((14U << 16U) + 91U)
#define sfMintURIToken ((14U << 16U) + 92U)
#define sfHookEmission ((14U << 16U) + 93U)
@@ -302,9 +293,6 @@
#define sfActiveValidator ((14U << 16U) + 95U)
#define sfGenesisMint ((14U << 16U) + 96U)
#define sfRemark ((14U << 16U) + 97U)
#define sfHighReward ((14U << 16U) + 98U)
#define sfLowReward ((14U << 16U) + 99U)
#define sfExportResult ((14U << 16U) + 100U)
#define sfSigners ((15U << 16U) + 3U)
#define sfSignerEntries ((15U << 16U) + 4U)
#define sfTemplate ((15U << 16U) + 5U)

View File

@@ -61,7 +61,6 @@
#define ttNFTOKEN_MODIFY 70
#define ttPERMISSIONED_DOMAIN_SET 71
#define ttPERMISSIONED_DOMAIN_DELETE 72
#define ttEXPORT 91
#define ttCRON 92
#define ttCRON_SET 93
#define ttREMARKS_SET 94
@@ -75,4 +74,3 @@
#define ttUNL_MODIFY 102
#define ttEMIT_FAILURE 103
#define ttUNL_REPORT 104
#define ttCONSENSUS_ENTROPY 105

View File

@@ -1,122 +0,0 @@
// Generated using generate_txflags.sh
#include "ls_flags.h"
#include <stdint.h>
enum UniversalFlags : uint32_t {
tfFullyCanonicalSig = 0x80000000,
};
enum AccountSetFlags : uint32_t {
tfRequireDestTag = 0x00010000,
tfOptionalDestTag = 0x00020000,
tfRequireAuth = 0x00040000,
tfOptionalAuth = 0x00080000,
tfDisallowXRP = 0x00100000,
tfAllowXRP = 0x00200000,
};
enum AccountFlags : uint32_t {
asfRequireDest = 1,
asfRequireAuth = 2,
asfDisallowXRP = 3,
asfDisableMaster = 4,
asfAccountTxnID = 5,
asfNoFreeze = 6,
asfGlobalFreeze = 7,
asfDefaultRipple = 8,
asfDepositAuth = 9,
asfAuthorizedNFTokenMinter = 10,
asfTshCollect = 11,
asfDisallowIncomingNFTokenOffer = 12,
asfDisallowIncomingCheck = 13,
asfDisallowIncomingPayChan = 14,
asfDisallowIncomingTrustline = 15,
asfDisallowIncomingRemit = 16,
asfAllowTrustLineClawback = 17,
};
enum OfferCreateFlags : uint32_t {
tfPassive = 0x00010000,
tfImmediateOrCancel = 0x00020000,
tfFillOrKill = 0x00040000,
tfSell = 0x00080000,
};
enum PaymentFlags : uint32_t {
tfNoRippleDirect = 0x00010000,
tfPartialPayment = 0x00020000,
tfLimitQuality = 0x00040000,
};
enum TrustSetFlags : uint32_t {
tfSetfAuth = 0x00010000,
tfSetNoRipple = 0x00020000,
tfClearNoRipple = 0x00040000,
tfSetFreeze = 0x00100000,
tfClearFreeze = 0x00200000,
tfSetDeepFreeze = 0x00400000,
tfClearDeepFreeze = 0x00800000
};
enum EnableAmendmentFlags : uint32_t {
tfGotMajority = 0x00010000,
tfLostMajority = 0x00020000,
tfTestSuite = 0x80000000,
};
enum PaymentChannelClaimFlags : uint32_t {
tfRenew = 0x00010000,
tfClose = 0x00020000,
};
enum NFTokenMintFlags : uint32_t {
tfBurnable = 0x00000001,
tfOnlyXRP = 0x00000002,
tfTrustLine = 0x00000004,
tfTransferable = 0x00000008,
tfMutable = 0x00000010,
tfStrongTSH = 0x00008000,
};
enum MPTokenIssuanceCreateFlags : uint32_t {
tfMPTCanLock = lsfMPTCanLock,
tfMPTRequireAuth = lsfMPTRequireAuth,
tfMPTCanEscrow = lsfMPTCanEscrow,
tfMPTCanTrade = lsfMPTCanTrade,
tfMPTCanTransfer = lsfMPTCanTransfer,
tfMPTCanClawback = lsfMPTCanClawback,
};
enum MPTokenAuthorizeFlags : uint32_t {
tfMPTUnauthorize = 0x00000001,
};
enum MPTokenIssuanceSetFlags : uint32_t {
tfMPTLock = 0x00000001,
tfMPTUnlock = 0x00000002,
};
enum NFTokenCreateOfferFlags : uint32_t {
tfSellNFToken = 0x00000001,
};
enum ClaimRewardFlags : uint32_t {
tfOptOut = 0x00000001,
};
enum CronSetFlags : uint32_t {
tfCronUnset = 0x00000001,
};
enum AMMClawbackFlags : uint32_t {
tfClawTwoAssets = 0x00000001,
};
enum BridgeModifyFlags : uint32_t {
tfClearAccountCreateAmount = 0x00010000,
};
enum ConsensusEntropyFlags : uint32_t {
tfEntropyCommit = 0x00000001, // entry is a commitment in commitSet
tfEntropyReveal = 0x00000002, // entry is a reveal in entropySet
};

View File

@@ -21,6 +21,7 @@
#define RIPPLE_BASICS_LOCALVALUE_H_INCLUDED
#include <boost/thread/tss.hpp>
#include <chrono>
#include <memory>
#include <unordered_map>
@@ -33,6 +34,16 @@ struct LocalValues
explicit LocalValues() = default;
bool onCoro = true;
void* coroPtr = nullptr; // Pointer to owning JobQueue::Coro (if any)
// When true, SHAMap::finishFetch() will poll-wait for missing nodes
// instead of returning empty. Only set by partial sync code paths.
bool partialSyncWait = false;
// Configurable timeout for SHAMap node fetching during partial sync.
// Zero means use the default (30s). RPC handlers can set this to
// customize poll-wait behavior.
std::chrono::milliseconds fetchTimeout{0};
struct BasicValue
{
@@ -127,6 +138,57 @@ LocalValue<T>::operator*()
.emplace(this, std::make_unique<detail::LocalValues::Value<T>>(t_))
.first->second->get());
}
// Returns pointer to current coroutine if running inside one, nullptr otherwise
inline void*
getCurrentCoroPtr()
{
auto lvs = detail::getLocalValues().get();
if (lvs && lvs->onCoro)
return lvs->coroPtr;
return nullptr;
}
// Check if partial sync wait is enabled for the current coroutine context.
inline bool
isPartialSyncWaitEnabled()
{
auto lvs = detail::getLocalValues().get();
if (lvs && lvs->onCoro)
return lvs->partialSyncWait;
return false;
}
// Enable/disable partial sync wait for the current coroutine context.
inline void
setPartialSyncWait(bool enabled)
{
auto lvs = detail::getLocalValues().get();
if (lvs && lvs->onCoro)
lvs->partialSyncWait = enabled;
}
// Get the configured fetch timeout for current coroutine context.
// Returns 0ms if not in a coroutine or no custom timeout set.
inline std::chrono::milliseconds
getCoroFetchTimeout()
{
auto lvs = detail::getLocalValues().get();
if (lvs && lvs->onCoro)
return lvs->fetchTimeout;
return std::chrono::milliseconds{0};
}
// Set the fetch timeout for the current coroutine context.
// Only works if called from within a coroutine.
inline void
setCoroFetchTimeout(std::chrono::milliseconds timeout)
{
auto lvs = detail::getLocalValues().get();
if (lvs && lvs->onCoro)
lvs->fetchTimeout = timeout;
}
} // namespace ripple
#endif

View File

@@ -15,10 +15,7 @@
#define uint256 std::string
#define featureHooksUpdate1 "1"
#define featureHooksUpdate2 "1"
#define featureExport "1"
#define featureConsensusEntropy "1"
#define fix20250131 "1"
#define fixGuardDepth32 "1"
namespace hook_api {
struct Rules
{
@@ -322,7 +319,7 @@ namespace compare_mode {
enum compare_mode : uint32_t { EQUAL = 1, LESS = 2, GREATER = 4 };
}
enum class hook_return_code : int64_t {
enum hook_return_code : int64_t {
SUCCESS =
0, // return codes > 0 are reserved for hook apis to return "success"
OUT_OF_BOUNDS =
@@ -386,13 +383,10 @@ enum class hook_return_code : int64_t {
MEM_OVERLAP = -43, // one or more specified buffers are the same memory
TOO_MANY_STATE_MODIFICATIONS = -44, // more than 5000 modified state
// entires in the combined hook chains
TOO_MANY_NAMESPACES = -45,
EXPORT_FAILURE = -46,
TOO_MANY_EXPORTED_TXN = -47,
TOO_LITTLE_ENTROPY = -48,
TOO_MANY_NAMESPACES = -45
};
enum class ExitType : uint8_t {
enum ExitType : uint8_t {
UNSET = 0,
WASM_ERROR = 1,
ROLLBACK = 2,
@@ -403,7 +397,6 @@ const uint16_t max_state_modifications = 256;
const uint8_t max_slots = 255;
const uint8_t max_nonce = 255;
const uint8_t max_emit = 255;
const uint8_t max_export = 2;
const uint8_t max_params = 16;
const double fee_base_multiplier = 1.1f;
@@ -444,9 +437,12 @@ getImportWhitelist(Rules const& rules)
return whitelist;
}
#undef HOOK_API_DEFINITION
#undef I32
#undef I64
enum GuardRulesVersion : uint64_t {
GuardRuleFix20250131 = 0x00000001,
GuardRuleDepth32 = 0x00000002,
};
inline uint64_t
@@ -455,8 +451,6 @@ getGuardRulesVersion(Rules const& rules)
uint64_t version = 0;
if (rules.enabled(fix20250131))
version |= GuardRuleFix20250131;
if (rules.enabled(fixGuardDepth32))
version |= GuardRuleDepth32;
return version;
}

View File

@@ -204,13 +204,9 @@ struct WasmBlkInf
}
// compute worst case execution time
inline uint64_t
compute_wce(
const WasmBlkInf* blk,
int level,
int max_level,
bool* recursion_limit_reached)
compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached)
{
if (level > max_level)
if (level > 16)
{
*recursion_limit_reached = true;
return 0;
@@ -237,8 +233,8 @@ compute_wce(
if (blk->children.size() > 0)
for (auto const& child : blk->children)
worst_case_execution += compute_wce(
child, level + 1, max_level, recursion_limit_reached);
worst_case_execution +=
compute_wce(child, level + 1, recursion_limit_reached);
if (parent == 0 ||
parent->iteration_bound ==
@@ -792,17 +788,12 @@ check_guard(
}
bool recursion_limit_reached = false;
int max_level = 16;
if (rulesVersion & hook_api::GuardRuleDepth32)
max_level = 32;
uint64_t wce =
compute_wce(&(*root), 0, max_level, &recursion_limit_reached);
uint64_t wce = compute_wce(&(*root), 0, &recursion_limit_reached);
if (recursion_limit_reached)
{
GUARDLOG(hook::log::NESTING_LIMIT)
<< "GuardCheck "
<< "Maximum allowable depth of blocks reached (" << max_level
<< " levels). Flatten "
<< "Maximum allowable depth of blocks reached (16 levels). Flatten "
"your loops and conditions!.\n";
return {};
}

View File

@@ -89,69 +89,58 @@
#define WASM_VAL_TYPE(T, b) CAT2(TYP_, T)
#define UNSIGNED_TYPE(T) std::make_unsigned_t<T>
#define DECLARE_HOOK_FUNCTION(R, F, ...) \
std::variant<UNSIGNED_TYPE(R), hook_api::hook_return_code> F( \
hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__)); \
extern WasmEdge_Result WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out); \
extern WasmEdge_ValType WasmFunctionParams##F[]; \
extern WasmEdge_ValType WasmFunctionResult##F[]; \
extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \
#define DECLARE_HOOK_FUNCTION(R, F, ...) \
R F(hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__)); \
extern WasmEdge_Result WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out); \
extern WasmEdge_ValType WasmFunctionParams##F[]; \
extern WasmEdge_ValType WasmFunctionResult##F[]; \
extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \
extern WasmEdge_String WasmFunctionName##F;
#define DEFINE_HOOK_FUNCTION(R, F, ...) \
WasmEdge_Result hook_api::WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out) \
{ \
__VA_OPT__(int _stack = 0;) \
__VA_OPT__(FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__);) \
hook::HookContext* hookCtx = \
reinterpret_cast<hook::HookContext*>(data_ptr); \
auto const& return_code = hook_api::F( \
*hookCtx, \
*const_cast<WasmEdge_CallingFrameContext*>(frameCtx) \
__VA_OPT__(COMMA STRIP_TYPES(__VA_ARGS__))); \
if (std::holds_alternative<hook_api::hook_return_code>(return_code) && \
(std::get<hook_api::hook_return_code>(return_code) == \
RC_ROLLBACK || \
std::get<hook_api::hook_return_code>(return_code) == RC_ACCEPT)) \
return WasmEdge_Result_Terminate; \
out[0] = RET_ASSIGN( \
R, \
std::holds_alternative<UNSIGNED_TYPE(R)>(return_code) \
? std::get<UNSIGNED_TYPE(R)>(return_code) \
: R(std::get<hook_api::hook_return_code>(return_code))); \
return WasmEdge_Result_Success; \
}; \
WasmEdge_ValType hook_api::WasmFunctionParams##F[] = { \
__VA_OPT__(FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__))}; \
WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \
WASM_VAL_TYPE(R, dummy)}; \
WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \
WasmEdge_FunctionTypeCreate( \
WasmFunctionParams##F, \
VA_NARGS(NULL __VA_OPT__(, __VA_ARGS__)), \
WasmFunctionResult##F, \
1); \
WasmEdge_String hook_api::WasmFunctionName##F = \
WasmEdge_StringCreateByCString(#F); \
std::variant<UNSIGNED_TYPE(R), hook_api::hook_return_code> hook_api::F( \
hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
#define DEFINE_HOOK_FUNCTION(R, F, ...) \
WasmEdge_Result hook_api::WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out) \
{ \
__VA_OPT__(int _stack = 0;) \
__VA_OPT__(FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__);) \
hook::HookContext* hookCtx = \
reinterpret_cast<hook::HookContext*>(data_ptr); \
R return_code = hook_api::F( \
*hookCtx, \
*const_cast<WasmEdge_CallingFrameContext*>(frameCtx) \
__VA_OPT__(COMMA STRIP_TYPES(__VA_ARGS__))); \
if (return_code == RC_ROLLBACK || return_code == RC_ACCEPT) \
return WasmEdge_Result_Terminate; \
out[0] = RET_ASSIGN(R, return_code); \
return WasmEdge_Result_Success; \
}; \
WasmEdge_ValType hook_api::WasmFunctionParams##F[] = { \
__VA_OPT__(FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__))}; \
WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \
WASM_VAL_TYPE(R, dummy)}; \
WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \
WasmEdge_FunctionTypeCreate( \
WasmFunctionParams##F, \
VA_NARGS(NULL __VA_OPT__(, __VA_ARGS__)), \
WasmFunctionResult##F, \
1); \
WasmEdge_String hook_api::WasmFunctionName##F = \
WasmEdge_StringCreateByCString(#F); \
R hook_api::F( \
hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__))
#define HOOK_SETUP() \
using enum hook_api::hook_return_code; \
try \
{ \
[[maybe_unused]] ApplyContext& applyCtx = hookCtx.applyCtx; \
@@ -214,7 +203,7 @@
host_memory_ptr, \
guest_memory_length) \
{ \
uint64_t bytes_written = 0; \
int64_t bytes_written = 0; \
WRITE_WASM_MEMORY( \
bytes_written, \
guest_dst_ptr, \
@@ -283,7 +272,7 @@
data_ptr < (data_ptr_in)) \
return INTERNAL_ERROR; \
if (data_len == 0) \
return 0ULL; \
return 0; \
if ((write_ptr_in) == 0) \
return data_as_int64(data_ptr, data_len); \
if (data_len > (write_len_in)) \

View File

@@ -372,28 +372,3 @@ HOOK_API_DEFINITION(
HOOK_API_DEFINITION(
int64_t, prepare, (uint32_t, uint32_t, uint32_t, uint32_t),
featureHooksUpdate2)
// int64_t xport_reserve(uint32_t count);
HOOK_API_DEFINITION(
int64_t, xport_reserve, (uint32_t),
featureExport)
// int64_t xport(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, xport, (uint32_t, uint32_t, uint32_t, uint32_t),
featureExport)
// int64_t xport_cancel(uint32_t ticket_seq);
HOOK_API_DEFINITION(
int64_t, xport_cancel, (uint32_t),
featureExport)
// int64_t dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
HOOK_API_DEFINITION(
int64_t, dice, (uint32_t, uint32_t, uint32_t),
featureConsensusEntropy)
// int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier, uint32_t min_count);
HOOK_API_DEFINITION(
int64_t, random, (uint32_t, uint32_t, uint32_t, uint32_t),
featureConsensusEntropy)

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
---
DisableFormat: true

View File

@@ -153,11 +153,7 @@ message TMStatusChange
message TMProposeSet
{
required uint32 proposeSeq = 1;
// Proposed transaction-set identity. Legacy/plain proposals carry the
// tx-set hash directly; ConsensusExtensions proposals carry a serialized
// ExtendedPosition whose first field is that tx-set hash, followed by
// signed RNG/Export sidecar fields.
required bytes currentTxHash = 2;
required bytes currentTxHash = 2; // the hash of the ledger we are proposing
required bytes nodePubKey = 3;
required uint32 closeTime = 4;
required bytes signature = 5; // signature of above fields
@@ -170,14 +166,6 @@ message TMProposeSet
// Number of hops traveled
optional uint32 hops = 12 [deprecated=true];
// Export signatures for pending exports seen in the proposal set. The
// proposal's ExtendedPosition includes a digest of this repeated field, so
// these side-channel blobs are covered by the proposal signature.
// Each entry is: txnHash (32 bytes) + validator pubkey (33 bytes)
// + multisign signature (variable length). Validators attach these
// so export quorum can be reached within the same consensus round.
repeated bytes exportSignatures = 13;
}
enum TxSetStatus
@@ -396,3 +384,4 @@ message TMHaveTransactions
{
repeated bytes hashes = 1;
}

View File

@@ -1,42 +0,0 @@
#ifndef RIPPLE_PROTOCOL_ENTROPY_TIER_H_INCLUDED
#define RIPPLE_PROTOCOL_ENTROPY_TIER_H_INCLUDED
#include <cstdint>
namespace ripple {
/// Which gate the ledger's entropy passed. Stored in sfEntropyTier (UINT8)
/// on the ttCONSENSUS_ENTROPY pseudo-transaction and the ConsensusEntropy
/// ledger entry.
///
/// EntropyCount says how many validators contributed; EntropyTier says which
/// gate the result passed. Values are strength-ordered so consumers can gate
/// with a numeric comparison (tier >= required).
enum EntropyTier : std::uint8_t {
/// No usable entropy (reserved; a fresh ConsensusEntropy entry should
/// always carry one of the tiers below).
entropyTierNone = 0,
/// Consensus-bound deterministic fallback: derived from already-agreed
/// round inputs (parent ledger hash, base tx set hash, sequence) under
/// HashPrefix::entropyFallback when no agreed reveal set reaches either
/// participant_aligned or validator_quorum. Unpredictable in practice but
/// user-influenceable via transaction submission — never suitable for
/// value-bearing outcomes.
entropyTierConsensusFallback = 1,
/// Participant-aligned sub-quorum entropy: the agreed reveal set aligned at
/// the tier-2 participant threshold — below the 80% validator quorum but at
/// or above the equivocation-intersection floor over the original
/// (pre-nUNL)
/// view. Weaker than validator_quorum; opt-in for hooks via min_tier.
entropyTierParticipantAligned = 2,
/// Validator commit/reveal entropy whose sidecar set passed the
/// active-validator-view quorum alignment gate.
entropyTierValidatorQuorum = 3,
};
} // namespace ripple
#endif

View File

@@ -61,10 +61,12 @@ enum error_code_i {
rpcAMENDMENT_BLOCKED = 14,
// Networking
//@@start network-error-codes
rpcNO_CLOSED = 15,
rpcNO_CURRENT = 16,
rpcNO_NETWORK = 17,
rpcNOT_SYNCED = 18,
//@@end network-error-codes
// Ledger state
rpcACT_NOT_FOUND = 19,

View File

@@ -1,33 +0,0 @@
#ifndef RIPPLE_PROTOCOL_EXPORT_LIMITS_H_INCLUDED
#define RIPPLE_PROTOCOL_EXPORT_LIMITS_H_INCLUDED
#include <cstdint>
namespace ripple {
// Export system caps.
//
// These limits bound the DoS surface of the export signature system:
// - Each pending export requires every validator to sign it every round
// (sign-once, attach once via TMProposeSet)
// - Inbound signature processing involves crypto verification per sig
// - The open-ledger cap (maxPendingExports) is the root constraint;
// signing throughput and inbound processing are transitively bounded by it
struct ExportLimits
{
// Maximum exports a single hook execution may produce
// (also enforced by hook_api::max_export in Enum.h)
static constexpr std::uint8_t maxExportsPerHook = 2;
// Maximum pending export transactions in an open/apply ledger.
// Hook-emitted export backlog drains into the open ledger at this cap.
// This transitively caps:
// - signatures per TMProposeSet message (1 per pending export)
// - inbound proposal signature processing (clamped to this)
// - validator signing work per round
static constexpr std::uint8_t maxPendingExports = 8;
};
} // namespace ripple
#endif

View File

@@ -33,39 +33,35 @@
*
* Steps required to add new features to the code:
*
* 1) Add the appropriate XRPL_FEATURE or XRPL_FIX macro definition for the
* feature to features.macro with the feature's name, `Supported::no`, and
* `VoteBehavior::DefaultNo`.
*
* 2) Use the generated variable name as the parameter to `view.rules.enabled()`
* to control flow into new code that this feature limits. (featureName or
* fixName)
*
* 3) If the feature development is COMPLETE, and the feature is ready to be
* SUPPORTED, change the macro parameter in features.macro to Supported::yes.
*
* 4) In general, any newly supported amendments (`Supported::yes`) should have
* a `VoteBehavior::DefaultNo` indefinitely so that external governance can
* make the decision on when to activate it. High priority bug fixes can be
* an exception to this rule. In such cases, ensure the fix has been
* clearly communicated to the community using appropriate channels,
* then change the macro parameter in features.macro to
* `VoteBehavior::DefaultYes`. The communication process is beyond
* the scope of these instructions.
*
* 1) In this file, increment `numFeatures` and add a uint256 declaration
* for the feature at the bottom
* 2) Add a uint256 definition for the feature to the corresponding source
* file (Feature.cpp). Use `registerFeature` to create the feature with
* the feature's name, `Supported::no`, and `VoteBehavior::DefaultNo`. This
* should be the only place the feature's name appears in code as a string.
* 3) Use the uint256 as the parameter to `view.rules.enabled()` to
* control flow into new code that this feature limits.
* 4) If the feature development is COMPLETE, and the feature is ready to be
* SUPPORTED, change the `registerFeature` parameter to Supported::yes.
* 5) When the feature is ready to be ENABLED, change the `registerFeature`
* parameter to `VoteBehavior::DefaultYes`.
* In general, any newly supported amendments (`Supported::yes`) should have
* a `VoteBehavior::DefaultNo` for at least one full release cycle. High
* priority bug fixes can be an exception to this rule of thumb.
*
* When a feature has been enabled for several years, the conditional code
* may be removed, and the feature "retired". To retire a feature:
*
* 1) MOVE the macro definition in features.macro to the "retired features"
* section at the end of the file, and change the macro to XRPL_RETIRE.
*
* 1) Remove the uint256 declaration from this file.
* 2) MOVE the uint256 definition in Feature.cpp to the "retired features"
* section at the end of the file.
* 3) CHANGE the name of the variable to start with "retired".
* 4) CHANGE the parameters of the `registerFeature` call to `Supported::yes`
* and `VoteBehavior::DefaultNo`.
* The feature must remain registered and supported indefinitely because it
* may exist in the Amendments object on ledger. There is no need to vote
* for it because there's nothing to vote for. If the feature definition is
* removed completely from the code, any instances running that code will get
* amendment blocked. Removing the feature from the ledger is beyond the scope
* of these instructions.
* still exists in the ledger, but there is no need to vote for it because
* there's nothing to vote for. If it is removed completely from the code, any
* instances running that code will get amendment blocked. Removing the
* feature from the ledger is beyond the scope of these instructions.
*
*/
@@ -80,32 +76,11 @@ allAmendments();
namespace detail {
#pragma push_macro("XRPL_FEATURE")
#undef XRPL_FEATURE
#pragma push_macro("XRPL_FIX")
#undef XRPL_FIX
#pragma push_macro("XRPL_RETIRE")
#undef XRPL_RETIRE
#define XRPL_FEATURE(name, supported, vote) +1
#define XRPL_FIX(name, supported, vote) +1
#define XRPL_RETIRE(name) +1
// This value SHOULD be equal to the number of amendments registered in
// 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 =
(0 +
#include <xrpl/protocol/detail/features.macro>
);
#undef XRPL_RETIRE
#pragma pop_macro("XRPL_RETIRE")
#undef XRPL_FIX
#pragma pop_macro("XRPL_FIX")
#undef XRPL_FEATURE
#pragma pop_macro("XRPL_FEATURE")
static constexpr std::size_t numFeatures = 113;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -345,17 +320,12 @@ foreachFeature(FeatureBitset bs, F&& f)
#undef XRPL_FEATURE
#pragma push_macro("XRPL_FIX")
#undef XRPL_FIX
#pragma push_macro("XRPL_RETIRE")
#undef XRPL_RETIRE
#define XRPL_FEATURE(name, supported, vote) extern uint256 const feature##name;
#define XRPL_FIX(name, supported, vote) extern uint256 const fix##name;
#define XRPL_RETIRE(name)
#include <xrpl/protocol/detail/features.macro>
#undef XRPL_RETIRE
#pragma pop_macro("XRPL_RETIRE")
#undef XRPL_FIX
#pragma pop_macro("XRPL_FIX")
#undef XRPL_FEATURE

Some files were not shown because too many files have changed in this diff Show More