Compare commits

..

83 Commits

Author SHA1 Message Date
Richard Holland
34607e9d5e Merge pull request #824 from RichardAHBot/pr712-rebased
Pr712 rebased (A noni mousse changes)
2026-09-23 20:16:33 +10:00
Richard Holland
01a8c4fafd Merge branch 'Xahau:dev' into pr712-rebased 2026-09-23 20:11:36 +10:00
RichardAHBot
6e19a59298 Merge PR712: Performance improvements (base_uint 64-bit limbs, CountedObject, JobQueue, spinlock, slab allocator) + AMM amendments 2026-09-23 11:00:15 +02:00
Niq Dudfield
2e32b5c6bb feat(server_definitions): distinguish config-forced from ledger-enabled amendments (#765) 2026-09-23 15:33:43 +10:00
tequ
902ed9b492 Use normal consequences factory for standard transactions (#774)
Replace redundant custom consequence factories that returned normal consequences with the built-in Normal factory.
2026-09-21 21:12:15 +10:00
tequ
c208a40ce8 remove WASM: $COUNTER from SetHook_wasm.h (#804) 2026-09-09 15:31:11 +10:00
tequ
401a35957d retire fixXahauV1, fixXahauV2 Amendments (#722) 2026-09-09 13:00:24 +10:00
Niq Dudfield
65ff61d518 fix: fail fast when amendment blocked instead of degraded state (#707) 2026-09-08 13:39:45 +10:00
Richard Holland
6191c1e09d Onchain manifests (#779) 2026-09-07 13:19:25 +10:00
tequ
f1a3a775a1 Add optional Hooks data to account_info response (#789) 2026-09-02 17:22:53 +10:00
tequ
c1dabe5576 Fix comments (#768) 2026-09-01 14:18:28 +10:00
Niq Dudfield
c6d5b345eb fix: subscription webhook delivery stalling on HTTP errors (#677) 2026-08-27 12:13:21 +10:00
Richard Holland
bb244ef772 put release builds into a candidate folder to prevent auto-update scripts running before smoke tests (#761) 2026-06-21 12:12:43 +10:00
Richard Holland
639ea34377 Fixhookmap (#756) 2026-06-16 17:06:25 +10:00
tequ
089c0dc3fe Fix ammLPHolds logic to include escrowed cases (#757) 2026-06-16 15:26:29 +10:00
tequ
78167e09c0 change sfHookOn of ltHookDefinition to soeOptional for HookOnV2 (#755) 2026-06-16 13:58:53 +10:00
tequ
607a7fdf98 Change AMM to Supported::no (#758) 2026-06-16 13:56:58 +10:00
tequ
c55420bcd8 Fix duplicate and incorrect fields in server_definitions (#753) 2026-05-27 07:58:13 +10:00
tequ
cb91b4e88e Restore pre-reserved tem codes (#754) 2026-05-27 07:57:55 +10:00
tequ
90333b6fd0 Fix HookAPI Expected and Refactor Enum classes (#729) 2026-05-26 11:02:17 +10:00
tequ
7f9a9364b0 fix: Ensures canonical order for PriceDataSeries upon PriceOracle creation (#5485) (#744) 2026-05-25 11:34:24 +10:00
tequ
706d31f01d Update .git-blame-ignore-revs (#746) 2026-05-25 11:28:56 +10:00
tequ
083e9e4315 Generate hook extern declarations with C linkage (#750) 2026-05-25 11:27:21 +10:00
tequ
b9e0c56def Guard depth 32 (#653) 2026-05-25 11:10:49 +10:00
tequ
663ed4edb8 Named Hook (#718) 2026-05-19 12:00:25 +10:00
tequ
8422758d4d chore: Improve codecov coverage reporting (#4977) (#745) 2026-05-18 12:21:47 +10:00
tequ
586c78e812 fix: Replace badCurrency() checks with isBadCurrency() for improved clarity (#742) 2026-05-18 11:57:25 +10:00
Niq Dudfield
9b50b68d39 perf(ledger): optimize catalogue loading memory usage and performance (#548) 2026-05-06 17:29:44 +10:00
tequ
5e8d26f67a refactor: Calculate numFeatures automatically (#5324) (#739)
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-04-30 18:17:50 +10:00
tequ
a6186d7855 IOURewardClaim (#500) 2026-04-30 15:27:51 +10:00
tequ
b449599408 Add --definitions CLI flag to output static server definitions without starting server (#708) 2026-04-30 12:42:42 +10:00
tequ
9d4e507675 Treat suspicious gcov hit counts as coverage warnings (#737) 2026-04-29 17:06:44 +10:00
tequ
49fd0c33b5 fixIOULockedBalanceInvariant Amendment (#732) 2026-04-29 17:05:18 +10:00
tequ
0ffb6e8c21 Replace vendored magic_enum header with Conan package dependency (#727) 2026-04-29 17:00:25 +10:00
tequ
61138058a6 Delete unused sfHookDefinition (#715) 2026-04-29 16:45:35 +10:00
tequ
8dd17973d0 Add hook header ls_flags.h and tx_flags.h (#717) 2026-04-29 16:41:52 +10:00
Fomo
dd9e6053a0 fix: Expand the pathing tables and lower weight on sabbxd, also return up … (#723) 2026-04-29 14:28:00 +10:00
tequ
8c1be39f70 Fix LedgerNameSpace to ensure uniqueness (#710) 2026-04-29 12:01:57 +10:00
tequ
61d1d6e441 Add test for rejected Remit to AMM account (#698) 2026-04-29 11:20:13 +10:00
tequ
788ba43266 Refactor getFieldU16(sfTransactionType) with getTxnType() (#711) 2026-04-29 11:17:49 +10:00
tequ
55710c4baf Disallow setting a AMM account as Issuer/Destination/Inform (#709) 2026-04-29 11:00:25 +10:00
tequ
7bf5bcaf20 Add new TSH tests (#704) 2026-04-29 10:40:50 +10:00
Niq Dudfield
9f2160f42e fix: resolve switch fall-through in util_keylet unimplemented cases (#701) 2026-04-29 10:37:17 +10:00
tequ
ef7a03ec10 fix owner count assertion at XahauGenesis_test (#688) 2026-04-29 10:32:43 +10:00
tequ
6eb6c41ec6 fix: use OIDC token for Codecov uploads from fork PRs (#736) 2026-04-29 09:50:47 +10:00
tequ
9651f68b2e Add coverage workflow (#661)
Co-authored-by: Bronek Kozicki <brok@incorrekt.com>
Co-authored-by: Niq Dudfield <ndudfield@gmail.com>
2026-04-28 18:29:09 +10:00
tequ
f1f44ae232 Build xahau.h from source and check in workflow https://github.com/Xahau/xahaud/pull/674 2026-04-28 18:28:59 +10:00
Alloy Networks
c461dd9055 change build instructions url 2026-04-28 18:23:32 +10:00
tequ
73cf6d34cd Fix BEAST_ENHANCED_LOGGING not working and restore original behavior 2026-04-28 18:23:32 +10:00
tequ
ea92477d21 Test: hint build_test_hooks.sh when hook wasm is empty in hso() 2026-04-28 18:23:32 +10:00
Nicholas Dudfield
e4c7893bf0 Revert "chore: use improved levelization script with threading and argparse"
This reverts commit 5c1d7d9ae9.
2026-04-28 18:23:32 +10:00
Nicholas Dudfield
ad9d6a6eb3 chore: use improved levelization script with threading and argparse 2026-04-28 18:23:32 +10:00
Nicholas Dudfield
dbbffd917e chore: replace levelization shell script with python
Backport of XRPLF/rippled#6325. The python version runs ~80x faster.
2026-04-28 18:23:31 +10:00
tequ
1d7c7e5603 enable ccache direct_mode 2026-04-28 18:23:31 +10:00
tequ
52843e2321 output ccache configuration in release-builder 2026-04-28 18:23:31 +10:00
tequ
6aabbc940b fix: typo SignersListSet 2026-04-28 18:23:31 +10:00
tequ
3111ecea52 Update util_keylet fee test 2026-04-28 18:23:31 +10:00
tequ
1008508c9b Updated tests to align with the changes merged into the dev branch. 2026-04-28 18:23:31 +10:00
tequ
58e278289b Add tests for Hooks fee 2026-04-28 18:23:31 +10:00
tequ
d3d24f781b Merge fixAMMClawbackRounding amendment into featureAMMClawback amendment 2026-04-28 18:23:31 +10:00
yinyiqian1
131d659032 fixAMMClawbackRounding: adjust last holder's LPToken balance (#5513)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance. This was fixed for `AMMWithdraw` in `fixAMMv1_1` by adjusting the LPTokenBalance to be the same as the trustline balance. Since `AMMClawback` is also performing a withdrawal, we need to adjust LPTokenBalance as well in `AMMClawback.`

This change includes:
1. Refactored `verifyAndAdjustLPTokenBalance` function in `AMMUtils`, which both`AMMWithdraw` and `AMMClawback` call to adjust LPTokenBalance.
2. Added the unit test `testLastHolderLPTokenBalance` to test the scenario.
3. Modify the existing unit tests for `fixAMMClawbackRounding`.
2026-04-28 18:23:31 +10:00
tequ
503dee619a Merge fixAMMv1_3 amendment into featureAMM amendment 2026-04-28 18:23:31 +10:00
Gregory Tsipenyuk
1703d96a48 fix: Add AMMv1_3 amendment (#5203)
* Add AMM bid/create/deposit/swap/withdraw/vote invariants:
  - Deposit, Withdrawal invariants: `sqrt(asset1Balance * asset2Balance) >= LPTokens`.
  - Bid: `sqrt(asset1Balance * asset2Balance) > LPTokens` and the pool balances don't change.
  - Create: `sqrt(asset1Balance * assetBalance2) == LPTokens`.
  - Swap: `asset1BalanceAfter * asset2BalanceAfter >= asset1BalanceBefore * asset2BalanceBefore`
     and `LPTokens` don't change.
  - Vote: `LPTokens` and pool balances don't change.
  - All AMM and swap transactions: amounts and tokens are greater than zero, except on withdrawal if all tokens
    are withdrawn.
* Add AMM deposit and withdraw rounding to ensure AMM invariant:
  - On deposit, tokens out are rounded downward and deposit amount is rounded upward.
  - On withdrawal, tokens in are rounded upward and withdrawal amount is rounded downward.
* Add Order Book Offer invariant to verify consumed amounts. Consumed amounts are less than the offer.
* Fix Bid validation. `AuthAccount` can't have duplicate accounts or the submitter account.
2026-04-28 18:23:31 +10:00
Richard Holland
9e569b1641 Merge branch 'dev' into dev2 2026-04-24 11:14:01 +10:00
Alloy Networks
cd00ed72d8 change build instructions url 2026-04-24 11:12:28 +10:00
tequ
05a3e04f2d Fix BEAST_ENHANCED_LOGGING not working and restore original behavior 2026-04-24 11:11:40 +10:00
tequ
66f7294120 Test: hint build_test_hooks.sh when hook wasm is empty in hso() 2026-04-24 11:10:46 +10:00
Nicholas Dudfield
7f6ac75617 Revert "chore: use improved levelization script with threading and argparse"
This reverts commit 5c1d7d9ae9.
2026-04-24 11:09:19 +10:00
Nicholas Dudfield
4150f0383c chore: use improved levelization script with threading and argparse 2026-04-24 11:09:19 +10:00
Nicholas Dudfield
25123b370a chore: replace levelization shell script with python
Backport of XRPLF/rippled#6325. The python version runs ~80x faster.
2026-04-24 11:09:19 +10:00
tequ
f90ed41802 enable ccache direct_mode 2026-04-24 11:06:51 +10:00
tequ
8c4c158d3a output ccache configuration in release-builder 2026-04-24 11:06:51 +10:00
tequ
2d2951875d fix: typo SignersListSet 2026-04-24 11:05:20 +10:00
tequ
9bfca63574 Update util_keylet fee test 2026-04-24 11:00:31 +10:00
tequ
1ba444ae7f Updated tests to align with the changes merged into the dev branch. 2026-04-24 11:00:31 +10:00
tequ
f96d9b6e51 Add tests for Hooks fee 2026-04-24 11:00:31 +10:00
Alphonse Mousse
70edc6d631 Improve base_uint:
This commit makes several changes to the base_uint class, which was
originally taken from Bitcoin and has already been heavily modified
by Ripple engineers.

It introduces conditional widening of the internal limb type, going
from 32 to 64 bits if the requested number of bits is a multiple of
64. This change alone halves iteration counts for common operations
and, combined with use of add-with-carry compiler intrinsics, helps
leverage modern processor resources more effectively.

Other changes include:

 - Extracting the arithmetic operations into free helper functions
   which operate on the limb array directly.
 - Replacing the SFINAE-based is_contiguous_container trait with a
   byte_copy_source concept.
 - Rewriting the hex parsing around a consteval lookup table and a
   simple left-to-right loop, replacing the old shift-based nibble
   accumulator.
 - Using memmove instead of memcpy when copying to avoid UB in the
   unlikely case where the source and destination buffers overlap.
 - Eliminating a workaround in operator<=> to handle MacOS quirks.
 - Removing redundant parseHex overloads and introducing from_hex
   as a factory function.
 - Introducing a tagless_compare() for cross-tag comparison.
 - Adding noexcept, [[nodiscard]], and constexpr throughout.
2026-02-19 01:20:23 +01:00
Alphonse Mousse
b3f96aade0 Modernize CountedObject infrastructure 2026-01-13 10:42:33 +01:00
Alphonse Mousse
db6e457851 Refactor the JobQueue infrastructure and improve load tracking:
This commit cleans up and modernizes the JobQueue but does not change
the queueing logic. It focuses on simplifying the code by eliminating
awkward code constructs, like "invalid jobs" and the need for default
constructors.

It leverages modern C++ to initialize tables and data structures at
compile time and replaces `std:map` instances with directly indexed
arrays.

Lastly, it restructures the load tracking infrastructure and reduces
the need for dynamic memory allocations by supporting move semantics
and value types.

Co-authored-by: Nikolaos D. Bougalis <nikb@bougalis.net>
2022-07-02 08:04:46 +02:00
Alphonse Mousse
432c189c8e Restructure thread pool implementation:
Remove unused dynamic thread count adjustment and reduce indirection
layers. Retains lock-free design while improving code clarity and
reducing internal latency.

Co-authored-by: Nikolaos D. Bougalis <nikb@bougalis.net>
Co-authored-by: Chenna Keshava B S <ckbs.keshava56@gmail.com>
2026-01-04 23:48:31 +01:00
Alphonse Mousse
324dec1050 Restructure the slab allocator:
- Improve the slab allocator public interfaces
 - Support construction of slab allocators at compile time
 - Improve declaration of slab allocators
 - Reduce impact of thrashing around slab boundaries
 - Reduce overhead of locking and increase parallelization

Co-authored-by: Nikolaos D. Bougalis <nikb@bougalis.net>
2025-12-31 07:34:59 +01:00
Alphonse Mousse
eb29c5cf5a Restructure the spinlock code:
- Add function-based spinlock API
 - Properly use compare_exchange_strong for try_lock
 - Improve comments

Co-authored-by: Nikolaos D. Bougalis <nikb@bougalis.net>
2025-12-28 15:48:19 +01:00
Alphonse Mousse
194892cd21 Various fixes:
- Fix unlikely edge case in setCurrentThreadName
    setCurrentThreadName accepts a std::string_view parameter, which is
    not guaranteed to include a null terminator, which it then forwards
    to OS-specific APIs that expect null-terminated C strings.
- Fix several compilation warnings
- Fix header includes
- Fix unnecessarily verbose logging
- Simplify code using inline lambdas instead of std::bind
- Modernize the Beast "lexical cast" framework
- Use boost::asio types instead of legacy Beast type wrappers
- Clean up LogicError, custom exception throwing and termination handling
- Clean up and modernize PublicKey, SecretKey and Seed
    Simplify construction, buffer iteration and comparisons by leveraging
    modern C++ features.
- Clean up and modernize RFC1751 code
    Turn static-member-only class into namespace, thin out the public API
    and make code noexcept and constexpr. Verify internal data at compile
    time.
- Clean up CSPRNG engine
- Clean up rngfill function, eliminating GCC false-positive warning.
- Improve Slice comparison using C++ operator<=> and operator synthesis.
2025-12-28 08:17:34 +01:00
357 changed files with 21540 additions and 13678 deletions

37
.codecov.yml Normal file
View File

@@ -0,0 +1,37 @@
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,8 +1,25 @@
# 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
e2384885f5f630c8f0ffe4bf21a169b433a16858
241b9ddde9e11beb7480600fd5ed90e1ef109b21
760f16f56835663d9286bd29294d074de26a7ba6
0eebe6a5f4246fced516d52b83ec4e7f47373edd
# 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

View File

@@ -2,6 +2,14 @@ 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
@@ -20,6 +28,10 @@ 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
@@ -41,10 +53,11 @@ inputs:
required: false
default: 'dev'
stdlib:
description: 'C++ standard library to use'
description: 'C++ standard library to use (default = compiler default, e.g. GCC always uses libstdc++)'
required: true
type: choice
options:
- default
- libstdcxx
- libcxx
clang_gcc_toolchain:
@@ -87,11 +100,6 @@ 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 }}
@@ -122,6 +130,10 @@ 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
@@ -185,7 +197,8 @@ runs:
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${TOOLCHAIN_FILE} \
-DCMAKE_BUILD_TYPE=${{ inputs.configuration }} \
-Dtests=TRUE \
-Dxrpld=TRUE
-Dxrpld=TRUE \
${{ inputs.cmake-args }}
- name: Show ccache config before build
if: inputs.ccache_enabled == 'true'
@@ -209,7 +222,7 @@ runs:
VERBOSE_FLAG="-- -v"
fi
cmake --build . --config ${{ inputs.configuration }} --parallel $(nproc) ${VERBOSE_FLAG}
cmake --build . --config ${{ inputs.configuration }} --parallel $(nproc) --target ${{ inputs.cmake-target }} ${VERBOSE_FLAG}
- name: Show ccache statistics
if: inputs.ccache_enabled == 'true'

View File

@@ -0,0 +1,107 @@
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' \) -not -path "src/magic/magic_enum.h" -exec clang-format-${CLANG_VERSION} -i {} +
run: find include src -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format-${CLANG_VERSION} -i {} +
- name: Check for differences
id: assert
run: |

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Check levelization
run: Builds/levelization/levelization.sh
run: python Builds/levelization/levelization.py
- 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 './Builds/levelization/levelization.sh' in your repo,
2. Run 'python Builds/levelization/levelization.py' in your repo,
commit, and push.
See Builds/levelization/README.md for more info.

View File

@@ -18,6 +18,10 @@ 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,8 +57,9 @@ jobs:
"cc": "gcc-11",
"cxx": "g++-11",
"compiler_version": 11,
"stdlib": "libstdcxx",
"configuration": "Debug"
"stdlib": "default",
"configuration": "Debug",
"job_type": "build"
},
{
"compiler_id": "gcc-13-libstdcxx",
@@ -66,8 +67,20 @@ jobs:
"cc": "gcc-13",
"cxx": "g++-13",
"compiler_version": 13,
"stdlib": "libstdcxx",
"configuration": "Debug"
"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"
},
{
"compiler_id": "clang-14-libstdcxx-gcc11",
@@ -77,7 +90,8 @@ jobs:
"compiler_version": 14,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 11,
"configuration": "Debug"
"configuration": "Debug",
"job_type": "build"
},
{
"compiler_id": "clang-16-libstdcxx-gcc13",
@@ -87,7 +101,8 @@ jobs:
"compiler_version": 16,
"stdlib": "libstdcxx",
"clang_gcc_toolchain": 13,
"configuration": "Debug"
"configuration": "Debug",
"job_type": "build"
},
{
"compiler_id": "clang-17-libcxx",
@@ -96,7 +111,8 @@ jobs:
"cxx": "clang++-17",
"compiler_version": 17,
"stdlib": "libcxx",
"configuration": "Debug"
"configuration": "Debug",
"job_type": "build"
},
{
# Clang 18 - testing if it's faster than Clang 17 with libc++
@@ -107,14 +123,16 @@ jobs:
"cxx": "clang++-18",
"compiler_version": 18,
"stdlib": "libcxx",
"configuration": "Debug"
"configuration": "Debug",
"job_type": "build"
}
]
# Minimal matrix for PRs and feature branches
minimal_matrix = [
full_matrix[1], # gcc-13 (middle-ground gcc)
full_matrix[2] # clang-14 (mature, stable clang)
full_matrix[2], # gcc-13 coverage
full_matrix[3] # clang-14 (mature, stable clang)
]
# Determine which matrix to use based on the target branch
@@ -189,14 +207,21 @@ jobs:
# Select the appropriate matrix
if use_full:
if force_full:
print(f"Using FULL matrix (6 configs) - forced by [ci-nix-full-matrix] tag")
print(f"Using FULL matrix (7 configs) - forced by [ci-nix-full-matrix] tag")
else:
print(f"Using FULL matrix (6 configs) - targeting main branch")
print(f"Using FULL matrix (7 configs) - targeting main branch")
matrix = full_matrix
else:
print(f"Using MINIMAL matrix (2 configs) - feature branch/PR")
print(f"Using MINIMAL matrix (3 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:
@@ -204,7 +229,10 @@ jobs:
build:
needs: matrix-setup
runs-on: [self-hosted, generic, 20.04]
runs-on: ${{ fromJSON(matrix.runs_on) }}
permissions:
id-token: write
contents: read
container:
image: ubuntu:24.04
volumes:
@@ -233,7 +261,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 python3 python-is-python3 pipx
apt-get install -y git 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
@@ -304,6 +332,12 @@ 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:"
@@ -313,6 +347,13 @@ 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
@@ -340,6 +381,7 @@ 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
@@ -354,7 +396,27 @@ 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 }}"
@@ -367,6 +429,7 @@ 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
@@ -375,3 +438,42 @@ 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

3
.gitignore vendored
View File

@@ -53,6 +53,9 @@ Builds/levelization/results/paths.txt
Builds/levelization/results/includes/
Builds/levelization/results/includedby/
# Python
__pycache__
# Ignore tmp directory.
tmp

View File

@@ -50,7 +50,7 @@ that `test` code should *never* be included in `ripple` code.)
## Validation
The [levelization.sh](levelization.sh) script takes no parameters,
The [levelization.py](levelization.py) script takes no parameters,
reads no environment variables, and can be run from any directory,
as long as it is in the expected location in the rippled repo.
It can be run at any time from within a checked out repo, and will
@@ -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.sh`,
done anything else to improve levelization, run `levelization.py`,
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.sh`
1. Run `levelization.py`
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

@@ -0,0 +1,283 @@
#!/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

@@ -1,130 +0,0 @@
#!/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

@@ -77,6 +77,11 @@ test.ledger > xrpld.app
test.ledger > xrpld.core
test.ledger > xrpld.ledger
test.ledger > xrpl.protocol
test.net > test.toplevel
test.net > xrpl.basics
test.net > xrpld.core
test.net > xrpld.net
test.net > xrpl.json
test.nodestore > test.jtx
test.nodestore > test.toplevel
test.nodestore > test.unit_test
@@ -84,6 +89,7 @@ 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
@@ -118,6 +124,7 @@ 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
@@ -198,11 +205,11 @@ xrpld.rpc > xrpld.core
xrpld.rpc > xrpld.ledger
xrpld.rpc > xrpld.nodestore
xrpld.rpc > xrpld.shamap
xrpld.rpc > xrpl.hook
xrpld.rpc > xrpl.json
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,6 +122,7 @@ 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/infrastructure/building-xahau)
* [Read the build instructions in our documentation](https://xahau.network/docs/infrastructure/build-xahaud/)
* If you encounter any issues, please [open an issue](https://github.com/xahau/xahaud/issues)
## Highlights of Xahau

View File

@@ -71,6 +71,7 @@ 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 &&
@@ -94,8 +95,16 @@ if [[ "$4" == "" ]]; then
echo "Non GH, local building, no Action runner magic"
else
# GH Action, runner
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
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
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,17 +12,16 @@ 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());
@@ -68,15 +67,14 @@ 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 &&
export CXX='/usr/lib64/ccache/g++' &&
echo "-- Build Rippled --" &&
pwd &&
echo "MOVING TO [ build-core.sh ]"
echo "MOVING TO [ build-core.sh ]";
printenv > .env.temp;
cat .env.temp | grep '=' | sed s/\\\(^[^=]\\+=\\\)/\\1\\\"/g|sed s/\$/\\\"/g > .env;
rm .env.temp;
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

@@ -494,7 +494,7 @@
#
# Configure the maximum number of transactions to have in the job queue
#
# Must be a number between 100 and 1000, defaults to 250
# Must be a number between 100 and 1000, defaults to 1000
#
#
# [overlay]
@@ -593,7 +593,7 @@
# reaches or exceeds this number. After that the limit may still
# change, but will stay above the target. If consensus is not
# healthy, the limit will be clamped to this value or lower.
# Default: 50.
# Default: 1000.
#
# maximum_txn_in_ledger = <number>
#

View File

@@ -146,8 +146,6 @@ D686F2538F410C9D0D856788E98E3579595DAF7B38D38887F81ECAC934B06040 HooksUpdate1
3C43D9A973AA4443EF3FC38E42DD306160FBFFDAB901CD8BAA15D09F2597EB87 NonFungibleTokensV1
0285B7E5E08E1A8E4C15636F0591D87F73CB6A7B6452A932AD72BBC8E5D1CBE3 fixNFTokenDirV1
36799EA497B1369B170805C078AEFE6188345F9B3E324C21E9CA3FF574E3C3D6 fixNFTokenNegOffer
4C499D17719BB365B69010A436B64FD1A82AAB199FC1CEB06962EBD01059FB09 fixXahauV1
215181D23BF5C173314B5FDB9C872C92DE6CC918483727DE037C0C13E7E6EE9D fixXahauV2
0D8BF22FF7570D58598D1EF19EBB6E142AD46E59A223FD3816262FBB69345BEA Remit
7CA0426E7F411D39BB014E57CD9E08F61DE1750F0D41FCD428D9FB80BB7596B0 ZeroB2M
4B8466415FAB32FFA89D9DCBE166A42340115771DF611A7160F8D7439C87ECD8 fixNSDelete

View File

@@ -95,6 +95,9 @@
# - 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.
@@ -256,10 +259,10 @@ endif()
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# FORMAT "cobertura" # Output format, one of:
# # xml cobertura sonarqube json-summary
# # json-details coveralls csv txt
# # html-single html-nested html-details
# # (xml is an alias to cobertura;
# # 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;
# # 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+)
@@ -308,6 +311,8 @@ 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()
@@ -320,6 +325,14 @@ 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)
@@ -380,6 +393,7 @@ 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,6 +54,7 @@ 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
@@ -68,6 +69,17 @@ 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)

View File

@@ -29,6 +29,7 @@ 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',

View File

@@ -2,6 +2,9 @@
// 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);
@@ -336,5 +339,8 @@ prepare(
uint32_t read_ptr,
uint32_t read_len);
#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 hook_return_code/,/};/p' "$ENUM_FILE" |
sed -n '/enum class 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 hook_return_code/,/};/p' "$ENUM_FILE" |
{
line = $0
if (line ~ /enum[[:space:]]+hook_return_code/)
if (line ~ /enum[[:space:]]+class[[:space:]]+hook_return_code/)
next
if (line ~ /^[[:space:]]*\{/)
next

View File

@@ -11,6 +11,9 @@ 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) {
@@ -46,6 +49,9 @@ 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'
} | (

82
hook/generate_lsflags.sh Executable file
View File

@@ -0,0 +1,82 @@
#!/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'

25
hook/generate_txflags.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/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
}
'

203
hook/genesis/build_xahau_h.sh Executable file
View File

@@ -0,0 +1,203 @@
#!/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

@@ -0,0 +1,46 @@
// 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

@@ -0,0 +1,352 @@
// 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

@@ -0,0 +1,50 @@
/**
* 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

@@ -0,0 +1,671 @@
/**
* 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

@@ -0,0 +1,215 @@
// 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

@@ -0,0 +1,239 @@
#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../
wasmcc accept.c -o accept.wasm -Oz -Wl,--allow-undefined -I./headers
hook-cleaner accept.wasm
reward:
wasmcc reward.c -o reward.wasm -Oz -Wl,--allow-undefined -I../
wasmcc reward.c -o reward.wasm -Oz -Wl,--allow-undefined -I./headers
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../
wasmcc govern.c -o govern.wasm -Oz -Wl,--allow-undefined -I./headers
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../
wasmcc mint.c -o mint.wasm -Oz -Wl,--allow-undefined -I./headers
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../
wasmcc nftoken.c -o nftoken.wasm -Oz -Wl,--allow-undefined -I./headers
hook-cleaner nftoken.wasm

View File

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

75
hook/ls_flags.h Normal file
View File

@@ -0,0 +1,75 @@
// 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

@@ -152,6 +152,7 @@
#define sfEscrowID ((5U << 16U) + 35U)
#define sfURITokenID ((5U << 16U) + 36U)
#define sfDomainID ((5U << 16U) + 37U)
#define sfManifestID ((5U << 16U) + 91U)
#define sfHookOnOutgoing ((5U << 16U) + 93U)
#define sfHookOnIncoming ((5U << 16U) + 94U)
#define sfCron ((5U << 16U) + 95U)
@@ -189,6 +190,7 @@
#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)
@@ -220,6 +222,7 @@
#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)
@@ -255,6 +258,7 @@
#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)
@@ -274,7 +278,6 @@
#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)
@@ -286,6 +289,7 @@
#define sfXChainCreateAccountAttestationCollectionElement ((14U << 16U) + 31U)
#define sfPriceData ((14U << 16U) + 32U)
#define sfCredential ((14U << 16U) + 33U)
#define sfManifest ((14U << 16U) + 90U)
#define sfAmountEntry ((14U << 16U) + 91U)
#define sfMintURIToken ((14U << 16U) + 92U)
#define sfHookEmission ((14U << 16U) + 93U)
@@ -293,6 +297,8 @@
#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 sfSigners ((15U << 16U) + 3U)
#define sfSignerEntries ((15U << 16U) + 4U)
#define sfTemplate ((15U << 16U) + 5U)

View File

@@ -61,6 +61,7 @@
#define ttNFTOKEN_MODIFY 70
#define ttPERMISSIONED_DOMAIN_SET 71
#define ttPERMISSIONED_DOMAIN_DELETE 72
#define ttMANIFEST_SET 91
#define ttCRON 92
#define ttCRON_SET 93
#define ttREMARKS_SET 94

117
hook/tx_flags.h Normal file
View File

@@ -0,0 +1,117 @@
// 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,
};

View File

@@ -1,75 +1,31 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_BASICS_COUNTEDOBJECT_H_INCLUDED
#define RIPPLE_BASICS_COUNTEDOBJECT_H_INCLUDED
#include <xrpl/beast/type_name.h>
#include <atomic>
#include <cstddef>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
namespace ripple {
/** Manages all counted object types. */
class CountedObjects
{
public:
static CountedObjects&
getInstance() noexcept;
using Entry = std::pair<std::string, int>;
using List = std::vector<Entry>;
List
getCounts(int minimumThreshold) const;
public:
/** Implementation for @ref CountedObject.
@internal
*/
class Counter
{
public:
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
{
// Insert ourselves at the front of the lock-free linked list
CountedObjects& instance = CountedObjects::getInstance();
Counter* head;
do
{
head = instance.m_head.load();
next_ = head;
} while (instance.m_head.exchange(this) != head);
++instance.m_count;
}
~Counter() noexcept = default;
Counter(std::string name) noexcept;
int
increment() noexcept
{
return ++count_;
auto const newCount = ++count_;
if (auto maxCount = maxCount_.load(); newCount > maxCount)
maxCount_.compare_exchange_strong(maxCount, newCount);
return newCount;
}
int
@@ -79,78 +35,136 @@ public:
}
int
getCount() const noexcept
count() const noexcept
{
return count_.load();
}
Counter*
getNext() const noexcept
int
max() const noexcept
{
return next_;
return std::max(count_.load(), maxCount_.load());
}
std::string const&
getName() const noexcept
name() const noexcept
{
return name_;
}
private:
std::string const name_;
std::atomic<int> count_;
friend class CountedObjects;
Counter* next_;
std::atomic<std::uint32_t> count_ = 0;
std::atomic<std::uint32_t> maxCount_ = 0;
std::string const name_;
};
private:
CountedObjects() noexcept;
~CountedObjects() noexcept = default;
class Iterator
{
public:
using value_type = Counter const;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
explicit Iterator(Counter* c = nullptr) noexcept : current_(c)
{
}
reference
operator*() const noexcept
{
return *current_;
}
pointer
operator->() const noexcept
{
return current_;
}
Iterator&
operator++() noexcept
{
current_ = current_->next_;
return *this;
}
Iterator
operator++(int) noexcept
{
auto tmp = *this;
++*this;
return tmp;
}
bool
operator==(Iterator const&) const noexcept = default;
private:
Counter* current_;
};
constexpr CountedObjects() noexcept = default;
auto
begin() const noexcept
{
return Iterator{head_.load()};
}
auto
end() const noexcept
{
return Iterator{};
}
private:
std::atomic<int> m_count;
std::atomic<Counter*> m_head;
friend class Counter;
std::atomic<Counter*> head_ = nullptr;
};
inline constinit CountedObjects countedObjects;
inline CountedObjects::Counter::Counter(std::string name) noexcept
: name_(std::move(name))
{
do
next_ = countedObjects.head_.load();
while (!countedObjects.head_.compare_exchange_weak(next_, this));
}
//------------------------------------------------------------------------------
/** Tracks the number of instances of an object.
Derived classes have their instances counted automatically. This is used
for reporting purposes.
@ingroup ripple_basics
*/
template <class Object>
class CountedObject
{
private:
static auto&
getCounter() noexcept
{
static CountedObjects::Counter c{beast::type_name<Object>()};
return c;
}
static CountedObjects::Counter counter_;
public:
CountedObject() noexcept
{
getCounter().increment();
counter_.increment();
}
CountedObject(CountedObject const&) noexcept
{
getCounter().increment();
counter_.increment();
}
CountedObject&
operator=(CountedObject const&) noexcept = default;
~CountedObject() noexcept
{
getCounter().decrement();
counter_.decrement();
}
};
// Instantiation of the static CountedObject<T>::counter_
template <class Object>
CountedObjects::Counter CountedObject<Object>::counter_{
beast::type_name<Object>()};
} // namespace ripple
#endif

View File

@@ -21,7 +21,6 @@
#define RIPPLE_BASICS_LOCALVALUE_H_INCLUDED
#include <boost/thread/tss.hpp>
#include <chrono>
#include <memory>
#include <unordered_map>
@@ -34,16 +33,6 @@ 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
{
@@ -138,57 +127,6 @@ 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

@@ -113,7 +113,7 @@ template <>
inline std::size_t
extract(SHAMapHash const& key)
{
return *reinterpret_cast<std::size_t const*>(key.as_uint256().data());
return extract(key.as_uint256());
}
} // namespace ripple

View File

@@ -21,283 +21,481 @@
#define RIPPLE_BASICS_SLABALLOCATOR_H_INCLUDED
#include <xrpl/basics/ByteUtilities.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/basics/spinlock.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/align.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/predef.h>
#include <boost/predef/os.h>
#include <algorithm>
#include <array>
#include <atomic>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <vector>
#include <bit>
#if BOOST_OS_LINUX
#include <limits>
#include <new>
#ifdef BOOST_OS_LINUX
#include <sys/mman.h>
#endif
namespace ripple {
namespace slab {
namespace detail {
template <typename Type>
class SlabAllocator
/// Alignment for hugepage support.
inline constexpr std::size_t pageSize = megabytes(std::size_t(2));
/** Allocate a new 2MB-aligned data buffer.
@param size Size of the buffer to allocate in bytes.
@return Pointer to allocated memory, or nullptr on failure.
@note On Linux, advises the kernel to back the allocation with
transparent huge pages if available.
*/
[[nodiscard, gnu::malloc]] inline std::uint8_t*
allocateBuffer(std::size_t size) noexcept
{
static_assert(
sizeof(Type) >= sizeof(std::uint8_t*),
"SlabAllocator: the requested object must be larger than a pointer.");
auto ptr = reinterpret_cast<std::uint8_t*>(
boost::alignment::aligned_alloc(pageSize, size));
static_assert(alignof(Type) == 8 || alignof(Type) == 4);
#if BOOST_OS_LINUX
if (ptr != nullptr) [[likely]]
madvise(ptr, size, MADV_HUGEPAGE);
#endif
/** A block of memory that is owned by a slab allocator */
struct SlabBlock
return ptr;
}
/** Deallocate a buffer previously allocated by allocateBuffer.
@param ptr Pointer to buffer, or nullptr (no-op).
*/
inline void
deallocateBuffer(std::uint8_t* ptr) noexcept
{
boost::alignment::aligned_free(ptr);
}
//------------------------------------------------------------------------------
/** A block of memory metadata for slab allocators.
Each block manages a single slab buffer and its associated free list.
Blocks are linked together to form a list of slabs per allocator.
@note slab_t instances are intentionally never destroyed or reclaimed
once allocated, to support lock-free iteration of lists of slabs
without introducing ABA hazards. The data buffer the class holds
can be released independently.
*/
struct slab_t
{
/** A node in the intrusive free list.
Constructed in-place within free memory blocks. The next pointer
is const because nodes are never modified after construction;
they are simply constructed anew when reused.
*/
struct item_t
{
// A mutex to protect the freelist for this block:
std::mutex m_;
item_t* const next;
// A linked list of appropriately sized free buffers:
std::uint8_t* l_ = nullptr;
// The next memory block
SlabBlock* next_;
// The underlying memory block:
std::uint8_t const* const p_ = nullptr;
// The extent of the underlying memory block:
std::size_t const size_;
SlabBlock(
SlabBlock* next,
std::uint8_t* data,
std::size_t size,
std::size_t item)
: next_(next), p_(data), size_(size)
constexpr explicit item_t(item_t* n) noexcept : next(n)
{
// We don't need to grab the mutex here, since we're the only
// ones with access at this moment.
while (data + item <= p_ + size_)
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(data, &l_, sizeof(std::uint8_t*));
l_ = data;
data += item;
}
}
~SlabBlock()
{
// Calling this destructor will release the allocated memory but
// will not properly destroy any objects that are constructed in
// the block itself.
}
SlabBlock(SlabBlock const& other) = delete;
SlabBlock&
operator=(SlabBlock const& other) = delete;
SlabBlock(SlabBlock&& other) = delete;
SlabBlock&
operator=(SlabBlock&& other) = delete;
/** Determines whether the given pointer belongs to this allocator */
bool
own(std::uint8_t const* p) const noexcept
{
return (p >= p_) && (p < p_ + size_);
}
std::uint8_t*
allocate() noexcept
{
std::uint8_t* ret;
{
std::lock_guard l(m_);
ret = l_;
if (ret)
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(&l_, ret, sizeof(std::uint8_t*));
}
}
return ret;
}
/** Return an item to this allocator's freelist.
@param ptr The pointer to the chunk of memory being deallocated.
@note This is a dangerous, private interface; the item being
returned should belong to this allocator. Debug builds
will check and assert if this is not the case. Release
builds will not.
*/
void
deallocate(std::uint8_t* ptr) noexcept
{
XRPL_ASSERT(
own(ptr),
"ripple::SlabAllocator::SlabBlock::deallocate : own input");
std::lock_guard l(m_);
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(ptr, &l_, sizeof(std::uint8_t*));
l_ = ptr;
}
};
private:
// A linked list of slabs
std::atomic<SlabBlock*> slabs_ = nullptr;
// We need this to be trivially destructible so that we do not
// have to explicitly invoke the destructor when popping items
// from the free list or when deallocating the slab buffer.
static_assert(std::is_trivially_destructible_v<item_t>);
// The alignment requirements of the item we're allocating:
std::size_t const itemAlignment_;
item_t* head_ = nullptr;
slab_t* const next_ = nullptr;
std::atomic<std::uint8_t*> data_{nullptr};
std::atomic<std::uint8_t> lock_{0};
std::uint16_t cycles_ = 0;
std::uint32_t outstanding_ = 0;
// The size of an item, including the extra bytes requested and
// any padding needed for alignment purposes:
std::size_t const itemSize_;
/** Construct a block that is unlinked. */
constexpr slab_t() noexcept = default;
// The size of each individual slab:
std::size_t const slabSize_;
/** Construct a block with a link to the next block in the chain.
@param next Pointer to the next block, or nullptr for the tail.
*/
constexpr explicit slab_t(slab_t* next) noexcept : next_(next)
{
}
~slab_t() = default;
slab_t(slab_t const&) = delete;
slab_t&
operator=(slab_t const&) = delete;
slab_t(slab_t&&) = delete;
slab_t&
operator=(slab_t&&) = delete;
/** Attempt to acquire the block's spinlock.
@return true if lock was acquired, false if already held.
*/
[[nodiscard]] bool
try_lock() noexcept
{
return spin_try_lock(lock_);
}
/** Acquire the block's spinlock, blocking until available. */
void
lock() noexcept
{
spin_lock(lock_);
}
/** Release the block's spinlock. */
void
unlock() noexcept
{
spin_unlock(lock_);
}
/** Attempt to assign a buffer to this block.
If the block has no buffer, takes ownership and initializes
the free list.
@param buffer Pointer to the buffer to assign.
@param slabSize Size of the buffer in bytes.
@param itemSize Size of each item in bytes.
@return Pointer to first item if assigned, nullptr if block already
has a buffer.
*/
[[nodiscard]] std::uint8_t*
try_assign(
std::uint8_t* buffer,
std::size_t slabSize,
std::size_t itemSize) noexcept
{
std::uint8_t* expected = nullptr;
if (!data_.compare_exchange_strong(
expected, buffer, std::memory_order::acquire))
return nullptr;
spin_lock(lock_);
head_ = nullptr;
outstanding_ = 1;
auto p = buffer;
for (std::size_t n = (slabSize / itemSize) - 1; n--; p += itemSize)
head_ = std::construct_at(reinterpret_cast<item_t*>(p), head_);
spin_unlock(lock_);
return p;
}
/** Attempt to allocate from this block.
@return Pointer to allocated memory, or nullptr if block is empty.
*/
[[nodiscard]] std::uint8_t*
try_allocate() noexcept
{
spin_lock(lock_);
auto ret = head_;
if (ret)
{
head_ = ret->next;
++outstanding_;
}
spin_unlock(lock_);
return reinterpret_cast<std::uint8_t*>(ret);
}
/** Attempt to return a pointer to this block.
@param ptr Pointer to memory block.
@param slabSize Size of the slab's data buffer.
@param releaseBuffer Callback invoked with a pointer to the block's
buffer so that it can be released.
@return true if ptr belonged to this block and was freed,
false otherwise.
*/
template <typename ReleaseFunc>
[[nodiscard]] bool
try_deallocate(
std::uint8_t* ptr,
std::size_t slabSize,
ReleaseFunc&& releaseBuffer) noexcept
{
auto data = data_.load(std::memory_order::acquire);
if (!data || ptr < data || ptr >= data + slabSize)
return false;
std::uint8_t* buf = nullptr;
spin_lock(lock_);
assert(ptr != nullptr);
assert(outstanding_ > 0);
head_ = std::construct_at(reinterpret_cast<item_t*>(ptr), head_);
// If this block became empty and it is not the first block, we
// release its buffer. The first block (at the tail of the list
// with next_ == nullptr) is exempt to keep one block ready.
if (--outstanding_ == 0 && next_ != nullptr)
{
head_ = nullptr;
buf = data_.exchange(buf, std::memory_order::relaxed);
cycles_ += (cycles_ < std::numeric_limits<std::uint16_t>::max());
}
spin_unlock(lock_);
if (buf != nullptr)
releaseBuffer(buf);
return true;
}
};
//------------------------------------------------------------------------------
/** Global pool of slab_t objects shared by all slab allocators.
Provides a fixed-size array of pre-allocated slabs with fallback
to heap allocation if exhausted.
*/
class slab_pool_t
{
std::array<slab_t, 32768> blocks_{};
std::atomic<std::size_t> count_{0};
public:
/** Constructs a slab allocator able to allocate objects of a fixed size
constexpr slab_pool_t() = default;
@param count the number of items the slab allocator can allocate; note
that a count of 0 is valid and means that the allocator
is, effectively, disabled. This can be very useful in some
contexts (e.g. when mimimal memory usage is needed) and
allows for graceful failure.
slab_pool_t(slab_pool_t const&) = delete;
slab_pool_t&
operator=(slab_pool_t const&) = delete;
slab_pool_t(slab_pool_t&&) = delete;
slab_pool_t&
operator=(slab_pool_t&&) = delete;
/** Acquire a slab from the pool.
@param next Pointer to link as the slab's next pointer.
@return Pointer to acquired block, or nullptr if allocation failed.
*/
constexpr explicit SlabAllocator(
[[nodiscard]] slab_t*
acquire(slab_t* next) noexcept
{
auto idx = count_.load(std::memory_order::relaxed);
while (idx < blocks_.size())
{
if (count_.compare_exchange_weak(
idx, idx + 1, std::memory_order::relaxed))
{
return std::construct_at(&blocks_[idx], next);
}
}
// Pool exhausted (unlikely!) so fall back to heap
return new (std::nothrow) slab_t(next);
}
};
/// Single global block pool for all slab allocators.
inline constinit slab_pool_t globalSlabPool;
} // namespace detail
//------------------------------------------------------------------------------
/** A slab allocator for fixed-size memory blocks.
Allocates memory in large slabs (multiples of 2MB) and sub-allocates
fixed-size blocks from them. Provides fast allocation with minimal
fragmentation for objects of uniform size.
@tparam Type The type used to determine minimum block size and alignment.
@tparam Align Alignment for allocated blocks (must be >= alignof(Type)).
*/
template <typename Type, std::size_t Align = alignof(Type)>
requires(
sizeof(Type) >= sizeof(void*) && Align >= alignof(Type) &&
std::has_single_bit(Align))
class sized_allocator_t
{
/// Linked list of slabs for this allocator.
std::atomic<detail::slab_t*> slabs_{nullptr};
/// Cached buffer to avoid thrashing when oscillating around block
/// boundaries.
std::atomic<std::uint8_t*> cachedBuffer_{nullptr};
/// Spinlock to serialize the slow allocation path.
std::atomic<std::uint8_t> blockLock_{0};
/// Item size (sizeof(Type) + extra, rounded up for alignment).
std::size_t itemSize_;
/// Size of each slab's data buffer (multiple of 2 MB).
std::size_t slabSize_;
/** Allocate a memory block from the existing slabs.
Fast path allocation that doesn't acquire the global lock.
@return Pointer to allocated memory, or nullptr if all slabs are full.
*/
[[nodiscard]] std::uint8_t*
fast_allocate() noexcept
{
for (auto slab = slabs_.load(std::memory_order::acquire); slab;
slab = slab->next_)
{
if (auto ret = slab->try_allocate())
return ret;
}
return nullptr;
}
/** Allocate a memory block, potentially creating new capacity.
Slow path allocation that may allocate a new buffer or slab.
Must be called with blockLock_ held.
@return Pointer to allocated memory, or nullptr on failure.
*/
[[nodiscard]] std::uint8_t*
slow_allocate() noexcept
{
if (auto ret = fast_allocate())
return ret;
// Use a cached buffer first, if we have one, or allocate memory
// from the operating system.
auto buf = cachedBuffer_.exchange(nullptr, std::memory_order::acquire);
if (!buf)
buf = detail::allocateBuffer(slabSize_);
if (!buf) [[unlikely]]
return nullptr;
// Try to give buffer to an existing empty slab
for (auto slab = slabs_.load(std::memory_order::acquire); slab;
slab = slab->next_)
{
if (auto ret = slab->try_assign(buf, slabSize_, itemSize_))
return ret;
}
// Create new slab from global pool
auto* slab = detail::globalSlabPool.acquire(
slabs_.load(std::memory_order::relaxed));
if (!slab) [[unlikely]]
{
detail::deallocateBuffer(buf);
return nullptr;
}
auto ret = slab->try_assign(buf, slabSize_, itemSize_);
assert(ret);
// Link the new slab. Since we hold the lock, we can just do a
// direct assignment here.
slabs_.store(slab, std::memory_order::release);
return ret;
}
public:
/** Construct a slab allocator.
@param extra Extra bytes per item beyond sizeof(Type).
@param minItems Minimum number of items per slab (rounded up to 2MB).
*/
constexpr explicit sized_allocator_t(
std::size_t extra,
std::size_t alloc = 0,
std::size_t align = 0)
: itemAlignment_(align ? align : alignof(Type))
, itemSize_(
boost::alignment::align_up(sizeof(Type) + extra, itemAlignment_))
, slabSize_(alloc)
std::size_t minItems)
: itemSize_(boost::alignment::align_up(sizeof(Type) + extra, Align))
, slabSize_(
boost::alignment::align_up(
itemSize_ * minItems,
detail::pageSize))
{
XRPL_ASSERT(
(itemAlignment_ & (itemAlignment_ - 1)) == 0,
"ripple::SlabAllocator::SlabAllocator : valid alignment");
}
SlabAllocator(SlabAllocator const& other) = delete;
SlabAllocator&
operator=(SlabAllocator const& other) = delete;
// Data buffers are intentionally not released. C++ destruction order
// does not guarantee that all items allocated from this allocator
// have been freed by the time this destructor runs.
~sized_allocator_t() = default;
SlabAllocator(SlabAllocator&& other) = delete;
SlabAllocator&
operator=(SlabAllocator&& other) = delete;
sized_allocator_t(sized_allocator_t const&) = delete;
sized_allocator_t&
operator=(sized_allocator_t const&) = delete;
sized_allocator_t(sized_allocator_t&&) = delete;
sized_allocator_t&
operator=(sized_allocator_t&&) = delete;
~SlabAllocator()
{
// FIXME: We can't destroy the memory blocks we've allocated, because
// we can't be sure that they are not being used. Cleaning the
// shutdown process up could make this possible.
}
/** Returns the size of memory blocks returned by this allocator.
/** Returns the size of the memory block this allocator returns. */
constexpr std::size_t
@return Size of each allocated block in bytes.
*/
[[nodiscard]] constexpr std::size_t
size() const noexcept
{
return itemSize_;
}
/** Returns a suitably aligned pointer, if one is available.
/** Allocate a memory block.
@return a pointer to a block of memory from the allocator, or
nullptr if the allocator can't satisfy this request.
@return Pointer to allocated memory, or nullptr on failure.
@note The gnu::malloc attribute is an optimization hint that can
be leveraged by GCC and Clang.
*/
std::uint8_t*
[[nodiscard, gnu::malloc]] std::uint8_t*
allocate() noexcept
{
auto slab = slabs_.load();
// Fast path: try existing slabs
auto ret = fast_allocate();
while (slab != nullptr)
if (ret == nullptr)
{
if (auto ret = slab->allocate())
return ret;
slab = slab->next_;
// Slow path: need new capacity. Grab the lock to serialize access
// to this code path because it is expensive. Then re-check in case
// another thread added capacity while we waited.
spin_lock(blockLock_);
ret = slow_allocate();
spin_unlock(blockLock_);
}
// No slab can satisfy our request, so we attempt to allocate a new
// one here:
std::size_t size = slabSize_;
// We want to allocate the memory at a 2 MiB boundary, to make it
// possible to use hugepage mappings on Linux:
auto buf =
boost::alignment::aligned_alloc(megabytes(std::size_t(2)), size);
// clang-format off
if (!buf) [[unlikely]]
return nullptr;
// clang-format on
#if BOOST_OS_LINUX
// When allocating large blocks, attempt to leverage Linux's
// transparent hugepage support. It is unclear and difficult
// to accurately determine if doing this impacts performance
// enough to justify using platform-specific tricks.
if (size >= megabytes(std::size_t(4)))
madvise(buf, size, MADV_HUGEPAGE);
#endif
// We need to carve out a bit of memory for the slab header
// and then align the rest appropriately:
auto slabData = reinterpret_cast<void*>(
reinterpret_cast<std::uint8_t*>(buf) + sizeof(SlabBlock));
auto slabSize = size - sizeof(SlabBlock);
// This operation is essentially guaranteed not to fail but
// let's be careful anyways.
if (!boost::alignment::align(
itemAlignment_, itemSize_, slabData, slabSize))
{
boost::alignment::aligned_free(buf);
return nullptr;
}
slab = new (buf) SlabBlock(
slabs_.load(),
reinterpret_cast<std::uint8_t*>(slabData),
slabSize,
itemSize_);
// Link the new slab
while (!slabs_.compare_exchange_weak(
slab->next_,
slab,
std::memory_order_release,
std::memory_order_relaxed))
{
; // Nothing to do
}
return slab->allocate();
return ret;
}
/** Returns the memory block to the allocator.
/** Return a memory block to the allocator.
@param ptr A pointer to a memory block.
@param size If non-zero, a hint as to the size of the block.
@return true if this memory block belonged to the allocator and has
been released; false otherwise.
@param ptr Pointer to memory block.
@return true if the block belonged to this allocator and was freed.
*/
bool
[[nodiscard]] bool
deallocate(std::uint8_t* ptr) noexcept
{
XRPL_ASSERT(
@@ -305,125 +503,146 @@ public:
"ripple::SlabAllocator::SlabAllocator::deallocate : non-null "
"input");
for (auto slab = slabs_.load(); slab != nullptr; slab = slab->next_)
auto release = [this](std::uint8_t* buf) {
std::uint8_t* expected = nullptr;
if (buf != nullptr &&
!cachedBuffer_.compare_exchange_strong(
expected, buf, std::memory_order::release))
detail::deallocateBuffer(buf);
};
for (auto slab = slabs_.load(std::memory_order::acquire); slab;
slab = slab->next_)
{
if (slab->own(ptr))
{
slab->deallocate(ptr);
if (slab->try_deallocate(ptr, slabSize_, release))
return true;
}
}
return false;
}
};
/** A collection of slab allocators of various sizes for a given type. */
template <typename Type>
class SlabAllocatorSet
//------------------------------------------------------------------------------
/** Configuration for a single slab allocator.
@tparam MinItems Minimum number of items per slab (must be > 0).
@tparam Extra Extra bytes per item beyond sizeof(Type).
*/
template <std::size_t MinItems, std::size_t Extra = 0>
requires(MinItems > 0)
struct config
{
private:
// The list of allocators that belong to this set
boost::container::static_vector<SlabAllocator<Type>, 64> allocators_;
static constexpr std::size_t minItems = MinItems;
static constexpr std::size_t extra = Extra;
};
std::size_t maxSize_ = 0;
/** Concept for valid slab configuration types. */
template <typename T>
concept SlabConfig = requires {
{ T::minItems } -> std::convertible_to<std::size_t>;
{ T::extra } -> std::convertible_to<std::size_t>;
requires T::minItems > 0;
};
public:
class SlabConfig
{
friend class SlabAllocatorSet;
/** Validate slab configurations at compile time.
private:
std::size_t extra;
std::size_t alloc;
std::size_t align;
Checks that configurations produce strictly increasing sizes after
alignment. This catches both unsorted configs and configs that
collapse to the same size after alignment.
public:
constexpr SlabConfig(
std::size_t extra_,
std::size_t alloc_ = 0,
std::size_t align_ = alignof(Type))
: extra(extra_), alloc(alloc_), align(align_)
{
}
@tparam Type The type used to determine base size.
@tparam Align Alignment for allocated blocks.
@tparam Configs Configuration types to validate.
@return true if configurations are valid, false otherwise.
*/
template <typename Type, std::size_t Align, SlabConfig... Configs>
consteval bool
validate_slab_config()
{
constexpr auto align_up = [](std::size_t n, std::size_t a) {
return (n + a - 1) & ~(a - 1);
};
constexpr SlabAllocatorSet(std::vector<SlabConfig> cfg)
{
// Ensure that the specified allocators are sorted from smallest to
// largest by size:
std::sort(
std::begin(cfg),
std::end(cfg),
[](SlabConfig const& a, SlabConfig const& b) {
return a.extra < b.extra;
});
constexpr std::array<std::size_t, sizeof...(Configs)> sizes{
align_up(sizeof(Type) + Configs::extra, Align)...};
// We should never have two slabs of the same size
if (std::adjacent_find(
std::begin(cfg),
std::end(cfg),
[](SlabConfig const& a, SlabConfig const& b) {
return a.extra == b.extra;
}) != cfg.end())
{
throw std::runtime_error(
"SlabAllocatorSet<" + beast::type_name<Type>() +
">: duplicate slab size");
}
for (std::size_t i = 1; i < sizes.size(); ++i)
if (sizes[i - 1] >= sizes[i])
return false;
for (auto const& c : cfg)
{
auto& a = allocators_.emplace_back(c.extra, c.alloc, c.align);
return true;
}
if (a.size() > maxSize_)
maxSize_ = a.size();
}
}
//------------------------------------------------------------------------------
SlabAllocatorSet(SlabAllocatorSet const& other) = delete;
SlabAllocatorSet&
operator=(SlabAllocatorSet const& other) = delete;
/** A collection of slab allocators for different sizes.
SlabAllocatorSet(SlabAllocatorSet&& other) = delete;
SlabAllocatorSet&
operator=(SlabAllocatorSet&& other) = delete;
Manages multiple sized_allocator_t instances, each configured for a
different block size. Allocations are routed to the smallest allocator
that can satisfy the request.
~SlabAllocatorSet()
@tparam Type The type used to determine minimum block size and alignment.
@tparam Align Alignment for allocated blocks (must be >= alignof(Type)).
@tparam Configs Configuration types specifying each size class.
*/
template <typename Type, std::size_t Align, SlabConfig... Configs>
requires(
sizeof(Type) >= sizeof(void*) && Align >= alignof(Type) &&
std::has_single_bit(Align) && sizeof...(Configs) > 0 &&
validate_slab_config<Type, Align, Configs...>())
class aligned_allocator_t
{
std::array<sized_allocator_t<Type, Align>, sizeof...(Configs)> allocators_;
public:
/** Construct an allocator set. */
constexpr aligned_allocator_t()
: allocators_{sized_allocator_t<Type, Align>(
Configs::extra,
Configs::minItems)...}
{
}
/** Returns a suitably aligned pointer, if one is available.
~aligned_allocator_t() = default;
@param extra The number of extra bytes, above and beyond the size of
the object, that should be returned by the allocator.
aligned_allocator_t(aligned_allocator_t const&) = delete;
aligned_allocator_t&
operator=(aligned_allocator_t const&) = delete;
aligned_allocator_t(aligned_allocator_t&&) = delete;
aligned_allocator_t&
operator=(aligned_allocator_t&&) = delete;
@return a pointer to a block of memory, or nullptr if the allocator
can't satisfy this request.
/** Allocate memory for an object with extra bytes.
@param extra Extra bytes needed beyond sizeof(Type).
@return Pointer to memory, or nullptr if no suitable allocator
or allocation failed.
@note The gnu::malloc attribute is an optimization hint that can
be leveraged by GCC and Clang.
*/
std::uint8_t*
[[nodiscard, gnu::malloc]] std::uint8_t*
allocate(std::size_t extra) noexcept
{
if (auto const size = sizeof(Type) + extra; size <= maxSize_)
auto const size = sizeof(Type) + extra;
for (auto& a : allocators_)
{
for (auto& a : allocators_)
{
if (a.size() >= size)
return a.allocate();
}
if (a.size() >= size)
return a.allocate();
}
return nullptr;
}
/** Returns the memory block to the allocator.
/** Return memory to the allocator set.
@param ptr A pointer to a memory block.
@return true if this memory block belonged to one of the allocators
in this set and has been released; false otherwise.
@param ptr Pointer to memory block.
@return true if memory belonged to this set and was freed.
*/
bool
[[nodiscard]] bool
deallocate(std::uint8_t* ptr) noexcept
{
for (auto& a : allocators_)
@@ -431,11 +650,16 @@ public:
if (a.deallocate(ptr))
return true;
}
return false;
}
};
/** Alias for aligned_allocator_t with default alignment. */
template <typename Type, SlabConfig... Configs>
using allocator_t = aligned_allocator_t<Type, alignof(Type), Configs...>;
} // namespace slab
} // namespace ripple
#endif // RIPPLE_BASICS_SLABALLOCATOR_H_INCLUDED

View File

@@ -28,6 +28,7 @@
#include <cstdint>
#include <cstring>
#include <limits>
#include <span>
#include <stdexcept>
#include <string>
#include <type_traits>
@@ -200,34 +201,26 @@ hash_append(Hasher& h, Slice const& v)
h(v.data(), v.size());
}
inline std::strong_ordering
operator<=>(Slice const& lhs, Slice const& rhs) noexcept
{
return std::lexicographical_compare_three_way(
lhs.data(),
lhs.data() + lhs.size(),
rhs.data(),
rhs.data() + rhs.size());
;
}
inline bool
operator==(Slice const& lhs, Slice const& rhs) noexcept
{
if (lhs.size() != rhs.size())
return false;
if (lhs.size() == 0)
return true;
return std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
inline bool
operator!=(Slice const& lhs, Slice const& rhs) noexcept
{
return !(lhs == rhs);
}
inline bool
operator<(Slice const& lhs, Slice const& rhs) noexcept
{
return std::lexicographical_compare(
return std::equal(
lhs.data(),
lhs.data() + lhs.size(),
rhs.data(),
rhs.data() + rhs.size());
}
template <class Stream>
Stream&
operator<<(Stream& s, Slice const& v)
@@ -245,6 +238,15 @@ makeSlice(std::array<T, N> const& a)
return Slice(a.data(), a.size());
}
template <class T, std::size_t N>
std::enable_if_t<
std::is_same<T, char>::value || std::is_same<T, unsigned char>::value,
Slice>
makeSlice(std::span<T, N> const& a)
{
return Slice(a.data(), a.size());
}
template <class T, class Alloc>
std::enable_if_t<
std::is_same<T, char>::value || std::is_same<T, unsigned char>::value,

File diff suppressed because it is too large Load Diff

View File

@@ -22,54 +22,54 @@
#include <xrpl/beast/type_name.h>
#include <exception>
#include <string>
#include <typeinfo>
#include <string_view>
#include <type_traits>
#include <utility>
namespace ripple {
/* Programming By Contract
namespace detail {
This routines are used when checking
preconditions, postconditions, and invariants.
*/
/** Generates and logs a call stack */
/** Throws an exception, logging its type and message before doing so. */
void
LogThrow(std::string const& title);
LogThrow(std::string_view type, std::string_view what);
/** Rethrow the exception currently being handled.
} // namespace detail
/** Throws an exception, logging its type and message before doing so.
When called from within a catch block, it will pass
control to the next matching exception handler, if any.
Otherwise, std::terminate will be called.
*/
[[noreturn]] inline void
Rethrow()
{
LogThrow("Re-throwing exception");
throw;
}
@tparam E The exception type. Must derive from std::exception.
@tparam Args Constructor argument types for E.
@param args Arguments forwarded to the constructor of E.
*/
template <class E, class... Args>
[[noreturn]] inline void
[[noreturn]] constexpr void
Throw(Args&&... args)
{
static_assert(
std::is_convertible<E*, std::exception*>::value,
std::derived_from<E, std::exception>,
"Exception must derive from std::exception.");
E e(std::forward<Args>(args)...);
LogThrow(
std::string(
"Throwing exception of type " + beast::type_name<E>() + ": ") +
e.what());
E e{std::forward<Args>(args)...};
// This will avoid the logging call when we the call is being evaluated at
// compile time and logging would not be possible or helpful. This enables
// `Throw` to be called at from constexpr/consteval functions.
if (!std::is_constant_evaluated())
detail::LogThrow(beast::type_name<E>().c_str(), e.what());
throw e;
}
/** Called when faulty logic causes a broken invariant. */
/** Logs a fatal message and terminates the process unconditionally.
This should be called when code detects a broken invariant
or a condition from which recovery is not possible.
@param msg A description of the error.
*/
[[noreturn]] void
LogicError(std::string const& how) noexcept;
LogicError(std::string_view msg) noexcept;
} // namespace ripple

View File

@@ -20,6 +20,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <atomic>
#include <concepts>
#include <limits>
#include <type_traits>
@@ -30,6 +31,7 @@
namespace ripple {
namespace detail {
/** Inform the processor that we are in a tight spin-wait loop.
Spinlocks caught in tight loops can result in the processor's pipeline
@@ -52,11 +54,132 @@ spin_pause() noexcept
} // namespace detail
/** @{ */
/** Classes to handle arrays of spinlocks packed into a single atomic integer:
//------------------------------------------------------------------------------
Packed spinlocks allow for tremendously space-efficient lock-sharding
but they come at a cost.
/** Attempt to acquire a spinlock without blocking.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
@return true if the lock was acquired, false if it was already held.
*/
template <typename T>
requires(std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free)
[[nodiscard]] bool
spin_try_lock(std::atomic<T>& lock) noexcept
{
T expected = 0;
return lock.compare_exchange_strong(
expected,
std::numeric_limits<T>::max(),
std::memory_order::acquire,
std::memory_order::relaxed);
}
/** Acquire a spinlock, blocking until available.
Uses a TTAS (test-and-test-and-set) pattern to reduce cache coherency
traffic during contention.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
*/
template <typename T>
requires(std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free)
void
spin_lock(std::atomic<T>& lock) noexcept
{
T expected = 0;
while (!lock.compare_exchange_weak(
expected,
std::numeric_limits<T>::max(),
std::memory_order::acquire,
std::memory_order::relaxed))
{
expected = 0;
while (lock.load(std::memory_order::relaxed) != 0)
detail::spin_pause();
}
}
/** Release a spinlock.
@tparam T An unsigned integral type.
@param lock The atomic variable used as the lock.
*/
template <typename T>
requires(std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free)
void
spin_unlock(std::atomic<T>& lock) noexcept
{
lock.store(0, std::memory_order::release);
}
//------------------------------------------------------------------------------
/** A Lockable interface to a spinlock implemented on top of an atomic.
@tparam T An unsigned integral type.
@note Using `packed_spinlock` and `spinlock` against the same underlying
atomic integer can result in `spinlock` not being able to actually
acquire the lock during periods of high contention, because of how
the two locks operate: `spinlock` will spin trying to grab all the
bits at once, whereas any given `packed_spinlock` will only try to
grab one bit at a time. Caveat emptor.
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <typename T>
requires(std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free)
class spinlock
{
std::atomic<T>& lock_;
public:
spinlock(spinlock const&) = delete;
spinlock&
operator=(spinlock const&) = delete;
/** Construct a spinlock handle.
@param lock The atomic integer to spin against.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
*/
explicit spinlock(std::atomic<T>& lock) noexcept : lock_(lock)
{
}
[[nodiscard]] bool
try_lock() noexcept
{
return spin_try_lock(lock_);
}
void
lock() noexcept
{
spin_lock(lock_);
}
void
unlock() noexcept
{
spin_unlock(lock_);
}
};
//------------------------------------------------------------------------------
/** A Lockable interface to a packed spinlock implemented on top of an atomic.
Packed spinlocks offer tremendous space-efficient lock-sharding but
they come at a cost.
First, the implementation is necessarily low-level and uses advanced
features like memory ordering and highly platform-specific tricks to
@@ -78,26 +201,19 @@ spin_pause() noexcept
that it can, usually, outperform spinlocks.
@tparam T An unsigned integral type (e.g. std::uint16_t)
*/
/** A class that grabs a single packed spinlock from an atomic integer.
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <class T>
template <typename T>
requires(
std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free &&
requires(std::atomic<T>& a, T v) {
{ a.fetch_or(v) } -> std::same_as<T>;
{ a.fetch_and(v) } -> std::same_as<T>;
})
class packed_spinlock
{
// clang-format off
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
static_assert(
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
"std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
// clang-format on
private:
std::atomic<T>& bits_;
T const mask_;
@@ -106,7 +222,7 @@ public:
packed_spinlock&
operator=(packed_spinlock const&) = delete;
/** A single spinlock packed inside the specified atomic
/** Construct a packed spinlock handle for a single bit.
@param lock The atomic integer inside which the spinlock is packed.
@param index The index of the spinlock this object acquires.
@@ -114,7 +230,7 @@ public:
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
*/
packed_spinlock(std::atomic<T>& lock, int index)
packed_spinlock(std::atomic<T>& lock, int index) noexcept
: bits_(lock), mask_(static_cast<T>(1) << index)
{
XRPL_ASSERT(
@@ -123,13 +239,13 @@ public:
}
[[nodiscard]] bool
try_lock()
try_lock() noexcept
{
return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
return (bits_.fetch_or(mask_, std::memory_order::acquire) & mask_) == 0;
}
void
lock()
lock() noexcept
{
while (!try_lock())
{
@@ -137,89 +253,18 @@ public:
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
while ((bits_.load(std::memory_order::relaxed) & mask_) != 0)
detail::spin_pause();
}
}
void
unlock()
unlock() noexcept
{
bits_.fetch_and(~mask_, std::memory_order_release);
bits_.fetch_and(~mask_, std::memory_order::release);
}
};
/** A spinlock implemented on top of an atomic integer.
@note Using `packed_spinlock` and `spinlock` against the same underlying
atomic integer can result in `spinlock` not being able to actually
acquire the lock during periods of high contention, because of how
the two locks operate: `spinlock` will spin trying to grab all the
bits at once, whereas any given `packed_spinlock` will only try to
grab one bit at a time. Caveat emptor.
This class meets the requirements of Lockable:
https://en.cppreference.com/w/cpp/named_req/Lockable
*/
template <class T>
class spinlock
{
static_assert(std::is_unsigned_v<T>);
static_assert(std::atomic<T>::is_always_lock_free);
private:
std::atomic<T>& lock_;
public:
spinlock(spinlock const&) = delete;
spinlock&
operator=(spinlock const&) = delete;
/** Grabs the
@param lock The atomic integer to spin against.
@note For performance reasons, you should strive to have `lock` be
on a cacheline by itself.
*/
spinlock(std::atomic<T>& lock) : lock_(lock)
{
}
[[nodiscard]] bool
try_lock()
{
T expected = 0;
return lock_.compare_exchange_weak(
expected,
std::numeric_limits<T>::max(),
std::memory_order_acquire,
std::memory_order_relaxed);
}
void
lock()
{
while (!try_lock())
{
// The use of relaxed memory ordering here is intentional and
// serves to help reduce cache coherency traffic during times
// of contention by avoiding writes that would definitely not
// result in the lock being acquired.
while (lock_.load(std::memory_order_relaxed) != 0)
detail::spin_pause();
}
}
void
unlock()
{
lock_.store(0, std::memory_order_release);
}
};
/** @} */
} // namespace ripple
#endif

View File

@@ -20,187 +20,161 @@
#ifndef BEAST_MODULE_CORE_TEXT_LEXICALCAST_H_INCLUDED
#define BEAST_MODULE_CORE_TEXT_LEXICALCAST_H_INCLUDED
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/beast/type_name.h>
#include <boost/beast/core/string_type.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/utility/string_view.hpp>
#include <algorithm>
#include <cerrno>
#include <array>
#include <charconv>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <string>
#include <string_view>
#include <type_traits>
#include <typeinfo>
#include <utility>
namespace beast {
namespace detail {
// These specializatons get called by the non-member functions to do the work
template <class Out, class In>
struct LexicalCast;
// conversion to std::string
template <class In>
struct LexicalCast<std::string, In>
{
explicit LexicalCast() = default;
template <class Arithmetic = In>
std::enable_if_t<std::is_arithmetic_v<Arithmetic>, bool>
operator()(std::string& out, Arithmetic in)
{
out = std::to_string(in);
return true;
}
template <class Enumeration = In>
std::enable_if_t<std::is_enum_v<Enumeration>, bool>
operator()(std::string& out, Enumeration in)
{
out = std::to_string(
static_cast<std::underlying_type_t<Enumeration>>(in));
return true;
}
};
// Parse a std::string_view into a number
template <typename Out>
struct LexicalCast<Out, std::string_view>
{
explicit LexicalCast() = default;
static_assert(
std::is_integral_v<Out>,
"beast::LexicalCast can only be used with integral types");
template <class Integral = Out>
std::enable_if_t<
std::is_integral_v<Integral> && !std::is_same_v<Integral, bool>,
bool>
operator()(Integral& out, std::string_view in) const
{
auto first = in.data();
auto last = in.data() + in.size();
if (first != last && *first == '+')
++first;
auto ret = std::from_chars(first, last, out);
return ret.ec == std::errc() && ret.ptr == last;
}
bool
operator()(bool& out, std::string_view in) const
{
std::string result;
// Convert the input to lowercase
std::transform(
in.begin(), in.end(), std::back_inserter(result), [](auto c) {
return std::tolower(static_cast<unsigned char>(c));
});
if (result == "1" || result == "true")
{
out = true;
return true;
}
if (result == "0" || result == "false")
{
out = false;
return true;
}
return false;
}
};
//------------------------------------------------------------------------------
// Parse boost library's string_view to number or boolean value
// Note: As of Jan 2024, Boost contains three different types of string_view
// (boost::core::basic_string_view<char>, boost::string_ref and
// boost::string_view). The below template specialization is included because
// it is used in the handshake.cpp file
template <class Out>
struct LexicalCast<Out, boost::core::basic_string_view<char>>
{
explicit LexicalCast() = default;
bool
operator()(Out& out, boost::core::basic_string_view<char> in) const
{
return LexicalCast<Out, std::string_view>()(out, in);
}
};
// Parse std::string to number or boolean value
template <class Out>
struct LexicalCast<Out, std::string>
{
explicit LexicalCast() = default;
bool
operator()(Out& out, std::string in) const
{
return LexicalCast<Out, std::string_view>()(out, in);
}
};
// Conversion from null terminated char const*
template <class Out>
struct LexicalCast<Out, char const*>
{
explicit LexicalCast() = default;
bool
operator()(Out& out, char const* in) const
{
XRPL_ASSERT(
in, "beast::detail::LexicalCast(char const*) : non-null input");
return LexicalCast<Out, std::string_view>()(out, in);
}
};
// Conversion from null terminated char*
// The string is not modified.
template <class Out>
struct LexicalCast<Out, char*>
{
explicit LexicalCast() = default;
bool
operator()(Out& out, char* in) const
{
XRPL_ASSERT(in, "beast::detail::LexicalCast(char*) : non-null input");
return LexicalCast<Out, std::string_view>()(out, in);
}
};
} // namespace detail
//------------------------------------------------------------------------------
/** Thrown when a conversion is not possible with LexicalCast.
Only used in the throw variants of lexicalCast.
*/
struct BadLexicalCast : public std::bad_cast
struct BadLexicalCast : std::bad_cast
{
explicit BadLexicalCast() = default;
private:
std::string msg;
public:
explicit BadLexicalCast(std::string m = {}) : msg(std::bad_cast::what())
{
if (!m.empty())
msg += ": " + m;
}
[[nodiscard]] char const*
what() const noexcept override
{
return msg.c_str();
}
};
/** Intelligently convert from one type to another.
//------------------------------------------------------------------------------
/** Convert from std::string_view to integral type.
@return `false` if there was a parsing or range error
*/
template <class Out, class In>
bool
lexicalCastChecked(Out& out, In in)
template <class Out>
requires std::is_integral_v<Out> && (!std::is_same_v<Out, bool>)
[[nodiscard]] bool
lexicalCastChecked(Out& out, std::string_view in) noexcept
{
return detail::LexicalCast<Out, In>()(out, in);
if (in.empty())
return false;
if (in.front() == '+')
{
in.remove_prefix(1);
if (in.empty() || in.front() == '-')
return false;
}
auto [ptr, ec] = std::from_chars(in.data(), in.data() + in.size(), out);
return ec == std::errc{} && ptr == in.data() + in.size();
}
/** Convert from std::string_view to bool.
@return `false` if there was a parsing error
*/
[[nodiscard]] inline bool
lexicalCastChecked(bool& out, std::string_view in) noexcept
{
auto iequals = [](std::string_view a, std::string_view b) {
return std::equal(
a.begin(), a.end(), b.begin(), b.end(), [](char ca, char cb) {
// We avoid std::tolower because it is locale-dependent. It
// would be really nice if C++ added support for std::ascii
// as outlined in P3688.
if (ca >= 'A' && ca <= 'Z')
ca = ca + ('a' - 'A');
if (cb >= 'A' && ca <= 'Z')
cb = cb + ('a' - 'A');
return ca == cb;
});
};
if (in == "1" || iequals(in, "true"))
{
out = true;
return true;
}
if (in == "0" || iequals(in, "false"))
{
out = false;
return true;
}
return false;
}
/** Convert from integral type to std::string.
@return `false` if there was a conversion error
*/
template <class In>
requires std::is_integral_v<In>
[[nodiscard]] bool
lexicalCastChecked(std::string& out, In in) noexcept
{
std::array<char, std::numeric_limits<In>::digits10 + 3> buf;
auto [ptr, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), in);
if (ec != std::errc{})
return false;
out.assign(buf.data(), ptr);
return true;
}
/** Convert from enum type to std::string.
@return `false` if there was a conversion error
*/
template <class In>
requires std::is_enum_v<In>
[[nodiscard]] bool
lexicalCastChecked(std::string& out, In in) noexcept
{
return lexicalCastChecked(out, static_cast<std::underlying_type_t<In>>(in));
}
/** Convert from Boost string_view types to integral types.
Boost has multiple string_view variants (such as boost::beast::string_view
and boost::core::string_view) that can be aliases to std::string_view, but
which don't have to be.
Since we handle std::string_view separately, this constrained overload for
the Boost custom implementations is needed because they are not implicitly
convertible to std::string_view.
@return `false` if there was a parsing or range error
*/
template <class Out, class In>
requires(
!std::is_same_v<In, std::string_view> &&
(std::is_same_v<In, boost::core::string_view> ||
std::is_same_v<In, boost::beast::string_view> ||
std::is_same_v<In, boost::string_view>))
[[nodiscard]] bool
lexicalCastChecked(Out& out, In in) noexcept
{
return lexicalCastChecked(out, std::string_view(in.data(), in.size()));
}
/** Convert from one type to another, throw on error
@@ -216,16 +190,21 @@ lexicalCastThrow(In in)
if (Out out; lexicalCastChecked(out, in))
return out;
throw BadLexicalCast();
throw BadLexicalCast(
#ifdef DEBUG
beast::type_name<In>() + " -> " + beast::type_name<Out>()
#endif
);
}
/** Convert from one type to another.
@param defaultValue The value returned if parsing fails
@param in The value to convert.
@param defaultValue The value returned if parsing fails.
@return The new type.
*/
template <class Out, class In>
Out
[[nodiscard]] Out
lexicalCast(In in, Out defaultValue = Out())
{
if (Out out; lexicalCastChecked(out, in))

View File

@@ -1,307 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_INTRUSIVE_LOCKFREESTACK_H_INCLUDED
#define BEAST_INTRUSIVE_LOCKFREESTACK_H_INCLUDED
#include <atomic>
#include <iterator>
#include <type_traits>
namespace beast {
//------------------------------------------------------------------------------
template <class Container, bool IsConst>
class LockFreeStackIterator
{
protected:
using Node = typename Container::Node;
using NodePtr =
typename std::conditional<IsConst, Node const*, Node*>::type;
public:
using iterator_category = std::forward_iterator_tag;
using value_type = typename Container::value_type;
using difference_type = typename Container::difference_type;
using pointer = typename std::conditional<
IsConst,
typename Container::const_pointer,
typename Container::pointer>::type;
using reference = typename std::conditional<
IsConst,
typename Container::const_reference,
typename Container::reference>::type;
LockFreeStackIterator() : m_node()
{
}
LockFreeStackIterator(NodePtr node) : m_node(node)
{
}
template <bool OtherIsConst>
explicit LockFreeStackIterator(
LockFreeStackIterator<Container, OtherIsConst> const& other)
: m_node(other.m_node)
{
}
LockFreeStackIterator&
operator=(NodePtr node)
{
m_node = node;
return static_cast<LockFreeStackIterator&>(*this);
}
LockFreeStackIterator&
operator++()
{
m_node = m_node->m_next.load();
return static_cast<LockFreeStackIterator&>(*this);
}
LockFreeStackIterator
operator++(int)
{
LockFreeStackIterator result(*this);
m_node = m_node->m_next;
return result;
}
NodePtr
node() const
{
return m_node;
}
reference
operator*() const
{
return *this->operator->();
}
pointer
operator->() const
{
return static_cast<pointer>(m_node);
}
private:
NodePtr m_node;
};
//------------------------------------------------------------------------------
template <class Container, bool LhsIsConst, bool RhsIsConst>
bool
operator==(
LockFreeStackIterator<Container, LhsIsConst> const& lhs,
LockFreeStackIterator<Container, RhsIsConst> const& rhs)
{
return lhs.node() == rhs.node();
}
template <class Container, bool LhsIsConst, bool RhsIsConst>
bool
operator!=(
LockFreeStackIterator<Container, LhsIsConst> const& lhs,
LockFreeStackIterator<Container, RhsIsConst> const& rhs)
{
return lhs.node() != rhs.node();
}
//------------------------------------------------------------------------------
/** Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
This stack is implemented using the same intrusive interface as List.
All mutations are lock-free.
The caller is responsible for preventing the "ABA" problem:
http://en.wikipedia.org/wiki/ABA_problem
@param Tag A type name used to distinguish lists and nodes, for
putting objects in multiple lists. If this parameter is
omitted, the default tag is used.
*/
template <class Element, class Tag = void>
class LockFreeStack
{
public:
class Node
{
public:
Node() : m_next(nullptr)
{
}
explicit Node(Node* next) : m_next(next)
{
}
Node(Node const&) = delete;
Node&
operator=(Node const&) = delete;
private:
friend class LockFreeStack;
template <class Container, bool IsConst>
friend class LockFreeStackIterator;
std::atomic<Node*> m_next;
};
public:
using value_type = Element;
using pointer = Element*;
using reference = Element&;
using const_pointer = Element const*;
using const_reference = Element const&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = LockFreeStackIterator<LockFreeStack<Element, Tag>, false>;
using const_iterator =
LockFreeStackIterator<LockFreeStack<Element, Tag>, true>;
LockFreeStack() : m_end(nullptr), m_head(&m_end)
{
}
LockFreeStack(LockFreeStack const&) = delete;
LockFreeStack&
operator=(LockFreeStack const&) = delete;
/** Returns true if the stack is empty. */
bool
empty() const
{
return m_head.load() == &m_end;
}
/** Push a node onto the stack.
The caller is responsible for preventing the ABA problem.
This operation is lock-free.
Thread safety:
Safe to call from any thread.
@param node The node to push.
@return `true` if the stack was previously empty. If multiple threads
are attempting to push, only one will receive `true`.
*/
// VFALCO NOTE Fix this, shouldn't it be a reference like intrusive list?
bool
push_front(Node* node)
{
bool first;
Node* old_head = m_head.load(std::memory_order_relaxed);
do
{
first = (old_head == &m_end);
node->m_next = old_head;
} while (!m_head.compare_exchange_strong(
old_head,
node,
std::memory_order_release,
std::memory_order_relaxed));
return first;
}
/** Pop an element off the stack.
The caller is responsible for preventing the ABA problem.
This operation is lock-free.
Thread safety:
Safe to call from any thread.
@return The element that was popped, or `nullptr` if the stack
was empty.
*/
Element*
pop_front()
{
Node* node = m_head.load();
Node* new_head;
do
{
if (node == &m_end)
return nullptr;
new_head = node->m_next.load();
} while (!m_head.compare_exchange_strong(
node,
new_head,
std::memory_order_release,
std::memory_order_relaxed));
return static_cast<Element*>(node);
}
/** Return a forward iterator to the beginning or end of the stack.
Undefined behavior results if push_front or pop_front is called
while an iteration is in progress.
Thread safety:
Caller is responsible for synchronization.
*/
/** @{ */
iterator
begin()
{
return iterator(m_head.load());
}
iterator
end()
{
return iterator(&m_end);
}
const_iterator
begin() const
{
return const_iterator(m_head.load());
}
const_iterator
end() const
{
return const_iterator(&m_end);
}
const_iterator
cbegin() const
{
return const_iterator(m_head.load());
}
const_iterator
cend() const
{
return const_iterator(&m_end);
}
/** @} */
private:
Node m_end;
std::atomic<Node*> m_head;
};
} // namespace beast
#endif

View File

@@ -27,57 +27,22 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/asio/ip/address.hpp>
#include <boost/functional/hash.hpp>
#include <cstdint>
#include <ios>
#include <sstream>
#include <string>
#include <typeinfo>
//------------------------------------------------------------------------------
namespace beast {
namespace IP {
using Address = boost::asio::ip::address;
/** Returns the address represented as a string. */
inline std::string
to_string(Address const& addr)
{
return addr.to_string();
}
/** Returns `true` if this is a loopback address. */
inline bool
is_loopback(Address const& addr)
{
return addr.is_loopback();
}
/** Returns `true` if the address is unspecified. */
inline bool
is_unspecified(Address const& addr)
{
return addr.is_unspecified();
}
/** Returns `true` if the address is a multicast address. */
inline bool
is_multicast(Address const& addr)
{
return addr.is_multicast();
}
/** Returns `true` if the address is a private unroutable address. */
inline bool
is_private(Address const& addr)
is_private(boost::asio::ip::address const& addr)
{
return (addr.is_v4()) ? is_private(addr.to_v4()) : is_private(addr.to_v6());
}
/** Returns `true` if the address is a public routable address. */
inline bool
is_public(Address const& addr)
is_public(boost::asio::ip::address const& addr)
{
return (addr.is_v4()) ? is_public(addr.to_v4()) : is_public(addr.to_v6());
}
@@ -88,7 +53,7 @@ is_public(Address const& addr)
template <class Hasher>
void
hash_append(Hasher& h, beast::IP::Address const& addr) noexcept
hash_append(Hasher& h, boost::asio::ip::address const& addr) noexcept
{
using beast::hash_append;
if (addr.is_v4())
@@ -102,12 +67,12 @@ hash_append(Hasher& h, beast::IP::Address const& addr) noexcept
namespace boost {
template <>
struct hash<::beast::IP::Address>
struct hash<::boost::asio::ip::address>
{
explicit hash() = default;
std::size_t
operator()(::beast::IP::Address const& addr) const
operator()(::boost::asio::ip::address const& addr) const
{
return ::beast::uhash<>{}(addr);
}

View File

@@ -22,65 +22,26 @@
#include <xrpl/beast/net/IPEndpoint.h>
#include <sstream>
#include <boost/asio.hpp>
namespace beast {
namespace IP {
/** Convert to Endpoint.
The port is set to zero.
*/
Endpoint
from_asio(boost::asio::ip::address const& address);
/** Convert to Endpoint. */
Endpoint
from_asio(boost::asio::ip::tcp::endpoint const& endpoint);
/** Convert to asio::ip::address.
The port is ignored.
*/
boost::asio::ip::address
to_asio_address(Endpoint const& endpoint);
[[nodiscard]] inline Endpoint
from_asio(boost::asio::ip::tcp::endpoint const& endpoint)
{
return Endpoint{endpoint.address(), endpoint.port()};
}
/** Convert to asio::ip::tcp::endpoint. */
boost::asio::ip::tcp::endpoint
to_asio_endpoint(Endpoint const& endpoint);
[[nodiscard]] inline boost::asio::ip::tcp::endpoint
to_asio_endpoint(Endpoint const& endpoint)
{
return boost::asio::ip::tcp::endpoint{endpoint.address(), endpoint.port()};
}
} // namespace IP
} // namespace beast
namespace beast {
// DEPRECATED
struct IPAddressConversion
{
explicit IPAddressConversion() = default;
static IP::Endpoint
from_asio(boost::asio::ip::address const& address)
{
return IP::from_asio(address);
}
static IP::Endpoint
from_asio(boost::asio::ip::tcp::endpoint const& endpoint)
{
return IP::from_asio(endpoint);
}
static boost::asio::ip::address
to_asio_address(IP::Endpoint const& address)
{
return IP::to_asio_address(address);
}
static boost::asio::ip::tcp::endpoint
to_asio_endpoint(IP::Endpoint const& address)
{
return IP::to_asio_endpoint(address);
}
};
} // namespace beast
#endif

View File

@@ -20,32 +20,30 @@
#ifndef BEAST_NET_IPADDRESSV4_H_INCLUDED
#define BEAST_NET_IPADDRESSV4_H_INCLUDED
#include <xrpl/beast/hash/hash_append.h>
#include <boost/asio/ip/address_v4.hpp>
#include <cstdint>
#include <functional>
#include <ios>
#include <string>
#include <utility>
namespace beast {
namespace IP {
using AddressV4 = boost::asio::ip::address_v4;
/** Returns `true` if the address is a private unroutable address. */
bool
is_private(AddressV4 const& addr);
[[nodiscard]] inline bool
is_private(boost::asio::ip::address_v4 const& addr)
{
return ((addr.to_ulong() & 0xff000000) ==
0x0a000000) || // Prefix /8, 10. #.#.#
((addr.to_ulong() & 0xfff00000) ==
0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.#
((addr.to_ulong() & 0xffff0000) ==
0xc0a80000) || // Prefix /16 192.168.#.#
addr.is_loopback();
}
/** Returns `true` if the address is a public routable address. */
bool
is_public(AddressV4 const& addr);
/** Returns the address class for the given address.
@note Class 'D' represents multicast addresses (224.*.*.*).
*/
char
get_class(AddressV4 const& address);
[[nodiscard]] inline bool
is_public(boost::asio::ip::address_v4 const& addr)
{
return !is_private(addr) && !addr.is_multicast();
}
} // namespace IP
} // namespace beast

View File

@@ -20,26 +20,29 @@
#ifndef BEAST_NET_IPADDRESSV6_H_INCLUDED
#define BEAST_NET_IPADDRESSV6_H_INCLUDED
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/asio/ip/address_v6.hpp>
#include <cstdint>
#include <functional>
#include <ios>
#include <string>
#include <utility>
namespace beast {
namespace IP {
using AddressV6 = boost::asio::ip::address_v6;
/** Returns `true` if the address is a private unroutable address. */
bool
is_private(AddressV6 const& addr);
[[nodiscard]] inline bool
is_private(boost::asio::ip::address_v6 const& addr)
{
auto b0 = addr.to_bytes()[0];
return (
addr.is_link_local() || // fe80::/10
addr.is_loopback() || // ::1
((b0 & 0xfe) == 0xfc) || // fc00::/7 (all ULA)
(addr.is_v4_mapped() && is_private(addr.to_v4())));
}
/** Returns `true` if the address is a public routable address. */
bool
is_public(AddressV6 const& addr);
[[nodiscard]] inline bool
is_public(boost::asio::ip::address_v6 const& addr)
{
return !is_private(addr) && !addr.is_multicast() && !addr.is_unspecified();
}
} // namespace IP
} // namespace beast

View File

@@ -24,10 +24,10 @@
#include <xrpl/beast/hash/uhash.h>
#include <xrpl/beast/net/IPAddress.h>
#include <cstdint>
#include <ios>
#include <compare>
#include <optional>
#include <string>
#include <string_view>
namespace beast {
namespace IP {
@@ -39,19 +39,40 @@ class Endpoint
{
public:
/** Create an unspecified endpoint. */
Endpoint();
Endpoint() noexcept = default;
/** Create an endpoint from the address and optional port. */
explicit Endpoint(Address const& addr, Port port = 0);
explicit Endpoint(
boost::asio::ip::address const& addr,
Port port = 0) noexcept
: m_addr(addr), m_port(port)
{
}
/** Create an Endpoint from a string.
If the port is omitted, the endpoint will have a zero port.
@return An optional endpoint; will be `std::nullopt` on failure
*/
Supported formats:
- IPv4: `1.2.3.4`
- IPv4 with port: `1.2.3.4:80` or `1.2.3.4 80`
- IPv6: `::1` or `2001:db8::1`
- IPv6 with port: `::1 80` or `2001:db8::1 80`
- Bracketed IPv6: `[::1]`
- Bracketed IPv6 port: `[::1]:80`
Leading and trailing whitespace is ignored. If the port is
omitted, the endpoint will have a zero port.
@param s The string to parse
@return The parsed endpoint, or `std::nullopt` on failure
*/
static std::optional<Endpoint>
from_string_checked(std::string const& s);
from_string_checked(std::string_view s);
static Endpoint
from_string(std::string const& s);
from_string(std::string_view s)
{
return from_string_checked(s).value_or(Endpoint{});
}
/** Returns a string representing the endpoint. */
std::string
@@ -59,7 +80,7 @@ public:
/** Returns the port number on the endpoint. */
Port
port() const
port() const noexcept
{
return m_port;
}
@@ -72,8 +93,8 @@ public:
}
/** Returns the address portion of this endpoint. */
Address const&
address() const
boost::asio::ip::address const&
address() const noexcept
{
return m_addr;
}
@@ -90,47 +111,18 @@ public:
{
return m_addr.is_v6();
}
AddressV4 const
boost::asio::ip::address_v4 const
to_v4() const
{
return m_addr.to_v4();
}
AddressV6 const
boost::asio::ip::address_v6 const
to_v6() const
{
return m_addr.to_v6();
}
/** @} */
/** Arithmetic comparison. */
/** @{ */
friend bool
operator==(Endpoint const& lhs, Endpoint const& rhs);
friend bool
operator<(Endpoint const& lhs, Endpoint const& rhs);
friend bool
operator!=(Endpoint const& lhs, Endpoint const& rhs)
{
return !(lhs == rhs);
}
friend bool
operator>(Endpoint const& lhs, Endpoint const& rhs)
{
return rhs < lhs;
}
friend bool
operator<=(Endpoint const& lhs, Endpoint const& rhs)
{
return !(lhs > rhs);
}
friend bool
operator>=(Endpoint const& lhs, Endpoint const& rhs)
{
return !(rhs > lhs);
}
/** @} */
template <class Hasher>
friend void
hash_append(Hasher& h, Endpoint const& endpoint)
@@ -140,10 +132,29 @@ public:
}
private:
Address m_addr;
Port m_port;
boost::asio::ip::address m_addr;
Port m_port = 0;
};
/** Comparison operators. */
[[nodiscard]] inline bool
operator==(Endpoint const& lhs, Endpoint const& rhs) noexcept
{
return lhs.address() == rhs.address() && lhs.port() == rhs.port();
}
[[nodiscard]] inline std::strong_ordering
operator<=>(Endpoint const& lhs, Endpoint const& rhs) noexcept
{
if (lhs.address() < rhs.address())
return std::strong_ordering::less;
if (rhs.address() < lhs.address())
return std::strong_ordering::greater;
return lhs.port() <=> rhs.port();
}
//------------------------------------------------------------------------------
// Properties
@@ -152,21 +163,21 @@ private:
inline bool
is_loopback(Endpoint const& endpoint)
{
return is_loopback(endpoint.address());
return endpoint.address().is_loopback();
}
/** Returns `true` if the endpoint is unspecified. */
inline bool
is_unspecified(Endpoint const& endpoint)
{
return is_unspecified(endpoint.address());
return endpoint.address().is_unspecified();
}
/** Returns `true` if the endpoint is a multicast address. */
inline bool
is_multicast(Endpoint const& endpoint)
{
return is_multicast(endpoint.address());
return endpoint.address().is_multicast();
}
/** Returns `true` if the endpoint is a private unroutable address. */
@@ -201,10 +212,6 @@ operator<<(OutputStream& os, Endpoint const& endpoint)
return os;
}
/** Input stream conversion. */
std::istream&
operator>>(std::istream& is, Endpoint& endpoint);
} // namespace IP
} // namespace beast

View File

@@ -39,19 +39,14 @@ namespace beast {
returns a positive, zero or negative number.
*/
struct Zero
inline constexpr struct Zero
{
explicit Zero() = default;
};
namespace {
static constexpr Zero zero{};
}
} zero;
/** Default implementation of signum calls the method on the class. */
template <typename T>
auto
signum(T const& t)
constexpr auto
signum(T const& t) noexcept
{
return t.signum();
}
@@ -60,10 +55,10 @@ namespace detail {
namespace zero_helper {
// For argument dependent lookup to function properly, calls to signum must
// be made from a namespace that does not include overloads of the function..
// be made from a namespace that does not include overloads of the function.
template <class T>
auto
call_signum(T const& t)
constexpr auto
call_signum(T const& t) noexcept
{
return signum(t);
}
@@ -71,95 +66,21 @@ call_signum(T const& t)
} // namespace zero_helper
} // namespace detail
// Handle operators where T is on the left side using signum.
template <typename T>
constexpr auto
operator<=>(T const& t, Zero) noexcept
{
auto s = detail::zero_helper::call_signum(t);
return s <=> 0;
}
template <typename T>
bool
operator==(T const& t, Zero)
constexpr bool
operator==(T const& t, Zero) noexcept
{
return detail::zero_helper::call_signum(t) == 0;
}
template <typename T>
bool
operator!=(T const& t, Zero)
{
return detail::zero_helper::call_signum(t) != 0;
}
template <typename T>
bool
operator<(T const& t, Zero)
{
return detail::zero_helper::call_signum(t) < 0;
}
template <typename T>
bool
operator>(T const& t, Zero)
{
return detail::zero_helper::call_signum(t) > 0;
}
template <typename T>
bool
operator>=(T const& t, Zero)
{
return detail::zero_helper::call_signum(t) >= 0;
}
template <typename T>
bool
operator<=(T const& t, Zero)
{
return detail::zero_helper::call_signum(t) <= 0;
}
// Handle operators where T is on the right side by
// reversing the operation, so that T is on the left side.
template <typename T>
bool
operator==(Zero, T const& t)
{
return t == zero;
}
template <typename T>
bool
operator!=(Zero, T const& t)
{
return t != zero;
}
template <typename T>
bool
operator<(Zero, T const& t)
{
return t > zero;
}
template <typename T>
bool
operator>(Zero, T const& t)
{
return t < zero;
}
template <typename T>
bool
operator>=(Zero, T const& t)
{
return t <= zero;
}
template <typename T>
bool
operator<=(Zero, T const& t)
{
return t >= zero;
}
} // namespace beast
#endif

View File

@@ -20,62 +20,88 @@
#ifndef BEAST_RANDOM_RNGFILL_H_INCLUDED
#define BEAST_RANDOM_RNGFILL_H_INCLUDED
#include <xrpl/beast/utility/instrumentation.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <span>
#include <type_traits>
namespace beast {
template <class Generator>
void
rngfill(void* buffer, std::size_t bytes, Generator& g)
rngfill(std::span<std::byte> buf, Generator& g)
{
using result_type = typename Generator::result_type;
if constexpr (std::is_invocable_r_v<
void,
Generator,
decltype(buf.data()),
decltype(buf.size())>)
return g(buf.data(), buf.size());
while (bytes >= sizeof(result_type))
using result_type = decltype(g());
auto constexpr bs = sizeof(result_type);
auto fill_impl = [](std::span<std::byte> s, result_type v) {
std::copy_n(reinterpret_cast<std::byte const*>(&v), s.size(), s.data());
};
if (auto misalign =
reinterpret_cast<std::uintptr_t>(buf.data()) % alignof(result_type))
{
auto const v = g();
std::memcpy(buffer, &v, sizeof(v));
buffer = reinterpret_cast<std::uint8_t*>(buffer) + sizeof(v);
bytes -= sizeof(v);
auto const prefix =
std::min(buf.size(), alignof(result_type) - misalign);
fill_impl(buf.first(prefix), g());
buf = buf.subspan(prefix);
}
XRPL_ASSERT(
bytes < sizeof(result_type), "beast::rngfill(void*) : maximum bytes");
auto const count = buf.size() / bs;
std::generate_n(
reinterpret_cast<result_type*>(buf.data()), count, std::ref(g));
buf = buf.subspan(count * bs);
#ifdef __GNUC__
// gcc 11.1 (falsely) warns about an array-bounds overflow in release mode.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
if (bytes > 0)
{
auto const v = g();
std::memcpy(buffer, &v, bytes);
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
if (!buf.empty())
fill_impl(buf, g());
}
template <
class Generator,
std::size_t N,
class = std::enable_if_t<N % sizeof(typename Generator::result_type) == 0>>
template <class T, std::size_t Extent, class Generator>
requires std::is_integral_v<T>
void
rngfill(std::array<std::uint8_t, N>& a, Generator& g)
rngfill(std::span<T, Extent> buf, Generator& g)
{
using result_type = typename Generator::result_type;
auto i = N / sizeof(result_type);
result_type* p = reinterpret_cast<result_type*>(a.data());
while (i--)
*p++ = g();
rngfill(std::as_writable_bytes(buf), g);
}
template <class T, std::size_t N, class Generator>
requires std::is_integral_v<T> && (N != 0)
void
rngfill(std::array<T, N>& a, Generator& g)
{
rngfill(std::as_writable_bytes(std::span{a}), g);
}
template <class T, std::size_t N, class Generator>
requires std::is_integral_v<T> && (N != 0)
void
rngfill(T (&a)[N], Generator& g)
{
rngfill(std::as_writable_bytes(std::span{a}), g);
}
template <class T, class Generator>
requires(
std::is_same_v<T, std::byte> || std::is_same_v<T, char> ||
std::is_same_v<T, signed char> || std::is_same_v<T, unsigned char> ||
std::is_same_v<T, std::uint8_t>)
void
rngfill(T* ptr, std::size_t count, Generator& g)
{
rngfill(std::as_writable_bytes(std::span{ptr, count}), g);
}
} // namespace beast
#endif

View File

@@ -20,47 +20,40 @@
#ifndef RIPPLE_CRYPTO_RFC1751_H_INCLUDED
#define RIPPLE_CRYPTO_RFC1751_H_INCLUDED
#include <array>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <vector>
namespace ripple {
namespace rfc1751 {
class RFC1751
{
public:
static int
getKeyFromEnglish(std::string& strKey, std::string const& strHuman);
/** Convert a 128-bit value (big-endian) to a human-readable word string.
static void
getEnglishFromKey(std::string& strHuman, std::string const& strKey);
@param key The 128-bit value. The span must be precisely 16 bytes long.
@return The human readable word string if successful; nullopt on failure
*/
[[nodiscard]] std::optional<std::string>
englishFromKey(std::span<std::uint8_t const> key);
/** Chooses a single dictionary word from the data.
/** Convert words separated by spaces into a 128-bit value (big-endian).
This is not particularly secure but it can be useful to provide
a unique name for something given a GUID or fixed data. We use
it to turn the pubkey_node into an easily remembered and identified
4 character string.
*/
static std::string
getWordFromBlob(void const* blob, size_t bytes);
@param key The human readable word string.
@return The 128-bit value if successful; nullopt on failure
*/
[[nodiscard]] std::optional<std::array<std::uint8_t, 16>>
keyFromEnglish(std::string_view human);
private:
static unsigned long
extract(char const* s, int start, int length);
static void
btoe(std::string& strHuman, std::string const& strData);
static void
insert(char* s, int x, int start, int length);
static void
standard(std::string& strWord);
static int
wsrch(std::string const& strWord, int iMin, int iMax);
static int
etob(std::string& strData, std::vector<std::string> vsHuman);
/** Pick a single dictionary word from arbitrary data.
static char const* s_dictionary[];
};
Not cryptographically secure. Useful for generating a short
human-readable label from a GUID or public key.
*/
[[nodiscard]] std::string_view
wordFromBlob(std::span<std::uint8_t const> blob);
} // namespace rfc1751
} // namespace ripple
#endif

View File

@@ -20,23 +20,25 @@
#ifndef RIPPLE_CRYPTO_RANDOM_H_INCLUDED
#define RIPPLE_CRYPTO_RANDOM_H_INCLUDED
#include <cstddef>
#include <limits>
#include <mutex>
#include <string>
#include <type_traits>
namespace ripple {
/** A cryptographically secure random number engine
The engine is thread-safe (it uses a lock to serialize
access) and will, automatically, mix in some randomness
from std::random_device.
The engine is thread-safe (it will uses a lock to serialize
access, if needed) and will not produce random data when it
does not have sufficient entropy.
Meets the requirements of UniformRandomNumberEngine
It allows callers to supply additional data that gets mixed
into the pool. Doing so is not required.
Meets the requirements of UniformRandomBitGenerator
*/
class csprng_engine
{
private:
std::mutex mutex_;
public:
@@ -58,7 +60,7 @@ public:
mix_entropy(void* buffer = nullptr, std::size_t count = 0);
/** Generate a random integer */
result_type
[[nodiscard]] result_type
operator()();
/** Fill a buffer with the requested amount of random data */
@@ -66,14 +68,14 @@ public:
operator()(void* ptr, std::size_t count);
/* The smallest possible value that can be returned */
static constexpr result_type
[[nodiscard]] static constexpr result_type
min()
{
return std::numeric_limits<result_type>::min();
}
/* The largest possible value that can be returned */
static constexpr result_type
[[nodiscard]] static constexpr result_type
max()
{
return std::numeric_limits<result_type>::max();
@@ -86,7 +88,7 @@ public:
data that will be used for encryption or passed into
cryptographic routines.
This meets the requirements of UniformRandomNumberEngine
This meets the requirements of UniformRandomBitGenerator
*/
csprng_engine&
crypto_prng();

View File

@@ -16,6 +16,7 @@
#define featureHooksUpdate1 "1"
#define featureHooksUpdate2 "1"
#define fix20250131 "1"
#define fixGuardDepth32 "1"
namespace hook_api {
struct Rules
{
@@ -319,7 +320,7 @@ namespace compare_mode {
enum compare_mode : uint32_t { EQUAL = 1, LESS = 2, GREATER = 4 };
}
enum hook_return_code : int64_t {
enum class hook_return_code : int64_t {
SUCCESS =
0, // return codes > 0 are reserved for hook apis to return "success"
OUT_OF_BOUNDS =
@@ -381,12 +382,12 @@ enum hook_return_code : int64_t {
INVALID_KEY = -41, // user supplied key was not valid
NOT_A_STRING = -42, // nul terminator missing from a string argument
MEM_OVERLAP = -43, // one or more specified buffers are the same memory
TOO_MANY_STATE_MODIFICATIONS = -44, // more than 5000 modified state
TOO_MANY_STATE_MODIFICATIONS = -44, // more than 256 modified state
// entires in the combined hook chains
TOO_MANY_NAMESPACES = -45
};
enum ExitType : uint8_t {
enum class ExitType : uint8_t {
UNSET = 0,
WASM_ERROR = 1,
ROLLBACK = 2,
@@ -443,6 +444,7 @@ getImportWhitelist(Rules const& rules)
enum GuardRulesVersion : uint64_t {
GuardRuleFix20250131 = 0x00000001,
GuardRuleDepth32 = 0x00000002,
};
inline uint64_t
@@ -451,6 +453,8 @@ 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,9 +204,13 @@ struct WasmBlkInf
}
// compute worst case execution time
inline uint64_t
compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached)
compute_wce(
const WasmBlkInf* blk,
int level,
int max_level,
bool* recursion_limit_reached)
{
if (level > 16)
if (level > max_level)
{
*recursion_limit_reached = true;
return 0;
@@ -233,8 +237,8 @@ compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached)
if (blk->children.size() > 0)
for (auto const& child : blk->children)
worst_case_execution +=
compute_wce(child, level + 1, recursion_limit_reached);
worst_case_execution += compute_wce(
child, level + 1, max_level, recursion_limit_reached);
if (parent == 0 ||
parent->iteration_bound ==
@@ -788,12 +792,17 @@ check_guard(
}
bool recursion_limit_reached = false;
uint64_t wce = compute_wce(&(*root), 0, &recursion_limit_reached);
int max_level = 16;
if (rulesVersion & hook_api::GuardRuleDepth32)
max_level = 32;
uint64_t wce =
compute_wce(&(*root), 0, max_level, &recursion_limit_reached);
if (recursion_limit_reached)
{
GUARDLOG(hook::log::NESTING_LIMIT)
<< "GuardCheck "
<< "Maximum allowable depth of blocks reached (16 levels). Flatten "
<< "Maximum allowable depth of blocks reached (" << max_level
<< " levels). Flatten "
"your loops and conditions!.\n";
return {};
}

View File

@@ -89,58 +89,69 @@
#define WASM_VAL_TYPE(T, b) CAT2(TYP_, T)
#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; \
#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; \
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); \
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__( \
#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__( \
COMMA __VA_ARGS__))
#define HOOK_SETUP() \
using enum hook_api::hook_return_code; \
try \
{ \
[[maybe_unused]] ApplyContext& applyCtx = hookCtx.applyCtx; \
@@ -203,7 +214,7 @@
host_memory_ptr, \
guest_memory_length) \
{ \
int64_t bytes_written = 0; \
uint64_t bytes_written = 0; \
WRITE_WASM_MEMORY( \
bytes_written, \
guest_dst_ptr, \
@@ -272,7 +283,7 @@
data_ptr < (data_ptr_in)) \
return INTERNAL_ERROR; \
if (data_len == 0) \
return 0; \
return 0ULL; \
if ((write_ptr_in) == 0) \
return data_as_int64(data_ptr, data_len); \
if (data_len > (write_len_in)) \

View File

@@ -1,523 +0,0 @@
#ifndef HOOKMISC_INCLUDED
#define HOOKMISC_INCLUDED 1
#include <xrpl/basics/base_uint.h>
namespace ripple {
// RH TODO: there's definitely a mucher nicer way to do this, but it involves
// modifying the base_uint class and we don't want to do that yet.
static const std::array<ripple::uint256, 256> UINT256_BIT = {
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000001"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000002"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000004"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000008"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000010"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000020"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000040"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000080"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000100"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000200"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000400"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000000800"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000001000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000002000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000004000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000008000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000010000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000020000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000040000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000080000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000100000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000200000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000400000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000000800000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000001000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000002000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000004000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000008000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000010000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000020000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000040000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000080000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000100000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000200000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000400000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000000800000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000001000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000002000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000004000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000008000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000010000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000020000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000040000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000080000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000100000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000200000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000400000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000000800000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000001000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000002000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000004000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000008000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000010000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000020000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000040000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000080000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000100000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000200000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000400000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000000800000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000001000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000002000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000004000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000008000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000010000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000020000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000040000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000080000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000100000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000200000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000400000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000000800000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000001000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000002000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000004000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000008000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000010000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000020000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000040000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000080000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000100000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000200000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000400000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000000800000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000001000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000002000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000004000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000008000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000010000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000020000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000040000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000080000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000100000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000200000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000400000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000000800000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000001000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000002000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000004000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000008000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000010000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000020000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000040000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000080000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000100000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000200000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000400000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000000800000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000001000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000002000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000004000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000008000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000010000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000020000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000040000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000080000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000100000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000200000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000400000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000000800000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000001000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000002000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000004000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000008000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000010000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000020000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000040000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000080000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000100000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000200000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000400000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000000800000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000001000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000002000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000004000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000008000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000010000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000020000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000040000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000080000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000100000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000200000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000400000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000000800000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000001000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000002000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000004000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000008000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000010000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000020000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000040000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000080000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000100000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000200000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000400000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000000800000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000001000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000002000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000004000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000008000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000010000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000020000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000040000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000080000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000100000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000200000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000400000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000000800000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000001000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000002000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000004000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000008000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000010000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000020000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000040000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000080000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000100000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000200000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000400000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000000800000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000001000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000002000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000004000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000008000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000010000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000020000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000040000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000080000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000100000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000200000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000400000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000000800000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000001000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000002000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000004000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000008000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000010000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000020000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000040000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000080000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000100000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000200000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000400000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000000800000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000001000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000002000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000004000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000008000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000010000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000020000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000040000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000080000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000100000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000200000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000400000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000000800000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000001000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000002000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000004000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000008000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000010000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000020000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000040000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000080000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000100000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000200000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000400000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000000800000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000001000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000002000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000004000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000008000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000010000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000020000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000040000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000080000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000100000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000200000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000400000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0000800000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0001000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0002000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0004000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0008000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0010000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0020000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0040000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0080000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0100000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0200000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0400000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"0800000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"1000000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"2000000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"4000000000000000000000000000000000000000000000000000000000000000"),
ripple::uint256(
"8000000000000000000000000000000000000000000000000000000000000000")};
} // namespace ripple
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,8 @@
#ifndef RIPPLE_JSON_JSON_ASSERT_H_INCLUDED
#define RIPPLE_JSON_JSON_ASSERT_H_INCLUDED
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_errors.h>
#define JSON_ASSERT_MESSAGE(condition, message) \

View File

@@ -24,6 +24,7 @@
#include <cstring>
#include <map>
#include <string>
#include <string_view>
#include <vector>
/** \brief JSON (JavaScript Object Notation).
@@ -76,6 +77,12 @@ public:
return str_;
}
constexpr
operator std::string_view() const
{
return std::string_view(str_);
}
private:
const char* str_;
};
@@ -214,7 +221,13 @@ public:
Value(Int value);
Value(UInt value);
Value(double value);
Value(const char* value);
Value(std::string_view value);
Value(std::string const& value) : Value(std::string_view(value))
{
}
Value(char const* value) : Value(std::string_view(value))
{
}
/** \brief Constructs a value from a static string.
* Like other value string constructor but do not duplicate the string for
@@ -227,7 +240,6 @@ public:
* \endcode
*/
Value(const StaticString& value);
Value(std::string const& value);
Value(bool value);
Value(const Value& other);
~Value();
@@ -336,19 +348,13 @@ public:
/// Access an object value by name, create a null member if it does not
/// exist.
Value&
operator[](const char* key);
operator[](std::string_view key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value&
operator[](const char* key) const;
/// Access an object value by name, create a null member if it does not
/// exist.
Value&
operator[](std::string const& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value&
operator[](std::string const& key) const;
Value const&
operator[](std::string_view key) const;
/** \brief Access an object value by name, create a null member if it does
not exist.

View File

@@ -61,12 +61,10 @@ 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

@@ -33,35 +33,39 @@
*
* Steps required to add new features to the code:
*
* 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.
* 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.
*
*
* When a feature has been enabled for several years, the conditional code
* may be removed, and the feature "retired". To retire a feature:
* 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`.
*
* 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.
*
* The feature must remain registered and supported indefinitely because it
* 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.
* 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.
*
*/
@@ -76,11 +80,32 @@ 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 = 113;
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")
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -320,12 +345,17 @@ 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

View File

@@ -372,6 +372,10 @@ permissionedDomain(AccountID const& account, std::uint32_t seq) noexcept;
Keylet
permissionedDomain(uint256 const& domainID) noexcept;
Keylet
manifest(PublicKey const& pk) noexcept;
} // namespace keylet
// Everything below is deprecated and should be removed in favor of keylets:

View File

@@ -24,6 +24,7 @@
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/STExchange.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/detail/KeyBase.h>
#include <xrpl/protocol/json_get_or_throw.h>
#include <xrpl/protocol/tokens.h>
@@ -36,6 +37,26 @@
namespace ripple {
/** Returns the type of public key.
@return std::nullopt If the public key does not
represent a known type.
*/
[[nodiscard]] inline std::optional<KeyType>
publicKeyType(Slice const& slice)
{
if (slice.size() == 33)
{
if (slice[0] == 0xED)
return KeyType::ed25519;
if (slice[0] == 0x02 || slice[0] == 0x03)
return KeyType::secp256k1;
}
return std::nullopt;
}
/** A public key.
Public keys are used in the public-key cryptography
@@ -58,77 +79,47 @@ namespace ripple {
prefix constant 0xED, followed by 32 bytes of
public key data.
*/
class PublicKey
class PublicKey : public detail::KeyBase<PublicKey, 33>
{
protected:
// All the constructed public keys are valid, non-empty and contain 33
// bytes of data.
static constexpr std::size_t size_ = 33;
std::uint8_t buf_[size_]; // should be large enough
public:
using const_iterator = std::uint8_t const*;
public:
PublicKey() = delete;
PublicKey(PublicKey const& other);
PublicKey(PublicKey const&) = default;
PublicKey&
operator=(PublicKey const& other);
operator=(PublicKey const&) = default;
/** Create a public key.
Preconditions:
publicKeyType(slice) != std::nullopt
*/
explicit PublicKey(Slice const& slice);
std::uint8_t const*
data() const noexcept
explicit PublicKey(Slice const& slice) noexcept
{
return buf_;
if (slice.size() < buf_.size())
LogicError("PublicKey::PublicKey: undersized buffer");
if (!publicKeyType(slice))
LogicError("PublicKey::PublicKey: invalid type");
std::copy_n(slice.data(), buf_.size(), buf_.data());
}
std::size_t
size() const noexcept
explicit PublicKey(value_t const& data) noexcept
: PublicKey(makeSlice(data))
{
return size_;
}
const_iterator
begin() const noexcept
{
return buf_;
}
const_iterator
cbegin() const noexcept
{
return buf_;
}
const_iterator
end() const noexcept
{
return buf_ + size_;
}
const_iterator
cend() const noexcept
{
return buf_ + size_;
}
Slice
[[nodiscard]] Slice
slice() const noexcept
{
return {buf_, size_};
return {buf_.data(), buf_.size()};
}
operator Slice() const noexcept
{
return slice();
}
[[nodiscard]] auto
operator<=>(PublicKey const& rhs) const
{
return buf_ <=> rhs.buf_;
}
};
/** Print the public key to a stream.
@@ -136,22 +127,6 @@ public:
std::ostream&
operator<<(std::ostream& os, PublicKey const& pk);
inline bool
operator==(PublicKey const& lhs, PublicKey const& rhs)
{
return std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
}
inline bool
operator<(PublicKey const& lhs, PublicKey const& rhs)
{
return std::lexicographical_compare(
lhs.data(),
lhs.data() + lhs.size(),
rhs.data(),
rhs.data() + rhs.size());
}
template <class Hasher>
void
hash_append(Hasher& h, PublicKey const& pk)
@@ -221,22 +196,6 @@ enum class ECDSACanonicality { canonical, fullyCanonical };
std::optional<ECDSACanonicality>
ecdsaCanonicality(Slice const& sig);
/** Returns the type of public key.
@return std::nullopt If the public key does not
represent a known type.
*/
/** @{ */
[[nodiscard]] std::optional<KeyType>
publicKeyType(Slice const& slice);
[[nodiscard]] inline std::optional<KeyType>
publicKeyType(PublicKey const& publicKey)
{
return publicKeyType(publicKey.slice());
}
/** @} */
/** Verify a secp256k1 signature on the digest of a message. */
[[nodiscard]] bool
verifyDigest(

View File

@@ -1,31 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PROTOCOL_RIPPLELEDGERHASH_H_INCLUDED
#define RIPPLE_PROTOCOL_RIPPLELEDGERHASH_H_INCLUDED
#include <xrpl/basics/base_uint.h>
namespace ripple {
using LedgerHash = uint256;
}
#endif

View File

@@ -25,90 +25,39 @@
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/Seed.h>
#include <xrpl/protocol/detail/KeyBase.h>
#include <xrpl/protocol/tokens.h>
#include <array>
#include <cstring>
#include <string>
namespace ripple {
/** A secret key. */
class SecretKey
class SecretKey : public detail::KeyBase<SecretKey, 32>
{
private:
std::uint8_t buf_[32];
public:
using const_iterator = std::uint8_t const*;
SecretKey() = delete;
SecretKey(SecretKey const&) = default;
SecretKey&
operator=(SecretKey const&) = default;
/** Destroy the secret key. This will try to securely erase the buffer. */
~SecretKey();
SecretKey(std::array<std::uint8_t, 32> const& data);
SecretKey(Slice const& slice);
std::uint8_t const*
data() const
SecretKey(Slice const& slice)
{
return buf_;
if (slice.size() != buf_.size())
LogicError("SecretKey::SecretKey: invalid size");
std::copy_n(slice.data(), buf_.size(), buf_.data());
}
std::size_t
size() const
explicit SecretKey(span_t const& data) noexcept : KeyBase(data)
{
return sizeof(buf_);
}
/** Convert the secret key to a hexadecimal string.
@note The operator<< function is deliberately omitted
to avoid accidental exposure of secret key material.
*/
std::string
to_string() const;
const_iterator
begin() const noexcept
{
return buf_;
}
const_iterator
cbegin() const noexcept
{
return buf_;
}
const_iterator
end() const noexcept
{
return buf_ + sizeof(buf_);
}
const_iterator
cend() const noexcept
{
return buf_ + sizeof(buf_);
}
};
inline bool
operator==(SecretKey const& lhs, SecretKey const& rhs)
{
return lhs.size() == rhs.size() &&
std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
}
inline bool
operator!=(SecretKey const& lhs, SecretKey const& rhs)
{
return !(lhs == rhs);
}
//------------------------------------------------------------------------------
/** Parse a secret key */

View File

@@ -22,72 +22,37 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/detail/KeyBase.h>
#include <xrpl/protocol/tokens.h>
#include <algorithm>
#include <array>
#include <optional>
namespace ripple {
/** Seeds are used to generate deterministic secret keys. */
class Seed
class Seed : public detail::KeyBase<Seed, 16>
{
private:
std::array<uint8_t, 16> buf_;
public:
using const_iterator = std::array<uint8_t, 16>::const_iterator;
Seed() = delete;
Seed(Seed const&) = default;
Seed&
operator=(Seed const&) = default;
/** Destroy the seed.
The buffer will first be securely erased.
*/
/** Destroy the seed. This will attempt to securely erase the buffer. */
~Seed();
/** Construct a seed */
/** @{ */
explicit Seed(Slice const& slice);
explicit Seed(uint128 const& seed);
/** @} */
std::uint8_t const*
data() const
explicit Seed(Slice const& slice)
{
return buf_.data();
if (slice.size() != buf_.size())
LogicError("Seed::Seed: invalid size");
std::copy_n(slice.data(), buf_.size(), buf_.data());
}
std::size_t
size() const
explicit Seed(span_t seed) noexcept : KeyBase(seed)
{
return buf_.size();
}
const_iterator
begin() const noexcept
{
return buf_.begin();
}
const_iterator
cbegin() const noexcept
{
return buf_.cbegin();
}
const_iterator
end() const noexcept
{
return buf_.end();
}
const_iterator
cend() const noexcept
{
return buf_.cend();
}
};

View File

@@ -458,18 +458,18 @@ template <std::size_t Bits, class Tag>
base_uint<Bits, Tag>
SerialIter::getBitString()
{
auto const n = Bits / 8;
auto constexpr N = base_uint<Bits, Tag>::bytes;
if (remain_ < n)
if (remain_ < N)
Throw<std::runtime_error>("invalid SerialIter getBitString");
auto const x = p_;
p_ += n;
used_ += n;
remain_ -= n;
p_ += N;
used_ += N;
remain_ -= N;
return base_uint<Bits, Tag>::fromVoid(x);
return base_uint<Bits, Tag>(std::span<uint8_t const, N>{x, N});
}
} // namespace ripple

View File

@@ -22,29 +22,26 @@
#include <xrpl/basics/chrono.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <string>
#include <string_view>
namespace ripple {
// Various protocol and system specific constant globals.
/* The name of the system. */
static inline std::string const&
systemName()
{
static std::string const name = "xahau";
return name;
}
inline constexpr std::string_view systemName = "xahau";
/** Configure the native currency. */
/* The currency code for the native currency. */
inline constexpr std::string_view systemCurrencyCode = "XAH";
/** Number of drops in the genesis account. */
constexpr XRPAmount INITIAL_XRP{100'000'000'000 * DROPS_PER_XRP};
inline constexpr XRPAmount INITIAL_XRP{100'000'000'000 * DROPS_PER_XRP};
/** Returns true if the amount does not exceed the initial XRP in existence. */
inline bool
isLegalAmount(XRPAmount const& amount)
isLegalAmount(XRPAmount const& amount) noexcept
{
return amount <= INITIAL_XRP;
}
@@ -52,30 +49,22 @@ isLegalAmount(XRPAmount const& amount)
/** Returns true if the absolute value of the amount does not exceed the initial
* XRP in existence. */
inline bool
isLegalAmountSigned(XRPAmount const& amount)
isLegalAmountSigned(XRPAmount const& amount) noexcept
{
return amount >= -INITIAL_XRP && amount <= INITIAL_XRP;
}
/* The currency code for the native currency. */
static inline std::string const&
systemCurrencyCode()
{
static std::string const code = "XAH";
return code;
}
/** The XRP ledger network's earliest allowed sequence */
static constexpr std::uint32_t XRP_LEDGER_EARLIEST_SEQ{1U};
inline constexpr std::uint32_t XRP_LEDGER_EARLIEST_SEQ{1U};
/** The minimum amount of support an amendment should have.
@note This value is used by legacy code and will become obsolete
once the fixAmendmentMajorityCalc amendment activates.
*/
constexpr std::ratio<204, 256> preFixAmendmentMajorityCalcThreshold;
inline constexpr std::ratio<204, 256> preFixAmendmentMajorityCalcThreshold;
constexpr std::ratio<80, 100> postFixAmendmentMajorityCalcThreshold;
inline constexpr std::ratio<80, 100> postFixAmendmentMajorityCalcThreshold;
/** The minimum amount of time an amendment must hold a majority */
constexpr std::chrono::seconds const defaultAmendmentMajorityTime =

View File

@@ -137,6 +137,7 @@ enum TEMcodes : TERUnderlyingType {
temXCHAIN_BRIDGE_NONDOOR_OWNER,
temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT,
temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT,
temXCHAIN_TOO_MANY_ATTESTATIONS, // RESERVED - not used
temHOOK_DATA_TOO_LARGE,
temEMPTY_DID,
@@ -193,6 +194,8 @@ enum TEFcodes : TERUnderlyingType {
tefNONDIR_EMIT,
tefIMPORT_BLACKHOLED,
tefINVALID_LEDGER_FIX_TYPE,
tefPAST_MANIFEST_SEQ,
tefREVOKED_MANIFEST,
};
//------------------------------------------------------------------------------

View File

@@ -132,7 +132,7 @@ constexpr std::uint32_t tfTrustSetMask =
tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze);
// EnableAmendment flags:
enum EnableAmendmentFlags : std::uint32_t {
enum EnableAmendmentFlags : uint32_t {
tfGotMajority = 0x00010000,
tfLostMajority = 0x00020000,
tfTestSuite = 0x80000000,

View File

@@ -24,84 +24,163 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/SystemParameters.h>
namespace ripple {
namespace detail {
class CurrencyTag
{
public:
explicit CurrencyTag() = default;
};
class DirectoryTag
{
public:
explicit DirectoryTag() = default;
};
class NodeIDTag
{
public:
explicit NodeIDTag() = default;
};
} // namespace detail
using LedgerHash = uint256;
/** Directory is an index into the directory of offer books.
The last 64 bits of this are the quality. */
using Directory = base_uint<256, detail::DirectoryTag>;
using Directory = base_uint<256, struct DirectoryTag>;
/** Currency is a hash representing a specific currency. */
using Currency = base_uint<160, detail::CurrencyTag>;
using Currency = base_uint<160, struct CurrencyTag>;
/** NodeID is a 160-bit hash representing one node. */
using NodeID = base_uint<160, detail::NodeIDTag>;
using NodeID = base_uint<160, struct NodeIDTag>;
/** MPTID is a 192-bit value representing MPT Issuance ID,
* which is a concatenation of a 32-bit sequence (big endian)
* and a 160-bit account */
/** A Multi-Purpose Token Issuance ID,
The ID is the a concatenation of a 32-bit sequence number,
in big endian, and a 160-bit account.
@note This type is, unfortunately, untagged because the authors
of the original code used a deserialization APIs that did
not support tags. It should be fixed.
*/
using MPTID = base_uint<192>;
namespace detail {
// A table mapping which characters we are willing to allow in the ASCII
// representation of a three-letter currency code.
inline constexpr auto validIsoChars = []() consteval {
std::string_view isoCharSet =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"<>(){}[]|?!@#$%^&*";
std::array<bool, 256> table{};
for (unsigned char c : isoCharSet)
table[c] = true;
return table;
}();
// Determines if the given code is composed entirely of letters valid in
// ISO 4217 codes.
[[nodiscard]] inline constexpr bool
isIsoCode(std::string_view code) noexcept
{
return std::all_of(code.begin(), code.end(), [](char c) {
return validIsoChars[static_cast<unsigned char>(c)];
});
}
// The location (in bytes) of the 3 digit currency inside a 160-bit value
inline constexpr std::size_t isoCodeOffset = 12;
// The length of an ISO-4217 like code
inline constexpr std::size_t isoCodeLength = 3;
inline constexpr Currency isoMaskBits = ~Currency(0xFFFFFF0000000000);
// The special currency identifier for the system currency: all-zero
inline constexpr Currency xrpCurrency{0x0000000000000000};
// The special currency identifier meaning "no currency"
inline constexpr Currency noCurrency{0x0000000000000001};
// The system currency identifier in "ISO4217 format" which is reserved.
// We derive this value dynamically from the system currency code.
inline constexpr Currency badCurrency = []() consteval {
if (systemCurrencyCode.size() != isoCodeLength)
throw "Incorrect systemCurrencyCode size (must be 3 digits)";
std::array<std::uint8_t, Currency::bytes> bytes{};
for (std::size_t i = 0; i < isoCodeLength; ++i)
bytes[isoCodeOffset + i] =
static_cast<std::uint8_t>(systemCurrencyCode[i]);
return Currency(bytes);
}();
// We take advantage of the fact that an ASCII value in the [a-z] range
// transforms into the equivalent character in the [A-Z] range when you
// AND it with 0xDF, so we can compare against all possible variants of
// "XAH" in one go:
inline constexpr Currency badCurrencyCodeMask{0xDFDFDF0000000000};
} // namespace detail
/** XRP currency. */
Currency const&
xrpCurrency();
[[nodiscard]] constexpr Currency const&
xrpCurrency() noexcept
{
return detail::xrpCurrency;
}
[[nodiscard]] constexpr bool
isXRP(Currency const& c) noexcept
{
return c == detail::xrpCurrency;
}
/** A placeholder for empty currencies. */
Currency const&
noCurrency();
[[nodiscard]] constexpr Currency const&
noCurrency() noexcept
{
return detail::noCurrency;
}
/** We deliberately disallow the currency that looks like "XAH" because too
many people were using it instead of the correct XAH currency. */
Currency const&
badCurrency();
inline bool
isXRP(Currency const& c)
[[nodiscard]] constexpr Currency const&
badCurrency() noexcept
{
return c == beast::zero;
return detail::badCurrency;
}
inline bool
isBadCurrency(Currency const& c)
[[nodiscard]] constexpr bool
isBadCurrency(Currency const& c) noexcept
{
static const std::set<Currency> badCurrencies{
Currency(0x7861680000000000), // xah
Currency(0x7861480000000000), // xaH
Currency(0x7841680000000000), // xAh
Currency(0x7841480000000000), // xAH
Currency(0x5861680000000000), // Xah
Currency(0x5861480000000000), // XaH
Currency(0x5841680000000000), // XAh
Currency(0x5841480000000000) // XAH
};
return badCurrencies.find(c) != badCurrencies.end();
// We take advantage of the fact that an ASCII value in the [a-z] range
// transforms into the equivalent character in the [A-Z] range when you
// AND it with 0xDF, so we can compare against all possible variants of
// the bad currency code in one go:
return (c & detail::badCurrencyCodeMask) == badCurrency();
}
/** Returns "", "XAH", or three letter ISO code. */
std::string
to_string(Currency const& c);
/** Returns "", "XAH", three letter ISO code or the hex representation. */
[[nodiscard]] inline std::string
to_string(Currency const& currency)
{
if (currency == xrpCurrency())
return std::string{systemCurrencyCode};
if (currency == noCurrency())
return "1";
if ((currency & detail::isoMaskBits) == beast::zero)
{
std::string_view const iso(
reinterpret_cast<char const*>(currency.data()) +
detail::isoCodeOffset,
detail::isoCodeLength);
// Specifying the system currency code using ISO-style representation
// is not allowed. Note that the check is case-sensitive; yet another
// instance of legacy code smell.
if (detail::isIsoCode(iso) && iso != systemCurrencyCode)
return std::string{iso};
}
return strHex(currency);
}
/** Tries to convert a string to a Currency, returns true on success.
@@ -110,8 +189,29 @@ to_string(Currency const& c);
will require very careful checking everywhere and may mean having
to rewrite some unit test code.
*/
bool
to_currency(Currency&, std::string const&);
[[nodiscard]] constexpr bool
to_currency(Currency& currency, std::string_view code) noexcept
{
if (code.empty() || code == systemCurrencyCode)
{
currency = xrpCurrency();
return true;
}
// Handle ISO-4217-like 3-digit character codes.
if (code.size() != detail::isoCodeLength)
return currency.parseHex(code);
if (!detail::isIsoCode(code))
return false;
currency = beast::zero;
std::copy_n(
code.data(), code.size(), currency.begin() + detail::isoCodeOffset);
return true;
}
/** Tries to convert a string to a Currency, returns noCurrency() on failure.
@@ -119,8 +219,14 @@ to_currency(Currency&, std::string const&);
unfortunate; changing this will require very careful checking
everywhere and may mean having to rewrite some unit test code.
*/
Currency
to_currency(std::string const&);
[[nodiscard]] constexpr Currency
to_currency(std::string_view code) noexcept
{
if (Currency currency; to_currency(currency, code))
return currency;
return noCurrency();
}
inline std::ostream&
operator<<(std::ostream& os, Currency const& x)

View File

@@ -0,0 +1,112 @@
//------------------------------------------------------------------------------
/*
This file is part of xahaud: https://github.com/xahau/xahaud
Copyright (c) 2026, the Xahaud developers.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef XAHAU_PROTOCOL_KEYBASE_H_INCLUDED
#define XAHAU_PROTOCOL_KEYBASE_H_INCLUDED
#include <algorithm>
#include <cstdint>
namespace ripple {
namespace detail {
/** Common base for fixed-size key and seed types.
This CRTP base provides storage, accessors, and equality comparison
for types like seeds and secret/public keys, whichwrap a fixed-size
byte array. The Derived parameter ensures that unrelated types with
the same size do not implicitly interoperate with one another.
Equality comparison is provided automatically. Derived classes that
want to can opt in to ordering by declaring operator<=> as a
hidden friend.
@tparam Derived The concrete type inheriting from this base (CRTP).
@tparam N The size of the underlying byte array.
*/
template <class Derived, std::size_t N>
class KeyBase
{
public:
/** The type we use to store the data. */
using value_t = std::array<std::uint8_t, N>;
/** The span type corresponding to the type we use to store the data. */
using span_t = std::span<std::uint8_t, N>;
protected:
value_t buf_;
KeyBase() noexcept = default;
explicit KeyBase(span_t data) noexcept
{
std::copy_n(data.data(), N, buf_.data());
}
public:
[[nodiscard]] std::uint8_t const*
data() const
{
return buf_.data();
}
[[nodiscard]] std::size_t
size() const
{
return buf_.size();
}
[[nodiscard]] auto
begin() const noexcept
{
return buf_.begin();
}
[[nodiscard]] auto
cbegin() const noexcept
{
return buf_.cbegin();
}
[[nodiscard]] auto
end() const noexcept
{
return buf_.end();
}
[[nodiscard]] auto
cend() const noexcept
{
return buf_.cend();
}
[[nodiscard]] friend bool
operator==(Derived const& lhs, Derived const& rhs)
{
// This is not a constant time comparison. This is probably OK
// for the xahaud codebase.
return lhs.buf_ == rhs.buf_;
}
};
} // namespace detail
} // namespace ripple
#endif

View File

@@ -23,6 +23,9 @@
#if !defined(XRPL_FIX)
#error "undefined macro: XRPL_FIX"
#endif
#if !defined(XRPL_RETIRE)
#error "undefined macro: XRPL_RETIRE"
#endif
// clang-format off
@@ -31,6 +34,13 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(OnChainManifests, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookMap, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (GuardDepth32, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(NamedHooks, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(IOURewardClaim, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (IOULockedBalanceInvariant, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ImportIssuer, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HookAPISerializedType240, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(DynamicNFT, Supported::no, VoteBehavior::DefaultNo)
@@ -52,18 +62,18 @@ XRPL_FIX (FillOrKill, Supported::yes, VoteBehavior::DefaultYe
XRPL_FEATURE(DID, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (DisallowIncomingV1, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(XChainBridge, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMM, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMM, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (ReducedOffersV1, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HooksUpdate2, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FEATURE(HookOnV2, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FIX (HookAPI20251128, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FIX (CronStacking, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FEATURE(Cron, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FEATURE(IOUIssuerWeakTSH, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo);
XRPL_FIX (ProvisionalDoubleThreading, Supported::yes, VoteBehavior::DefaultYes);
XRPL_FEATURE(HooksUpdate2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(HookOnV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookAPI20251128, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (CronStacking, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(Cron, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(IOUIssuerWeakTSH, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ProvisionalDoubleThreading, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(Clawback, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (RewardClaimFlags, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HookCanEmit, Supported::yes, VoteBehavior::DefaultNo)
@@ -79,8 +89,6 @@ XRPL_FIX (240819, Supported::yes, VoteBehavior::DefaultYe
XRPL_FIX (NSDelete, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (XahauV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (XahauV1, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(Import, Supported::yes, VoteBehavior::DefaultYes)
@@ -147,4 +155,26 @@ XRPL_FIX (NFTokenDirV1, Supported::yes, VoteBehavior::Obsolete)
XRPL_FEATURE(NonFungibleTokensV1, Supported::yes, VoteBehavior::Obsolete)
XRPL_FEATURE(CryptoConditionsSuite, Supported::yes, VoteBehavior::Obsolete)
// The following amendments have been active for at least two years. Their
// pre-amendment code has been removed and the identifiers are deprecated.
// All known amendments and amendments that may appear in a validated
// ledger must be registered either here or above with the "active" amendments
XRPL_RETIRE(fixXahauV2)
XRPL_RETIRE(fixXahauV1)
XRPL_RETIRE(MultiSign)
XRPL_RETIRE(TrustSetAuth)
XRPL_RETIRE(FeeEscalation)
XRPL_RETIRE(PayChan)
XRPL_RETIRE(CryptoConditions)
XRPL_RETIRE(TickSize)
XRPL_RETIRE(fix1368)
XRPL_RETIRE(Escrow)
XRPL_RETIRE(fix1373)
XRPL_RETIRE(EnforceInvariants)
XRPL_RETIRE(SortedDirectories)
XRPL_RETIRE(fix1201)
XRPL_RETIRE(fix1512)
XRPL_RETIRE(fix1523)
XRPL_RETIRE(fix1528)
// clang-format on

View File

@@ -93,7 +93,7 @@ LEDGER_ENTRY(ltCHECK, 0x0043, Check, check, ({
*/
LEDGER_ENTRY(ltHOOK_DEFINITION, 'D', HookDefinition, hook_definition, ({
{sfHookHash, soeREQUIRED},
{sfHookOn, soeREQUIRED},
{sfHookOn, soeOPTIONAL},
{sfHookOnIncoming, soeOPTIONAL},
{sfHookOnOutgoing, soeOPTIONAL},
{sfHookCanEmit, soeOPTIONAL},
@@ -262,6 +262,7 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({
{sfHookStateScale, soeOPTIONAL},
{sfCron, soeOPTIONAL},
{sfAMMID, soeOPTIONAL},
{sfManifestID, soeOPTIONAL},
}))
/** A ledger object which contains a list of object identifiers.
@@ -396,6 +397,8 @@ LEDGER_ENTRY(ltRIPPLE_STATE, 0x0072, RippleState, state, ({
{sfHighQualityOut, soeOPTIONAL},
{sfLockedBalance, soeOPTIONAL},
{sfLockCount, soeOPTIONAL},
{sfHighReward, soeOPTIONAL},
{sfLowReward, soeOPTIONAL},
}))
/** The ledger object which lists the network's fee settings.
@@ -590,6 +593,26 @@ LEDGER_ENTRY(ltDID, 0x008D, DID, did, ({
{sfPreviousTxnLgrSeq, soeREQUIRED},
}))
LEDGER_ENTRY(ltMANIFEST, 0x004D, Manifest, manifest_entry, ({
{sfAccount, soeREQUIRED},
{sfPublicKey, soeREQUIRED},
{sfSigningPubKey, soeOPTIONAL}, /* may be absent if the master is revoked */
{sfSequence, soeREQUIRED},
/* sfVersion defaults to 0 and is omitted from the signed payload when
absent. Storing it unconditionally would change the bytes and break
signature verification when the manifest is reconstructed. */
{sfVersion, soeOPTIONAL},
{sfDomain, soeOPTIONAL},
/* The signatures are mirrored so the object is a lossless copy of the
manifest and can be independently verified, and re-served to peers,
by any node reading it (see ManifestCache::applyLedger). */
{sfMasterSignature, soeREQUIRED},
{sfSignature, soeOPTIONAL}, /* absent if the master is revoked */
{sfManifestID, soeOPTIONAL}, /* pointer to the identical other copy on the other key */
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
}))
#undef EXPAND
#undef LEDGER_ENTRY_DUPLICATE

View File

@@ -210,6 +210,7 @@ TYPED_SFIELD(sfOfferID, UINT256, 34)
TYPED_SFIELD(sfEscrowID, UINT256, 35)
TYPED_SFIELD(sfURITokenID, UINT256, 36)
TYPED_SFIELD(sfDomainID, UINT256, 37)
TYPED_SFIELD(sfManifestID, UINT256, 91)
TYPED_SFIELD(sfHookOnOutgoing, UINT256, 93)
TYPED_SFIELD(sfHookOnIncoming, UINT256, 94)
TYPED_SFIELD(sfCron, UINT256, 95)
@@ -257,6 +258,7 @@ TYPED_SFIELD(sfPrice, AMOUNT, 28)
TYPED_SFIELD(sfSignatureReward, AMOUNT, 29)
TYPED_SFIELD(sfMinAccountCreateAmount, AMOUNT, 30)
TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31)
TYPED_SFIELD(sfTrustLineRewardAccumulator,AMOUNT, 99)
// variable length (common)
TYPED_SFIELD(sfPublicKey, VL, 1)
@@ -292,6 +294,7 @@ TYPED_SFIELD(sfAssetClass, VL, 29)
TYPED_SFIELD(sfProvider, VL, 30)
TYPED_SFIELD(sfMPTokenMetadata, VL, 31)
TYPED_SFIELD(sfCredentialType, VL, 32)
TYPED_SFIELD(sfHookName, VL, 97)
TYPED_SFIELD(sfRemarkValue, VL, 98)
TYPED_SFIELD(sfRemarkName, VL, 99)
@@ -340,6 +343,7 @@ TYPED_SFIELD(sfLockingChainIssue, ISSUE, 1)
TYPED_SFIELD(sfIssuingChainIssue, ISSUE, 2)
TYPED_SFIELD(sfAsset, ISSUE, 3)
TYPED_SFIELD(sfAsset2, ISSUE, 4)
TYPED_SFIELD(sfClaimCurrency, ISSUE, 5)
// bridge
TYPED_SFIELD(sfXChainBridge, XCHAIN_BRIDGE, 1)
@@ -367,7 +371,7 @@ UNTYPED_SFIELD(sfMajority, OBJECT, 18)
UNTYPED_SFIELD(sfDisabledValidator, OBJECT, 19)
UNTYPED_SFIELD(sfEmittedTxn, OBJECT, 20)
UNTYPED_SFIELD(sfHookExecution, OBJECT, 21)
UNTYPED_SFIELD(sfHookDefinition, OBJECT, 22)
// 22 unused
UNTYPED_SFIELD(sfHookParameter, OBJECT, 23)
UNTYPED_SFIELD(sfHookGrant, OBJECT, 24)
UNTYPED_SFIELD(sfVoteEntry, OBJECT, 25)
@@ -379,6 +383,7 @@ UNTYPED_SFIELD(sfXChainClaimAttestationCollectionElement, OBJECT, 30)
UNTYPED_SFIELD(sfXChainCreateAccountAttestationCollectionElement, OBJECT, 31)
UNTYPED_SFIELD(sfPriceData, OBJECT, 32)
UNTYPED_SFIELD(sfCredential, OBJECT, 33)
UNTYPED_SFIELD(sfManifest, OBJECT, 90)
UNTYPED_SFIELD(sfAmountEntry, OBJECT, 91)
UNTYPED_SFIELD(sfMintURIToken, OBJECT, 92)
UNTYPED_SFIELD(sfHookEmission, OBJECT, 93)
@@ -386,6 +391,8 @@ UNTYPED_SFIELD(sfImportVLKey, OBJECT, 94)
UNTYPED_SFIELD(sfActiveValidator, OBJECT, 95)
UNTYPED_SFIELD(sfGenesisMint, OBJECT, 96)
UNTYPED_SFIELD(sfRemark, OBJECT, 97)
UNTYPED_SFIELD(sfHighReward, OBJECT, 98)
UNTYPED_SFIELD(sfLowReward, OBJECT, 99)
// array of objects (common)
// ARRAY/1 is reserved for end of array

View File

@@ -500,6 +500,12 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 72, PermissionedDomainDelete, ({
{sfDomainID, soeREQUIRED},
}))
/* sfAccount is supplied by TxFormats::commonFields; listing it here as well
makes the SOTemplate reject the format at construction. */
TRANSACTION(ttMANIFEST_SET, 91, SetManifest, ({
{sfManifest, soeREQUIRED},
}))
/* A pseudo-txn alarm signal for invoking a hook, emitted by validators after alarm set conditions are met */
TRANSACTION(ttCRON, 92, Cron, ({
{sfOwner, soeREQUIRED},
@@ -550,6 +556,7 @@ TRANSACTION(ttIMPORT, 97, Import, ({
* from a specified hook */
TRANSACTION(ttCLAIM_REWARD, 98, ClaimReward, ({
{sfIssuer, soeOPTIONAL},
{sfClaimCurrency, soeOPTIONAL},
}))
/** This transaction invokes a hook, providing arbitrary data. Essentially as a 0 drop payment. **/

View File

@@ -76,6 +76,7 @@ JSS(Holder); // field.
JSS(HookApiVersion); // field
JSS(HookCanEmit); // field
JSS(HookHash); // field
JSS(HookName); // field
JSS(HookNamespace); // field
JSS(HookOn); // field
JSS(HookOnIncoming); // field
@@ -291,7 +292,9 @@ JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(elapsed_seconds);
JSS(enabled); // out: AmendmentTable
JSS(enabled); // out: AmendmentTable (on-ledger);
// ServerDefinitions (this server)
JSS(ledger_enabled); // out: ServerDefinitions (on-ledger)
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
@@ -349,6 +352,7 @@ JSS(hash_mismatches); // out: catalogue
JSS(have_header); // out: InboundLedger
JSS(have_state); // out: InboundLedger
JSS(have_transactions); // out: InboundLedger
JSS(hooks); // in/out: AccountInfo
JSS(high); // out: BookChanges
JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
@@ -464,6 +468,7 @@ JSS(max_ledger); // in/out: LedgerCleaner
JSS(max_queue_size); // out: TxQ
JSS(max_spend_drops); // out: AccountInfo
JSS(max_spend_drops_total); // out: AccountInfo
JSS(maximum);
JSS(mean); // out: get_aggregate_price
JSS(median); // out: get_aggregate_price
JSS(median_fee); // out: TxQ

View File

@@ -36,7 +36,6 @@
#include <boost/beast/http/read.hpp>
#include <atomic>
#include <chrono>
#include <functional>
#include <memory>
#include <mutex>
#include <type_traits>
@@ -48,6 +47,8 @@ namespace ripple {
template <class Handler, class Impl>
class BaseHTTPPeer : public io_list::work, public Session
{
inline static std::atomic<std::uint32_t> next_id = 0;
protected:
using clock_type = std::chrono::system_clock;
using error_code = boost::system::error_code;
@@ -76,6 +77,8 @@ protected:
std::size_t used;
};
std::uint32_t const id_;
Port const& port_;
Handler& handler_;
boost::asio::executor_work_guard<boost::asio::executor> work_;
@@ -83,9 +86,6 @@ protected:
endpoint_type remote_address_;
beast::Journal const journal_;
std::string id_;
std::size_t nid_;
boost::asio::streambuf read_buf_;
http_request_type message_;
std::vector<buffer> wq_;
@@ -176,7 +176,7 @@ protected:
beast::IP::Endpoint
remoteAddress() override
{
return beast::IPAddressConversion::from_asio(remote_address_);
return beast::IP::from_asio(remote_address_);
}
http_request_type&
@@ -212,26 +212,26 @@ BaseHTTPPeer<Handler, Impl>::BaseHTTPPeer(
beast::Journal journal,
endpoint_type remote_address,
ConstBufferSequence const& buffers)
: port_(port)
: id_(++next_id)
, port_(port)
, handler_(handler)
, work_(executor)
, strand_(executor)
, remote_address_(remote_address)
, journal_(journal)
{
read_buf_.commit(boost::asio::buffer_copy(
read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers));
static std::atomic<int> sid;
nid_ = ++sid;
id_ = std::string("#") + std::to_string(nid_) + " ";
JLOG(journal_.trace()) << id_ << "accept: " << remote_address_.address();
read_buf_.commit(
boost::asio::buffer_copy(
read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers));
JLOG(journal_.trace()) << id_
<< ": accept: " << remote_address_.address();
}
template <class Handler, class Impl>
BaseHTTPPeer<Handler, Impl>::~BaseHTTPPeer()
{
handler_.onClose(session(), ec_);
JLOG(journal_.trace()) << id_ << "destroyed: " << request_count_
JLOG(journal_.trace()) << id_ << ": destroyed: " << request_count_
<< ((request_count_ == 1) ? " request"
: " requests");
}
@@ -242,10 +242,7 @@ BaseHTTPPeer<Handler, Impl>::close()
{
if (!strand_.running_in_this_thread())
return post(
strand_,
std::bind(
(void(BaseHTTPPeer::*)(void)) & BaseHTTPPeer::close,
impl().shared_from_this()));
strand_, [self = impl().shared_from_this()]() { self->close(); });
boost::beast::get_lowest_layer(impl().stream_).close();
}
@@ -259,7 +256,7 @@ BaseHTTPPeer<Handler, Impl>::fail(error_code ec, char const* what)
{
ec_ = ec;
JLOG(journal_.trace())
<< id_ << std::string(what) << ": " << ec.message();
<< id_ << ": " << std::string(what) << ": " << ec.message();
boost::beast::get_lowest_layer(impl().stream_).close();
}
}
@@ -269,9 +266,10 @@ void
BaseHTTPPeer<Handler, Impl>::start_timer()
{
boost::beast::get_lowest_layer(impl().stream_)
.expires_after(std::chrono::seconds(
remote_address_.address().is_loopback() ? timeoutSecondsLocal
: timeoutSeconds));
.expires_after(
std::chrono::seconds(
remote_address_.address().is_loopback() ? timeoutSecondsLocal
: timeoutSeconds));
}
// Convenience for discarding the error code
@@ -345,22 +343,19 @@ BaseHTTPPeer<Handler, Impl>::on_write(
v,
bind_executor(
strand_,
std::bind(
&BaseHTTPPeer::on_write,
impl().shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
[self = impl().shared_from_this()](
error_code const& ec, std::size_t bytes_transferred) {
self->on_write(ec, bytes_transferred);
}));
}
if (!complete_)
return;
if (graceful_)
return do_close();
boost::asio::spawn(
strand_,
std::bind(
&BaseHTTPPeer<Handler, Impl>::do_read,
impl().shared_from_this(),
std::placeholders::_1));
strand_, [self = impl().shared_from_this()](yield_context do_yield) {
self->do_read(do_yield);
});
}
template <class Handler, class Impl>
@@ -370,24 +365,18 @@ BaseHTTPPeer<Handler, Impl>::do_writer(
bool keep_alive,
yield_context do_yield)
{
std::function<void(void)> resume;
{
auto const p = impl().shared_from_this();
resume = std::function<void(void)>([this, p, writer, keep_alive]() {
boost::asio::spawn(
strand_,
std::bind(
&BaseHTTPPeer<Handler, Impl>::do_writer,
p,
writer,
keep_alive,
std::placeholders::_1));
});
}
for (;;)
{
if (!writer->prepare(bufferSize, resume))
if (!writer->prepare(
bufferSize,
[this, writer, keep_alive, self = impl().shared_from_this()]() {
boost::asio::spawn(
strand_,
[self, writer, keep_alive](
boost::asio::yield_context yield) {
self->do_writer(writer, keep_alive, yield);
});
}))
return;
error_code ec;
auto const bytes_transferred = boost::asio::async_write(
@@ -407,10 +396,9 @@ BaseHTTPPeer<Handler, Impl>::do_writer(
boost::asio::spawn(
strand_,
std::bind(
&BaseHTTPPeer<Handler, Impl>::do_read,
impl().shared_from_this(),
std::placeholders::_1));
[self = impl().shared_from_this()](boost::asio::yield_context yield) {
self->do_read(yield);
});
}
//------------------------------------------------------------------------------
@@ -422,22 +410,19 @@ BaseHTTPPeer<Handler, Impl>::write(void const* buf, std::size_t bytes)
{
if (bytes == 0)
return;
if ([&] {
std::lock_guard lock(mutex_);
wq_.emplace_back(buf, bytes);
return wq_.size() == 1 && wq2_.size() == 0;
return wq_.size() == 1 && wq2_.empty();
}())
{
if (!strand_.running_in_this_thread())
return post(
strand_,
std::bind(
&BaseHTTPPeer::on_write,
impl().shared_from_this(),
error_code{},
0));
else
return on_write(error_code{}, 0);
return post(strand_, [self = impl().shared_from_this()]() {
self->on_write(error_code{}, 0);
});
return on_write(error_code{}, 0);
}
}
@@ -449,12 +434,10 @@ BaseHTTPPeer<Handler, Impl>::write(
{
boost::asio::spawn(bind_executor(
strand_,
std::bind(
&BaseHTTPPeer<Handler, Impl>::do_writer,
impl().shared_from_this(),
writer,
keep_alive,
std::placeholders::_1)));
[self = impl().shared_from_this(), writer, keep_alive](
boost::asio::yield_context yield) {
self->do_writer(writer, keep_alive, yield);
}));
}
// DEPRECATED
@@ -473,28 +456,22 @@ void
BaseHTTPPeer<Handler, Impl>::complete()
{
if (!strand_.running_in_this_thread())
return post(
strand_,
std::bind(
&BaseHTTPPeer<Handler, Impl>::complete,
impl().shared_from_this()));
return post(strand_, [self = impl().shared_from_this()]() {
self->complete();
});
message_ = {};
complete_ = true;
{
std::lock_guard lock(mutex_);
if (!wq_.empty() && !wq2_.empty())
return;
}
if (std::lock_guard lock(mutex_); !wq_.empty() && !wq2_.empty())
return;
// keep-alive
boost::asio::spawn(bind_executor(
strand_,
std::bind(
&BaseHTTPPeer<Handler, Impl>::do_read,
impl().shared_from_this(),
std::placeholders::_1)));
[self = impl().shared_from_this()](boost::asio::yield_context yield) {
self->do_read(yield);
}));
}
// DEPRECATED
@@ -504,23 +481,19 @@ void
BaseHTTPPeer<Handler, Impl>::close(bool graceful)
{
if (!strand_.running_in_this_thread())
return post(
strand_,
std::bind(
(void(BaseHTTPPeer::*)(bool)) &
BaseHTTPPeer<Handler, Impl>::close,
impl().shared_from_this(),
graceful));
return post(strand_, [self = impl().shared_from_this(), graceful]() {
self->close(graceful);
});
complete_ = true;
if (graceful)
{
graceful_ = true;
{
std::lock_guard lock(mutex_);
if (!wq_.empty() || !wq2_.empty())
return;
}
if (std::lock_guard lock(mutex_); !wq_.empty() || !wq2_.empty())
return;
return do_close();
}

View File

@@ -11,11 +11,11 @@ echo "START BUILDING (HOST)"
echo "Cleaning previously built binary"
rm -f release-build/xahaud
BUILD_CORES=$(echo "scale=0 ; `nproc` / 1.337" | bc)
BUILD_CORES=$(echo "scale=0 ; $(nproc) / 1.337" | bc)
if [[ "$GITHUB_REPOSITORY" == "" ]]; then
#Default
BUILD_CORES=${BUILD_CORES:-8}
BUILD_CORES=${BUILD_CORES:-8}
fi
# Ensure still works outside of GH Actions by setting these to /dev/null
@@ -31,21 +31,19 @@ echo "-- GITHUB_SHA: $GITHUB_SHA"
echo "-- GITHUB_RUN_NUMBER: $GITHUB_RUN_NUMBER"
echo "-- CONTAINER_NAME: $CONTAINER_NAME"
which docker 2> /dev/null 2> /dev/null
if [ "$?" -eq "1" ]
then
which docker 2>/dev/null 2>/dev/null
if [ "$?" -eq "1" ]; then
echo 'Docker not found. Install it first.'
exit 1
fi
stat .git 2> /dev/null 2> /dev/null
if [ "$?" -eq "1" ]
then
stat .git 2>/dev/null 2>/dev/null
if [ "$?" -eq "1" ]; then
echo 'Run this inside the source directory. (.git dir not found).'
exit 1
fi
STATIC_CONTAINER=$(docker ps -a | grep $CONTAINER_NAME |wc -l)
STATIC_CONTAINER=$(docker ps -a | grep $CONTAINER_NAME | wc -l)
CACHE_VOLUME_NAME="xahau-release-builder-cache"
@@ -57,13 +55,14 @@ if false; then
docker stop $CONTAINER_NAME
else
echo "No static container, build on temp container"
rm -rf release-build;
mkdir -p release-build;
rm -rf release-build
mkdir -p release-build
docker volume create $CACHE_VOLUME_NAME
# Create inline Dockerfile with environment setup for build-full.sh
DOCKERFILE_CONTENT=$(cat <<'DOCKERFILE_EOF'
DOCKERFILE_CONTENT=$(
cat <<'DOCKERFILE_EOF'
FROM ghcr.io/phusion/holy-build-box:4.0.1-amd64
ARG BUILD_CORES=8
@@ -196,6 +195,7 @@ ENV PATH=/usr/local/bin:$PATH
RUN /hbb_exe/activate-exec bash -c "ccache -M 100G && \
ccache -o cache_dir=/cache/ccache && \
ccache -o compiler_check=content && \
ccache -o direct_mode=true && \
mkdir -p ~/.conan2 /cache/conan2 /cache/conan2_download /cache/conan2_sources && \
echo 'core.cache:storage_path=/cache/conan2' > ~/.conan2/global.conf && \
echo 'core.download:download_cache=/cache/conan2_download' >> ~/.conan2/global.conf && \
@@ -217,7 +217,7 @@ RUN /hbb_exe/activate-exec bash -c "ccache -M 100G && \
ln -s ../../bin/ccache /usr/lib64/ccache/c++"
DOCKERFILE_EOF
)
)
# Build custom Docker image
IMAGE_NAME="xahaud-builder:latest"
@@ -227,14 +227,14 @@ DOCKERFILE_EOF
if [[ "$GITHUB_REPOSITORY" == "" ]]; then
# Non GH, local building
echo "Non-GH runner, local building, temp container"
docker run -i --user 0:$(id -g) --rm -v /data/builds:/data/builds -v `pwd`:/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash -c "source /opt/rh/gcc-toolset-11/enable && bash -x /io/build-full.sh '$GITHUB_REPOSITORY' '$GITHUB_SHA' '$BUILD_CORES' '$GITHUB_RUN_NUMBER'"
docker run -i --user 0:$(id -g) --rm -v /data/builds:/data/builds -v $(pwd):/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash -c "source /opt/rh/gcc-toolset-11/enable && bash -x /io/build-full.sh '$GITHUB_REPOSITORY' '$GITHUB_SHA' '$BUILD_CORES' '$GITHUB_RUN_NUMBER'"
else
# GH Action, runner
echo "GH Action, runner, clean & re-create create persistent container"
docker rm -f $CONTAINER_NAME
echo "echo 'Stopping container: $CONTAINER_NAME'" >> "$JOB_CLEANUP_SCRIPT"
echo "docker stop --time=15 \"$CONTAINER_NAME\" || echo 'Failed to stop container or container not running'" >> "$JOB_CLEANUP_SCRIPT"
docker run -di --user 0:$(id -g) --name $CONTAINER_NAME -v /data/builds:/data/builds -v `pwd`:/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash
echo "echo 'Stopping container: $CONTAINER_NAME'" >>"$JOB_CLEANUP_SCRIPT"
echo "docker stop --time=15 \"$CONTAINER_NAME\" || echo 'Failed to stop container or container not running'" >>"$JOB_CLEANUP_SCRIPT"
docker run -di --user 0:$(id -g) --name $CONTAINER_NAME -v /data/builds:/data/builds -v $(pwd):/io -v "$CACHE_VOLUME_NAME":/cache --network host "$IMAGE_NAME" /hbb_exe/activate-exec bash
docker exec -i $CONTAINER_NAME /hbb_exe/activate-exec bash -c "source /opt/rh/gcc-toolset-11/enable && bash -x /io/build-full.sh '$GITHUB_REPOSITORY' '$GITHUB_SHA' '$BUILD_CORES' '$GITHUB_RUN_NUMBER'"
docker stop $CONTAINER_NAME
fi

View File

@@ -1,58 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/basics/CountedObject.h>
#include <algorithm>
#include <type_traits>
namespace ripple {
CountedObjects&
CountedObjects::getInstance() noexcept
{
static CountedObjects instance;
return instance;
}
CountedObjects::CountedObjects() noexcept : m_count(0), m_head(nullptr)
{
}
CountedObjects::List
CountedObjects::getCounts(int minimumThreshold) const
{
List counts;
// When other operations are concurrent, the count
// might be temporarily less than the actual count.
counts.reserve(m_count.load());
for (auto* ctr = m_head.load(); ctr != nullptr; ctr = ctr->getNext())
{
if (ctr->getCount() >= minimumThreshold)
counts.emplace_back(ctr->getName(), ctr->getCount());
}
std::sort(counts.begin(), counts.end());
return counts;
}
} // namespace ripple

View File

@@ -202,8 +202,11 @@ public:
{
if (m_stop_called.exchange(true) == false)
{
m_io_service.dispatch(m_strand.wrap(std::bind(
&ResolverAsioImpl::do_stop, this, CompletionCounter(this))));
m_io_service.dispatch(m_strand.wrap(
std::bind(
&ResolverAsioImpl::do_stop,
this,
CompletionCounter(this))));
JLOG(m_journal.debug()) << "Queued a stop request";
}
@@ -234,12 +237,13 @@ public:
// TODO NIKB use rvalue references to construct and move
// reducing cost.
m_io_service.dispatch(m_strand.wrap(std::bind(
&ResolverAsioImpl::do_resolve,
this,
names,
handler,
CompletionCounter(this))));
m_io_service.dispatch(m_strand.wrap(
std::bind(
&ResolverAsioImpl::do_resolve,
this,
names,
handler,
CompletionCounter(this))));
}
//-------------------------------------------------------------------------
@@ -279,16 +283,16 @@ public:
{
while (iter != boost::asio::ip::tcp::resolver::iterator())
{
addresses.push_back(
beast::IPAddressConversion::from_asio(*iter));
addresses.push_back(beast::IP::from_asio(*iter));
++iter;
}
}
handler(name, addresses);
m_io_service.post(m_strand.wrap(std::bind(
&ResolverAsioImpl::do_work, this, CompletionCounter(this))));
m_io_service.post(m_strand.wrap(
std::bind(
&ResolverAsioImpl::do_work, this, CompletionCounter(this))));
}
HostAndPort
@@ -369,8 +373,11 @@ public:
{
JLOG(m_journal.error()) << "Unable to parse '" << name << "'";
m_io_service.post(m_strand.wrap(std::bind(
&ResolverAsioImpl::do_work, this, CompletionCounter(this))));
m_io_service.post(m_strand.wrap(
std::bind(
&ResolverAsioImpl::do_work,
this,
CompletionCounter(this))));
return;
}
@@ -409,10 +416,11 @@ public:
if (m_work.size() > 0)
{
m_io_service.post(m_strand.wrap(std::bind(
&ResolverAsioImpl::do_work,
this,
CompletionCounter(this))));
m_io_service.post(m_strand.wrap(
std::bind(
&ResolverAsioImpl::do_work,
this,
CompletionCounter(this))));
}
}
}

View File

@@ -20,42 +20,36 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <cstdlib>
#include <iostream>
namespace ripple {
namespace detail {
[[noreturn]] void
accessViolation() noexcept
void
LogThrow(std::string_view type, std::string_view what)
{
// dereference memory location zero
int volatile* j = 0;
(void)*j;
std::abort();
JLOG(debugLog().warn())
<< "Throwing exception of type " << type << ": " << what;
}
} // namespace detail
void
LogThrow(std::string const& title)
LogicError(std::string_view msg) noexcept
{
JLOG(debugLog().warn()) << title;
}
JLOG(debugLog().fatal()) << "LogicError: " << msg;
std::cerr << "Logic error: " << msg << std::endl;
[[noreturn]] void
LogicError(std::string const& s) noexcept
{
JLOG(debugLog().fatal()) << s;
std::cerr << "Logic error: " << s << std::endl;
// Use a non-standard contract naming here (without namespace) because
// it's the only location where various unrelated execution paths may
// register an error; this is also why the "message" parameter is passed
// here.
// For the above reasons, we want this contract to stand out.
UNREACHABLE("LogicError", {{"message", s}});
detail::accessViolation();
std::abort();
}
} // namespace ripple

View File

@@ -33,7 +33,7 @@
namespace beast::detail {
inline void
setCurrentThreadNameImpl(std::string_view name)
setCurrentThreadNameImpl(char const* name)
{
#if DEBUG && BOOST_COMP_MSVC
// This technique is documented by Microsoft and works for all versions
@@ -54,7 +54,7 @@ setCurrentThreadNameImpl(std::string_view name)
THREADNAME_INFO ni;
ni.dwType = 0x1000;
ni.szName = name.data();
ni.szName = name;
ni.dwThreadID = GetCurrentThreadId();
ni.dwFlags = 0;
@@ -81,9 +81,9 @@ setCurrentThreadNameImpl(std::string_view name)
namespace beast::detail {
inline void
setCurrentThreadNameImpl(std::string_view name)
setCurrentThreadNameImpl(char const* name)
{
pthread_setname_np(name.data());
pthread_setname_np(name);
}
} // namespace beast::detail
@@ -95,9 +95,9 @@ setCurrentThreadNameImpl(std::string_view name)
namespace beast::detail {
inline void
setCurrentThreadNameImpl(std::string_view name)
setCurrentThreadNameImpl(char const* name)
{
pthread_setname_np(pthread_self(), name.data());
pthread_setname_np(pthread_self(), name);
}
} // namespace beast::detail
@@ -119,7 +119,7 @@ void
setCurrentThreadName(std::string_view name)
{
detail::threadName = name;
detail::setCurrentThreadNameImpl(name);
detail::setCurrentThreadNameImpl(detail::threadName.c_str());
}
} // namespace beast

View File

@@ -1,50 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/beast/net/IPAddressConversion.h>
namespace beast {
namespace IP {
Endpoint
from_asio(boost::asio::ip::address const& address)
{
return Endpoint{address};
}
Endpoint
from_asio(boost::asio::ip::tcp::endpoint const& endpoint)
{
return Endpoint{endpoint.address(), endpoint.port()};
}
boost::asio::ip::address
to_asio_address(Endpoint const& endpoint)
{
return endpoint.address();
}
boost::asio::ip::tcp::endpoint
to_asio_endpoint(Endpoint const& endpoint)
{
return boost::asio::ip::tcp::endpoint{endpoint.address(), endpoint.port()};
}
} // namespace IP
} // namespace beast

View File

@@ -1,54 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/beast/net/IPAddressV4.h>
#include <sstream>
#include <stdexcept>
namespace beast {
namespace IP {
bool
is_private(AddressV4 const& addr)
{
return ((addr.to_ulong() & 0xff000000) ==
0x0a000000) || // Prefix /8, 10. #.#.#
((addr.to_ulong() & 0xfff00000) ==
0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.#
((addr.to_ulong() & 0xffff0000) ==
0xc0a80000) || // Prefix /16 192.168.#.#
addr.is_loopback();
}
bool
is_public(AddressV4 const& addr)
{
return !is_private(addr) && !addr.is_multicast();
}
char
get_class(AddressV4 const& addr)
{
static char const* table = "AAAABBCD";
return table[(addr.to_ulong() & 0xE0000000) >> 29];
}
} // namespace IP
} // namespace beast

View File

@@ -1,42 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/beast/net/IPAddressV4.h>
#include <xrpl/beast/net/IPAddressV6.h>
namespace beast {
namespace IP {
bool
is_private(AddressV6 const& addr)
{
return (
(addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ?
(addr.is_v4_mapped() && is_private(addr.to_v4())));
}
bool
is_public(AddressV6 const& addr)
{
// TODO is this correct?
return !is_private(addr) && !addr.is_multicast();
}
} // namespace IP
} // namespace beast

View File

@@ -18,164 +18,120 @@
//==============================================================================
#include <xrpl/beast/net/IPEndpoint.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <charconv>
#include <system_error>
namespace beast {
namespace IP {
Endpoint::Endpoint() : m_port(0)
namespace {
Port
make_port(std::string_view s)
{
Port port = 0;
if (!s.empty())
{
auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), port);
if (ec != std::errc{} || ptr != s.data() + s.size())
throw std::system_error(std::make_error_code(ec));
}
return port;
}
Endpoint::Endpoint(Address const& addr, Port port) : m_addr(addr), m_port(port)
{
}
} // namespace
std::optional<Endpoint>
Endpoint::from_string_checked(std::string const& s)
Endpoint::from_string_checked(std::string_view s)
{
if (s.size() <= 64)
{
std::stringstream is(boost::trim_copy(s));
Endpoint endpoint;
is >> endpoint;
if (!is.fail() && is.rdbuf()->in_avail() == 0)
return endpoint;
}
return {};
}
using namespace boost::asio::ip;
Endpoint
Endpoint::from_string(std::string const& s)
{
if (std::optional<Endpoint> const result = from_string_checked(s))
return *result;
return Endpoint{};
// We need to catch exceptions here because we use the throwing versions of
// the boost address parsing functions. It is also possible that exceptions
// come from std::string_view, even though we are careful.
try
{
auto is_space = [](std::string_view::value_type c) {
return std::isspace(std::string_view::traits_type::to_int_type(c));
};
s.remove_prefix(
std::distance(
s.begin(), std::find_if_not(s.begin(), s.end(), is_space)));
s.remove_suffix(
std::distance(
s.rbegin(), std::find_if_not(s.rbegin(), s.rend(), is_space)));
if (s.empty())
return std::nullopt;
if (s[0] == '[')
{ // Bracketed notation: must be an IPv6 address
auto close = s.find(']');
if (close == std::string_view::npos)
return std::nullopt;
auto addr = s.substr(1, close - 1);
auto rest = s.substr(close + 1);
if (rest.empty())
return Endpoint{make_address_v6(addr)};
if (rest[0] != ':')
return std::nullopt;
return Endpoint{make_address_v6(addr), make_port(rest.substr(1))};
}
// We now need to check if a space is present. We already trimmed
// whitespace from the end of the input string, so if we find any
// it means we have a port present.
auto sp = std::find_if(s.begin(), s.end(), is_space);
if (sp == s.end())
{
auto colon = s.find(':');
// A single colon suggests this is an IPv4 address with a port.
if (colon != std::string_view::npos && colon == s.rfind(':'))
return Endpoint{
make_address_v4(s.substr(0, colon)),
make_port(s.substr(colon + 1))};
// It's a standalone address (either v4 or v6)
return Endpoint{make_address(s)};
}
// Either a v4 or a v6 address followed by one or more spaces and a port
auto rest = s.substr(std::distance(s.begin(), sp));
return Endpoint{
make_address(s.substr(0, std::distance(s.begin(), sp))),
make_port(rest.substr(
std::distance(
rest.begin(),
std::find_if_not(rest.begin(), rest.end(), is_space))))};
}
catch (...)
{
return std::nullopt;
}
}
std::string
Endpoint::to_string() const
{
std::string s;
s.reserve(
(address().is_v6() ? INET6_ADDRSTRLEN - 1 : 15) +
(port() == 0 ? 0 : 6 + (address().is_v6() ? 2 : 0)));
if (port() == 0)
return address().to_string();
if (port() != 0 && address().is_v6())
s += '[';
s += address().to_string();
if (port())
{
if (address().is_v6())
s += ']';
s += ":" + std::to_string(port());
}
if (address().is_v6())
return "[" + address().to_string() + "]:" + std::to_string(port());
return s;
}
bool
operator==(Endpoint const& lhs, Endpoint const& rhs)
{
return lhs.address() == rhs.address() && lhs.port() == rhs.port();
}
bool
operator<(Endpoint const& lhs, Endpoint const& rhs)
{
if (lhs.address() < rhs.address())
return true;
if (lhs.address() > rhs.address())
return false;
return lhs.port() < rhs.port();
}
//------------------------------------------------------------------------------
std::istream&
operator>>(std::istream& is, Endpoint& endpoint)
{
std::string addrStr;
// valid addresses only need INET6_ADDRSTRLEN-1 chars, but allow the extra
// char to check for invalid lengths
addrStr.reserve(INET6_ADDRSTRLEN);
char i{0};
char readTo{0};
is.get(i);
if (i == '[') // we are an IPv6 endpoint
readTo = ']';
else
addrStr += i;
while (is && is.rdbuf()->in_avail() > 0 && is.get(i))
{
// NOTE: There is a legacy data format
// that allowed space to be used as address / port separator
// so we continue to honor that here by assuming we are at the end
// of the address portion if we hit a space (or the separator
// we were expecting to see)
if (isspace(static_cast<unsigned char>(i)) || (readTo && i == readTo))
break;
if ((i == '.') || (i >= '0' && i <= ':') || (i >= 'a' && i <= 'f') ||
(i >= 'A' && i <= 'F'))
{
addrStr += i;
// don't exceed a reasonable length...
if (addrStr.size() == INET6_ADDRSTRLEN ||
(readTo && readTo == ':' && addrStr.size() > 15))
{
is.setstate(std::ios_base::failbit);
return is;
}
if (!readTo && (i == '.' || i == ':'))
{
// if we see a dot first, must be IPv4
// otherwise must be non-bracketed IPv6
readTo = (i == '.') ? ':' : ' ';
}
}
else // invalid char
{
is.unget();
is.setstate(std::ios_base::failbit);
return is;
}
}
if (readTo == ']' && is.rdbuf()->in_avail() > 0)
{
is.get(i);
if (!(isspace(static_cast<unsigned char>(i)) || i == ':'))
{
is.unget();
is.setstate(std::ios_base::failbit);
return is;
}
}
boost::system::error_code ec;
auto addr = Address::from_string(addrStr, ec);
if (ec)
{
is.setstate(std::ios_base::failbit);
return is;
}
if (is.rdbuf()->in_avail() > 0)
{
Port port;
is >> port;
if (is.fail())
return is;
endpoint = Endpoint(addr, port);
}
else
endpoint = Endpoint(addr);
return is;
return address().to_string() + ":" + std::to_string(port());
}
} // namespace IP

View File

@@ -20,17 +20,28 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/crypto/RFC1751.h>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/copied.hpp>
#include <algorithm>
#include <array>
#include <cstdint>
#include <ranges>
#include <span>
#include <string>
#include <vector>
namespace ripple {
namespace rfc1751 {
//
// RFC 1751 code converted to C++/Boost.
//
namespace {
char const* RFC1751::s_dictionary[2048] = {
/** The list of words we use.
The list may appear sorted, but it is not: it is composed of two
separately sorted parts: the short words, which are shorter than
four characters long, come first; the remaining words, which are
all exactly four characters long, follow.
*/
constexpr std::string_view wordlist[] = {
"A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", "AGO", "AID",
"AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA", "AND",
"ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK",
@@ -260,252 +271,263 @@ char const* RFC1751::s_dictionary[2048] = {
"WRIT", "WYNN", "YALE", "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN",
"YEAH", "YEAR", "YELL", "YOGA", "YOKE"};
/* Extract 'length' bits from the char array 's'
starting with bit 'start' */
unsigned long
RFC1751::extract(char const* s, int start, int length)
{
unsigned char cl;
unsigned char cc;
unsigned char cr;
unsigned long x;
static_assert(
[]() consteval {
if (!std::has_single_bit(std::size(wordlist)))
return false;
XRPL_ASSERT(length <= 11, "ripple::RFC1751::extract : maximum length");
XRPL_ASSERT(start >= 0, "ripple::RFC1751::extract : minimum start");
XRPL_ASSERT(length >= 0, "ripple::RFC1751::extract : minimum length");
XRPL_ASSERT(
start + length <= 66,
"ripple::RFC1751::extract : maximum start + length");
auto check = [](std::string_view curr,
std::string_view prev) consteval {
if (prev.size() == 4 && curr.size() < 4)
return false;
int const shiftR = 24 - (length + (start % 8));
cl = s[start / 8]; // get components
cc = (shiftR < 16) ? s[start / 8 + 1] : 0;
cr = (shiftR < 8) ? s[start / 8 + 2] : 0;
// At the partition point (the boundary between short and long
// words) the lexicographical ordering breaks.
if (prev.size() < 4 && curr.size() == 4)
return true;
x = ((long)(cl << 8 | cc) << 8 | cr); // Put bits together
x = x >> shiftR; // Right justify number
x = (x & (0xffff >> (16 - length))); // Trim extra bits.
return curr > prev;
};
return x;
}
std::string_view last;
// Encode 8 bytes in 'c' as a string of English words.
// Returns a pointer to a static buffer
void
RFC1751::btoe(std::string& strHuman, std::string const& strData)
{
char caBuffer[9]; /* add in room for the parity 2 bits*/
int p, i;
memcpy(caBuffer, strData.c_str(), 8);
// compute parity: merely add groups of two bits.
for (p = 0, i = 0; i < 64; i += 2)
p += extract(caBuffer, i, 2);
caBuffer[8] = char(p) << 6;
strHuman = std::string() + s_dictionary[extract(caBuffer, 0, 11)] + " " +
s_dictionary[extract(caBuffer, 11, 11)] + " " +
s_dictionary[extract(caBuffer, 22, 11)] + " " +
s_dictionary[extract(caBuffer, 33, 11)] + " " +
s_dictionary[extract(caBuffer, 44, 11)] + " " +
s_dictionary[extract(caBuffer, 55, 11)];
}
void
RFC1751::insert(char* s, int x, int start, int length)
{
unsigned char cl;
unsigned char cc;
unsigned char cr;
unsigned long y;
int shift;
XRPL_ASSERT(length <= 11, "ripple::RFC1751::insert : maximum length");
XRPL_ASSERT(start >= 0, "ripple::RFC1751::insert : minimum start");
XRPL_ASSERT(length >= 0, "ripple::RFC1751::insert : minimum length");
XRPL_ASSERT(
start + length <= 66,
"ripple::RFC1751::insert : maximum start + length");
shift = ((8 - ((start + length) % 8)) % 8);
y = (long)x << shift;
cl = (y >> 16) & 0xff;
cc = (y >> 8) & 0xff;
cr = y & 0xff;
if (shift + length > 16)
{
s[start / 8] |= cl;
s[start / 8 + 1] |= cc;
s[start / 8 + 2] |= cr;
}
else if (shift + length > 8)
{
s[start / 8] |= cc;
s[start / 8 + 1] |= cr;
}
else
{
s[start / 8] |= cr;
}
}
void
RFC1751::standard(std::string& strWord)
{
for (auto& letter : strWord)
{
if (islower(static_cast<unsigned char>(letter)))
letter = toupper(static_cast<unsigned char>(letter));
else if (letter == '1')
letter = 'L';
else if (letter == '0')
letter = 'O';
else if (letter == '5')
letter = 'S';
}
}
// Binary search of dictionary.
int
RFC1751::wsrch(std::string const& strWord, int iMin, int iMax)
{
int iResult = -1;
while (iResult < 0 && iMin != iMax)
{
// Have a range to search.
int iMid = iMin + (iMax - iMin) / 2;
int iDir = strWord.compare(s_dictionary[iMid]);
if (!iDir)
for (auto word : wordlist)
{
iResult = iMid; // Found it.
if (word.empty() || word.size() > 4)
return false;
if (!last.empty() && !check(word, last))
return false;
last = word;
}
else if (iDir < 0)
{
iMax = iMid; // key < middle, middle is new max.
}
else
{
iMin = iMid + 1; // key > middle, new min is past the middle.
}
}
return iResult;
}
return true;
}(),
"rfc1751 wordlist incorrectly sized or improperly sorted");
// Convert 6 words to binary.
//
// Returns 1 OK - all good words and parity is OK
// 0 word not in data base
// -1 badly formed in put ie > 4 char word
// -2 words OK but parity is wrong
int
RFC1751::etob(std::string& strData, std::vector<std::string> vsHuman)
{
if (6 != vsHuman.size())
return -1;
int i, p = 0;
char b[9] = {0};
for (auto& strWord : vsHuman)
/** The cutoff point between "short" and "long" words in the wordlist */
constexpr std::size_t dictionaryPartition = []() consteval {
for (std::size_t i = 0; i < std::size(wordlist); ++i)
{
int l = strWord.length();
if (l > 4 || l < 1)
return -1;
standard(strWord);
auto v = wsrch(strWord, l < 4 ? 0 : 571, l < 4 ? 570 : 2048);
if (v < 0)
return 0;
insert(b, v, p, 11);
p += 11;
if (wordlist[i].size() == 4)
return i;
}
throw "no partition boundary found";
}();
/* now check the parity of what we got */
for (p = 0, i = 0; i < 64; i += 2)
p += extract(b, i, 2);
/** How many bits each word encodes. Depends on the size of the table. */
constexpr auto bitsPerWord = std::countr_zero(std::size(wordlist));
if ((p & 3) != extract(b, 64, 2))
return -2;
// Extract `length` bits from byte array `s` starting at bit `start`.
[[nodiscard]] constexpr std::size_t
extractBits(
std::span<std::uint8_t const, 9> s,
std::size_t start,
std::size_t length) noexcept
{
auto const byte = start / 8;
auto const shift = 24 - (length + (start % 8));
auto const mask = (std::size_t{1} << length) - 1;
strData.assign(b, 8);
// Load up to 3 bytes straddling the target bits into a 24-bit window.
std::size_t window = s[byte] << 16;
return 1;
if (shift < 16)
window |= s[byte + 1] << 8;
if (shift < 8)
window |= s[byte + 2];
return (window >> shift) & mask;
}
/** Convert words separated by spaces into a 128 bit key in big-endian format.
@return
1 if succeeded
0 if word not in dictionary
-1 if badly formed string
-2 if words are okay but parity is wrong.
*/
int
RFC1751::getKeyFromEnglish(std::string& strKey, std::string const& strHuman)
// Insert `length` bits of `x` into byte array `s` at bit position `start`.
constexpr std::size_t
insertBits(
std::span<std::uint8_t, 9> s,
std::size_t x,
std::size_t start) noexcept
{
std::vector<std::string> vWords;
std::string strFirst, strSecond;
int rc = 0;
auto const byte = start / 8;
auto const y = x << (24 - (start % 8) - bitsPerWord);
std::string strTrimmed(strHuman);
s[byte] |= static_cast<std::uint8_t>((y >> 16) & 0xff);
s[byte + 1] |= static_cast<std::uint8_t>((y >> 8) & 0xff);
s[byte + 2] |= static_cast<std::uint8_t>(y & 0xff);
boost::algorithm::trim(strTrimmed);
return bitsPerWord;
}
[[nodiscard]] constexpr std::size_t
parityBits(std::span<std::uint8_t const, 9> buf) noexcept
{
std::size_t parity = 0;
for (std::size_t i = 0; i < 64; i += 2)
parity += extractBits(buf, i, 2);
return parity & 3;
}
// Normalize a word to uppercase with common substitutions (1->L, 0->O, 5->S).
[[nodiscard]] constexpr std::string
normalize(std::string word)
{
for (auto& ch : word)
{
if (ch >= 'a' && ch <= 'z')
ch -= ('a' - 'A');
else if (ch == '1')
ch = 'L';
else if (ch == '0')
ch = 'O';
else if (ch == '5')
ch = 'S';
}
return word;
}
// Binary search the wordlist for `word`.
[[nodiscard]] constexpr std::optional<std::size_t>
dictionaryLookup(std::string_view const& word) noexcept
{
auto const begin = (word.size() < 4)
? std::begin(wordlist)
: std::begin(wordlist) + dictionaryPartition;
auto const end = (word.size() < 4)
? std::begin(wordlist) + dictionaryPartition
: std::end(wordlist);
auto it = std::lower_bound(begin, end, word);
if (it != end && *it == word)
return static_cast<std::size_t>(
std::distance(std::begin(wordlist), it));
return std::nullopt;
}
// Encode 8 bytes as 6 words.
[[nodiscard]] constexpr std::array<std::string_view, 6>
bytesToEnglish(std::span<std::uint8_t const, 8> data)
{
std::array<std::uint8_t, 9> buf{};
std::copy_n(data.data(), data.size(), buf.data());
buf[8] = static_cast<std::uint8_t>(parityBits(buf) << 6);
std::array<std::string_view, 6> words;
for (std::size_t i = 0; i < 6; ++i)
words[i] = wordlist[extractBits(buf, i * bitsPerWord, bitsPerWord)];
return words;
}
// Decode 6 words into 8 bytes. Returns true on success.
[[nodiscard]] constexpr bool
englishToBytes(
std::span<std::uint8_t, 8> out,
std::span<std::string const, 6> words)
{
// The additional byte is needed for parity
std::array<std::uint8_t, 9> buf{};
std::size_t pos = 0;
for (auto word : words)
{
if (word.empty() || word.size() > 4)
return false;
word = normalize(std::move(word));
auto const idx = dictionaryLookup(word);
if (!idx)
return false;
pos += insertBits(buf, *idx, pos);
}
if (parityBits(buf) != (buf[8] >> 6))
return false;
std::copy_n(buf.data(), out.size(), out.begin());
return true;
}
} // namespace
std::optional<std::array<std::uint8_t, 16>>
keyFromEnglish(std::string_view human)
{
if (human.size() < 23 || human.size() > 128)
return std::nullopt;
std::vector<std::string> words;
std::string trimmed{human};
boost::algorithm::trim(trimmed);
boost::algorithm::split(
vWords,
strTrimmed,
words,
trimmed,
boost::algorithm::is_space(),
boost::algorithm::token_compress_on);
rc = 12 == vWords.size() ? 1 : -1;
if (words.size() != 12)
return std::nullopt;
if (1 == rc)
rc = etob(strFirst, vWords | boost::adaptors::copied(0, 6));
std::array<std::uint8_t, 16> key{};
if (1 == rc)
rc = etob(strSecond, vWords | boost::adaptors::copied(6, 12));
if (!englishToBytes(
std::span{key}.subspan<0, 8>(), std::span(words).subspan<0, 6>()))
return std::nullopt;
if (1 == rc)
strKey = strFirst + strSecond;
if (!englishToBytes(
std::span{key}.subspan<8, 8>(), std::span(words).subspan<6, 6>()))
return std::nullopt;
return rc;
return key;
}
/** Convert to human from a 128 bit key in big-endian format
*/
void
RFC1751::getEnglishFromKey(std::string& strHuman, std::string const& strKey)
std::optional<std::string>
englishFromKey(std::span<std::uint8_t const> key)
{
std::string strFirst, strSecond;
if (key.size() != 16)
return std::nullopt;
btoe(strFirst, strKey.substr(0, 8));
btoe(strSecond, strKey.substr(8, 8));
std::string result;
strHuman = strFirst + " " + strSecond;
for (auto const w : bytesToEnglish(key.subspan<0, 8>()))
{
if (!result.empty())
result += ' ';
result += w;
}
for (auto const w : bytesToEnglish(key.subspan<8, 8>()))
{
result += ' ';
result += w;
}
return result;
}
std::string
RFC1751::getWordFromBlob(void const* blob, size_t bytes)
std::string_view
wordFromBlob(std::span<std::uint8_t const> blob)
{
// This is a simple implementation of the Jenkins one-at-a-time hash
// algorithm:
// http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time
unsigned char const* data = static_cast<unsigned char const*>(blob);
std::uint32_t hash = 0;
for (size_t i = 0; i < bytes; ++i)
for (auto byte : blob)
{
hash += data[i];
hash += byte;
hash += (hash << 10);
hash ^= (hash >> 6);
}
@@ -514,8 +536,8 @@ RFC1751::getWordFromBlob(void const* blob, size_t bytes)
hash ^= (hash >> 11);
hash += (hash << 15);
return s_dictionary
[hash % (sizeof(s_dictionary) / sizeof(s_dictionary[0]))];
return wordlist[hash % std::size(wordlist)];
}
} // namespace rfc1751
} // namespace ripple

View File

@@ -18,19 +18,30 @@
//==============================================================================
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/crypto/csprng.h>
#include <array>
#include <openssl/rand.h>
#include <random>
#include <stdexcept>
namespace ripple {
namespace {
/** The largest number of bytes we can request from the CSPRNG at once. */
constexpr std::size_t max_csprng_bytes_per_request = 8 * 1024 * 1024;
} // namespace
csprng_engine::csprng_engine()
{
// This is not strictly necessary
if (RAND_poll() != 1)
// This is not strictly necessary: on any modern system by the time
// this is invoked, there should be enough entropy already. Despite
// that, we are being conservative:
if (RAND_status() != 1)
RAND_poll();
if (RAND_status() != 1)
Throw<std::runtime_error>("CSPRNG: Initial polling failed");
}
@@ -45,34 +56,25 @@ csprng_engine::~csprng_engine()
void
csprng_engine::mix_entropy(void* buffer, std::size_t count)
{
std::array<std::random_device::result_type, 128> entropy;
{
// On every platform we support, std::random_device
// is non-deterministic and should provide some good
// quality entropy.
std::random_device rd;
for (auto& e : entropy)
e = rd();
}
if (buffer == nullptr || count == 0)
return;
std::lock_guard lock(mutex_);
// We add data to the pool, but we conservatively assume that
// it contributes no actual entropy.
// We add the data to the pool, but we conservatively assume
// that it contributes no actual entropy.
RAND_add(
entropy.data(),
entropy.size() * sizeof(std::random_device::result_type),
buffer,
static_cast<int>(std::min(count, max_csprng_bytes_per_request)),
0);
if (buffer != nullptr && count != 0)
RAND_add(buffer, count, 0);
}
void
csprng_engine::operator()(void* ptr, std::size_t count)
{
if (count > max_csprng_bytes_per_request) [[unlikely]]
Throw<std::runtime_error>("CSPRNG: Request too large");
// RAND_bytes is thread-safe on OpenSSL 1.1.0 and later when compiled
// with thread support, so we don't need to grab a mutex.
// https://mta.openssl.org/pipermail/openssl-users/2020-November/013146.html
@@ -80,8 +82,8 @@ csprng_engine::operator()(void* ptr, std::size_t count)
std::lock_guard lock(mutex_);
#endif
auto const result =
RAND_bytes(reinterpret_cast<unsigned char*>(ptr), count);
auto const result = RAND_bytes(
reinterpret_cast<unsigned char*>(ptr), static_cast<int>(count));
if (result != 1)
Throw<std::runtime_error>("CSPRNG: Insufficient entropy");
@@ -98,6 +100,7 @@ csprng_engine::operator()()
csprng_engine&
crypto_prng()
{
/** The single instance of the cryptographically secure PRNG */
static csprng_engine engine;
return engine;
}

View File

@@ -226,15 +226,10 @@ Value::Value(double value) : type_(realValue)
value_.real_ = value;
}
Value::Value(const char* value) : type_(stringValue), allocated_(true)
Value::Value(std::string_view value) : type_(stringValue), allocated_(true)
{
value_.string_ = valueAllocator()->duplicateStringValue(value);
}
Value::Value(std::string const& value) : type_(stringValue), allocated_(true)
{
value_.string_ = valueAllocator()->duplicateStringValue(
value.c_str(), (unsigned int)value.length());
value_.string_ =
valueAllocator()->duplicateStringValue(value.data(), value.size());
}
Value::Value(const StaticString& value) : type_(stringValue), allocated_(false)
@@ -809,9 +804,9 @@ Value::operator[](UInt index) const
}
Value&
Value::operator[](const char* key)
Value::operator[](std::string_view key)
{
return resolveReference(key, false);
return resolveReference(key.data(), false);
}
Value&
@@ -851,7 +846,7 @@ Value::isValidIndex(UInt index) const
}
const Value&
Value::operator[](const char* key) const
Value::operator[](std::string_view key) const
{
XRPL_ASSERT(
type_ == nullValue || type_ == objectValue,
@@ -860,7 +855,7 @@ Value::operator[](const char* key) const
if (type_ == nullValue)
return null;
CZString actualKey(key, CZString::noDuplication);
CZString actualKey(key.data(), CZString::noDuplication);
ObjectValues::const_iterator it = value_.map_->find(actualKey);
if (it == value_.map_->end())
@@ -869,18 +864,6 @@ Value::operator[](const char* key) const
return (*it).second;
}
Value&
Value::operator[](std::string const& key)
{
return (*this)[key.c_str()];
}
const Value&
Value::operator[](std::string const& key) const
{
return (*this)[key.c_str()];
}
Value&
Value::operator[](const StaticString& key)
{

View File

@@ -66,7 +66,7 @@ invalidAMMAsset(
Issue const& issue,
std::optional<std::pair<Issue, Issue>> const& pair)
{
if (badCurrency() == issue.currency)
if (isBadCurrency(issue.currency))
return temBAD_CURRENCY;
if (isXRP(issue) && issue.account.isNonZero())
return temBAD_ISSUER;

View File

@@ -89,11 +89,9 @@ constexpr static ErrorInfo unorderedErrorInfos[]{
{rpcNOT_SUPPORTED, "notSupported", "Operation not supported.", 501},
{rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable.", 503},
{rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable.", 503},
//@@start network-error-messages
{rpcNOT_SYNCED, "notSynced", "Not synced to the network.", 503},
{rpcNO_EVENTS, "noEvents", "Current transport does not support events.", 405},
{rpcNO_NETWORK, "noNetwork", "Not synced to the network.", 503},
//@@end network-error-messages
{rpcWRONG_NETWORK, "wrongNetwork", "Wrong network.", 503},
{rpcNO_PERMISSION, "noPermission", "You don't have permission for this command.", 401},
{rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress.", 404},

View File

@@ -250,12 +250,9 @@ FeatureCollections::registerFeature(
Feature const* i = getByName(name);
if (!i)
{
// If this check fails, and you just added a feature, increase the
// numFeatures value in Feature.h
check(
features.size() < detail::numFeatures,
"More features defined than allocated. Adjust numFeatures in "
"Feature.h.");
"More features defined than allocated.");
auto const f = sha512Half(Slice(name.data(), name.size()));
@@ -424,45 +421,26 @@ featureToName(uint256 const& 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) \
uint256 const feature##name = registerFeature(#name, supported, vote);
#define XRPL_FIX(name, supported, vote) \
uint256 const fix##name = registerFeature("fix" #name, supported, vote);
#define XRPL_RETIRE(name) \
[[deprecated("The referenced amendment has been retired"), maybe_unused]] \
uint256 const retired##name = retireFeature(#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
#pragma pop_macro("XRPL_FEATURE")
// clang-format off
// The following amendments have been active for at least two years. Their
// pre-amendment code has been removed and the identifiers are deprecated.
// All known amendments and amendments that may appear in a validated
// ledger must be registered either here or above with the "active" amendments
[[deprecated("The referenced amendment has been retired"), maybe_unused]]
uint256 const
retiredMultiSign = retireFeature("MultiSign"),
retiredTrustSetAuth = retireFeature("TrustSetAuth"),
retiredFeeEscalation = retireFeature("FeeEscalation"),
retiredPayChan = retireFeature("PayChan"),
retiredCryptoConditions = retireFeature("CryptoConditions"),
retiredTickSize = retireFeature("TickSize"),
retiredFix1368 = retireFeature("fix1368"),
retiredEscrow = retireFeature("Escrow"),
retiredFix1373 = retireFeature("fix1373"),
retiredEnforceInvariants = retireFeature("EnforceInvariants"),
retiredSortedDirectories = retireFeature("SortedDirectories"),
retiredFix1201 = retireFeature("fix1201"),
retiredFix1512 = retireFeature("fix1512"),
retiredFix1523 = retireFeature("fix1523"),
retiredFix1528 = retireFeature("fix1528");
// clang-format on
// All of the features should now be registered, since variables in a cpp file
// are initialized from top to bottom.
//

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