Compare commits

...
Author SHA1 Message Date
Timothy Banks 025aa71930 refactor: Retire FillOrKill fix (#7366) 2026-10-21 16:12:29 +00:00
Mayukha Vadari ef5064eeb9 feat: Remove XChainBridge feature (#8255) 2026-10-20 20:37:02 +00:00
Alex KremerandAyaz Salikhov c2e739f670 refactor: Migrate handlers to rpc-spec (E) (#8613)
Co-authored-by: Ayaz Salikhov <[email protected]>
2026-10-20 15:39:27 +00:00
Alex Kremer 15fac6250d chore: Simplify account and ident checks via rpc-spec (#8620) 2026-10-20 13:25:47 +00:00
Alex KremerandAyaz Salikhov 3608433514 test: Check error consistently in rpc handlers (#8604)
Co-authored-by: Ayaz Salikhov <[email protected]>
2026-10-19 16:17:30 +00:00
Nik Bougalis 7939339a65 chore: Fix an issue that breaks CLion (#8619) 2026-10-17 22:43:53 +00:00
4ea2db2eda feat: Add TransactionProposal invariants for Cosign (#8130)
Co-authored-by: Shawn Xie <[email protected]>
Co-authored-by: Kassaking7 <[email protected]>
Co-authored-by: Shawn Xie <[email protected]>
Co-authored-by: Zhiyuan Wang <[email protected]>
Co-authored-by: Claude Fable 5.1 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
2026-10-16 20:59:53 +00:00
Ayaz Salikhov d4ffe32534 ci: Fix release/staging branches running conditions (#8615) 2026-10-16 17:06:19 +00:00
Gregory Tsipenyuk 7354816f65 fix: Assorted MPT/DEX offer related fixes - 1 (#8468) 2026-10-16 16:28:05 +00:00
Gregory Tsipenyuk 12cb3932f3 fix: Check vault share freeze at MPT strand endpoints (#8478) 2026-10-16 15:26:00 +00:00
Gregory Tsipenyuk dc64a8daac fix: Allow IOU rounding in Clawback invariant balance check (#8475) 2026-10-16 15:25:03 +00:00
Nik Bougalis dc524e9cc6 refactor: Optimize the binary layout of Number (#8550) 2026-10-16 15:20:01 +00:00
Alex KremerandAyaz Salikhov 4060b1bcd4 refactor: Migrate handlers to rpc-spec (D) (#8573)
Co-authored-by: Ayaz Salikhov <[email protected]>
2026-10-16 14:06:30 +00:00
Mayukha Vadari bfbb440fc2 feat: Handle Smart Escrow's Bytecode, Data and Gas fields (#8215) 2026-10-16 13:03:48 +00:00
BartandBart 4087861411 fix: Refuse to walk an invalid SHAMap in getMissingNodes (#8086)
Co-authored-by: Bart <[email protected]>
2026-10-16 05:47:08 +00:00
BartandBart dc43faafed fix: Refuse to make an invalid SHAMap or Ledger immutable (#8085)
Co-authored-by: Bart <[email protected]>
2026-10-16 05:47:07 +00:00
6af97c07d9 fix: Report a node the sync path refuses as invalid data (#8084)
Co-authored-by: Bart <[email protected]>
Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-10-16 05:47:06 +00:00
4c7d920e66 fix: Make the SHAMap sync-path state atomic (#8083)
Co-authored-by: Bart <[email protected]>
Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-10-16 05:47:06 +00:00
yinyiqian1 8b59d9d307 docs: Add todo comment for key rotation zkproof (#8589) 2026-10-16 00:11:06 +00:00
b0fa295595 fix: Judge a header's account hash on both acquisition routes (#8476)
Co-authored-by: Bart <[email protected]>
Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-10-15 21:00:21 +00:00
BartandBart 9af432cb0e fix: Signal every InboundLedger failure found in local data (#8082)
Co-authored-by: Bart <[email protected]>
2026-10-15 21:00:20 +00:00
BartandBart 4435ca4cbf refactor: Add a reusable peer harness for acquisition tests (#8310)
Co-authored-by: Bart <[email protected]>
2026-10-15 21:00:19 +00:00
Olek cd72a11786 refactor: TxQ (#8064) 2026-10-15 18:11:46 +00:00
Peter Chen b398b88385 fix: Reject a zero credential ID in deposit_authorized (#8533) 2026-10-15 18:11:18 +00:00
Olek 31f6f90206 feat: Add IssuerMirrorEncryptionKey and InitialIssuerEncryptionKey (#8325) 2026-10-15 18:04:29 +00:00
Olek 1d8c2f9172 test: Sponsor checkSign additional tests (#8331) 2026-10-15 18:03:48 +00:00
f105418bdb feat: Book FixedPrecision LoanPay and LoanManage (#8271)
Co-authored-by: Jingchen <[email protected]>
Co-authored-by: Alex Kremer <[email protected]>
2026-10-15 18:02:36 +00:00
Ayaz Salikhov 4a134eb883 docs: Clarify OS and toolchain requirements (#8588) 2026-10-15 17:58:11 +00:00
Ayaz Salikhov ec3f251ad8 ci: Fix running pre-commit after clang-tidy (#8601) 2026-10-15 16:15:35 +00:00
Mayukha Vadari 5faa861226 feat: Add Smart Escrow fee voting (#8214) 2026-10-15 15:50:16 +00:00
Jingchen 88fd1222d6 build: Fix unity build by addressing name clashes (#8599) 2026-10-15 12:59:20 +00:00
Ayaz SalikhovandEd Hennis f1ce9449f2 fix: Skip bootstrap DNS lookups in standalone mode (#8583)
Co-authored-by: Ed Hennis <[email protected]>
2026-10-14 21:02:45 +00:00
Timur Yalymov 7237b3321c fix: Charge transfer fee on third-party vault withdrawals (#8267) 2026-10-14 16:35:42 +00:00
Timur Yalymov 84d980d883 fix: Reject VaultClawback with a nonexistent holder (#8470) 2026-10-14 14:29:05 +00:00
Timur YalymovandVito Tumas 791c96faad fix: Enforce pseudo-account directory contents with an invariant (#8181)
Co-authored-by: Vito Tumas <[email protected]>
2026-10-14 13:51:24 +00:00
Ayaz Salikhov 31473e7e6d build: Unify clang variables (#8581) 2026-10-14 13:51:05 +00:00
Timur YalymovandVito Tumas aa70b368b5 fix: Enforce asset authorization on LoanBrokerDelete (#8204)
Co-authored-by: Vito Tumas <[email protected]>
2026-10-14 13:51:05 +00:00
Vito TumasandJingchen d3db569b34 feat: Book FixedPrecision LoanSet through AssetsDeployed (#8260)
Co-authored-by: Jingchen <[email protected]>
2026-10-14 13:20:58 +00:00
Vito TumasandJingchen 94d6b7daf8 feat: Round LoanBroker cover on the FixedPrecision grid (#8258)
Co-authored-by: Jingchen <[email protected]>
2026-10-14 12:59:39 +00:00
Vito TumasandJingchen e089abe982 feat: Use FixedPrecision accounting in Vault transactors (#8460)
Co-authored-by: Jingchen <[email protected]>
2026-10-14 12:52:25 +00:00
311 changed files with 25909 additions and 20894 deletions

No files matched your search

+2 -2
View File
@@ -103,6 +103,7 @@ words:
- dearmor
- decryptor
- dedented
- dedup
- deleteme
- demultiplexer
- deserializaton
@@ -317,7 +318,6 @@ words:
- sttx
- stvar
- stvector
- stxchainattestations
- summands
- superpeer
- superpeers
@@ -344,6 +344,7 @@ words:
- unambiguity
- unauthorizes
- unauthorizing
- undeserializable
- unergonomic
- unfetched
- unfindable
@@ -375,7 +376,6 @@ words:
- writeme
- wsrch
- wthread
- xbridge
- xchain
- xcrun
- ximinez
@@ -62,6 +62,7 @@ libxrpl.tx > xrpl.protocol
libxrpl.tx > xrpl.server
libxrpl.tx > xrpl.tx
test.app > test.jtx
test.app > tests.libxrpl
test.app > test.unit_test
test.app > xrpl.basics
test.app > xrpl.config
+1
View File
@@ -8,6 +8,7 @@ on:
branches:
- "develop"
- "release/*"
- "staging/*"
paths:
- "crates/**/Cargo.toml"
- "crates/Cargo.lock"
+1 -1
View File
@@ -243,7 +243,7 @@ jobs:
- should-run
- build-test
# Only run when committing to a PR that targets a release branch.
if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release') }}
if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release/') }}
uses: ./.github/workflows/reusable-upload-recipe.yml
secrets:
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
+8 -6
View File
@@ -1,14 +1,16 @@
# This workflow runs all workflows to build and test the code on various Linux
# flavors, as well as on MacOS and Windows, on a scheduled basis, on merge into
# the 'develop' or 'release*' branches, or when requested manually. Upon pushes
# to the develop branch it also uploads the libxrpl recipe to the Conan remote.
# the 'develop', 'release/*' or 'staging/*' branches, or when requested
# manually. Upon pushes to the develop branch, it also uploads the libxrpl
# recipe to the Conan remote.
name: Trigger
on:
push:
branches:
- "develop"
- "release*"
- "release/*"
- "staging/*"
paths:
# These paths are unique to `on-trigger.yml`.
- ".github/workflows/on-trigger.yml"
@@ -62,9 +64,9 @@ on:
workflow_dispatch:
concurrency:
# When a PR is merged into the develop branch it will be assigned a unique
# When a PR is merged into the develop branch, it will be assigned a unique
# group identifier, so execution will continue even if another PR is merged
# while it is still running. In all other cases the group identifier is shared
# while it is still running. In all other cases, the group identifier is shared
# per branch, so that any in-progress runs are cancelled when a new commit is
# pushed.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }}
@@ -98,7 +100,7 @@ jobs:
# However, we do not enable ccache for events targeting a release branch,
# to protect against the rare case that the output produced by ccache is
# not identical to a regular compilation.
ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release') }}
ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release/') }}
os: ${{ matrix.os }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+2 -1
View File
@@ -8,7 +8,8 @@ on:
push:
branches:
- "develop"
- "release*"
- "release/*"
- "staging/*"
workflow_dispatch:
jobs:
+8 -2
View File
@@ -123,13 +123,19 @@ jobs:
- name: Fix style
if: ${{ steps.files_changed.outcome != 'success' }}
# pre-commit installs every hook's environment, even for hooks that match
# no files, and node-based hooks need libatomic, which this image lacks.
# Exit code 1 just means hooks modified files or found issues.
env:
SKIP: prettier,cspell
run: |
pre-commit run --all-files || true
git add --update
pre-commit run || [ $? -eq 1 ]
- name: Generate git diff
if: ${{ steps.files_changed.outcome != 'success' }}
run: |
git diff | tee "${DIFF_FILE}"
git diff HEAD | tee "${DIFF_FILE}"
- name: Upload clang-tidy diff output
if: ${{ github.event.repository.visibility == 'public' && steps.files_changed.outcome != 'success' }}
+8
View File
@@ -26,6 +26,10 @@ This version is supported by all `xrpld` versions. For WebSocket and HTTP JSON-R
Version 3.5.0 is not yet released.
### Breaking changes in 3.5.0
- The `XChainBridge` amendment and its ledger entries and transactions are removed. `XChainBridge` and `fixXChainRewardRounding` are now obsolete and can no longer be voted for; neither was ever enabled on Mainnet. `ledger_entry` no longer accepts `bridge`, `xchain_owned_claim_id`, or `xchain_owned_create_account_claim_id`; `account_objects` no longer accepts those values in `type`; and `server_definitions` no longer lists the removed ledger entry types, transaction types, fields, and the `XChainBridge` serialized type.
### Additions in 3.5.0
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive the same `transaction` message as the `transactions` stream for each validated transaction whose metadata affects a subscribed issuance. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
@@ -36,6 +40,10 @@ Version 3.5.0 is not yet released.
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
- `account_currencies`, `account_nfts`, `account_offers`: A request with more than one problem may now report a different one of them. An invalid `ledger_hash` or `ledger_index` is reported before an invalid or missing `account`. An invalid `limit` or `marker` is reported before `lgrNotFound` or `actNotFound`, and for `account_offers` also before `actMalformed`. A request with a single problem returns the same error as before. [#8561](https://github.com/XRPLF/rippled/pull/8561)
- `deposit_authorized`: A `credentials` entry that is all zeros (for example `"0"` or 64 `0` characters) now returns a `badCredentials` error instead of looking up a credential that can never exist.
- `account_info`, `account_lines`, `account_channels`: A request with more than one problem may now report a different one of them. An invalid `ledger_hash` or `ledger_index` is reported before a missing or invalid `account`, or `ident` for `account_info`. For `account_lines`, a `peer` that is not a string, or an invalid `limit` or `marker`, is reported before `lgrNotFound`, `actNotFound` or a malformed `account`, and an invalid `limit` or `marker` also before a malformed `peer`. For `account_channels`, a malformed `account` is reported before `lgrNotFound`, and an invalid `destination_account`, `limit` or `marker` before `lgrNotFound` or `actNotFound`. A request with a single problem returns the same error as before. [#8573](https://github.com/XRPLF/rippled/pull/8573)
- `deposit_authorized`, `nft_buy_offers`, `nft_sell_offers`: A request with more than one problem may now report a different one of them. An invalid `ledger_hash` or `ledger_index` is reported before a missing or invalid `source_account`, `destination_account` or `nft_id`, and for `nft_buy_offers` / `nft_sell_offers` also before an invalid `limit`. For `deposit_authorized`, an invalid `credentials` array is reported before `lgrNotFound`, `srcActNotFound`, `dstActNotFound` or `badCredentials`. For `nft_buy_offers` and `nft_sell_offers`, an invalid `marker` is reported before `lgrNotFound` or `objectNotFound`. A request with a single problem returns the same error as before. [#8613](https://github.com/XRPLF/rippled/pull/8613)
- `nft_buy_offers`, `nft_sell_offers`: An `nft_id` that is an array or an object now returns an `invalidParams` error instead of an `internal` error. [#8613](https://github.com/XRPLF/rippled/pull/8613)
### Bugfixes in 3.5.0
+5 -26
View File
@@ -2,33 +2,12 @@
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| These instructions assume you have a C++ development environment ready with Git, Python, Conan, CMake, and a C++ compiler. For help setting one up on Linux, macOS, or Windows, [see this guide](./docs/build/environment.md).<br><br>These instructions also assume a basic familiarity with Conan and CMake. If you are unfamiliar with Conan, you can read our [crash course](./docs/build/conan.md) or the official [Getting Started][conan-getting-started] walkthrough. |
## Minimum Requirements
## Supported platforms
For the hardware needed to run a node, see
[System Requirements](https://xrpl.org/system-requirements.html).
For the software needed to build xrpld, see the
[environment setup guide](./docs/build/environment.md).
## Operating Systems
### Linux
The Ubuntu Linux distribution has received the highest level of quality
assurance, testing, and support. We also support Red Hat and use Debian
internally.
Our Linux CI tooling is distro-independent and uses a Nix-based environment, so it should be possible to build on other Linux distributions as well, although we have not tested them.
### macOS
Many `xrpld` engineers use macOS for development.
The minimum supported version is macOS 15 (Sequoia).
CI testing is done in macOS 26 (Tahoe), but the build defaults `CMAKE_OSX_DEPLOYMENT_TARGET` to 15.
### Windows
Windows is used by some engineers for development only, and is not recommended
for production use.
Building and testing are supported on Linux, macOS, and (best effort) Windows;
see [Supported development platforms](./docs/build/environment.md#supported-development-platforms).
Running `xrpld` is officially supported on Linux only;
see [Requirements](./docs/install.md#requirements).
## Steps
+1 -1
View File
@@ -42,7 +42,7 @@ if(is_gcc)
# GCC-specific fixes
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
elseif(is_clang)
elseif(is_any_clang)
# Clang-specific fixes
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
elseif(is_msvc)
+1 -1
View File
@@ -332,7 +332,7 @@ This ensures that configuration changes don't introduce new warnings across the
### Installing clang-tidy
See the [environment setup guide](./docs/build/environment.md#clang-tidy) for how to get clang-tidy.
The [Nix development shell](./docs/build/nix.md) provides `clang-tidy` at the [tested version](./docs/build/environment.md#tested-compiler-versions).
### Running clang-tidy locally
+33
View File
@@ -1360,6 +1360,39 @@
# Example:
# owner_reserve = 200000 # 0.2 XRP
#
# gas_limit = <gas>
#
# The gas limit is the maximum amount of gas that can be
# consumed by a single transaction. The gas limit is used to prevent
# transactions from consuming too many resources.
#
# If this parameter is unspecified, xrpld will use an internal
# default. Don't change this without understanding the consequences.
#
# Example:
# gas_limit = 1000000 # 1 million gas
#
# bytecode_size_limit = <bytes>
#
# The bytecode size limit is the maximum size of a WASM extension in
# bytes. The size limit is used to prevent extensions from consuming
# too many resources.
#
# If this parameter is unspecified, xrpld will use an internal
# default. Don't change this without understanding the consequences.
#
# Example:
# bytecode_size_limit = 100000 # 100 kb
#
# gas_price = <micro-drops>
#
# The gas price is the conversion between WASM gas and its price in drops.
#
# If this parameter is unspecified, xrpld will use an internal
# default. Don't change this without understanding the consequences.
#
# Example:
# gas_price = 1000000 # 1 drop per gas
#-------------------------------------------------------------------------------
#
# 9. Misc Settings
+11 -3
View File
@@ -9,13 +9,21 @@ include_guard(GLOBAL)
# --------------------------------------------------------------------
# Compiler detection (C++)
# --------------------------------------------------------------------
set(is_clang FALSE)
# is_any_clang is true for both LLVM Clang and AppleClang. Their version numbers
# and feature sets differ, so use is_llvm_clang or is_apple_clang when it matters.
set(is_any_clang FALSE)
set(is_llvm_clang FALSE)
set(is_apple_clang FALSE)
set(is_gcc FALSE)
set(is_msvc FALSE)
set(is_xcode FALSE)
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
set(is_clang TRUE)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(is_any_clang TRUE)
set(is_llvm_clang TRUE)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(is_any_clang TRUE)
set(is_apple_clang TRUE)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(is_gcc TRUE)
elseif(MSVC)
+6 -3
View File
@@ -130,7 +130,7 @@ else()
INTERFACE
-Wall
-Wdeprecated
$<$<BOOL:${is_clang}>:-Wno-deprecated-declarations>
$<$<BOOL:${is_any_clang}>:-Wno-deprecated-declarations>
$<$<BOOL:${wextra}>:-Wextra
-Wno-unused-parameter>
$<$<BOOL:${werr}>:-Werror>
@@ -177,7 +177,7 @@ else()
if(
is_nix_compiler
AND is_linux
AND is_clang
AND is_any_clang
AND CMAKE_CXX_FLAGS MATCHES "stdlib=libstdc"
)
string(
@@ -204,7 +204,10 @@ if(voidstar)
"Antithesis instrumentation requires Linux, aborting..."
)
elseif(
NOT (is_clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0)
NOT (
is_llvm_clang
AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0
)
)
message(
FATAL_ERROR
+5
View File
@@ -297,11 +297,16 @@ if(xrpld)
VALUE_TYPE "::json::Value"
VIEW_HEADER "xrpld/rpc/detail/JsonObjectView.hpp"
HANDLERS
account_channels
account_currencies
account_info
account_lines
account_nfts
account_offers
book_changes
deposit_authorized
ledger
nft_offers_common
transaction_entry
)
target_sources(xrpld PRIVATE ${rpcspec_instantiations})
+1 -1
View File
@@ -58,7 +58,7 @@ target_link_options(common INTERFACE ${sanitizers_linker_flags})
# This module appends -fsanitize-ignorelist=<path> for Clang builds.
# The ignorelist path contains CMAKE_SOURCE_DIR, so it must be set here, rather than in the Conan profile.
# GCC does not support -fsanitize-ignorelist.
if(is_clang)
if(is_any_clang)
set(ignorelist_path
"${CMAKE_SOURCE_DIR}/sanitizers/suppressions/sanitizer-ignorelist.txt"
)
+2 -5
View File
@@ -23,11 +23,8 @@ if(NOT is_multiconfig)
endif()
endif()
if(is_clang) # both Clang and AppleClang
if(
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang"
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0
)
if(is_llvm_clang)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
message(FATAL_ERROR "This project requires clang 16 or later")
endif()
elseif(is_gcc)
+3 -3
View File
@@ -62,11 +62,11 @@ if(unity)
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
endif()
if(is_clang AND is_linux)
if(is_llvm_clang AND is_linux)
option(voidstar "Enable Antithesis instrumentation." OFF)
endif()
if(is_gcc OR is_clang)
if(is_gcc OR is_any_clang)
include(ProcessorCount)
ProcessorCount(PROCESSOR_COUNT)
@@ -125,7 +125,7 @@ else()
set(use_mold OFF CACHE BOOL "mold linker, linux only" FORCE)
endif()
if(is_clang)
if(is_any_clang)
option(use_lld "enables detection of lld linker" ON)
else()
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)
+2 -2
View File
@@ -3,11 +3,11 @@
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
"xrpl-rpc-spec/0.1.23#56e8946f5b3fb949a3732e1a51135ca2%1790977482.197086",
"xrpl-rpc-spec/0.1.29#852afc87cac32aca93cc50a985d73fe0%1792359643.19817",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
"secp256k1/0.7.1#b1f450b7f78a36fff75bb6934a356f3a%1782338841.3729",
"secp256k1/0.7.1#f4d16a4d907dd057598954476ca6f88c%1792234329.790275",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1782392413.075713",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
+1 -1
View File
@@ -40,7 +40,7 @@ class Xrpl(ConanFile):
"nudb/2.0.9",
"openssl/3.6.3",
"soci/4.0.3",
"xrpl-rpc-spec/0.1.23",
"xrpl-rpc-spec/0.1.29",
"zlib/1.3.2",
]
+32 -77
View File
@@ -4,6 +4,18 @@ This document explains how to set one up.
[BUILD.md]: ../../BUILD.md
## Supported development platforms
- **Linux**: the [Nix development shell](./nix.md),
which provides the same toolchain CI uses on any distribution.
- **macOS** 15 (Sequoia) or newer: the [Nix development shell](./nix.md),
or the system [Apple Clang](#macos-apple-clang).
CI runs on macOS 26 (Tahoe), but the build defaults `CMAKE_OSX_DEPLOYMENT_TARGET` to 15.
- **Windows**: best effort, see [Windows](#windows).
For the platforms supported for running `xrpld`,
see [Requirements](../install.md#requirements).
## Tested compiler versions
`xrpld` is built in the **C++23** dialect by default, so your toolchain has to
@@ -35,20 +47,16 @@ compiler building. Treat support for anything outside the table as best-effort.
## Required tools
Besides a compiler, building `xrpld` requires:
| Tool | Minimum version |
| ------------------------------------------- | --------------- |
| [Git](https://git-scm.com/downloads) | any recent |
| [Python](https://www.python.org/downloads/) | 3.11 |
| [Conan](https://conan.io/downloads.html) | 2.17 |
| [CMake](https://cmake.org/download/) | 3.16 |
Besides a compiler, building `xrpld` requires
[Git](https://git-scm.com/downloads),
[Python](https://www.python.org/downloads/),
[Conan](https://conan.io/downloads.html),
and [CMake](https://cmake.org/download/).
Building with `-Drust=ON` additionally requires a Rust toolchain,
see [Rust](#rust).
On Linux and macOS, the [Nix development shell](./nix.md) provides all of them
(see below). On Windows they have to be installed manually.
Building with `-Drust=ON` additionally requires a Rust toolchain, see
[Rust](#rust). A default build does not, so it is not in the table above.
at the versions CI uses, recorded in [`nix/check-tools/`](../../nix/check-tools).
Once they are in place, verify that everything is installed and runnable with:
@@ -58,60 +66,19 @@ Once they are in place, verify that everything is installed and runnable with:
## Linux and macOS
The **recommended way** to get a development environment on Linux and macOS is
the Nix development shell. It provides the exact tooling used in CI — `git`,
`python`, `conan`, `cmake`, `clang-tidy`, `clang-format`, and everything else —
with a single command and without installing anything system-wide:
Use the [Nix development shell](./nix.md):
it provides the compiler and every tool CI uses,
without installing anything system-wide.
```bash
nix --experimental-features 'nix-command flakes' develop
```
### macOS: Apple Clang
On **Linux**, Nix also provides the compiler (GCC); on **macOS**, it provides
Clang. If you instead opt to use your system-wide Apple Clang (via
`nix develop .#apple-clang`), you need to manage its version yourself (see
below).
See [Using the Nix development shell](./nix.md) for installation and usage
details, including how to select a different compiler and why we recommend Nix
over a hand-maintained environment.
### macOS: managing the Apple Clang version
If you use your system-wide Apple Clang on macOS (via `nix develop .#apple-clang`),
the compiler version is whatever your installed Xcode (or Command Line Tools)
provides. The following command should return a version greater than or equal to
the [tested one](#tested-compiler-versions):
```bash
clang --version
```
If you develop other applications using Xcode, you might be consistently
updating to the newest version of Apple Clang, which will likely cause issues
building xrpld. You may want to install and pin a specific version of Xcode:
1. **Download Xcode**
- Visit [Apple Developer Downloads](https://developer.apple.com/download/more/)
- Sign in with your Apple Developer account
- Search for an Xcode version that includes the expected Apple Clang version
- Download the `.xip` file
2. **Install and configure Xcode**
```bash
# Extract the .xip file and rename for version management
# Example: Xcode_16.2.app
# Move to Applications directory
sudo mv Xcode_16.2.app /Applications/
# Set as default toolchain (persistent)
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
# Set as environment variable (temporary)
export DEVELOPER_DIR=/Applications/Xcode_16.2.app/Contents/Developer
```
You can also build with the system Apple Clang,
installing the other [required tools](#required-tools) any way you like
(or getting them from `nix develop .#apple-clang`).
Its version is whatever your installed Xcode (or Command Line Tools) provides,
so check that `clang --version` is at least the [tested one](#tested-compiler-versions).
If several Xcode versions are installed,
select one with `sudo xcode-select -s <path>` or the `DEVELOPER_DIR` environment variable.
## Windows
@@ -123,8 +90,7 @@ manually:
"x64 Native Tools Command Prompt". CI configures CMake with the
`Visual Studio 18 2026` generator.
- [Git for Windows](https://git-scm.com/download/win)
- Python, Conan, and CMake, at the versions listed in
[Required tools](#required-tools).
- recent releases of Python, Conan, and CMake
- a [Rust toolchain](https://rustup.rs) — only needed to build with
`-Drust=ON`, see [Rust](#rust)
@@ -145,16 +111,5 @@ version.
Everything else the Rust build needs on the CMake side comes from Conan along
with the rest of the dependencies, so there is nothing further to install.
## Clang-tidy
`clang-tidy` is required to run static analysis checks locally (see
[CONTRIBUTING.md](../../CONTRIBUTING.md)). It is not required to build the
project. The version this project uses is listed in
[Tested compiler versions](#tested-compiler-versions).
On Linux and macOS, the [Nix development shell](./nix.md) provides that exact
version out of the box — run it via `run-clang-tidy`. No separate installation
is needed.
[cpp23-support]: https://en.cppreference.com/w/cpp/compiler_support/23
[packages.nix]: ../../nix/packages.nix
+2 -9
View File
@@ -9,9 +9,7 @@ This guide explains how to use Nix to set up a reproducible development environm
- **Reproducible environment**: Everyone gets the same versions of tools and compilers
- **Matches CI**: The Linux CI runs in Docker images built from this exact Nix environment, and CI builds some macOS configurations in it as well
- **No system pollution**: Dependencies are isolated and don't affect your system packages
- **Consistent compilers**: The GCC and Clang shells use the same versions as CI
- **Quick setup**: Get started with a single command
- **Works on Linux and macOS**: Consistent experience across platforms
## Install Nix
@@ -27,13 +25,8 @@ From the root of the xrpld repository, enter the default development shell:
nix --experimental-features 'nix-command flakes' develop
```
This will:
- Download and set up all required development tools (CMake, Ninja, Conan, etc.)
- Configure the appropriate compiler for your platform:
- **Linux**: GCC (provided by Nix)
- **macOS**: Clang (provided by Nix)
This downloads and sets up all required development tools (CMake, Ninja, Conan, etc.)
and the compiler for your platform (see [Platform notes](#platform-notes)).
The first time you run this command, it will take a few minutes to download and build the environment. Subsequent runs will be much faster.
### Platform notes
+13
View File
@@ -10,6 +10,19 @@ and DNF on Red Hat-based distributions such as RHEL, AlmaLinux, and Rocky Linux,
where `yum` is a symlink to `dnf`.
To build from source instead, see [BUILD.md](../BUILD.md).
## Requirements
Linux is the only operating system officially supported for running `xrpld`;
macOS and Windows builds are for development only.
Any x86-64 Linux distribution with APT or DNF and glibc 2.31 or newer should work
(see [How libc is handled](../nix/docker/README.md#how-libc-is-handled)).
CI tests the packages on Debian 11–13, Ubuntu 20.04–26.04,
RHEL (UBI) 9–10, AlmaLinux 9–10, and Rocky Linux 9–10.
For the hardware needed to run a node,
see [System Requirements](https://xrpl.org/system-requirements.html).
## Release channels
Packages are published to four channels:
+6 -2
View File
@@ -352,9 +352,9 @@ class Number final
using rep = std::int64_t;
using InternalRep = MantissaRange::rep;
bool negative_{false};
InternalRep mantissa_{0};
int exponent_{std::numeric_limits<int>::lowest()};
bool negative_{false};
public:
// The range for the exponent when normalized
@@ -650,8 +650,12 @@ private:
shiftExponent(int exponentDelta) const;
};
// The number class currently has 3 tail padding bytes. If you are adding new
// members, try to fit them there before expanding the size of the class.
static_assert(sizeof(Number) == 16);
constexpr Number::Number(bool negative, InternalRep mantissa, int exponent, Unchecked) noexcept
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
: mantissa_{mantissa}, exponent_{exponent}, negative_(negative)
{
}
+3
View File
@@ -94,6 +94,7 @@ struct Keys
static constexpr auto kBbtOptions = "bbt_options";
static constexpr auto kBgThreads = "bg_threads";
static constexpr auto kBlockSize = "block_size";
static constexpr auto kBytecodeSizeLimit = "bytecode_size_limit";
static constexpr auto kCacheAge = "cache_age";
static constexpr auto kCacheMb = "cache_mb";
static constexpr auto kCacheSize = "cache_size";
@@ -108,6 +109,8 @@ struct Keys
static constexpr auto kFileSizeMult = "file_size_mult";
static constexpr auto kFilterBits = "filter_bits";
static constexpr auto kFilterFull = "filter_full";
static constexpr auto kGasLimit = "gas_limit";
static constexpr auto kGasPrice = "gas_price";
static constexpr auto kHardSet = "hard_set";
static constexpr auto kHighThreads = "high_threads";
static constexpr auto kHoldTime = "hold_time";
+65 -15
View File
@@ -257,13 +257,42 @@ public:
header_.validated = true;
}
void
/**
* Mark this ledger as accepted and attempt to make it immutable.
*
* The close-time fields are recorded first, since the ledger hash covers
* them.
*
* @param closeTime The consensus-agreed close time.
* @param closeResolution The close time resolution.
* @param correctCloseTime Whether consensus agreed on the close time. If
* false, kSLcfNoConsensusTime is recorded in closeFlags instead.
* @return What setImmutable() returned.
*/
[[nodiscard]] bool
setAccepted(
NetClock::time_point closeTime,
NetClock::duration closeResolution,
bool correctCloseTime);
void
/**
* Mark this ledger as immutable, so it can no longer be modified.
*
* A ledger built or loaded locally cannot have an invalid map, since only
* a map syncing against hashes from outside can be proven impossible (see
* SHAMap::addKnownNode), so a caller on such a path may treat a false
* return as a broken internal invariant. A caller assembling a ledger from
* peer data may not: for it, false is an outcome a peer can produce.
*
* @param rehash Whether to recompute the ledger hash from the header
* fields. The transaction and account hashes are recomputed from the
* maps too, but only the first time.
* @return false if either map is Invalid, leaving the immutable flag as it
* was and the header untouched. A map invalidated partway through can
* still leave the other one immutable, so a false return means the
* ledger must be discarded rather than retried.
*/
[[nodiscard]] bool
setImmutable(bool rehash = true);
bool
@@ -272,23 +301,28 @@ public:
return immutable_;
}
/* Mark this ledger as "should be full".
/**
* @return Whether both maps can still be the maps the header names. See
* SHAMap::isValid().
*/
[[nodiscard]] bool
mapsValid() const
{
return txMap_.isValid() && stateMap_.isValid();
}
"Full" is metadata property of the ledger, it indicates
that the local server wants all the corresponding nodes
in durable storage.
This is marked `const` because it reflects metadata
and not data that is in common with other nodes on the
network.
*/
/**
* Mark this ledger as "should be full", indicating that the local server
* wants all the corresponding nodes in durable storage.
*
* Const because it reflects metadata, not data this ledger shares with
* other nodes on the network.
*/
void
setFull() const
{
txMap_.setFull();
txMap_.setLedgerSeq(header_.seq);
stateMap_.setFull();
stateMap_.setLedgerSeq(header_.seq);
txMap_.setFull(header_.seq);
stateMap_.setFull(header_.seq);
}
void
@@ -418,6 +452,22 @@ private:
static std::pair<std::shared_ptr<STTx const>, std::shared_ptr<STObject const>>
deserializeTxPlusMeta(SHAMapItem const& item);
/**
* Make both maps immutable.
*
* Each map is settled on its own, so neither is left mid-sync because the
* other refused.
*
* @return Whether both maps are immutable.
*/
[[nodiscard]] bool
setMapsImmutable()
{
bool const txImmutable = txMap_.setImmutable();
bool const stateImmutable = stateMap_.setImmutable();
return txImmutable && stateImmutable;
}
bool immutable_;
// A SHAMap containing the transactions associated with this ledger.
+9 -1
View File
@@ -265,6 +265,13 @@ canWithdraw(
[[nodiscard]] TER
canWithdraw(ReadView const& view, STTx const& tx);
/**
* Pays out a withdrawal from a vault or loan broker pseudo-account.
*
* `sourceAmount` leaves `sourceAcct` and `destinationAmount` reaches `dstAcct`.
* The two differ only when the withdrawal carries a transfer fee; the
* difference is the fee and is settled through the issuer.
*/
[[nodiscard]] TER
doWithdraw(
ApplyViewContext ctx,
@@ -272,7 +279,8 @@ doWithdraw(
AccountID const& dstAcct,
AccountID const& sourceAcct,
XRPAmount priorBalance,
STAmount const& amount,
STAmount const& destinationAmount,
STAmount const& sourceAmount,
beast::Journal j);
/**
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STXChainBridge.h>
namespace xrpl {
template <typename ViewT>
class BridgeEntry : public SLEBase<ViewT, ltBRIDGE>
{
public:
using Base = SLEBase<ViewT, ltBRIDGE>;
// Inherit base constructors: adopt an existing SLE, or resolve one from a
// Keylet against the view.
using Base::Base;
explicit BridgeEntry(
STXChainBridge const& bridge,
STXChainBridge::ChainType chainType,
Base::ViewRefType view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::bridge(bridge, chainType), view, j)
{
}
};
using BridgeEntryR = BridgeEntry<ReadView>;
using BridgeEntryW = BridgeEntry<ApplyView>;
} // namespace xrpl
@@ -1,38 +0,0 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
class XChainOwnedClaimIDEntry : public SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>
{
public:
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>;
// Inherit base constructors: adopt an existing SLE, or resolve one from a
// Keylet against the view.
using Base::Base;
explicit XChainOwnedClaimIDEntry(
STXChainBridge const& bridge,
std::uint64_t seq,
Base::ViewRefType view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::xChainClaimID(bridge, seq), view, j)
{
}
};
using XChainOwnedClaimIDEntryR = XChainOwnedClaimIDEntry<ReadView>;
using XChainOwnedClaimIDEntryW = XChainOwnedClaimIDEntry<ApplyView>;
} // namespace xrpl
@@ -1,39 +0,0 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
class XChainOwnedCreateAccountClaimIDEntry
: public SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>
{
public:
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>;
// Inherit base constructors: adopt an existing SLE, or resolve one from a
// Keylet against the view.
using Base::Base;
explicit XChainOwnedCreateAccountClaimIDEntry(
STXChainBridge const& bridge,
std::uint64_t seq,
Base::ViewRefType view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::xChainCreateAccountClaimID(bridge, seq), view, j)
{
}
};
using XChainOwnedCreateAccountClaimIDEntryR = XChainOwnedCreateAccountClaimIDEntry<ReadView>;
using XChainOwnedCreateAccountClaimIDEntryW = XChainOwnedCreateAccountClaimIDEntry<ApplyView>;
} // namespace xrpl
@@ -14,6 +14,7 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Concepts.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Fees.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/Keylet.h>
@@ -27,6 +28,8 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
namespace xrpl {
/**
@@ -560,4 +563,54 @@ escrowUnlockApplyHelper<MPTIssue>(
journal);
}
/**
* Smart escrow kill switches, driven by the voted FeeSettings.
*
* Zeroing `bytecodeSizeLimit` stops new uploads while leaving existing escrows
* finishable without forcing holders to wait for `CancelAfter`.
* Zeroing `gasLimit` stops both.
*/
/** @{ */
inline bool
isBytecodeUploadDisabled(Fees const& fees)
{
return fees.bytecodeSizeLimit == 0 || fees.gasLimit == 0;
}
inline bool
isBytecodeExecutionDisabled(Fees const& fees)
{
return fees.gasLimit == 0;
}
/** @} */
/**
* Owner count an escrow costs.
*
* An escrow costs one increment per started 500 bytes of bytecode, and one
* increment if it carries no bytecode.
*/
template <class T>
int32_t
calculateAdditionalReserve(T const& finishFunction)
{
static auto constexpr kBytecodeReserveIncrement = 500;
if (!finishFunction)
return 1;
auto const size = finishFunction->size();
if (size == 0)
{
// LCOV_EXCL_START
// Empty Bytecode is refused at create, so no escrow carries it. Ceiling
// division would answer 0, which would subtract less than the create added.
UNREACHABLE("xrpl::calculateAdditionalReserve : empty Bytecode");
return 1;
// LCOV_EXCL_STOP
}
return static_cast<int32_t>((size + kBytecodeReserveIncrement - 1) / kBytecodeReserveIncrement);
}
} // namespace xrpl
+124 -38
View File
@@ -7,10 +7,10 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/VaultHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
@@ -31,9 +31,9 @@ namespace xrpl {
/**
* Broker cover preclaim precision guard (fixCleanup3_2_0).
*
* Prevents a "silent sub-ULP no-op" where a deposit, withdrawal, or clawback
* amount is so small that it rounds to zero at `sfCoverAvailable`'s scale.
* Without this guard, both the pseudo trust-line and `sfCoverAvailable` would
* Prevents a silent sub-ULP no-op where a deposit, withdrawal, or clawback
* amount is so small that it rounds to zero at sfCoverAvailable's scale.
* Without this guard, both the pseudo trust-line and sfCoverAvailable would
* identically absorb the rounded zero, resulting in a successful transaction
* (tesSUCCESS) where no funds actually moved.
*
@@ -44,8 +44,8 @@ namespace xrpl {
* @param j Journal for logging.
* @param logPrefix Transactor name for log diagnostics.
*
* @return `tecPRECISION_LOSS` if the request rounds to zero at cover scale.
* `tesSUCCESS` if the amendment is disabled or the request is safely supra-ULP.
* @return tecPRECISION_LOSS if the request rounds to zero at cover scale.
* tesSUCCESS if the amendment is disabled or the request is safely supra-ULP.
*/
[[nodiscard]] TER
canApplyToBrokerCover(
@@ -56,6 +56,82 @@ canApplyToBrokerCover(
beast::Journal j,
std::string_view logPrefix);
namespace detail {
/**
* Return a LoanBroker's posterior live cover exponent after applying an
* unrounded delta.
*/
[[nodiscard]] int
getPosteriorBrokerCoverScale(SLE::ConstRef vault, SLE::ConstRef broker, Number const& delta);
/**
* Round a cover outflow delta (cover withdraw, cover clawback, the
* broker-side decrease of a default's cover credit) at the LoanBroker's
* posterior live exponent. Safe to round the delta directly for an outflow:
* amount on the posterior grid of CoverAvailable always leaves CoverAvailable
* minus amount representable, since the posterior grid is exactly the grid
* CoverAvailable itself will canonicalize to after the subtraction.
*/
[[nodiscard]] STAmount
roundToPosteriorBrokerCoverScale(
SLE::ConstRef vault,
SLE::ConstRef broker,
STAmount const& delta,
Number::RoundingMode roundingMode);
} // namespace detail
/**
* Round a cover debit (cover withdraw, cover clawback, the broker-side
* decrease of a default's cover credit): amount is the non-negative
* magnitude leaving CoverAvailable. Negates so the posterior is computed for
* CoverAvailable minus amount, then negates the result back to a
* non-negative magnitude, per roundToPosteriorBrokerCoverScale's outflow
* rationale above. Magnitude in, magnitude out -- unlike the vault-side
* outflow dispatcher (clampVaultOutflow), which takes and returns a negative
* delta; callers on each side already hold the value in that side's native
* form.
*/
[[nodiscard]] STAmount
debitToPosteriorBrokerCoverScale(
SLE::ConstRef vault,
SLE::ConstRef broker,
STAmount const& amount,
Number::RoundingMode roundingMode);
/**
* Round a cover inflow (cover deposit, LoanPay's fee redirect into cover)
* into CoverAvailable by flooring the sum rather than the delta:
* credit = floor16(CoverAvailable + raw) - CoverAvailable, never finer
* than the Vault's base exponent (-Scale). See
* creditToPosteriorAvailableScale's doc for why the sum, not the delta,
* must be floored; the same reasoning applies to CoverAvailable.
*/
[[nodiscard]] STAmount
creditToPosteriorBrokerCoverScale(
SLE::ConstRef vault,
SLE::ConstRef broker,
Number const& raw,
Number::RoundingMode roundingMode);
/**
* Check whether amount is an admissible optional cover inflow.
*
* Legacy and CashBasis Vaults always succeed. A LoanBroker attached to a
* FixedPrecision Vault must remain at the Vault's base scale after applying
* the rounded amount, and its posterior CoverAvailable must stay within the
* Open zone.
*
* LoanPay's fee redirect into cover is a mandatory inflow and does not call
* this check, so it can push CoverAvailable past the Open zone limit this
* check enforces. Once that happens, every subsequent LoanBrokerCoverDeposit
* returns tecLIMIT_EXCEEDED until the owner withdraws cover back under the
* limit; LoanBrokerCoverWithdraw and LoanBrokerCoverClawback are unaffected.
*/
[[nodiscard]] TER
checkOptionalBrokerCoverInflow(SLE::ConstRef vault, SLE::ConstRef broker, STAmount const& amount);
// Lending protocol has dependencies, so capture them here.
bool
checkLendingProtocolDependencies(Rules const& rules, STTx const& tx);
@@ -64,13 +140,13 @@ checkLendingProtocolDependencies(Rules const& rules, STTx const& tx);
* The accounts and asset that LoanManage::defaultLoan's fixCleanup3_4_0
* freeze/lock exemption applies to.
*
* `defaultLoan` moves funds from the LoanBroker pseudo-account to the Vault
* pseudo-account via `accountSend`. Since neither is the vault asset's
* defaultLoan moves funds from the LoanBroker pseudo-account to the Vault
* pseudo-account via accountSend. Since neither is the vault asset's
* issuer, this is a third-party transfer that transits through the issuer in
* two hops (broker -> issuer, issuer -> vault; see
* `directSendNoLimitIOU`/`directSendNoLimitMPT`), so the exemption must cover
* directSendNoLimitIOU/directSendNoLimitMPT), so the exemption must cover
* both the issuer/broker and issuer/vault pairs, not a direct broker/vault
* pair. `asset` scopes it further to the vault's own currency/MPT issuance,
* pair. asset scopes it further to the vault's own currency/MPT issuance,
* so an unrelated one the same accounts happen to hold is still protected.
*/
struct LoanDefaultFreezeExemptAccounts
@@ -88,10 +164,10 @@ struct LoanDefaultFreezeExemptAccounts
* @param view Ledger view used to resolve the Loan -> LoanBroker -> Vault
* chain.
* @param tx The transaction under invariant review.
* @return The exempt accounts and asset if `tx` is a `ttLOAN_MANAGE`
* transaction with the `tfLoanDefault` flag set, `fixCleanup3_4_0` is
* @return The exempt accounts and asset if tx is a ttLOAN_MANAGE
* transaction with the tfLoanDefault flag set, fixCleanup3_4_0 is
* enabled, and the loan/broker/vault objects it references can all be
* resolved; `std::nullopt` otherwise.
* resolved; std::nullopt otherwise.
*/
[[nodiscard]] std::optional<LoanDefaultFreezeExemptAccounts>
getLoanDefaultFreezeExemptAccounts(ReadView const& view, STTx const& tx);
@@ -254,28 +330,38 @@ adjustImpreciseNumber(
value = 0;
}
inline int
getAssetsTotalScale(SLE::ConstRef vaultSle)
{
if (!vaultSle)
return Number::kMinExponent - 1; // LCOV_EXCL_LINE
return scale(vaultSle->at(sfAssetsTotal), vaultSle->at(sfAsset));
}
/**
* Apply a signed delta to a LoanBroker's DebtTotal: on FixedPrecision Vaults,
* add the exact delta (DebtTotal moves by exactly the same amount as the
* Vault's AssetsDeployed, never re-rounded); on Legacy/CashBasis Vaults,
* round through adjustImpreciseNumber at vaultScale. Shared by LoanSet
* (origination) and LoanPay (payment, where delta is negative).
*
* @param brokerSle The LoanBroker whose DebtTotal is adjusted.
* @param vaultSle The LoanBroker's Vault.
* @param delta The signed change to DebtTotal.
* @param vaultScale The Vault scale the caller captured before changing
* AssetsTotal. Legacy/CashBasis round DebtTotal at that scale, not at
* the scale after this transaction's AssetsTotal change. Unused on
* FixedPrecision Vaults.
*/
void
adjustBrokerDebtTotal(
SLE::Ref brokerSle,
SLE::ConstRef vaultSle,
Number const& delta,
int vaultScale);
// Compute the minimum required broker cover, rounded consistently.
// DebtTotal is a broker-level aggregate maintained at vault scale, so the
// rounding must also use vault scale — never an individual loan's scale.
inline Number
minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::ConstRef vaultSle)
{
XRPL_ASSERT(
vaultSle && vaultSle->getType() == ltVAULT, "xrpl::minimumBrokerCover : valid Vault sle");
NumberRoundModeGuard const mg(Number::RoundingMode::Upward);
return roundToAsset(
vaultSle->at(sfAsset),
tenthBipsOfValue(debtTotal, coverRateMinimum),
getAssetsTotalScale(vaultSle));
}
/**
* Minimum required broker cover, rounded up.
*
* DebtTotal is a broker-level aggregate, never rounded at an individual
* loan's scale. Legacy and CashBasis Vaults round at the live AssetsTotal
* exponent. FixedPrecision Vaults round at the Vault's base exponent
* (-Scale, or 0 for integral assets).
*/
Number
minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::ConstRef vaultSle);
TER
checkLoanGuards(
@@ -379,10 +465,10 @@ loanPaymentDeltas(LoanPaymentParts const& parts);
} // namespace cash_basis
// Public dispatchers: pick cash_basis:: if featureLendingProtocolV1_1 is
// enabled AND the Vault's LEVersion (VaultHelpers::getVaultVersion) is
// VaultVersion::CashBasis, else instant_recognition::. These are the only entry points
// transactors call.
// Public dispatchers: pick cash_basis:: if the Vault's LEVersion
// (VaultHelpers::getVaultVersion) is CashBasis or later (CashBasis or
// FixedPrecision), else instant_recognition::. These are the only entry
// points transactors call.
AccountingDeltas
loanOriginationDeltas(
SLE::ConstRef vaultSle,
@@ -142,11 +142,11 @@ deleteProposal(ApplyView& view, SLE::pointer const& sleProposal, beast::Journal
* off-ledger signatures it carries, and any change to a non-signature field
* results in a false-return value.
*
* Signature (PaymentChannelClaim and the XChain attestation transactions)
* is deliberately not compared: it is outside the account's own TxnSignature
* even for an ordinary submission, so a proposal cannot bind it more tightly
* than the signatures it collects do, and those transactors still validate a
* substituted value exactly as they would for an ordinary submission.
* Signature (PaymentChannelClaim) is deliberately not compared: it is
* outside the account's own TxnSignature even for an ordinary submission, so
* a proposal cannot bind it more tightly than the signatures it collects do,
* and that transactor still validates a substituted value exactly as it would
* for an ordinary submission.
* MasterSignature appears in no transaction format.
*
* @param proposedTx The proposal's ProposedTransaction field.
@@ -316,6 +316,20 @@ transferRate(ReadView const& view, Asset const& asset);
[[nodiscard]] Rate
transferRate(ReadView const& view, STAmount const& amount);
/**
* Returns the amount a sender must part with for `amount` to reach the
* receiver once the issuer takes the transfer fee. Rounds up.
*/
[[nodiscard]] STAmount
addTransferFee(STAmount const& amount, Rate const& rate);
/**
* Returns the amount a receiver gets when `amount` leaves the sender and the
* issuer takes the transfer fee. Rounds down.
*/
[[nodiscard]] STAmount
subtractTransferFee(STAmount const& amount, Rate const& rate);
//------------------------------------------------------------------------------
//
// Holding operations (Asset-based dispatchers)
+55 -35
View File
@@ -14,6 +14,7 @@
#include <cstdint>
#include <expected>
#include <optional>
#include <string_view>
namespace xrpl {
@@ -80,6 +81,21 @@ struct VaultBalanceChange
[[nodiscard]] TER
adjustVaultBalances(SLE::Ref vault, VaultBalanceChange const& change, beast::Journal j);
/**
* @brief Apply a signed cash movement to a Vault of any version. FixedPrecision
* Vaults go through adjustVaultBalances; Legacy and CashBasis Vaults add the
* amount to both AssetsTotal and AssetsAvailable.
*
* @param vault The vault SLE.
* @param delta Signed change to AssetsAvailable; the exact amount moved on the
* Vault pseudo-account.
* @param j Journal passed through to adjustVaultBalances.
*
* @return tesSUCCESS, or the adjustVaultBalances error for FixedPrecision Vaults.
*/
[[nodiscard]] TER
adjustVaultCash(SLE::Ref vault, STAmount const& delta, beast::Journal j);
/**
* Return the Vault's current live exponent.
*
@@ -148,18 +164,20 @@ posteriorAssetScale(
/**
* Shared core of creditToPosteriorAvailableScale and
* creditToPosteriorBrokerCoverScale: floors reference + raw at scale and
* returns the difference from reference, so the delta applied is exactly
* what moves reference onto the floored sum. See
* creditToPosteriorAvailableScale's doc for why the sum, not raw alone,
* must be floored. raw is the exact amount; it is not rounded on its own
* before being added to reference.
* creditToPosteriorBrokerCoverScale: floors reference + raw at the sum's
* own live scale (never finer than baseScale) and returns the difference
* from reference, so the delta applied is exactly what moves reference onto
* the floored sum. See creditToPosteriorAvailableScale's doc for why the
* sum, not raw alone, must be floored. raw is the exact amount; it is not
* rounded on its own before being added to reference. The sum and its scale
* are computed once, in roundingMode, so a Downward credit is always
* between zero and raw.
*/
[[nodiscard]] STAmount
creditToPosteriorScale(
Asset const& asset,
Number const& reference,
int atScale,
int baseScale,
Number const& raw,
Number::RoundingMode roundingMode);
@@ -333,6 +351,18 @@ vaultBaseScale(Asset const& asset, std::uint8_t scale);
[[nodiscard]] bool
isOnVaultBaseGrid(Asset const& asset, Number const& value, int baseScale);
/**
* Checks that a requested AssetsMaximum is exactly representable on the
* Vault's base grid, otherwise associateAsset would silently round the cap
* the owner asked for. Used by VaultSet::preclaim on an existing Vault.
*
* @param vault The vault ledger entry.
* @param amount The requested AssetsMaximum.
* @return tesSUCCESS, or tecPRECISION_LOSS if amount is not representable.
*/
[[nodiscard]] TER
checkAssetsMaximum(SLE::ConstRef vault, Number const& amount);
/**
* Controls whether to truncate shares instead of rounding.
*/
@@ -360,35 +390,24 @@ enum class WaiveUnrealizedLoss : bool { No = false, Yes = true };
assetsTotalForWithdrawal(SLE::ConstRef vault, WaiveUnrealizedLoss waive);
/**
* Returns true if debiting amount from total (the current value of a
* vault's sfAssetsTotal or sfAssetsAvailable) would canonicalize to the
* same STAmount value. This happens when amount is non-zero but too small
* to change the stored total at STAmount's precision. Shares would still
* move, so the ValidVault invariant would fail after apply; callers use
* this to reject the transaction upfront instead.
*
* @param asset The vault's underlying asset, used to canonicalize both
* sides the same way the ledger will when the field is stored.
* @param total The field's current value.
* @param amount The amount to debit. Zero always returns false; that case
* is rejected separately.
*/
[[nodiscard]] bool
debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount);
/**
* Returns the reference balance a cash outflow's dust check
* (debitIsNonZeroDust) should compare against: FixedPrecision Vaults use
* AssetsAvailable, the balance the cash actually leaves; Legacy/CashBasis
* Vaults use the given AssetsTotal. Shared by VaultWithdraw and
* VaultClawback.
* Reject a cash outflow that is dust (see debitIsNonZeroDust) or whose check
* overflows Number, which is easy to hit with a large Scale.
*
* @param vault The vault SLE.
* @param assetsTotal The vault's current AssetsTotal, as already read by the
* caller.
* @param amount The amount to debit.
* @param j Journal for the debug log.
* @param name Transaction name used as the log prefix.
*
* @return tesSUCCESS; tecPRECISION_LOSS if amount is dust; tecPATH_DRY if the
* check overflows. Overflow means the transaction cannot apply, but
* ledger state is still consistent.
*/
[[nodiscard]] Number
vaultDebitDustReference(SLE::ConstRef vault, Number const& assetsTotal);
[[nodiscard]] TER
checkDebitNotDust(
SLE::ConstRef vault,
Number const& amount,
beast::Journal j,
std::string_view name);
/**
* From the perspective of a vault, return the number of shares to demand from
@@ -483,8 +502,9 @@ decodeVaultVersion(std::optional<std::uint8_t> leVersion);
*
* @param rules The active ledger rules.
*
* @return VaultVersion::FixedPrecision once featureLendingProtocolV1_2,
* fixCleanup3_2_0 and fixCleanup3_4_0 are all enabled; VaultVersion::CashBasis
* @return VaultVersion::FixedPrecision once featureLendingProtocolV1_1,
* featureLendingProtocolV1_2 and fixCleanup3_4_0 are all enabled;
* VaultVersion::CashBasis
* once featureLendingProtocolV1_1 is enabled; VaultVersion::Legacy otherwise.
*/
[[nodiscard]] VaultVersion
+10 -5
View File
@@ -348,16 +348,19 @@ isAuditorMirrorCurrent(SLE const& issuance, SLE const& mptoken);
areMirrorsCurrent(SLE const& issuance, SLE const& mptoken);
/**
* @brief Set the holder's issuer mirror epoch to match the issuance's current issuer key epoch.
* @brief Set the holder's issuer mirror epoch, and the issuer mirror key, to match the issuance's
* current key.
*
* Call this after writing the issuer mirror ciphertext under the issuance's
* currently registered issuer key, so that the mirror reads as current afterwards.
* Also records sfIssuerMirrorEncryptionKey as the issuance's current issuer key
* (both fields stay absent at epoch 0, where the original issuer key applies).
*
* @param issuance The MPTokenIssuance ledger object.
* @param mptoken The holder's MPToken ledger entry to update.
*/
void
setIssuerMirrorEpoch(SLE const& issuance, SLE& mptoken);
setIssuerMirrorCurrent(SLE const& issuance, SLE& mptoken);
/**
* @brief Set the holder's auditor mirror epoch to match the issuance's current auditor key epoch.
@@ -372,16 +375,18 @@ void
setAuditorMirrorEpoch(SLE const& issuance, SLE& mptoken);
/**
* @brief Set the holder's MPToken mirror epochs to match the issuance's current key epochs.
* @brief Set the holder's MPToken mirror epochs, and the issuer mirror key, to match the issuance's
* current keys.
*
* Call this after writing mirror ciphertexts under the issuance's currently
* registered keys, so that the mirrors read as current afterwards.
* registered keys, so that the mirrors read as current afterwards. Also records
* sfIssuerMirrorEncryptionKey via setIssuerMirrorCurrent.
*
* @param issuance The MPTokenIssuance ledger object.
* @param mptoken The holder's MPToken ledger entry to update.
*/
void
setMirrorEpochs(SLE const& issuance, SLE& mptoken);
setMirrorsCurrent(SLE const& issuance, SLE& mptoken);
/**
* @brief Verifies revealed amount encryptions for all recipients.
+54 -1
View File
@@ -10,6 +10,25 @@ namespace xrpl {
// This was the reference fee units used in the old fee calculation.
inline constexpr std::uint32_t kFeeUnitsDeprecated = 10;
// Number of micro-drops in one drop.
inline constexpr std::uint32_t microDropsPerDrop{1'000'000};
/**
* Hard protocol bounds on the Feature Extension fee settings. A voted value
* can never fall outside these, so `preflight`, which has no view, may bound
* against them. Gas and bytecode limits are capped; gas price has a floor
* (zero would make WASM execution effectively free).
*/
inline constexpr std::uint32_t kMaxGasLimit{2'000'000};
inline constexpr std::uint32_t kMaxBytecodeSizeLimit{200'000};
inline constexpr std::uint32_t kMinGasPrice{1};
// The following default values of fee settings will seed into FeeSettings
// and write to the ledger on featureSmartEscrow activation.
inline constexpr std::uint32_t kDefaultGasLimit{1'000'000};
inline constexpr std::uint32_t kDefaultBytecodeSizeLimit{100'000};
inline constexpr std::uint32_t kDefaultGasPrice{1'000'000};
/**
* Reflects the fee settings for a particular ledger.
*
@@ -33,13 +52,47 @@ struct Fees
*/
XRPAmount increment{0};
/**
* @brief Gas limit for Feature Extensions (instructions).
*
* This and the one below default to the protocol values rather than 0,
* because an explicit 0 is the voted kill switch.
*/
std::uint32_t gasLimit{kDefaultGasLimit};
/**
* @brief Bytecode size limit for Feature Extensions (bytes).
*/
std::uint32_t bytecodeSizeLimit{kDefaultBytecodeSizeLimit};
/**
* @brief Price of WASM gas (micro-drops).
*/
std::uint32_t gasPrice{kDefaultGasPrice};
explicit Fees() = default;
Fees(Fees const&) = default;
Fees&
operator=(Fees const&) = default;
Fees(XRPAmount base, XRPAmount reserve, XRPAmount increment)
: base(base), reserve(reserve), increment(increment)
: base{base}, reserve{reserve}, increment{increment}
{
}
Fees(
XRPAmount base,
XRPAmount reserve,
XRPAmount increment,
std::uint32_t gasLimit,
std::uint32_t bytecodeSizeLimit,
std::uint32_t gasPrice)
: base{base}
, reserve{reserve}
, increment{increment}
, gasLimit{gasLimit}
, bytecodeSizeLimit{bytecodeSizeLimit}
, gasPrice{gasPrice}
{
}
-12
View File
@@ -11,7 +11,6 @@
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/protocol/UintTypes.h>
@@ -321,17 +320,6 @@ amm(UInt256 const& amm) noexcept;
Keylet
delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept;
Keylet
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType);
// `seq` is stored as `sfXChainClaimID` in the object
Keylet
xChainClaimID(STXChainBridge const& bridge, std::uint64_t const seq);
// `seq` is stored as `sfXChainAccountCreateCount` in the object
Keylet
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t const seq);
Keylet
did(AccountID const& account) noexcept;
+26
View File
@@ -103,6 +103,32 @@ enum LedgerEntryType : std::uint16_t {
*/
ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] =
0x0067,
/**
* A deprecated type, removed with the XChainBridge amendment.
*
* @deprecated **This object type is not supported and should not be used.**
* The XChainBridge amendment was never enabled on Mainnet.
*/
ltBRIDGE [[deprecated("This object type is not supported and should not be used.")]] = 0x0069,
/**
* A deprecated type, removed with the XChainBridge amendment.
*
* @deprecated **This object type is not supported and should not be used.**
* The XChainBridge amendment was never enabled on Mainnet.
*/
ltXCHAIN_OWNED_CLAIM_ID
[[deprecated("This object type is not supported and should not be used.")]] = 0x0071,
/**
* A deprecated type, removed with the XChainBridge amendment.
*
* @deprecated **This object type is not supported and should not be used.**
* The XChainBridge amendment was never enabled on Mainnet.
*/
ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID
[[deprecated("This object type is not supported and should not be used.")]] = 0x0074,
};
/**
+7 -2
View File
@@ -317,12 +317,12 @@ constexpr std::uint8_t kVaultDefaultIouScale = 6;
* Chosen so 1 IOU can always convert to shares:
* 10^19 > maxMPTokenAmount (2^64-1) > 10^18.
*/
constexpr std::uint8_t kVaultMaximumIouScale = 18;
constexpr std::uint8_t kLegacyVaultMaximumIouScale = 18;
/**
* Maximum Scale for a Vault created under featureLendingProtocolV1_2.
*/
constexpr std::uint8_t kVaultMaximumFixedPrecisionIouScale = 10;
constexpr std::uint8_t kFixedPrecisionVaultMaximumIouScale = 10;
/**
* Vault ledger-entry schema versions, persisted as sfLEVersion.
@@ -398,6 +398,11 @@ constexpr std::uint32_t kMaxInvestmentPeriod = std::chrono::seconds{std::chrono:
*/
constexpr std::uint8_t kMaxAssetCheckDepth = 5;
/**
* Maximum length of a Data field in Escrow object that can be updated by WASM code.
*/
constexpr std::size_t kMaxWasmDataLength = 1 * 1024; // 1KB
/**
* A ledger index.
*/
+1 -3
View File
@@ -31,7 +31,6 @@ class STBitString;
template <class>
class STInteger;
class STNumber;
class STXChainBridge;
class STVector256;
class STCurrency;
@@ -71,7 +70,7 @@ class STCurrency;
STYPE(STI_UINT384, 22) \
STYPE(STI_UINT512, 23) \
STYPE(STI_ISSUE, 24) \
STYPE(STI_XCHAIN_BRIDGE, 25) \
/* 25 is reserved */ \
STYPE(STI_CURRENCY, 26) \
\
/* high-level types */ \
@@ -361,7 +360,6 @@ using SF_CURRENCY = TypedField<STCurrency>;
using SF_NUMBER = TypedField<STNumber>;
using SF_VL = TypedField<STBlob>;
using SF_VECTOR256 = TypedField<STVector256>;
using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
// NOLINTEND(readability-identifier-naming)
//------------------------------------------------------------------------------
+10
View File
@@ -696,6 +696,16 @@ divRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundU
STAmount
divRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
/**
* Returns amt * num / den, rounded in the requested direction.
*
* Dispatches to the XRPAmount, IOUAmount or MPTAmount overload. XRP and MPT
* results are exact; an IOU result is rounded to the 16-digit mantissa in the
* requested direction. Only Upward and Downward are supported.
*/
[[nodiscard]] STAmount
mulRatio(STAmount const& amt, std::uint32_t num, std::uint32_t den, Number::RoundingMode rounding);
// Someone is offering X for Y, what is the rate?
// Rate: smaller is better, the taker wants the most out: in/out
// VFALCO TODO Return a Quality object
+1
View File
@@ -12,6 +12,7 @@
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/SOTemplate.h>
#include <xrpl/protocol/STAccount.h> // IWYU pragma: keep
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STBitString.h>
-224
View File
@@ -1,224 +0,0 @@
#pragma once
#include <xrpl/basics/CountedObject.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STIssue.h>
#include <xrpl/protocol/Serializer.h>
#include <cstddef>
#include <memory>
#include <string>
#include <tuple>
namespace xrpl {
class Serializer;
class STObject;
class STXChainBridge final : public STBase, public CountedObject<STXChainBridge>
{
STAccount lockingChainDoor_{sfLockingChainDoor};
STIssue lockingChainIssue_{sfLockingChainIssue};
STAccount issuingChainDoor_{sfIssuingChainDoor};
STIssue issuingChainIssue_{sfIssuingChainIssue};
public:
using value_type = STXChainBridge;
enum class ChainType { Locking, Issuing };
static ChainType
otherChain(ChainType ct);
static ChainType
srcChain(bool wasLockingChainSend);
static ChainType
dstChain(bool wasLockingChainSend);
STXChainBridge();
explicit STXChainBridge(SField const& name);
STXChainBridge(STXChainBridge const& rhs) = default;
STXChainBridge(STObject const& o);
STXChainBridge(
AccountID const& srcChainDoor,
Issue const& srcChainIssue,
AccountID const& dstChainDoor,
Issue const& dstChainIssue);
explicit STXChainBridge(json::Value const& v);
explicit STXChainBridge(SField const& name, json::Value const& v);
explicit STXChainBridge(SerialIter& sit, SField const& name);
STXChainBridge&
operator=(STXChainBridge const& rhs) = default;
[[nodiscard]] std::string
getText() const override;
[[nodiscard]] STObject
toSTObject() const;
[[nodiscard]] AccountID const&
lockingChainDoor() const;
[[nodiscard]] Issue const&
lockingChainIssue() const;
[[nodiscard]] AccountID const&
issuingChainDoor() const;
[[nodiscard]] Issue const&
issuingChainIssue() const;
[[nodiscard]] AccountID const&
door(ChainType ct) const;
[[nodiscard]] Issue const&
issue(ChainType ct) const;
[[nodiscard]] SerializedTypeID
getSType() const override;
[[nodiscard]] json::Value getJson(JsonOptions) const override;
void
add(Serializer& s) const override;
[[nodiscard]] bool
isEquivalent(STBase const& t) const override;
[[nodiscard]] bool
isDefault() const override;
[[nodiscard]] value_type const&
value() const noexcept;
private:
static std::unique_ptr<STXChainBridge>
construct(SerialIter&, SField const& name);
STBase*
copy(std::size_t n, void* buf) const override;
STBase*
move(std::size_t n, void* buf) override;
friend bool
operator==(STXChainBridge const& lhs, STXChainBridge const& rhs);
friend bool
operator<(STXChainBridge const& lhs, STXChainBridge const& rhs);
};
inline bool
operator==(STXChainBridge const& lhs, STXChainBridge const& rhs)
{
return std::tie(
lhs.lockingChainDoor_,
lhs.lockingChainIssue_,
lhs.issuingChainDoor_,
lhs.issuingChainIssue_) ==
std::tie(
rhs.lockingChainDoor_,
rhs.lockingChainIssue_,
rhs.issuingChainDoor_,
rhs.issuingChainIssue_);
}
inline bool
operator<(STXChainBridge const& lhs, STXChainBridge const& rhs)
{
return std::tie(
lhs.lockingChainDoor_,
lhs.lockingChainIssue_,
lhs.issuingChainDoor_,
lhs.issuingChainIssue_) <
std::tie(
rhs.lockingChainDoor_,
rhs.lockingChainIssue_,
rhs.issuingChainDoor_,
rhs.issuingChainIssue_);
}
inline AccountID const&
STXChainBridge::lockingChainDoor() const
{
return lockingChainDoor_.value();
};
inline Issue const&
STXChainBridge::lockingChainIssue() const
{
return lockingChainIssue_.value().get<Issue>();
};
inline AccountID const&
STXChainBridge::issuingChainDoor() const
{
return issuingChainDoor_.value();
};
inline Issue const&
STXChainBridge::issuingChainIssue() const
{
return issuingChainIssue_.value().get<Issue>();
};
inline STXChainBridge::value_type const&
STXChainBridge::value() const noexcept
{
return *this;
}
inline AccountID const&
STXChainBridge::door(ChainType ct) const
{
if (ct == ChainType::Locking)
return lockingChainDoor();
return issuingChainDoor();
}
inline Issue const&
STXChainBridge::issue(ChainType ct) const
{
if (ct == ChainType::Locking)
return lockingChainIssue();
return issuingChainIssue();
}
inline STXChainBridge::ChainType
STXChainBridge::otherChain(ChainType ct)
{
if (ct == ChainType::Locking)
return ChainType::Issuing;
return ChainType::Locking;
}
inline STXChainBridge::ChainType
STXChainBridge::srcChain(bool wasLockingChainSend)
{
if (wasLockingChainSend)
return ChainType::Locking;
return ChainType::Issuing;
}
inline STXChainBridge::ChainType
STXChainBridge::dstChain(bool wasLockingChainSend)
{
if (wasLockingChainSend)
return ChainType::Issuing;
return ChainType::Locking;
}
} // namespace xrpl
+2 -1
View File
@@ -133,7 +133,6 @@ enum TEMcodes : TERUnderlyingType {
temBAD_MPT,
temBAD_CIPHERTEXT,
temINVALID_BYTECODE,
temTEMP_DISABLED,
};
//------------------------------------------------------------------------------
@@ -184,6 +183,8 @@ enum TEFcodes : TERUnderlyingType {
tefBAD_PATH_COUNT,
tefNO_BYTECODE,
tefBYTECODE_NOT_INCLUDED,
tefTEMP_DISABLED,
tefEXCEEDS_LIMIT,
};
//------------------------------------------------------------------------------
-4
View File
@@ -189,10 +189,6 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfClawTwoAssets, 0x00000001), \
MASK_ADJ(0)) \
\
TRANSACTION(XChainModifyBridge, \
TF_FLAG(tfClearAccountCreateAmount, 0x00010000), \
MASK_ADJ(0)) \
\
TRANSACTION(VaultCreate, \
TF_FLAG(tfVaultPrivate, lsfVaultPrivate) \
TF_FLAG(tfVaultShareNonTransferable, 0x00020000), \
+48
View File
@@ -73,6 +73,54 @@ enum TxType : std::uint16_t
* This transaction type installs a hook.
*/
TtHookSet [[maybe_unused]] = 22,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainCreateClaimID [[deprecated("This transaction type is not supported and should not be used.")]] = 41,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainCommit [[deprecated("This transaction type is not supported and should not be used.")]] = 42,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainClaim [[deprecated("This transaction type is not supported and should not be used.")]] = 43,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainAccountCreateCommit [[deprecated("This transaction type is not supported and should not be used.")]] = 44,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainAddClaimAttestation [[deprecated("This transaction type is not supported and should not be used.")]] = 45,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainAddAccountCreateAttestation [[deprecated("This transaction type is not supported and should not be used.")]] = 46,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainModifyBridge [[deprecated("This transaction type is not supported and should not be used.")]] = 47,
/**
* This transaction type was removed with the XChainBridge amendment, which was never enabled
* on Mainnet. The value is reserved for historical purposes.
*/
TtXChainCreateBridge [[deprecated("This transaction type is not supported and should not be used.")]] = 48,
};
// clang-format on
-471
View File
@@ -1,471 +0,0 @@
#pragma once
#include <xrpl/basics/Buffer.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/SecretKey.h>
#include <boost/container/flat_set.hpp>
#include <boost/container/vector.hpp>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
namespace xrpl {
namespace attestations {
struct AttestationBase
{
// Account associated with the public key
AccountID attestationSignerAccount;
// Public key from the witness server attesting to the event
PublicKey publicKey;
// Signature from the witness server attesting to the event
Buffer signature;
// Account on the sending chain that triggered the event (sent the
// transaction)
AccountID sendingAccount;
// Amount transferred on the sending chain
STAmount sendingAmount;
// Account on the destination chain that collects a share of the attestation
// reward
AccountID rewardAccount;
// Amount was transferred on the locking chain
bool wasLockingChainSend;
explicit AttestationBase(
AccountID attestationSignerAccount,
PublicKey const& publicKey,
Buffer signature,
AccountID const& sendingAccount,
STAmount sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend);
AttestationBase(AttestationBase const&) = default;
virtual ~AttestationBase() = default;
AttestationBase&
operator=(AttestationBase const&) = default;
// verify that the signature attests to the data.
[[nodiscard]] bool
verify(STXChainBridge const& bridge) const;
protected:
explicit AttestationBase(STObject const& o);
explicit AttestationBase(json::Value const& v);
[[nodiscard]] static bool
equalHelper(AttestationBase const& lhs, AttestationBase const& rhs);
[[nodiscard]] static bool
sameEventHelper(AttestationBase const& lhs, AttestationBase const& rhs);
void
addHelper(STObject& o) const;
private:
[[nodiscard]] virtual std::vector<std::uint8_t>
message(STXChainBridge const& bridge) const = 0;
};
// Attest to a regular cross-chain transfer
struct AttestationClaim : AttestationBase
{
std::uint64_t claimID;
std::optional<AccountID> dst;
explicit AttestationClaim(
AccountID attestationSignerAccount,
PublicKey const& publicKey,
Buffer signature,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t claimId,
std::optional<AccountID> const& dst);
explicit AttestationClaim(
STXChainBridge const& bridge,
AccountID attestationSignerAccount,
PublicKey const& publicKey,
SecretKey const& secretKey,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t claimId,
std::optional<AccountID> const& dst);
explicit AttestationClaim(STObject const& o);
explicit AttestationClaim(json::Value const& v);
[[nodiscard]] STObject
toSTObject() const;
// return true if the two attestations attest to the same thing
[[nodiscard]] bool
sameEvent(AttestationClaim const& rhs) const;
[[nodiscard]] static std::vector<std::uint8_t>
message(
STXChainBridge const& bridge,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t claimID,
std::optional<AccountID> const& dst);
[[nodiscard]] bool
validAmounts() const;
private:
[[nodiscard]] std::vector<std::uint8_t>
message(STXChainBridge const& bridge) const override;
friend bool
operator==(AttestationClaim const& lhs, AttestationClaim const& rhs);
};
struct CmpByClaimID
{
bool
operator()(AttestationClaim const& lhs, AttestationClaim const& rhs) const
{
return lhs.claimID < rhs.claimID;
}
};
// Attest to a cross-chain transfer that creates an account
struct AttestationCreateAccount : AttestationBase
{
// createCount on the sending chain. This is the value of the `CreateCount`
// field of the bridge on the sending chain when the transaction was
// executed.
std::uint64_t createCount;
// Account to create on the destination chain
AccountID toCreate;
// Total amount of the reward pool
STAmount rewardAmount;
explicit AttestationCreateAccount(STObject const& o);
explicit AttestationCreateAccount(json::Value const& v);
explicit AttestationCreateAccount(
AccountID attestationSignerAccount,
PublicKey const& publicKey,
Buffer signature,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
STAmount rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t createCount,
AccountID const& toCreate);
explicit AttestationCreateAccount(
STXChainBridge const& bridge,
AccountID attestationSignerAccount,
PublicKey const& publicKey,
SecretKey const& secretKey,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
STAmount const& rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t createCount,
AccountID const& toCreate);
[[nodiscard]] STObject
toSTObject() const;
// return true if the two attestations attest to the same thing
[[nodiscard]] bool
sameEvent(AttestationCreateAccount const& rhs) const;
friend bool
operator==(AttestationCreateAccount const& lhs, AttestationCreateAccount const& rhs);
[[nodiscard]] static std::vector<std::uint8_t>
message(
STXChainBridge const& bridge,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
STAmount const& rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t createCount,
AccountID const& dst);
[[nodiscard]] bool
validAmounts() const;
private:
[[nodiscard]] std::vector<std::uint8_t>
message(STXChainBridge const& bridge) const override;
};
struct CmpByCreateCount
{
bool
operator()(AttestationCreateAccount const& lhs, AttestationCreateAccount const& rhs) const
{
return lhs.createCount < rhs.createCount;
}
};
}; // namespace attestations
// Result when checking when two attestation match.
enum class AttestationMatch {
// One of the fields doesn't match, and it isn't the dst field
NonDstMismatch,
// all of the fields match, except the dst field
MatchExceptDst,
// all of the fields match
Match,
};
struct XChainClaimAttestation
{
using TSignedAttestation = attestations::AttestationClaim;
static SField const& arrayFieldName;
AccountID keyAccount;
PublicKey publicKey;
STAmount amount;
AccountID rewardAccount;
bool wasLockingChainSend;
std::optional<AccountID> dst;
struct MatchFields
{
STAmount amount;
bool wasLockingChainSend;
std::optional<AccountID> dst;
MatchFields(TSignedAttestation const& att);
MatchFields(STAmount a, bool b, std::optional<AccountID> const& d)
: amount{std::move(a)}, wasLockingChainSend{b}, dst{d}
{
}
};
explicit XChainClaimAttestation(
AccountID const& keyAccount,
PublicKey const& publicKey,
STAmount const& amount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::optional<AccountID> const& dst);
explicit XChainClaimAttestation(
STAccount const& keyAccount,
PublicKey const& publicKey,
STAmount const& amount,
STAccount const& rewardAccount,
bool wasLockingChainSend,
std::optional<STAccount> const& dst);
explicit XChainClaimAttestation(TSignedAttestation const& claimAtt);
explicit XChainClaimAttestation(STObject const& o);
explicit XChainClaimAttestation(json::Value const& v);
[[nodiscard]] AttestationMatch
match(MatchFields const& rhs) const;
[[nodiscard]] STObject
toSTObject() const;
friend bool
operator==(XChainClaimAttestation const& lhs, XChainClaimAttestation const& rhs);
};
struct XChainCreateAccountAttestation
{
using TSignedAttestation = attestations::AttestationCreateAccount;
static SField const& arrayFieldName;
AccountID keyAccount;
PublicKey publicKey;
STAmount amount;
STAmount rewardAmount;
AccountID rewardAccount;
bool wasLockingChainSend;
AccountID dst;
struct MatchFields
{
STAmount amount;
STAmount rewardAmount;
bool wasLockingChainSend;
AccountID dst;
MatchFields(TSignedAttestation const& att);
};
explicit XChainCreateAccountAttestation(
AccountID const& keyAccount,
PublicKey const& publicKey,
STAmount const& amount,
STAmount const& rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
AccountID const& dst);
explicit XChainCreateAccountAttestation(TSignedAttestation const& claimAtt);
explicit XChainCreateAccountAttestation(STObject const& o);
explicit XChainCreateAccountAttestation(json::Value const& v);
[[nodiscard]] STObject
toSTObject() const;
[[nodiscard]] AttestationMatch
match(MatchFields const& rhs) const;
friend bool
operator==(
XChainCreateAccountAttestation const& lhs,
XChainCreateAccountAttestation const& rhs);
};
// Attestations from witness servers for a particular claim ID and bridge.
// Only one attestation per signature is allowed.
template <class TAttestation>
class XChainAttestationsBase
{
public:
using AttCollection = std::vector<TAttestation>;
private:
// Set a max number of allowed attestations to limit the amount of memory
// allocated and processing time. This number is much larger than the actual
// number of attestation a server would ever expect.
static constexpr std::uint32_t kMaxAttestations = 256;
AttCollection attestations_;
protected:
// Prevent slicing to the base class
~XChainAttestationsBase() = default;
public:
XChainAttestationsBase() = default;
XChainAttestationsBase(XChainAttestationsBase const& rhs) = default;
XChainAttestationsBase&
operator=(XChainAttestationsBase const& rhs) = default;
explicit XChainAttestationsBase(AttCollection&& sigs);
explicit XChainAttestationsBase(json::Value const& v);
explicit XChainAttestationsBase(STArray const& arr);
[[nodiscard]] STArray
toSTArray() const;
[[nodiscard]] AttCollection::const_iterator
begin() const;
[[nodiscard]] AttCollection::const_iterator
end() const;
AttCollection::iterator
begin();
AttCollection::iterator
end();
template <class F>
std::size_t
eraseIf(F&& f);
[[nodiscard]] std::size_t
size() const;
[[nodiscard]] bool
empty() const;
[[nodiscard]] AttCollection const&
attestations() const;
template <class T>
void
emplaceBack(T&& att);
};
template <class TAttestation>
[[nodiscard]] inline bool
operator==(
XChainAttestationsBase<TAttestation> const& lhs,
XChainAttestationsBase<TAttestation> const& rhs)
{
return lhs.attestations() == rhs.attestations();
}
template <class TAttestation>
inline XChainAttestationsBase<TAttestation>::AttCollection const&
XChainAttestationsBase<TAttestation>::attestations() const
{
return attestations_;
};
template <class TAttestation>
template <class T>
inline void
XChainAttestationsBase<TAttestation>::emplaceBack(T&& att)
{
attestations_.emplace_back(std::forward<T>(att));
};
template <class TAttestation>
template <class F>
inline std::size_t
XChainAttestationsBase<TAttestation>::eraseIf(F&& f)
{
return std::erase_if(attestations_, std::forward<F>(f));
}
template <class TAttestation>
inline std::size_t
XChainAttestationsBase<TAttestation>::size() const
{
return attestations_.size();
}
template <class TAttestation>
inline bool
XChainAttestationsBase<TAttestation>::empty() const
{
return attestations_.empty();
}
class XChainClaimAttestations final : public XChainAttestationsBase<XChainClaimAttestation>
{
using TBase = XChainAttestationsBase<XChainClaimAttestation>;
using TBase::TBase;
};
class XChainCreateAccountAttestations final
: public XChainAttestationsBase<XChainCreateAccountAttestation>
{
using TBase = XChainAttestationsBase<XChainCreateAccountAttestation>;
using TBase::TBase;
};
} // namespace xrpl
+4 -3
View File
@@ -62,12 +62,9 @@ XRPL_FIX (EnforceNFTokenTrustline, Supported::Yes, VoteBehavior::DefaultNo
XRPL_FIX (ReducedOffersV2, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(NFTokenMintOffer, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (PreviousTxnID, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (XChainRewardRounding, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (EmptyDID, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PriceOracle, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (FillOrKill, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(DID, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(XChainBridge, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMM, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(XRPFees, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FIX (RemoveNFTokenAutoTrustLine, Supported::Yes, VoteBehavior::DefaultYes)
@@ -85,6 +82,9 @@ XRPL_FIX (RemoveNFTokenAutoTrustLine, Supported::Yes, VoteBehavior::DefaultYe
// to vote for it, the feature can be removed entirely from the code. Until
// then the feature needs to be marked explicitly as obsolete, e.g.
// XRPL_FEATURE(Example, Supported::yes, VoteBehavior::Obsolete)
XRPL_FEATURE(XChainBridge, Supported::Yes, VoteBehavior::Obsolete)
XRPL_FIX (XChainRewardRounding, 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
@@ -109,6 +109,7 @@ XRPL_RETIRE_FIX(AMMOverflowOffer)
XRPL_RETIRE_FIX(AMMv1_1)
XRPL_RETIRE_FIX(CheckThreading)
XRPL_RETIRE_FIX(DisallowIncomingV1)
XRPL_RETIRE_FIX(FillOrKill)
XRPL_RETIRE_FIX(InnerObjTemplate)
XRPL_RETIRE_FIX(MasterKeyAsRegularKey)
XRPL_RETIRE_FIX(NonFungibleTokensV1_2)
@@ -206,23 +206,6 @@ LEDGER_ENTRY(ltLEDGER_HASHES, 0x0068, LedgerHashes, hashes, ({
{sfHashes, SoeRequired},
}))
/** The ledger object which lists details about sidechains.
\sa keylet::bridge
*/
LEDGER_ENTRY(ltBRIDGE, 0x0069, Bridge, bridge, ({
{sfAccount, SoeRequired},
{sfSignatureReward, SoeRequired},
{sfMinAccountCreateAmount, SoeOptional},
{sfXChainBridge, SoeRequired},
{sfXChainClaimID, SoeRequired},
{sfXChainAccountCreateCount, SoeRequired},
{sfXChainAccountClaimCount, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
/** A ledger object which describes an offer on the DEX.
\sa keylet::offer
@@ -255,22 +238,6 @@ LEDGER_ENTRY_DUPLICATE(ltDEPOSIT_PREAUTH, 0x0070, DepositPreauth, deposit_preaut
{sfAuthorizeCredentials, SoeOptional},
}))
/** A claim id for a cross chain transaction.
\sa keylet::xChainClaimID
*/
LEDGER_ENTRY(ltXCHAIN_OWNED_CLAIM_ID, 0x0071, XChainOwnedClaimID, xchain_owned_claim_id, ({
{sfAccount, SoeRequired},
{sfXChainBridge, SoeRequired},
{sfXChainClaimID, SoeRequired},
{sfOtherChainSource, SoeRequired},
{sfXChainClaimAttestations, SoeRequired},
{sfSignatureReward, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
/** A ledger object which describes a bidirectional trust line.
@note Per Vinnie Falco this should be renamed to ltTRUST_LINE
@@ -318,20 +285,6 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({
{sfPreviousTxnLgrSeq, SoeOptional},
}))
/** A claim id for a cross chain create account transaction.
\sa keylet::xChainCreateAccountClaimID
*/
LEDGER_ENTRY(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID, 0x0074, XChainOwnedCreateAccountClaimID, xchain_owned_create_account_claim_id, ({
{sfAccount, SoeRequired},
{sfXChainBridge, SoeRequired},
{sfXChainAccountCreateCount, SoeRequired},
{sfXChainCreateAccountAttestations, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
/** A ledger object describing a single escrow.
\sa keylet::escrow
@@ -417,6 +370,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfAuditorEncryptionKey, SoeOptional},
{sfIssuerKeyEpoch, SoeOptional},
{sfAuditorKeyEpoch, SoeOptional},
{sfInitialIssuerEncryptionKey, SoeOptional},
{sfConfidentialOutstandingAmount, SoeDefault},
}))
@@ -438,6 +392,7 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfAuditorEncryptedBalance, SoeOptional},
{sfIssuerKeyMirrorEpoch, SoeOptional},
{sfAuditorKeyMirrorEpoch, SoeOptional},
{sfIssuerMirrorEncryptionKey, SoeOptional},
{sfHolderEncryptionKey, SoeOptional},
{sfRecoveryKey, SoeOptional},
}))
+24 -39
View File
@@ -5,6 +5,9 @@
#error "undefined macro: TYPED_SFIELD"
#endif
// A code marked "reserved" is not assigned to a field. Do not reuse a reserved
// code until a year or two after its field was removed, so that no supported
// release or tool still knows the old meaning.
// untyped
UNTYPED_SFIELD(sfLedgerEntry, LEDGERENTRY, 257)
@@ -23,8 +26,7 @@ TYPED_SFIELD(sfLEVersion, UINT8, 6)
// 8-bit integers (uncommon)
TYPED_SFIELD(sfTickSize, UINT8, 16)
TYPED_SFIELD(sfUNLModifyDisabling, UINT8, 17)
// 18 unused
TYPED_SFIELD(sfWasLockingChainSend, UINT8, 19)
// 18 to 19 reserved
TYPED_SFIELD(sfWithdrawalPolicy, UINT8, 20)
TYPED_SFIELD(sfContractResult, UINT8, 21)
TYPED_SFIELD(sfVaultKind, UINT8, 22)
@@ -39,7 +41,7 @@ TYPED_SFIELD(sfDiscountedFee, UINT16, 6)
// 16-bit integers (uncommon)
TYPED_SFIELD(sfVersion, UINT16, 16)
// 17 to 20 unused
// 17 to 20 reserved
TYPED_SFIELD(sfLedgerFixType, UINT16, 21)
TYPED_SFIELD(sfManagementFeeRate, UINT16, 22) // 1/10 basis points (bips)
@@ -90,7 +92,7 @@ TYPED_SFIELD(sfTicketSequence, UINT32, 41)
TYPED_SFIELD(sfNFTokenTaxon, UINT32, 42)
TYPED_SFIELD(sfMintedNFTokens, UINT32, 43)
TYPED_SFIELD(sfBurnedNFTokens, UINT32, 44)
// 45 to 47 unused
// 45 to 47 reserved
TYPED_SFIELD(sfVoteWeight, UINT32, 48)
TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50)
TYPED_SFIELD(sfOracleDocumentID, UINT32, 51)
@@ -145,11 +147,9 @@ TYPED_SFIELD(sfNFTokenOfferNode, UINT64, 12)
TYPED_SFIELD(sfEmitBurden, UINT64, 13)
// 64-bit integers (uncommon)
// 16 to 18 unused
// 16 to 18 reserved
TYPED_SFIELD(sfReferenceCount, UINT64, 19)
TYPED_SFIELD(sfXChainClaimID, UINT64, 20)
TYPED_SFIELD(sfXChainAccountCreateCount, UINT64, 21)
TYPED_SFIELD(sfXChainAccountClaimCount, UINT64, 22)
// 20 to 22 reserved
TYPED_SFIELD(sfAssetPrice, UINT64, 23)
TYPED_SFIELD(sfMaximumAmount, UINT64, 24, SField::kSmdBaseTen|SField::kSmdDefault)
TYPED_SFIELD(sfOutstandingAmount, UINT64, 25, SField::kSmdBaseTen|SField::kSmdDefault)
@@ -199,7 +199,7 @@ TYPED_SFIELD(sfBookDirectory, UINT256, 16)
TYPED_SFIELD(sfInvoiceID, UINT256, 17)
TYPED_SFIELD(sfNickname, UINT256, 18)
TYPED_SFIELD(sfAmendment, UINT256, 19)
// 20 unused
// 20 reserved
TYPED_SFIELD(sfDigest, UINT256, 21)
TYPED_SFIELD(sfChannel, UINT256, 22)
TYPED_SFIELD(sfConsensusHash, UINT256, 23)
@@ -209,7 +209,7 @@ TYPED_SFIELD(sfPreviousPageMin, UINT256, 26)
TYPED_SFIELD(sfNextPageMin, UINT256, 27)
TYPED_SFIELD(sfNFTokenBuyOffer, UINT256, 28)
TYPED_SFIELD(sfNFTokenSellOffer, UINT256, 29)
// 30 to 33 unused
// 30 to 33 reserved
TYPED_SFIELD(sfDomainID, UINT256, 34)
TYPED_SFIELD(sfVaultID, UINT256, 35,
SField::kSmdPseudoAccount | SField::kSmdDefault)
@@ -268,7 +268,7 @@ TYPED_SFIELD(sfMinimumOffer, AMOUNT, 16)
TYPED_SFIELD(sfRippleEscrow, AMOUNT, 17)
TYPED_SFIELD(sfDeliveredAmount, AMOUNT, 18)
TYPED_SFIELD(sfNFTokenBrokerFee, AMOUNT, 19)
// 20 to 21 unused
// 20 to 21 reserved
// currency amount (fees)
TYPED_SFIELD(sfBaseFeeDrops, AMOUNT, 22)
TYPED_SFIELD(sfReserveBaseDrops, AMOUNT, 23)
@@ -279,8 +279,7 @@ TYPED_SFIELD(sfLPTokenOut, AMOUNT, 25)
TYPED_SFIELD(sfLPTokenIn, AMOUNT, 26)
TYPED_SFIELD(sfEPrice, AMOUNT, 27)
TYPED_SFIELD(sfPrice, AMOUNT, 28)
TYPED_SFIELD(sfSignatureReward, AMOUNT, 29)
TYPED_SFIELD(sfMinAccountCreateAmount, AMOUNT, 30)
// 29 to 30 reserved
TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31)
TYPED_SFIELD(sfFeeAmount, AMOUNT, 32)
TYPED_SFIELD(sfMaxFee, AMOUNT, 33)
@@ -309,7 +308,7 @@ TYPED_SFIELD(sfMasterSignature, VL, 18, SField::kSmdDefault, SFi
TYPED_SFIELD(sfUNLModifyValidator, VL, 19)
TYPED_SFIELD(sfValidatorToDisable, VL, 20)
TYPED_SFIELD(sfValidatorToReEnable, VL, 21)
// 22 to 25 unused
// 22 to 25 reserved
TYPED_SFIELD(sfDIDDocument, VL, 26)
TYPED_SFIELD(sfData, VL, 27)
TYPED_SFIELD(sfAssetClass, VL, 28)
@@ -333,6 +332,8 @@ TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
TYPED_SFIELD(sfBytecode, VL, 47)
TYPED_SFIELD(sfRecoveryKey, VL, 48)
TYPED_SFIELD(sfInitialIssuerEncryptionKey, VL, 49)
TYPED_SFIELD(sfIssuerMirrorEncryptionKey, VL, 50)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
@@ -341,7 +342,7 @@ TYPED_SFIELD(sfDestination, ACCOUNT, 3)
TYPED_SFIELD(sfIssuer, ACCOUNT, 4)
TYPED_SFIELD(sfAuthorize, ACCOUNT, 5)
TYPED_SFIELD(sfUnauthorize, ACCOUNT, 6)
// 7 unused
// 7 reserved
TYPED_SFIELD(sfRegularKey, ACCOUNT, 8)
TYPED_SFIELD(sfNFTokenMinter, ACCOUNT, 9)
TYPED_SFIELD(sfEmitCallback, ACCOUNT, 10)
@@ -349,13 +350,7 @@ TYPED_SFIELD(sfHolder, ACCOUNT, 11)
TYPED_SFIELD(sfDelegate, ACCOUNT, 12)
// account (uncommon)
// 16 unused
TYPED_SFIELD(sfOtherChainSource, ACCOUNT, 18)
TYPED_SFIELD(sfOtherChainDestination, ACCOUNT, 19)
TYPED_SFIELD(sfAttestationSignerAccount, ACCOUNT, 20)
TYPED_SFIELD(sfAttestationRewardAccount, ACCOUNT, 21)
TYPED_SFIELD(sfLockingChainDoor, ACCOUNT, 22)
TYPED_SFIELD(sfIssuingChainDoor, ACCOUNT, 23)
// 16 to 23 reserved
TYPED_SFIELD(sfSubject, ACCOUNT, 24)
TYPED_SFIELD(sfBorrower, ACCOUNT, 25)
TYPED_SFIELD(sfCounterparty, ACCOUNT, 26)
@@ -381,14 +376,10 @@ TYPED_SFIELD(sfBaseAsset, CURRENCY, 1)
TYPED_SFIELD(sfQuoteAsset, CURRENCY, 2)
// issue
TYPED_SFIELD(sfLockingChainIssue, ISSUE, 1)
TYPED_SFIELD(sfIssuingChainIssue, ISSUE, 2)
// 1 to 2 reserved
TYPED_SFIELD(sfAsset, ISSUE, 3)
TYPED_SFIELD(sfAsset2, ISSUE, 4)
// bridge
TYPED_SFIELD(sfXChainBridge, XCHAIN_BRIDGE, 1)
// inner object
// OBJECT/1 is reserved for end of object
UNTYPED_SFIELD(sfTransactionMetaData, OBJECT, 2)
@@ -403,22 +394,19 @@ UNTYPED_SFIELD(sfMemo, OBJECT, 10)
UNTYPED_SFIELD(sfSignerEntry, OBJECT, 11)
UNTYPED_SFIELD(sfNFToken, OBJECT, 12)
UNTYPED_SFIELD(sfEmitDetails, OBJECT, 13)
// 14 unused
// 14 reserved
UNTYPED_SFIELD(sfPermission, OBJECT, 15)
// inner object (uncommon)
UNTYPED_SFIELD(sfSigner, OBJECT, 16)
// 17 unused
// 17 reserved
UNTYPED_SFIELD(sfMajority, OBJECT, 18)
UNTYPED_SFIELD(sfDisabledValidator, OBJECT, 19)
// 20 to 24 unused
// 20 to 24 reserved
UNTYPED_SFIELD(sfVoteEntry, OBJECT, 25)
UNTYPED_SFIELD(sfAuctionSlot, OBJECT, 26)
UNTYPED_SFIELD(sfAuthAccount, OBJECT, 27)
UNTYPED_SFIELD(sfXChainClaimProofSig, OBJECT, 28)
UNTYPED_SFIELD(sfXChainCreateAccountProofSig, OBJECT, 29)
UNTYPED_SFIELD(sfXChainClaimAttestationCollectionElement, OBJECT, 30)
UNTYPED_SFIELD(sfXChainCreateAccountAttestationCollectionElement, OBJECT, 31)
// 28 to 31 reserved
UNTYPED_SFIELD(sfPriceData, OBJECT, 32)
UNTYPED_SFIELD(sfCredential, OBJECT, 33)
UNTYPED_SFIELD(sfRawTransaction, OBJECT, 34)
@@ -440,17 +428,14 @@ UNTYPED_SFIELD(sfSufficient, ARRAY, 7)
UNTYPED_SFIELD(sfAffectedNodes, ARRAY, 8)
UNTYPED_SFIELD(sfMemos, ARRAY, 9)
UNTYPED_SFIELD(sfNFTokens, ARRAY, 10)
// 11 unused
// 11 reserved
UNTYPED_SFIELD(sfVoteSlots, ARRAY, 12)
UNTYPED_SFIELD(sfAdditionalBooks, ARRAY, 13)
// array of objects (uncommon)
UNTYPED_SFIELD(sfMajorities, ARRAY, 16)
UNTYPED_SFIELD(sfDisabledValidators, ARRAY, 17)
// 18 to 20 unused
UNTYPED_SFIELD(sfXChainClaimAttestations, ARRAY, 21)
UNTYPED_SFIELD(sfXChainCreateAccountAttestations, ARRAY, 22)
// 23 unused
// 18 to 23 reserved
UNTYPED_SFIELD(sfPriceDataSeries, ARRAY, 24)
UNTYPED_SFIELD(sfAuthAccounts, ARRAY, 25)
UNTYPED_SFIELD(sfAuthorizeCredentials, ARRAY, 26)
+1 -105
View File
@@ -457,111 +457,7 @@ TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
{sfAsset2, SoeRequired, SoeMptSupported},
}))
/** This transactions creates a crosschain sequence number */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/bridge/XChainBridge.h>
#endif
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}),
({
{sfXChainBridge, SoeRequired},
{sfSignatureReward, SoeRequired},
{sfOtherChainSource, SoeRequired},
}))
/** This transactions initiates a crosschain transaction */
TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit,
({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}),
({
{sfXChainBridge, SoeRequired},
{sfXChainClaimID, SoeRequired},
{sfAmount, SoeRequired},
{sfOtherChainDestination, SoeOptional},
}))
/** This transaction completes a crosschain transaction */
TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim,
({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}),
({
{sfXChainBridge, SoeRequired},
{sfXChainClaimID, SoeRequired},
{sfDestination, SoeRequired},
{sfDestinationTag, SoeOptional},
{sfAmount, SoeRequired},
}))
/** This transaction initiates a crosschain account create transaction */
TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit,
({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}),
({
{sfXChainBridge, SoeRequired},
{sfDestination, SoeRequired},
{sfAmount, SoeRequired},
{sfSignatureReward, SoeRequired},
}))
/** This transaction adds an attestation to a claim */
TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation,
({
.delegable = Delegation::Delegable,
.amendment = featureXChainBridge,
.privileges = Privilege::CreateAcct,
}),
({
{sfXChainBridge, SoeRequired},
{sfAttestationSignerAccount, SoeRequired},
{sfPublicKey, SoeRequired},
{sfSignature, SoeRequired},
{sfOtherChainSource, SoeRequired},
{sfAmount, SoeRequired},
{sfAttestationRewardAccount, SoeRequired},
{sfWasLockingChainSend, SoeRequired},
{sfXChainClaimID, SoeRequired},
{sfDestination, SoeOptional},
}))
/** This transaction adds an attestation to an account */
TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation,
({
.delegable = Delegation::Delegable,
.amendment = featureXChainBridge,
.privileges = Privilege::CreateAcct,
}),
({
{sfXChainBridge, SoeRequired},
{sfAttestationSignerAccount, SoeRequired},
{sfPublicKey, SoeRequired},
{sfSignature, SoeRequired},
{sfOtherChainSource, SoeRequired},
{sfAmount, SoeRequired},
{sfAttestationRewardAccount, SoeRequired},
{sfWasLockingChainSend, SoeRequired},
{sfXChainAccountCreateCount, SoeRequired},
{sfDestination, SoeRequired},
{sfSignatureReward, SoeRequired},
}))
/** This transaction modifies a sidechain */
TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge,
({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}),
({
{sfXChainBridge, SoeRequired},
{sfSignatureReward, SoeOptional},
{sfMinAccountCreateAmount, SoeOptional},
}))
/** This transactions creates a sidechain */
TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}),
({
{sfXChainBridge, SoeRequired},
{sfSignatureReward, SoeRequired},
{sfMinAccountCreateAmount, SoeOptional},
}))
// 41 to 48 deprecated
/** This transaction type creates or updates a DID */
#if TRANSACTION_INCLUDE
+3 -1
View File
@@ -159,7 +159,6 @@ JSS(both); // in: Subscribe, Unsubscribe
JSS(both_sides); // in: Subscribe, Unsubscribe
JSS(branch); // out: server_info
JSS(broadcast); // out: SubmitTransaction
JSS(bridge_account); // in: LedgerEntry
JSS(build_path); // in: TransactionSign
JSS(build_version); // out: NetworkOPs
JSS(cancel_after); // out: AccountChannels
@@ -252,6 +251,9 @@ JSS(expected_date); // out: any (warnings)
JSS(expected_date_UTC); // out: any (warnings)
JSS(expected_ledger_size); // out: TxQ
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
JSS(gas_limit); // out: NetworkOPs
JSS(bytecode_size_limit); // out: NetworkOPs
JSS(gas_price); // out: NetworkOPs
JSS(fail_hard); // in: Sign, Submit
JSS(failed); // out: InboundLedger
JSS(feature); // in: Feature
@@ -10,7 +10,6 @@
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TxFormats.h>
@@ -1,348 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
class BridgeBuilder;
/**
* @brief Ledger Entry: Bridge
*
* Type: ltBRIDGE (0x0069)
* RPC Name: bridge
*
* Immutable wrapper around SLE providing type-safe field access.
* Use BridgeBuilder to construct new ledger entries.
*/
class Bridge : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltBRIDGE;
/**
* @brief Construct a Bridge ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit Bridge(SLE::const_pointer sle)
: LedgerEntryBase(std::move(sle))
{
// Verify ledger entry type
if (sle_->getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for Bridge");
}
}
// Ledger entry-specific field getters
/**
* @brief Get sfAccount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_->at(sfAccount);
}
/**
* @brief Get sfSignatureReward (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSignatureReward() const
{
return this->sle_->at(sfSignatureReward);
}
/**
* @brief Get sfMinAccountCreateAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getMinAccountCreateAmount() const
{
if (hasMinAccountCreateAmount())
return this->sle_->at(sfMinAccountCreateAmount);
return std::nullopt;
}
/**
* @brief Check if sfMinAccountCreateAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMinAccountCreateAmount() const
{
return this->sle_->isFieldPresent(sfMinAccountCreateAmount);
}
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->sle_->at(sfXChainBridge);
}
/**
* @brief Get sfXChainClaimID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainClaimID() const
{
return this->sle_->at(sfXChainClaimID);
}
/**
* @brief Get sfXChainAccountCreateCount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainAccountCreateCount() const
{
return this->sle_->at(sfXChainAccountCreateCount);
}
/**
* @brief Get sfXChainAccountClaimCount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainAccountClaimCount() const
{
return this->sle_->at(sfXChainAccountClaimCount);
}
/**
* @brief Get sfOwnerNode (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_->at(sfOwnerNode);
}
/**
* @brief Get sfPreviousTxnID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_->at(sfPreviousTxnID);
}
/**
* @brief Get sfPreviousTxnLgrSeq (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_->at(sfPreviousTxnLgrSeq);
}
};
/**
* @brief Builder for Bridge ledger entries.
*
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses STObject internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class BridgeBuilder : public LedgerEntryBuilderBase<BridgeBuilder>
{
public:
/**
* @brief Construct a new BridgeBuilder with required fields.
* @param account The sfAccount field value.
* @param signatureReward The sfSignatureReward field value.
* @param xChainBridge The sfXChainBridge field value.
* @param xChainClaimID The sfXChainClaimID field value.
* @param xChainAccountCreateCount The sfXChainAccountCreateCount field value.
* @param xChainAccountClaimCount The sfXChainAccountClaimCount field value.
* @param ownerNode The sfOwnerNode field value.
* @param previousTxnID The sfPreviousTxnID field value.
* @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value.
*/
BridgeBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_AMOUNT::type::value_type> const& signatureReward,std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge,std::decay_t<typename SF_UINT64::type::value_type> const& xChainClaimID,std::decay_t<typename SF_UINT64::type::value_type> const& xChainAccountCreateCount,std::decay_t<typename SF_UINT64::type::value_type> const& xChainAccountClaimCount,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<BridgeBuilder>(ltBRIDGE)
{
setAccount(account);
setSignatureReward(signatureReward);
setXChainBridge(xChainBridge);
setXChainClaimID(xChainClaimID);
setXChainAccountCreateCount(xChainAccountCreateCount);
setXChainAccountClaimCount(xChainAccountClaimCount);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
/**
* @brief Construct a BridgeBuilder from an existing SLE object.
* @param sle The existing ledger entry to copy from.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
BridgeBuilder(SLE::const_pointer sle)
{
if (sle->at(sfLedgerEntryType) != ltBRIDGE)
{
throw std::runtime_error("Invalid ledger entry type for Bridge");
}
object_ = *sle;
}
/**
* @brief Ledger entry-specific field setters
*/
/**
* @brief Set sfAccount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* @brief Set sfSignatureReward (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* @brief Set sfMinAccountCreateAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setMinAccountCreateAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfMinAccountCreateAmount] = value;
return *this;
}
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfXChainClaimID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainClaimID(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainClaimID] = value;
return *this;
}
/**
* @brief Set sfXChainAccountCreateCount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainAccountCreateCount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainAccountCreateCount] = value;
return *this;
}
/**
* @brief Set sfXChainAccountClaimCount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setXChainAccountClaimCount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainAccountClaimCount] = value;
return *this;
}
/**
* @brief Set sfOwnerNode (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* @brief Set sfPreviousTxnID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* @brief Set sfPreviousTxnLgrSeq (SoeRequired)
* @return Reference to this builder for method chaining.
*/
BridgeBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* @brief Build and return the completed Bridge wrapper.
* @param index The ledger entry index.
* @return The constructed ledger entry wrapper.
*/
Bridge
build(UInt256 const& index)
{
return Bridge{std::make_shared<SLE>(std::move(object_), index)};
}
};
} // namespace xrpl::ledger_entries
@@ -316,6 +316,30 @@ public:
return this->sle_->isFieldPresent(sfAuditorKeyMirrorEpoch);
}
/**
* @brief Get sfIssuerMirrorEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerMirrorEncryptionKey() const
{
if (hasIssuerMirrorEncryptionKey())
return this->sle_->at(sfIssuerMirrorEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfIssuerMirrorEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerMirrorEncryptionKey() const
{
return this->sle_->isFieldPresent(sfIssuerMirrorEncryptionKey);
}
/**
* @brief Get sfHolderEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
@@ -565,6 +589,17 @@ public:
return *this;
}
/**
* @brief Set sfIssuerMirrorEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setIssuerMirrorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerMirrorEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
@@ -399,6 +399,30 @@ public:
return this->sle_->isFieldPresent(sfAuditorKeyEpoch);
}
/**
* @brief Get sfInitialIssuerEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getInitialIssuerEncryptionKey() const
{
if (hasInitialIssuerEncryptionKey())
return this->sle_->at(sfInitialIssuerEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfInitialIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasInitialIssuerEncryptionKey() const
{
return this->sle_->isFieldPresent(sfInitialIssuerEncryptionKey);
}
/**
* @brief Get sfConfidentialOutstandingAmount (SoeDefault)
* @return The field value, or std::nullopt if not present.
@@ -670,6 +694,17 @@ public:
return *this;
}
/**
* @brief Set sfInitialIssuerEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setInitialIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfInitialIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfConfidentialOutstandingAmount (SoeDefault)
* @return Reference to this builder for method chaining.
@@ -1,314 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
class XChainOwnedClaimIDBuilder;
/**
* @brief Ledger Entry: XChainOwnedClaimID
*
* Type: ltXCHAIN_OWNED_CLAIM_ID (0x0071)
* RPC Name: xchain_owned_claim_id
*
* Immutable wrapper around SLE providing type-safe field access.
* Use XChainOwnedClaimIDBuilder to construct new ledger entries.
*/
class XChainOwnedClaimID : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltXCHAIN_OWNED_CLAIM_ID;
/**
* @brief Construct a XChainOwnedClaimID ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit XChainOwnedClaimID(SLE::const_pointer sle)
: LedgerEntryBase(std::move(sle))
{
// Verify ledger entry type
if (sle_->getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for XChainOwnedClaimID");
}
}
// Ledger entry-specific field getters
/**
* @brief Get sfAccount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_->at(sfAccount);
}
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->sle_->at(sfXChainBridge);
}
/**
* @brief Get sfXChainClaimID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainClaimID() const
{
return this->sle_->at(sfXChainClaimID);
}
/**
* @brief Get sfOtherChainSource (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOtherChainSource() const
{
return this->sle_->at(sfOtherChainSource);
}
/**
* @brief Get sfXChainClaimAttestations (SoeRequired)
* @note This is an untyped field (unknown).
* @return The field value.
*/
[[nodiscard]]
STArray const&
getXChainClaimAttestations() const
{
return this->sle_->getFieldArray(sfXChainClaimAttestations);
}
/**
* @brief Get sfSignatureReward (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSignatureReward() const
{
return this->sle_->at(sfSignatureReward);
}
/**
* @brief Get sfOwnerNode (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_->at(sfOwnerNode);
}
/**
* @brief Get sfPreviousTxnID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_->at(sfPreviousTxnID);
}
/**
* @brief Get sfPreviousTxnLgrSeq (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_->at(sfPreviousTxnLgrSeq);
}
};
/**
* @brief Builder for XChainOwnedClaimID ledger entries.
*
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses STObject internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class XChainOwnedClaimIDBuilder : public LedgerEntryBuilderBase<XChainOwnedClaimIDBuilder>
{
public:
/**
* @brief Construct a new XChainOwnedClaimIDBuilder with required fields.
* @param account The sfAccount field value.
* @param xChainBridge The sfXChainBridge field value.
* @param xChainClaimID The sfXChainClaimID field value.
* @param otherChainSource The sfOtherChainSource field value.
* @param xChainClaimAttestations The sfXChainClaimAttestations field value.
* @param signatureReward The sfSignatureReward field value.
* @param ownerNode The sfOwnerNode field value.
* @param previousTxnID The sfPreviousTxnID field value.
* @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value.
*/
XChainOwnedClaimIDBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge,std::decay_t<typename SF_UINT64::type::value_type> const& xChainClaimID,std::decay_t<typename SF_ACCOUNT::type::value_type> const& otherChainSource,STArray const& xChainClaimAttestations,std::decay_t<typename SF_AMOUNT::type::value_type> const& signatureReward,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<XChainOwnedClaimIDBuilder>(ltXCHAIN_OWNED_CLAIM_ID)
{
setAccount(account);
setXChainBridge(xChainBridge);
setXChainClaimID(xChainClaimID);
setOtherChainSource(otherChainSource);
setXChainClaimAttestations(xChainClaimAttestations);
setSignatureReward(signatureReward);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
/**
* @brief Construct a XChainOwnedClaimIDBuilder from an existing SLE object.
* @param sle The existing ledger entry to copy from.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
XChainOwnedClaimIDBuilder(SLE::const_pointer sle)
{
if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CLAIM_ID)
{
throw std::runtime_error("Invalid ledger entry type for XChainOwnedClaimID");
}
object_ = *sle;
}
/**
* @brief Ledger entry-specific field setters
*/
/**
* @brief Set sfAccount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfXChainClaimID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setXChainClaimID(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainClaimID] = value;
return *this;
}
/**
* @brief Set sfOtherChainSource (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setOtherChainSource(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOtherChainSource] = value;
return *this;
}
/**
* @brief Set sfXChainClaimAttestations (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setXChainClaimAttestations(STArray const& value)
{
object_.setFieldArray(sfXChainClaimAttestations, value);
return *this;
}
/**
* @brief Set sfSignatureReward (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* @brief Set sfOwnerNode (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* @brief Set sfPreviousTxnID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* @brief Set sfPreviousTxnLgrSeq (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedClaimIDBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* @brief Build and return the completed XChainOwnedClaimID wrapper.
* @param index The ledger entry index.
* @return The constructed ledger entry wrapper.
*/
XChainOwnedClaimID
build(UInt256 const& index)
{
return XChainOwnedClaimID{std::make_shared<SLE>(std::move(object_), index)};
}
};
} // namespace xrpl::ledger_entries
@@ -1,266 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/LedgerEntryBase.h>
#include <xrpl/protocol_autogen/LedgerEntryBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::ledger_entries {
class XChainOwnedCreateAccountClaimIDBuilder;
/**
* @brief Ledger Entry: XChainOwnedCreateAccountClaimID
*
* Type: ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID (0x0074)
* RPC Name: xchain_owned_create_account_claim_id
*
* Immutable wrapper around SLE providing type-safe field access.
* Use XChainOwnedCreateAccountClaimIDBuilder to construct new ledger entries.
*/
class XChainOwnedCreateAccountClaimID : public LedgerEntryBase
{
public:
static constexpr LedgerEntryType entryType = ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID;
/**
* @brief Construct a XChainOwnedCreateAccountClaimID ledger entry wrapper from an existing SLE object.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
explicit XChainOwnedCreateAccountClaimID(SLE::const_pointer sle)
: LedgerEntryBase(std::move(sle))
{
// Verify ledger entry type
if (sle_->getType() != entryType)
{
throw std::runtime_error("Invalid ledger entry type for XChainOwnedCreateAccountClaimID");
}
}
// Ledger entry-specific field getters
/**
* @brief Get sfAccount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAccount() const
{
return this->sle_->at(sfAccount);
}
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->sle_->at(sfXChainBridge);
}
/**
* @brief Get sfXChainAccountCreateCount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainAccountCreateCount() const
{
return this->sle_->at(sfXChainAccountCreateCount);
}
/**
* @brief Get sfXChainCreateAccountAttestations (SoeRequired)
* @note This is an untyped field (unknown).
* @return The field value.
*/
[[nodiscard]]
STArray const&
getXChainCreateAccountAttestations() const
{
return this->sle_->getFieldArray(sfXChainCreateAccountAttestations);
}
/**
* @brief Get sfOwnerNode (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getOwnerNode() const
{
return this->sle_->at(sfOwnerNode);
}
/**
* @brief Get sfPreviousTxnID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getPreviousTxnID() const
{
return this->sle_->at(sfPreviousTxnID);
}
/**
* @brief Get sfPreviousTxnLgrSeq (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT32::type::value_type
getPreviousTxnLgrSeq() const
{
return this->sle_->at(sfPreviousTxnLgrSeq);
}
};
/**
* @brief Builder for XChainOwnedCreateAccountClaimID ledger entries.
*
* Provides a fluent interface for constructing ledger entries with method chaining.
* Uses STObject internally for flexible ledger entry construction.
* Inherits common field setters from LedgerEntryBuilderBase.
*/
class XChainOwnedCreateAccountClaimIDBuilder : public LedgerEntryBuilderBase<XChainOwnedCreateAccountClaimIDBuilder>
{
public:
/**
* @brief Construct a new XChainOwnedCreateAccountClaimIDBuilder with required fields.
* @param account The sfAccount field value.
* @param xChainBridge The sfXChainBridge field value.
* @param xChainAccountCreateCount The sfXChainAccountCreateCount field value.
* @param xChainCreateAccountAttestations The sfXChainCreateAccountAttestations field value.
* @param ownerNode The sfOwnerNode field value.
* @param previousTxnID The sfPreviousTxnID field value.
* @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value.
*/
XChainOwnedCreateAccountClaimIDBuilder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& account,std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge,std::decay_t<typename SF_UINT64::type::value_type> const& xChainAccountCreateCount,STArray const& xChainCreateAccountAttestations,std::decay_t<typename SF_UINT64::type::value_type> const& ownerNode,std::decay_t<typename SF_UINT256::type::value_type> const& previousTxnID,std::decay_t<typename SF_UINT32::type::value_type> const& previousTxnLgrSeq)
: LedgerEntryBuilderBase<XChainOwnedCreateAccountClaimIDBuilder>(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID)
{
setAccount(account);
setXChainBridge(xChainBridge);
setXChainAccountCreateCount(xChainAccountCreateCount);
setXChainCreateAccountAttestations(xChainCreateAccountAttestations);
setOwnerNode(ownerNode);
setPreviousTxnID(previousTxnID);
setPreviousTxnLgrSeq(previousTxnLgrSeq);
}
/**
* @brief Construct a XChainOwnedCreateAccountClaimIDBuilder from an existing SLE object.
* @param sle The existing ledger entry to copy from.
* @throws std::runtime_error if the ledger entry type doesn't match.
*/
XChainOwnedCreateAccountClaimIDBuilder(SLE::const_pointer sle)
{
if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID)
{
throw std::runtime_error("Invalid ledger entry type for XChainOwnedCreateAccountClaimID");
}
object_ = *sle;
}
/**
* @brief Ledger entry-specific field setters
*/
/**
* @brief Set sfAccount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedCreateAccountClaimIDBuilder&
setAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAccount] = value;
return *this;
}
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedCreateAccountClaimIDBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfXChainAccountCreateCount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedCreateAccountClaimIDBuilder&
setXChainAccountCreateCount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainAccountCreateCount] = value;
return *this;
}
/**
* @brief Set sfXChainCreateAccountAttestations (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedCreateAccountClaimIDBuilder&
setXChainCreateAccountAttestations(STArray const& value)
{
object_.setFieldArray(sfXChainCreateAccountAttestations, value);
return *this;
}
/**
* @brief Set sfOwnerNode (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedCreateAccountClaimIDBuilder&
setOwnerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfOwnerNode] = value;
return *this;
}
/**
* @brief Set sfPreviousTxnID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedCreateAccountClaimIDBuilder&
setPreviousTxnID(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfPreviousTxnID] = value;
return *this;
}
/**
* @brief Set sfPreviousTxnLgrSeq (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainOwnedCreateAccountClaimIDBuilder&
setPreviousTxnLgrSeq(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfPreviousTxnLgrSeq] = value;
return *this;
}
/**
* @brief Build and return the completed XChainOwnedCreateAccountClaimID wrapper.
* @param index The ledger entry index.
* @return The constructed ledger entry wrapper.
*/
XChainOwnedCreateAccountClaimID
build(UInt256 const& index)
{
return XChainOwnedCreateAccountClaimID{std::make_shared<SLE>(std::move(object_), index)};
}
};
} // namespace xrpl::ledger_entries
@@ -1,203 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainAccountCreateCommitBuilder;
/**
* @brief Transaction: XChainAccountCreateCommit
*
* Type: ttXCHAIN_ACCOUNT_CREATE_COMMIT (44)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainAccountCreateCommitBuilder to construct new transactions.
*/
class XChainAccountCreateCommit : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_ACCOUNT_CREATE_COMMIT;
/**
* @brief Construct a XChainAccountCreateCommit transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainAccountCreateCommit(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainAccountCreateCommit");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfDestination (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->tx_->at(sfDestination);
}
/**
* @brief Get sfAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->tx_->at(sfAmount);
}
/**
* @brief Get sfSignatureReward (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSignatureReward() const
{
return this->tx_->at(sfSignatureReward);
}
};
/**
* @brief Builder for XChainAccountCreateCommit transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainAccountCreateCommitBuilder : public TransactionBuilderBase<XChainAccountCreateCommitBuilder>
{
public:
/**
* @brief Construct a new XChainAccountCreateCommitBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param destination The sfDestination field value.
* @param amount The sfAmount field value.
* @param signatureReward The sfSignatureReward field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainAccountCreateCommitBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination, std::decay_t<typename SF_AMOUNT::type::value_type> const& amount, std::decay_t<typename SF_AMOUNT::type::value_type> const& signatureReward, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainAccountCreateCommitBuilder>(ttXCHAIN_ACCOUNT_CREATE_COMMIT, account, sequence, fee)
{
setXChainBridge(xChainBridge);
setDestination(destination);
setAmount(amount);
setSignatureReward(signatureReward);
}
/**
* @brief Construct a XChainAccountCreateCommitBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainAccountCreateCommitBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_ACCOUNT_CREATE_COMMIT)
{
throw std::runtime_error("Invalid transaction type for XChainAccountCreateCommitBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAccountCreateCommitBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfDestination (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAccountCreateCommitBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* @brief Set sfAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAccountCreateCommitBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* @brief Set sfSignatureReward (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAccountCreateCommitBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* @brief Build and return the XChainAccountCreateCommit wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainAccountCreateCommit
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainAccountCreateCommit{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -1,371 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainAddAccountCreateAttestationBuilder;
/**
* @brief Transaction: XChainAddAccountCreateAttestation
*
* Type: ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION (46)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::CreateAcct
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainAddAccountCreateAttestationBuilder to construct new transactions.
*/
class XChainAddAccountCreateAttestation : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION;
/**
* @brief Construct a XChainAddAccountCreateAttestation transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainAddAccountCreateAttestation(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainAddAccountCreateAttestation");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfAttestationSignerAccount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAttestationSignerAccount() const
{
return this->tx_->at(sfAttestationSignerAccount);
}
/**
* @brief Get sfPublicKey (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getPublicKey() const
{
return this->tx_->at(sfPublicKey);
}
/**
* @brief Get sfSignature (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getSignature() const
{
return this->tx_->at(sfSignature);
}
/**
* @brief Get sfOtherChainSource (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOtherChainSource() const
{
return this->tx_->at(sfOtherChainSource);
}
/**
* @brief Get sfAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->tx_->at(sfAmount);
}
/**
* @brief Get sfAttestationRewardAccount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAttestationRewardAccount() const
{
return this->tx_->at(sfAttestationRewardAccount);
}
/**
* @brief Get sfWasLockingChainSend (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT8::type::value_type
getWasLockingChainSend() const
{
return this->tx_->at(sfWasLockingChainSend);
}
/**
* @brief Get sfXChainAccountCreateCount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainAccountCreateCount() const
{
return this->tx_->at(sfXChainAccountCreateCount);
}
/**
* @brief Get sfDestination (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->tx_->at(sfDestination);
}
/**
* @brief Get sfSignatureReward (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSignatureReward() const
{
return this->tx_->at(sfSignatureReward);
}
};
/**
* @brief Builder for XChainAddAccountCreateAttestation transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainAddAccountCreateAttestationBuilder : public TransactionBuilderBase<XChainAddAccountCreateAttestationBuilder>
{
public:
/**
* @brief Construct a new XChainAddAccountCreateAttestationBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param attestationSignerAccount The sfAttestationSignerAccount field value.
* @param publicKey The sfPublicKey field value.
* @param signature The sfSignature field value.
* @param otherChainSource The sfOtherChainSource field value.
* @param amount The sfAmount field value.
* @param attestationRewardAccount The sfAttestationRewardAccount field value.
* @param wasLockingChainSend The sfWasLockingChainSend field value.
* @param xChainAccountCreateCount The sfXChainAccountCreateCount field value.
* @param destination The sfDestination field value.
* @param signatureReward The sfSignatureReward field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainAddAccountCreateAttestationBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::decay_t<typename SF_ACCOUNT::type::value_type> const& attestationSignerAccount, std::decay_t<typename SF_VL::type::value_type> const& publicKey, std::decay_t<typename SF_VL::type::value_type> const& signature, std::decay_t<typename SF_ACCOUNT::type::value_type> const& otherChainSource, std::decay_t<typename SF_AMOUNT::type::value_type> const& amount, std::decay_t<typename SF_ACCOUNT::type::value_type> const& attestationRewardAccount, std::decay_t<typename SF_UINT8::type::value_type> const& wasLockingChainSend, std::decay_t<typename SF_UINT64::type::value_type> const& xChainAccountCreateCount, std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination, std::decay_t<typename SF_AMOUNT::type::value_type> const& signatureReward, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainAddAccountCreateAttestationBuilder>(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, account, sequence, fee)
{
setXChainBridge(xChainBridge);
setAttestationSignerAccount(attestationSignerAccount);
setPublicKey(publicKey);
setSignature(signature);
setOtherChainSource(otherChainSource);
setAmount(amount);
setAttestationRewardAccount(attestationRewardAccount);
setWasLockingChainSend(wasLockingChainSend);
setXChainAccountCreateCount(xChainAccountCreateCount);
setDestination(destination);
setSignatureReward(signatureReward);
}
/**
* @brief Construct a XChainAddAccountCreateAttestationBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainAddAccountCreateAttestationBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION)
{
throw std::runtime_error("Invalid transaction type for XChainAddAccountCreateAttestationBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfAttestationSignerAccount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setAttestationSignerAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAttestationSignerAccount] = value;
return *this;
}
/**
* @brief Set sfPublicKey (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setPublicKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfPublicKey] = value;
return *this;
}
/**
* @brief Set sfSignature (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setSignature(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfSignature] = value;
return *this;
}
/**
* @brief Set sfOtherChainSource (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setOtherChainSource(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOtherChainSource] = value;
return *this;
}
/**
* @brief Set sfAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* @brief Set sfAttestationRewardAccount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setAttestationRewardAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAttestationRewardAccount] = value;
return *this;
}
/**
* @brief Set sfWasLockingChainSend (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setWasLockingChainSend(std::decay_t<typename SF_UINT8::type::value_type> const& value)
{
object_[sfWasLockingChainSend] = value;
return *this;
}
/**
* @brief Set sfXChainAccountCreateCount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setXChainAccountCreateCount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainAccountCreateCount] = value;
return *this;
}
/**
* @brief Set sfDestination (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* @brief Set sfSignatureReward (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddAccountCreateAttestationBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* @brief Build and return the XChainAddAccountCreateAttestation wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainAddAccountCreateAttestation
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainAddAccountCreateAttestation{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -1,360 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainAddClaimAttestationBuilder;
/**
* @brief Transaction: XChainAddClaimAttestation
*
* Type: ttXCHAIN_ADD_CLAIM_ATTESTATION (45)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::CreateAcct
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainAddClaimAttestationBuilder to construct new transactions.
*/
class XChainAddClaimAttestation : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_ADD_CLAIM_ATTESTATION;
/**
* @brief Construct a XChainAddClaimAttestation transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainAddClaimAttestation(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainAddClaimAttestation");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfAttestationSignerAccount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAttestationSignerAccount() const
{
return this->tx_->at(sfAttestationSignerAccount);
}
/**
* @brief Get sfPublicKey (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getPublicKey() const
{
return this->tx_->at(sfPublicKey);
}
/**
* @brief Get sfSignature (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getSignature() const
{
return this->tx_->at(sfSignature);
}
/**
* @brief Get sfOtherChainSource (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOtherChainSource() const
{
return this->tx_->at(sfOtherChainSource);
}
/**
* @brief Get sfAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->tx_->at(sfAmount);
}
/**
* @brief Get sfAttestationRewardAccount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getAttestationRewardAccount() const
{
return this->tx_->at(sfAttestationRewardAccount);
}
/**
* @brief Get sfWasLockingChainSend (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT8::type::value_type
getWasLockingChainSend() const
{
return this->tx_->at(sfWasLockingChainSend);
}
/**
* @brief Get sfXChainClaimID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainClaimID() const
{
return this->tx_->at(sfXChainClaimID);
}
/**
* @brief Get sfDestination (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getDestination() const
{
if (hasDestination())
{
return this->tx_->at(sfDestination);
}
return std::nullopt;
}
/**
* @brief Check if sfDestination is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasDestination() const
{
return this->tx_->isFieldPresent(sfDestination);
}
};
/**
* @brief Builder for XChainAddClaimAttestation transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainAddClaimAttestationBuilder : public TransactionBuilderBase<XChainAddClaimAttestationBuilder>
{
public:
/**
* @brief Construct a new XChainAddClaimAttestationBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param attestationSignerAccount The sfAttestationSignerAccount field value.
* @param publicKey The sfPublicKey field value.
* @param signature The sfSignature field value.
* @param otherChainSource The sfOtherChainSource field value.
* @param amount The sfAmount field value.
* @param attestationRewardAccount The sfAttestationRewardAccount field value.
* @param wasLockingChainSend The sfWasLockingChainSend field value.
* @param xChainClaimID The sfXChainClaimID field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainAddClaimAttestationBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::decay_t<typename SF_ACCOUNT::type::value_type> const& attestationSignerAccount, std::decay_t<typename SF_VL::type::value_type> const& publicKey, std::decay_t<typename SF_VL::type::value_type> const& signature, std::decay_t<typename SF_ACCOUNT::type::value_type> const& otherChainSource, std::decay_t<typename SF_AMOUNT::type::value_type> const& amount, std::decay_t<typename SF_ACCOUNT::type::value_type> const& attestationRewardAccount, std::decay_t<typename SF_UINT8::type::value_type> const& wasLockingChainSend, std::decay_t<typename SF_UINT64::type::value_type> const& xChainClaimID, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainAddClaimAttestationBuilder>(ttXCHAIN_ADD_CLAIM_ATTESTATION, account, sequence, fee)
{
setXChainBridge(xChainBridge);
setAttestationSignerAccount(attestationSignerAccount);
setPublicKey(publicKey);
setSignature(signature);
setOtherChainSource(otherChainSource);
setAmount(amount);
setAttestationRewardAccount(attestationRewardAccount);
setWasLockingChainSend(wasLockingChainSend);
setXChainClaimID(xChainClaimID);
}
/**
* @brief Construct a XChainAddClaimAttestationBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainAddClaimAttestationBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_ADD_CLAIM_ATTESTATION)
{
throw std::runtime_error("Invalid transaction type for XChainAddClaimAttestationBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfAttestationSignerAccount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setAttestationSignerAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAttestationSignerAccount] = value;
return *this;
}
/**
* @brief Set sfPublicKey (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setPublicKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfPublicKey] = value;
return *this;
}
/**
* @brief Set sfSignature (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setSignature(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfSignature] = value;
return *this;
}
/**
* @brief Set sfOtherChainSource (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setOtherChainSource(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOtherChainSource] = value;
return *this;
}
/**
* @brief Set sfAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* @brief Set sfAttestationRewardAccount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setAttestationRewardAccount(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfAttestationRewardAccount] = value;
return *this;
}
/**
* @brief Set sfWasLockingChainSend (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setWasLockingChainSend(std::decay_t<typename SF_UINT8::type::value_type> const& value)
{
object_[sfWasLockingChainSend] = value;
return *this;
}
/**
* @brief Set sfXChainClaimID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setXChainClaimID(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainClaimID] = value;
return *this;
}
/**
* @brief Set sfDestination (SoeOptional)
* @return Reference to this builder for method chaining.
*/
XChainAddClaimAttestationBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* @brief Build and return the XChainAddClaimAttestation wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainAddClaimAttestation
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainAddClaimAttestation{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -1,240 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainClaimBuilder;
/**
* @brief Transaction: XChainClaim
*
* Type: ttXCHAIN_CLAIM (43)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainClaimBuilder to construct new transactions.
*/
class XChainClaim : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_CLAIM;
/**
* @brief Construct a XChainClaim transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainClaim(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainClaim");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfXChainClaimID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainClaimID() const
{
return this->tx_->at(sfXChainClaimID);
}
/**
* @brief Get sfDestination (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->tx_->at(sfDestination);
}
/**
* @brief Get sfDestinationTag (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
{
if (hasDestinationTag())
{
return this->tx_->at(sfDestinationTag);
}
return std::nullopt;
}
/**
* @brief Check if sfDestinationTag is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasDestinationTag() const
{
return this->tx_->isFieldPresent(sfDestinationTag);
}
/**
* @brief Get sfAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->tx_->at(sfAmount);
}
};
/**
* @brief Builder for XChainClaim transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainClaimBuilder : public TransactionBuilderBase<XChainClaimBuilder>
{
public:
/**
* @brief Construct a new XChainClaimBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param xChainClaimID The sfXChainClaimID field value.
* @param destination The sfDestination field value.
* @param amount The sfAmount field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainClaimBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::decay_t<typename SF_UINT64::type::value_type> const& xChainClaimID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination, std::decay_t<typename SF_AMOUNT::type::value_type> const& amount, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainClaimBuilder>(ttXCHAIN_CLAIM, account, sequence, fee)
{
setXChainBridge(xChainBridge);
setXChainClaimID(xChainClaimID);
setDestination(destination);
setAmount(amount);
}
/**
* @brief Construct a XChainClaimBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainClaimBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_CLAIM)
{
throw std::runtime_error("Invalid transaction type for XChainClaimBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainClaimBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfXChainClaimID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainClaimBuilder&
setXChainClaimID(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainClaimID] = value;
return *this;
}
/**
* @brief Set sfDestination (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainClaimBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* @brief Set sfDestinationTag (SoeOptional)
* @return Reference to this builder for method chaining.
*/
XChainClaimBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
return *this;
}
/**
* @brief Set sfAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainClaimBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* @brief Build and return the XChainClaim wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainClaim
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainClaim{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -1,216 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainCommitBuilder;
/**
* @brief Transaction: XChainCommit
*
* Type: ttXCHAIN_COMMIT (42)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainCommitBuilder to construct new transactions.
*/
class XChainCommit : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_COMMIT;
/**
* @brief Construct a XChainCommit transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainCommit(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainCommit");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfXChainClaimID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getXChainClaimID() const
{
return this->tx_->at(sfXChainClaimID);
}
/**
* @brief Get sfAmount (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getAmount() const
{
return this->tx_->at(sfAmount);
}
/**
* @brief Get sfOtherChainDestination (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
getOtherChainDestination() const
{
if (hasOtherChainDestination())
{
return this->tx_->at(sfOtherChainDestination);
}
return std::nullopt;
}
/**
* @brief Check if sfOtherChainDestination is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasOtherChainDestination() const
{
return this->tx_->isFieldPresent(sfOtherChainDestination);
}
};
/**
* @brief Builder for XChainCommit transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainCommitBuilder : public TransactionBuilderBase<XChainCommitBuilder>
{
public:
/**
* @brief Construct a new XChainCommitBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param xChainClaimID The sfXChainClaimID field value.
* @param amount The sfAmount field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainCommitBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::decay_t<typename SF_UINT64::type::value_type> const& xChainClaimID, std::decay_t<typename SF_AMOUNT::type::value_type> const& amount, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainCommitBuilder>(ttXCHAIN_COMMIT, account, sequence, fee)
{
setXChainBridge(xChainBridge);
setXChainClaimID(xChainClaimID);
setAmount(amount);
}
/**
* @brief Construct a XChainCommitBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainCommitBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_COMMIT)
{
throw std::runtime_error("Invalid transaction type for XChainCommitBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCommitBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfXChainClaimID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCommitBuilder&
setXChainClaimID(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfXChainClaimID] = value;
return *this;
}
/**
* @brief Set sfAmount (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCommitBuilder&
setAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfAmount] = value;
return *this;
}
/**
* @brief Set sfOtherChainDestination (SoeOptional)
* @return Reference to this builder for method chaining.
*/
XChainCommitBuilder&
setOtherChainDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOtherChainDestination] = value;
return *this;
}
/**
* @brief Build and return the XChainCommit wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainCommit
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainCommit{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -1,192 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainCreateBridgeBuilder;
/**
* @brief Transaction: XChainCreateBridge
*
* Type: ttXCHAIN_CREATE_BRIDGE (48)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainCreateBridgeBuilder to construct new transactions.
*/
class XChainCreateBridge : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_CREATE_BRIDGE;
/**
* @brief Construct a XChainCreateBridge transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainCreateBridge(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainCreateBridge");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfSignatureReward (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSignatureReward() const
{
return this->tx_->at(sfSignatureReward);
}
/**
* @brief Get sfMinAccountCreateAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getMinAccountCreateAmount() const
{
if (hasMinAccountCreateAmount())
{
return this->tx_->at(sfMinAccountCreateAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfMinAccountCreateAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMinAccountCreateAmount() const
{
return this->tx_->isFieldPresent(sfMinAccountCreateAmount);
}
};
/**
* @brief Builder for XChainCreateBridge transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainCreateBridgeBuilder : public TransactionBuilderBase<XChainCreateBridgeBuilder>
{
public:
/**
* @brief Construct a new XChainCreateBridgeBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param signatureReward The sfSignatureReward field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainCreateBridgeBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::decay_t<typename SF_AMOUNT::type::value_type> const& signatureReward, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainCreateBridgeBuilder>(ttXCHAIN_CREATE_BRIDGE, account, sequence, fee)
{
setXChainBridge(xChainBridge);
setSignatureReward(signatureReward);
}
/**
* @brief Construct a XChainCreateBridgeBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainCreateBridgeBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_CREATE_BRIDGE)
{
throw std::runtime_error("Invalid transaction type for XChainCreateBridgeBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCreateBridgeBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfSignatureReward (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCreateBridgeBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* @brief Set sfMinAccountCreateAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
XChainCreateBridgeBuilder&
setMinAccountCreateAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfMinAccountCreateAmount] = value;
return *this;
}
/**
* @brief Build and return the XChainCreateBridge wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainCreateBridge
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainCreateBridge{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -1,179 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainCreateClaimIDBuilder;
/**
* @brief Transaction: XChainCreateClaimID
*
* Type: ttXCHAIN_CREATE_CLAIM_ID (41)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainCreateClaimIDBuilder to construct new transactions.
*/
class XChainCreateClaimID : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_CREATE_CLAIM_ID;
/**
* @brief Construct a XChainCreateClaimID transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainCreateClaimID(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainCreateClaimID");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfSignatureReward (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_AMOUNT::type::value_type
getSignatureReward() const
{
return this->tx_->at(sfSignatureReward);
}
/**
* @brief Get sfOtherChainSource (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getOtherChainSource() const
{
return this->tx_->at(sfOtherChainSource);
}
};
/**
* @brief Builder for XChainCreateClaimID transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainCreateClaimIDBuilder : public TransactionBuilderBase<XChainCreateClaimIDBuilder>
{
public:
/**
* @brief Construct a new XChainCreateClaimIDBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param signatureReward The sfSignatureReward field value.
* @param otherChainSource The sfOtherChainSource field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainCreateClaimIDBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::decay_t<typename SF_AMOUNT::type::value_type> const& signatureReward, std::decay_t<typename SF_ACCOUNT::type::value_type> const& otherChainSource, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainCreateClaimIDBuilder>(ttXCHAIN_CREATE_CLAIM_ID, account, sequence, fee)
{
setXChainBridge(xChainBridge);
setSignatureReward(signatureReward);
setOtherChainSource(otherChainSource);
}
/**
* @brief Construct a XChainCreateClaimIDBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainCreateClaimIDBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_CREATE_CLAIM_ID)
{
throw std::runtime_error("Invalid transaction type for XChainCreateClaimIDBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCreateClaimIDBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfSignatureReward (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCreateClaimIDBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* @brief Set sfOtherChainSource (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainCreateClaimIDBuilder&
setOtherChainSource(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfOtherChainSource] = value;
return *this;
}
/**
* @brief Build and return the XChainCreateClaimID wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainCreateClaimID
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainCreateClaimID{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -1,205 +0,0 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class XChainModifyBridgeBuilder;
/**
* @brief Transaction: XChainModifyBridge
*
* Type: ttXCHAIN_MODIFY_BRIDGE (47)
* Delegable: Delegation::Delegable
* Amendment: featureXChainBridge
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use XChainModifyBridgeBuilder to construct new transactions.
*/
class XChainModifyBridge : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttXCHAIN_MODIFY_BRIDGE;
/**
* @brief Construct a XChainModifyBridge transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit XChainModifyBridge(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for XChainModifyBridge");
}
}
// Transaction-specific field getters
/**
* @brief Get sfXChainBridge (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_XCHAIN_BRIDGE::type::value_type
getXChainBridge() const
{
return this->tx_->at(sfXChainBridge);
}
/**
* @brief Get sfSignatureReward (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getSignatureReward() const
{
if (hasSignatureReward())
{
return this->tx_->at(sfSignatureReward);
}
return std::nullopt;
}
/**
* @brief Check if sfSignatureReward is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasSignatureReward() const
{
return this->tx_->isFieldPresent(sfSignatureReward);
}
/**
* @brief Get sfMinAccountCreateAmount (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getMinAccountCreateAmount() const
{
if (hasMinAccountCreateAmount())
{
return this->tx_->at(sfMinAccountCreateAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfMinAccountCreateAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMinAccountCreateAmount() const
{
return this->tx_->isFieldPresent(sfMinAccountCreateAmount);
}
};
/**
* @brief Builder for XChainModifyBridge transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class XChainModifyBridgeBuilder : public TransactionBuilderBase<XChainModifyBridgeBuilder>
{
public:
/**
* @brief Construct a new XChainModifyBridgeBuilder with required fields.
* @param account The account initiating the transaction.
* @param xChainBridge The sfXChainBridge field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
XChainModifyBridgeBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& xChainBridge, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<XChainModifyBridgeBuilder>(ttXCHAIN_MODIFY_BRIDGE, account, sequence, fee)
{
setXChainBridge(xChainBridge);
}
/**
* @brief Construct a XChainModifyBridgeBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
XChainModifyBridgeBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttXCHAIN_MODIFY_BRIDGE)
{
throw std::runtime_error("Invalid transaction type for XChainModifyBridgeBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfXChainBridge (SoeRequired)
* @return Reference to this builder for method chaining.
*/
XChainModifyBridgeBuilder&
setXChainBridge(std::decay_t<typename SF_XCHAIN_BRIDGE::type::value_type> const& value)
{
object_[sfXChainBridge] = value;
return *this;
}
/**
* @brief Set sfSignatureReward (SoeOptional)
* @return Reference to this builder for method chaining.
*/
XChainModifyBridgeBuilder&
setSignatureReward(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfSignatureReward] = value;
return *this;
}
/**
* @brief Set sfMinAccountCreateAmount (SoeOptional)
* @return Reference to this builder for method chaining.
*/
XChainModifyBridgeBuilder&
setMinAccountCreateAmount(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfMinAccountCreateAmount] = value;
return *this;
}
/**
* @brief Build and return the XChainModifyBridge wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
XChainModifyBridge
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return XChainModifyBridge{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
+250 -28
View File
@@ -2,6 +2,7 @@
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/IntrusivePointer.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/SHAMapHash.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
@@ -17,6 +18,7 @@
#include <xrpl/shamap/SHAMapNodeID.h>
#include <xrpl/shamap/SHAMapTreeNode.h>
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
@@ -40,7 +42,7 @@ class SHAMapSyncFilter;
/**
* Describes the current state of a given SHAMap
*/
enum class SHAMapState {
enum class SHAMapState : std::uint8_t {
/**
* The map is in flux and objects can be added and removed.
*
@@ -120,16 +122,42 @@ private:
*/
std::uint32_t cowid_ = 1;
// ledgerSeq_, state_ and full_ are touched on the nodestore fetch path and on a
// getMissingNodes() walk, neither of which may block, so pin them lock-free.
static_assert(std::atomic<std::uint32_t>::is_always_lock_free);
static_assert(std::atomic<SHAMapState>::is_always_lock_free);
static_assert(std::atomic<bool>::is_always_lock_free);
/**
* The sequence of the ledger that this map references, if any.
*
* Written when a map's ledger sequence is established (Ledger::setFull(),
* InboundLedger) while a nodestore reader thread reads it. Relaxed either
* way: it serves as a lookup hint for a nodestore keyed by hash.
*/
std::uint32_t ledgerSeq_ = 0;
std::atomic<std::uint32_t> ledgerSeq_ = 0;
SHAMapTreeNodePtr root_;
mutable SHAMapState state_;
/**
* The map's state.
*
* A getMissingNodes() walk writes it, through setInvalid() and
* clearSynching(), while whatever drives the acquisition reads it.
* The walk runs with the acquisition's lock released, so this is atomic
* rather than guarded.
*/
mutable std::atomic<SHAMapState> state_;
SHAMapType const type_;
bool backed_ = true; // Map is backed by the database
mutable bool full_ = false; // Map is believed complete in database
bool backed_ = true; // Map is backed by the database
/**
* Map is believed complete in database.
*
* Atomic because finishFetch() clears it on any nodestore reader thread,
* several of which can run at once.
*/
mutable std::atomic<bool> full_ = false;
public:
/**
@@ -152,7 +180,14 @@ public:
SHAMap&
operator=(SHAMap const&) = delete;
// Take a snapshot of the given map:
/**
* Take a snapshot of the given map.
*
* @param other The map to snapshot. An Invalid source yields an Invalid
* snapshot.
* @param isMutable Whether the snapshot may be modified. Ignored when other
* is Invalid.
*/
SHAMap(SHAMap const& other, bool isMutable);
// build new map
@@ -190,17 +225,33 @@ public:
//--------------------------------------------------------------------------
// Returns a new map that's a snapshot of this one.
// Handles copy on write for mutable snapshots.
/**
* Return a new map that is a snapshot of this one.
*
* Handles copy on write for mutable snapshots. An invalid map yields an
* invalid snapshot.
*
* @param isMutable Whether the snapshot may be modified.
* @return The snapshot.
*/
std::shared_ptr<SHAMap>
snapShot(bool isMutable) const;
/* Mark this SHAMap as "should be full", indicating
that the local server wants all the corresponding nodes
in durable storage.
*/
/**
* Mark this map as "should be full": the local server wants every node
* it references in durable storage, so a nodestore miss is a gap to report.
*
* Publishes the ledger sequence before the flag. The sequence store is
* relaxed and the flag store is release, and finishFetch() reads the
* sequence only after acquiring the flag, so the one miss that withdraws
* the flag always reports the sequence stored here. Keeping both stores in
* one place is what makes that order impossible to get wrong.
*
* @param ledgerSeq Sequence of the ledger this map belongs to, which the
* gap report names.
*/
void
setFull();
setFull(std::uint32_t ledgerSeq);
void
setLedgerSeq(std::uint32_t lseq);
@@ -296,9 +347,13 @@ public:
* concurrency, to discover nodes referenced in the
* SHAMap but not available locally.
*
* Only a leaf may occupy a position at or beyond kLeafDepth. A map that
* breaks that is marked Invalid and the traversal is abandoned, so callers
* ask isValid() to tell an empty result from a satisfied map.
*
* @param maxNodes The maximum number of found nodes to return
* @param filter The filter to use when retrieving nodes
* @param return The nodes known to be missing
* @return The nodes known to be missing, or empty if the map is Invalid
*/
std::vector<std::pair<SHAMapNodeID, UInt256>>
getMissingNodes(int maxNodes, SHAMapSyncFilter const* filter);
@@ -341,6 +396,9 @@ public:
* This function is used when receiving the root node of a SHAMap from a peer during ledger
* synchronization. The node must already have been deserialized.
*
* A root offered under a hash the map does not hold names another tree and
* is reported as invalid data. A root matching that hash is a duplicate.
*
* @param hash The expected hash of the root node.
* @param rootNode A deserialized root node to add.
* @param filter Optional sync filter to track received nodes.
@@ -360,6 +418,10 @@ public:
* is inserted at the position specified by nodeID. The node must already have been
* deserialized.
*
* A node that no valid tree can hold makes the map Invalid, which is
* terminal: the root hash committed to an impossible shape, so no peer
* can satisfy it.
*
* @param nodeID The position in the tree where this node belongs.
* @param treeNode A deserialized tree node to add.
* @param filter Optional sync filter to track received nodes.
@@ -375,16 +437,57 @@ public:
SHAMapTreeNodePtr treeNode,
SHAMapSyncFilter const* filter);
// status functions
void
/**
* Mark this map as immutable, so it can no longer be modified.
*
* @return false if the map is Invalid and was left unchanged, true
* otherwise.
*/
[[nodiscard]] bool
setImmutable();
bool
/**
* @return Whether the map is being synced against a hash it was given, so
* its hash is fixed while nodes may still be added.
*/
[[nodiscard]] bool
isSynching() const;
/**
* @return Whether the map is settled, with its hash and nodes fixed.
*/
[[nodiscard]] bool
isImmutable() const;
/**
* Mark this map as syncing, fixing its hash while still allowing missing
* nodes to be added.
*
* The map must be freshly constructed. The body asserts that the map is not
* Invalid, so a caller that ignores the precondition stops a build with
* assertions enabled.
*/
void
setSynching();
/**
* Mark this map as no longer syncing, so it can be modified again.
*
* Moves only a Synching map. An Immutable map stays settled, a Modifying
* map has nothing to clear, and Invalid is terminal.
*/
void
clearSynching();
bool
/**
* Whether the map can still be the map it claims to be.
*
* A map that is merely missing nodes is valid, and stays valid until
* something proves the tree it is syncing against cannot exist.
*
* @return Whether the map has not been proven impossible.
*/
[[nodiscard]] bool
isValid() const;
// caution: otherMap must be accessed only by this function
@@ -524,6 +627,51 @@ private:
using DeltaRef =
std::pair<boost::intrusive_ptr<SHAMapItem const>, boost::intrusive_ptr<SHAMapItem const>>;
/**
* The sequence of the ledger this map references, read atomically.
*
* @return The sequence, or zero if the map references no ledger.
*/
[[nodiscard]] std::uint32_t
ledgerSeq() const;
/**
* The current state, read atomically.
*
* Orders state_ alone. The tree's nodes carry no ordering guarantees.
*
* @return The state as of the call, which a concurrent walk may already
* have moved past.
*/
[[nodiscard]] SHAMapState
state() const;
/**
* Record that the map is provably not the one it claims to be.
*
* Private because only the map itself can prove that, from a node that
* contradicts the hashes it is syncing against. Cannot fail, since
* Invalid outranks every other state; see trySetState().
*/
void
setInvalid();
/**
* Move the map to a new state, atomically.
*
* With clearSynching(), the only writer of state_ past construction, so
* the order between the states lives in one place: Invalid outranks all
* of them and is always stored, while every other transition is refused
* once the map is Invalid, which is what makes that verdict terminal.
* clearSynching() is narrower and moves only Synching.
*
* @param desired The state to move to.
* @return false if the map is Invalid and the requested state is not,
* leaving it unchanged; true otherwise.
*/
bool
trySetState(SHAMapState desired);
// tree node cache operations
SHAMapTreeNodePtr
cacheLookup(SHAMapHash const& hash) const;
@@ -739,46 +887,120 @@ private:
};
inline void
SHAMap::setFull()
SHAMap::setFull(std::uint32_t ledgerSeq)
{
full_ = true;
ledgerSeq_.store(ledgerSeq, std::memory_order_relaxed);
full_.store(true, std::memory_order_release);
}
inline void
SHAMap::setLedgerSeq(std::uint32_t lseq)
{
ledgerSeq_ = lseq;
ledgerSeq_.store(lseq, std::memory_order_relaxed);
}
inline void
inline std::uint32_t
SHAMap::ledgerSeq() const
{
return ledgerSeq_.load(std::memory_order_relaxed);
}
inline SHAMapState
SHAMap::state() const
{
return state_.load(std::memory_order_acquire);
}
inline bool
SHAMap::trySetState(SHAMapState desired)
{
// Invalid is stored outright: a walk reaching that verdict has to win against a thread
// settling the map.
if (desired == SHAMapState::Invalid)
{
state_.store(SHAMapState::Invalid, std::memory_order_release);
return true;
}
// A failed exchange both reports the state and refreshes expected, so no load is needed
// ahead of the loop.
auto expected = SHAMapState::Modifying;
while (expected != SHAMapState::Invalid)
{
if (state_.compare_exchange_weak(
expected, desired, std::memory_order_acq_rel, std::memory_order_acquire))
{
return true;
}
}
return false;
}
inline bool
SHAMap::setImmutable()
{
XRPL_ASSERT(state_ != SHAMapState::Invalid, "xrpl::SHAMap::setImmutable : state is valid");
state_ = SHAMapState::Immutable;
SOMETIMES(!isValid(), "xrpl::SHAMap::setImmutable : map is invalid");
return trySetState(SHAMapState::Immutable);
}
inline bool
SHAMap::isSynching() const
{
return state_ == SHAMapState::Synching;
return state() == SHAMapState::Synching;
}
inline bool
SHAMap::isImmutable() const
{
return state() == SHAMapState::Immutable;
}
inline void
SHAMap::setSynching()
{
state_ = SHAMapState::Synching;
// Guarded, so this is not a way out of Invalid.
if (!trySetState(SHAMapState::Synching))
{
// Only ever called on a freshly constructed map.
// LCOV_EXCL_START
UNREACHABLE("xrpl::SHAMap::setSynching : map is invalid");
// LCOV_EXCL_STOP
}
}
inline void
SHAMap::clearSynching()
{
state_ = SHAMapState::Modifying;
// Only Synching moves. A walk can end after the ledger settled, and then the map stays
// Immutable; a Modifying map has nothing to clear; an invalid map stays invalid. Only that
// last refusal is logged, since a concurrent walk is what produces it.
auto expected = SHAMapState::Synching;
if (state_.compare_exchange_strong(
expected, SHAMapState::Modifying, std::memory_order_acq_rel, std::memory_order_acquire))
{
return;
}
bool const invalid = expected == SHAMapState::Invalid;
SOMETIMES(invalid, "xrpl::SHAMap::clearSynching : map is invalid");
if (invalid)
{
JLOG(journal_.warn()) << "Refused to clear synching on an invalid map, root hash "
<< root_->getHash();
}
}
inline bool
SHAMap::isValid() const
{
return state_ != SHAMapState::Invalid;
return state() != SHAMapState::Invalid;
}
inline void
SHAMap::setInvalid()
{
// Through trySetState() like every other transition, so state_ has one writer funnel.
trySetState(SHAMapState::Invalid);
}
inline void
+15 -3
View File
@@ -31,7 +31,19 @@ private:
*/
TaggedPointer hashesAndChildren_;
std::uint32_t fullBelowGen_ = 0;
// Pin that wrapping fullBelowGen_ in an atomic keeps the packed layout, and that the load
// isFullBelow() does once per node of every walk stays lock-free.
static_assert(std::atomic<std::uint32_t>::is_always_lock_free);
static_assert(sizeof(std::atomic<std::uint32_t>) == sizeof(std::uint32_t));
static_assert(alignof(std::atomic<std::uint32_t>) == alignof(std::uint32_t));
/**
* Written from more than one thread, since canonicalization shares a node
* between maps and a walk can run with the acquisition lock released (see
* SHAMap::state_). Relaxed both ways, since a generation is only compared
* for equality and the children it vouches for are published through lock_.
*/
std::atomic<std::uint32_t> fullBelowGen_ = 0;
std::uint16_t isBranch_ = 0;
/**
@@ -204,13 +216,13 @@ SHAMapInnerNode::getBranchCount() const
inline bool
SHAMapInnerNode::isFullBelow(std::uint32_t generation) const
{
return fullBelowGen_ == generation;
return fullBelowGen_.load(std::memory_order_relaxed) == generation;
}
inline void
SHAMapInnerNode::setFullBelowGen(std::uint32_t gen)
{
fullBelowGen_ = gen;
fullBelowGen_.store(gen, std::memory_order_relaxed);
}
} // namespace xrpl
@@ -0,0 +1,52 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <set>
#include <vector>
namespace xrpl {
class ReadView;
/**
* @brief Invariants for TransactionProposal ledger entries.
*
* - Only transaction types implementing proposal lifecycle operations may
* create or modify proposals, and a failed one leaves no proposal effect.
* - A proposal is deleted only by TransactionProposalCancel or together with
* the Ticket it is keyed to, and a deleted Ticket takes the proposal keyed
* to it along.
* - Collected signer arrays remain sorted, unique, and bounded.
*/
class ValidTransactionProposal
{
// The Ticket each deleted proposal was keyed to; a proposal may go with
// its Ticket, or by Cancel, and no other way.
std::vector<uint256> ticketsOfDeletedProposals_;
// The proposal each deleted Ticket was keyed to, whether or not one
// existed; none may survive its Ticket.
std::vector<Keylet> proposalsOfDeletedTickets_;
std::set<uint256> deletedTickets_;
std::uint32_t created_ = 0;
std::uint32_t modified_ = 0;
std::uint32_t deleted_ = 0;
bool invalidSignerArrays_ = false;
public:
void
visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after);
[[nodiscard]] bool
finalize(STTx const& tx, TER result, XRPAmount, ReadView const& view, beast::Journal const& j)
const;
};
} // namespace xrpl
@@ -1,12 +1,15 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/AMMInvariant.h>
#include <xrpl/tx/invariants/CosignerInvariant.h>
#include <xrpl/tx/invariants/DirectoryInvariant.h>
#include <xrpl/tx/invariants/FreezeInvariant.h>
#include <xrpl/tx/invariants/LoanBrokerInvariant.h>
@@ -21,6 +24,7 @@
#include <boost/multiprecision/cpp_int.hpp>
#include <cstdint>
#include <map>
#include <set>
#include <string>
#include <tuple>
@@ -363,11 +367,27 @@ public:
* unique to pseudo-accounts. Check that all pseudo-accounts are following the
* rules, and that only pseudo-accounts look like pseudo-accounts.
*
* Post-fixCleanup3_5_0, a pseudo-account also owns only entries that belong
* to its own AMM, Vault or LoanBroker:
* - AMM: its AMM entry, its holdings of the two pool assets, and the trust
* lines of its LP token,
* - Vault: its share MPTokenIssuance, its holding of the vault asset, and the
* LoanBrokers operating on this vault,
* - LoanBroker: its Loans and its holding of the vault asset (the cover).
*
* A pseudo-account cannot sign transactions, so it could never accept or
* remove any other object linked into its owner directory. Such an object
* would pin the directory and block deletion of the owning object.
*/
class ValidPseudoAccounts
{
std::vector<std::string> errors_;
// Entries newly linked into owner directories, keyed by the directory
// owner. Whether an owner is a pseudo-account is decided in finalize,
// where the view is available.
std::map<AccountID, std::vector<uint256>> ownerDirAdditions_;
public:
void
visitEntry(bool, SLE::ConstRef, SLE::ConstRef);
@@ -464,6 +484,7 @@ using InvariantChecks = std::tuple<
ValidAmounts,
ValidMPTTransfer,
ObjectHasPseudoAccount,
ValidTransactionProposal,
SponsorshipOwnerCountsMatch,
SponsorshipAccountCountMatchesField>;
+46 -1
View File
@@ -8,6 +8,7 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
@@ -29,7 +30,7 @@ namespace xrpl {
* - vault must have MPTokenIssuance for shares
* - vault without shares outstanding must have no shares
* - loss unrealized does not exceed the difference between assets total and
* assets available
* assets available (Legacy/CashBasis), or AssetsDeployed (FixedPrecision)
* - assets available do not exceed assets total
* - vault deposit increases assets and share issuance, and adds to:
* total assets, assets available, shares outstanding
@@ -46,6 +47,14 @@ namespace xrpl {
* - vault withdrawal may not succeed when the vault phase is Investment
* - closed-ended loan origination (ttLOAN_SET) may only succeed when the
* vault phase is Investment
* - FixedPrecision only: AssetsDeployed is non-negative and exactly
* representable at the asset's precision; AssetsDeployed changes only for
* ttLOAN_SET, ttLOAN_PAY and ttLOAN_MANAGE; AssetsDeployed is zero right
* after ttVAULT_CREATE and whenever shares outstanding return to zero; the
* stored AssetsTotal cache equals AssetsAvailable + AssetsDeployed, rounded
* Downward to the asset's precision
* - FixedPrecision only: YieldUnrealized is non-negative; YieldUnrealized
* changes only for ttLOAN_SET, ttLOAN_PAY and ttLOAN_MANAGE
*
* Immutability of VaultKind, SubscriptionDate and RedemptionDate is enforced
* by NoModifiedUnmodifiableFields (see InvariantCheck.cpp). From
@@ -68,6 +77,9 @@ class ValidVault
Number assetsAvailable = 0;
Number assetsMaximum = 0;
Number lossUnrealized = 0;
Number assetsDeployed = 0;
Number yieldUnrealized = 0;
VaultVersion version = VaultVersion::Legacy;
std::optional<std::uint8_t> vaultKind;
std::optional<std::uint32_t> subscriptionDate;
std::optional<std::uint32_t> redemptionDate;
@@ -217,6 +229,39 @@ private:
[[nodiscard]] bool
finalizeLoanSet(ReadView const& view, beast::Journal const& j) const;
/**
* @brief Check that a vault's AssetsTotal and AssetsAvailable deltas add
* up against the real transfer, for deposit/withdraw/clawback.
*
* Shared body of the ttVAULT_DEPOSIT, ttVAULT_WITHDRAW and
* ttVAULT_CLAWBACK "assets outstanding"/"assets available" add-up checks,
* which differ only in the rounded/exact deltas plugged in and the verb
* used in the log message.
*
* @param afterVault Snapshot of the vault after the transaction.
* @param beforeVault Snapshot of the vault before the transaction.
* @param vaultAsset The vault's underlying asset.
* @param fix340Enabled Whether fixCleanup3_4_0 is enabled.
* @param minScale The posterior scale computed by computeVaultMinScale.
* @param roundedVaultDelta The real transfer's delta, rounded to minScale.
* @param exactVaultDelta The real transfer's exact (unrounded) delta.
* @param verb The transaction name used in the log message ("deposit",
* "withdrawal" or "clawback").
* @param j Journal for logging.
* @return false if either add-up check fails.
*/
[[nodiscard]] static bool
checkTotalsAddUp(
Vault const& afterVault,
Vault const& beforeVault,
Asset const& vaultAsset,
bool fix340Enabled,
std::int32_t minScale,
Number const& roundedVaultDelta,
Number const& exactVaultDelta,
char const* verb,
beast::Journal const& j);
public:
// Compute the coarsest scale required to represent all numbers
[[nodiscard]] static std::int32_t
+5 -18
View File
@@ -818,13 +818,10 @@ flow(
* TakerPays. We reverse pays and gets because during crossing
* we are taking, therefore the owner must deliver the full TakerPays and
* the entire TakerGets doesn't have to be spent.
* Pre-fixFillOrKill amendment code fails if the entire TakerGets
* is not spent. fixFillOrKill addresses this issue.
* 2. If tfSell is also set then the owner must spend the entire TakerGets
* even if it means obtaining more than TakerPays. Since the pays and gets
* are reversed, the owner must send the entire TakerGets.
*/
bool const fillOrKillEnabled = baseView.rules().enabled(fixFillOrKill);
if (actualOut != outReq)
{
@@ -840,13 +837,9 @@ flow(
}
if (!partialPayment)
{
// If we're offerCrossing a !partialPayment, then we're handling tfFillOrKill.
// Pre-fixFillOrKill amendment:
// That case is handled below; not here.
// fixFillOrKill amendment:
// That case is handled here if tfSell is also not set; i.e, case 1.
if (offerCrossing == OfferCrossing::No ||
(fillOrKillEnabled && offerCrossing != OfferCrossing::Sell))
// If partialPayment is false, the caller requires full outReq (payments, or FillOrKill
// offers).
if (offerCrossing != OfferCrossing::Sell)
{
return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
}
@@ -856,16 +849,10 @@ flow(
return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
}
}
if (offerCrossing != OfferCrossing::No &&
(!partialPayment && (!fillOrKillEnabled || offerCrossing == OfferCrossing::Sell)))
if (!partialPayment && offerCrossing == OfferCrossing::Sell)
{
// If we're offer crossing and partialPayment is *not* true, then
// we're handling a FillOrKill offer. In this case remainingIn must
// be zero (all funds must be consumed) or else we kill the offer.
// Pre-fixFillOrKill amendment:
// Handles both cases 1. and 2.
// fixFillOrKill amendment:
// Handles 2. 1. is handled above and falls through for tfSell.
// we're handling a FillOrKill offer.
XRPL_ASSERT(remainingIn, "xrpl::flow : nonzero remainingIn");
if (remainingIn && *remainingIn != beast::kZero)
return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
@@ -1,336 +0,0 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/Transactor.h>
#include <cstddef>
#include <cstdint>
namespace xrpl {
constexpr size_t kXbridgeMaxAccountCreateClaims = 128;
// Attach a new bridge to a door account. Once this is done, the cross-chain
// transfer transactions may be used to transfer funds from this account.
class XChainCreateBridge : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit XChainCreateBridge(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
class BridgeModify : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit BridgeModify(ApplyContext& ctx) : Transactor(ctx)
{
}
static std::uint32_t
getFlagsMask(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
using XChainModifyBridge = BridgeModify;
//------------------------------------------------------------------------------
// Claim funds from a `XChainCommit` transaction. This is normally not needed,
// but may be used to handle transaction failures or if the destination account
// was not specified in the `XChainCommit` transaction. It may only be used
// after a quorum of signatures have been sent from the witness servers.
//
// If the transaction succeeds in moving funds, the referenced `XChainClaimID`
// ledger object will be destroyed. This prevents transaction replay. If the
// transaction fails, the `XChainClaimID` will not be destroyed and the
// transaction may be re-run with different parameters.
class XChainClaim : public Transactor
{
public:
// Blocker since we cannot accurately calculate the consequences
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Blocker;
explicit XChainClaim(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
//------------------------------------------------------------------------------
// Put assets into trust on the locking-chain so they may be wrapped on the
// issuing-chain, or return wrapped assets on the issuing-chain so they can be
// unlocked on the locking-chain. The second step in a cross-chain transfer.
class XChainCommit : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Custom;
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
explicit XChainCommit(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
//------------------------------------------------------------------------------
// Create a new claim id owned by the account. This is the first step in a
// cross-chain transfer. The claim id must be created on the destination chain
// before the `XChainCommit` transaction (which must reference this number) can
// be sent on the source chain. The account that will send the `XChainCommit` on
// the source chain must be specified in this transaction (see note on the
// `SourceAccount` field in the `XChainClaimID` ledger object for
// justification). The actual sequence number must be retrieved from a validated
// ledger.
class XChainCreateClaimID : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit XChainCreateClaimID(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
//------------------------------------------------------------------------------
// Provide attestations from a witness server attesting to events on
// the other chain. The signatures must be from one of the keys on the door's
// signer's list at the time the signature was provided. However, if the
// signature list changes between the time the signature was submitted and the
// quorum is reached, the new signature set is used and some of the currently
// collected signatures may be removed. Also note the reward is only sent to
// accounts that have keys on the current list.
class XChainAddClaimAttestation : public Transactor
{
public:
// Blocker since we cannot accurately calculate the consequences
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Blocker;
explicit XChainAddClaimAttestation(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
class XChainAddAccountCreateAttestation : public Transactor
{
public:
// Blocker since we cannot accurately calculate the consequences
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Blocker;
explicit XChainAddAccountCreateAttestation(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
//------------------------------------------------------------------------------
// This is a special transaction used for creating accounts through a
// cross-chain transfer. A normal cross-chain transfer requires a "chain claim
// id" (which requires an existing account on the destination chain). One
// purpose of the "chain claim id" is to prevent transaction replay. For this
// transaction, we use a different mechanism: the accounts must be claimed on
// the destination chain in the same order that the `XChainCreateAccountCommit`
// transactions occurred on the source chain.
//
// This transaction can only be used for XRP to XRP bridges.
//
// IMPORTANT: This transaction should only be enabled if the witness
// attestations will be reliably delivered to the destination chain. If the
// signatures are not delivered (for example, the chain relies on user wallets
// to collect signatures) then account creation would be blocked for all
// transactions that happened after the one waiting on attestations. This could
// be used maliciously. To disable this transaction on XRP to XRP bridges, the
// bridge's `MinAccountCreateAmount` should not be present.
//
// Note: If this account already exists, the XRP is transferred to the existing
// account. However, note that unlike the `XChainCommit` transaction, there is
// no error handling mechanism. If the claim transaction fails, there is no
// mechanism for refunds. The funds are permanently lost. This transaction
// should still only be used for account creation.
class XChainCreateAccountCommit : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit XChainCreateAccountCommit(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
using XChainAccountCreateCommit = XChainCreateAccountCommit;
//------------------------------------------------------------------------------
} // namespace xrpl
@@ -20,11 +20,14 @@ public:
{
}
static bool
checkExtraFeatures(PreflightContext const& ctx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static bool
checkExtraFeatures(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static NotTEC
preflight(PreflightContext const& ctx);
@@ -5,6 +5,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
@@ -45,9 +46,29 @@ public:
SLE::Ref loanSle,
SLE::Ref brokerSle,
SLE::Ref vaultSle,
Asset const& vaultAsset,
beast::Journal j);
/**
* @brief Compute the First-Loss Capital cover amount for a defaulted Loan on a FixedPrecision
* Vault, capped at the LoanBroker's CoverAvailable and floored so that AssetsAvailable + cover
* is exactly on its posterior grid.
*
* The LoanBroker absorbs the rounding. When CoverAvailable - cover needs finer digits than the
* broker's posterior grid, it rounds to nearest, as the broker's trust line does, so
* CoverAvailable can move by up to half a posterior ulp more or less than the cover. When the
* two grids differ, no cover is exact on both sides; the Vault side is kept exact.
*
* The one exception: when AssetsAvailable has digits finer than the 16-digit cover can carry,
* no cover cancels them, and the balance writer rounds AssetsAvailable + cover to nearest,
* by at most half a posterior ulp. VaultDeposit and LoanPay credits have the same limit.
* @param loanSle The Loan being defaulted.
* @param brokerSle The Loan's LoanBroker.
* @param vaultSle The LoanBroker's Vault.
* @return The cover amount, in the Vault's Asset.
*/
static STAmount
calculateDefaultCover(SLE::Ref loanSle, SLE::Ref brokerSle, SLE::Ref vaultSle);
/**
* Helper function that might be needed by other transactors
*/
@@ -52,6 +52,13 @@ private:
TER
applyFee();
/**
* Write the default Feature Extension fee settings into FeeSettings on
* `featureSmartEscrow` activation.
*/
void
initializeVMFees();
TER
applyUNLModify();
};
+92 -14
View File
@@ -200,11 +200,23 @@ Ledger::Ledger(
sle->at(sfReserveIncrement) = *f;
sle->at(sfReferenceFeeUnits) = kFeeUnitsDeprecated;
}
if (std::ranges::find(amendments, featureSmartEscrow) != amendments.end())
{
sle->at(sfGasLimit) = fees.gasLimit;
sle->at(sfBytecodeSizeLimit) = fees.bytecodeSizeLimit;
sle->at(sfGasPrice) = fees.gasPrice;
}
rawInsert(sle);
}
stateMap_.flushDirty(NodeObjectType::AccountNode);
setImmutable();
// Built locally. See Ledger::setImmutable().
if (!setImmutable())
{
// LCOV_EXCL_START
logicError("Ledger::Ledger(CreateGenesisT, ...): genesis ledger map is invalid");
// LCOV_EXCL_STOP
}
}
Ledger::Ledger(
@@ -215,7 +227,7 @@ Ledger::Ledger(
Fees const& fees,
Family& family,
beast::Journal j)
: immutable_(true)
: immutable_(false)
, txMap_(SHAMapType::TRANSACTION, info.txHash, family)
, stateMap_(SHAMapType::STATE, info.accountHash, family)
, fees_(fees)
@@ -238,8 +250,20 @@ Ledger::Ledger(
JLOG(j.warn()) << "Don't have state data root for ledger" << header_.seq;
}
txMap_.setImmutable();
stateMap_.setImmutable();
// Loaded locally. See Ledger::setImmutable().
if (setMapsImmutable())
{
immutable_ = true;
}
else
{
// LCOV_EXCL_START
JLOG(j.error()) << "Invalid map for ledger " << header_.seq;
UNREACHABLE("xrpl::Ledger::Ledger(LedgerHeader const&, ...) : map is invalid");
// Treat it as a damaged ledger: the code below recomputes the hash and re-acquires.
loaded = false;
// LCOV_EXCL_STOP
}
if (!setup())
loaded = false;
@@ -310,27 +334,50 @@ Ledger::Ledger(
setup();
}
void
bool
Ledger::setImmutable(bool rehash)
{
// Force update, since this is the only
// place the hash transitions to valid
if (!immutable_ && rehash)
// A map found structurally invalid during sync must never be made immutable: isValid() tests
// only for Invalid, and an immutable ledger is treated as persistable. Asked before anything is
// written, so a refusal leaves the header exactly as it was rather than half relabeled.
if (!mapsValid())
return false;
// Read here but written to the header below, once the maps are settled: getHash() can unshare a
// dirty tree, so it has to run while the map is still mutable, while a write to the header must
// wait until both maps have made it. Skipped once the ledger is immutable, since its maps can
// no longer change.
bool const deriveMapHashes = !immutable_ && rehash;
UInt256 const txHash = deriveMapHashes ? txMap_.getHash().asUInt256() : UInt256{};
UInt256 const accountHash = deriveMapHashes ? stateMap_.getHash().asUInt256() : UInt256{};
// Both were valid at the check above, but a concurrent walk can invalidate one in between (see
// SHAMap::state_), so the result is checked. setInvalid() outranks Immutable and can land
// after both maps have been settled, so this narrows the window it guards.
bool const bothImmutable = setMapsImmutable();
SOMETIMES(!bothImmutable, "xrpl::Ledger::setImmutable : map invalidated while going immutable");
if (!bothImmutable)
return false;
// Written only now, so losing the race above leaves the header describing what the ledger was
// built from rather than a map that has since been abandoned. Forced rather than conditional,
// since this is the only place the hash transitions to valid.
if (deriveMapHashes)
{
header_.txHash = txMap_.getHash().asUInt256();
header_.accountHash = stateMap_.getHash().asUInt256();
header_.txHash = txHash;
header_.accountHash = accountHash;
}
if (rehash)
header_.hash = calculateLedgerHash(header_);
// Set last, so isImmutable() reports only a ledger whose maps are both immutable.
immutable_ = true;
txMap_.setImmutable();
stateMap_.setImmutable();
setup();
return true;
}
void
bool
Ledger::setAccepted(
NetClock::time_point closeTime,
NetClock::duration closeResolution,
@@ -342,7 +389,17 @@ Ledger::setAccepted(
header_.closeTime = closeTime;
header_.closeTimeResolution = closeResolution;
header_.closeFlags = correctCloseTime ? 0 : kSLcfNoConsensusTime;
setImmutable();
// Built locally. See Ledger::setImmutable().
if (!setImmutable())
{
// LCOV_EXCL_START
JLOG(j_.error()) << "Invalid map for accepted ledger " << header_.seq;
UNREACHABLE("xrpl::Ledger::setAccepted : map is invalid");
return false;
// LCOV_EXCL_STOP
}
return true;
}
bool
@@ -566,6 +623,7 @@ Ledger::setup()
{
bool oldFees = false;
bool newFees = false;
bool extensionFees = false;
{
auto const baseFee = sle->at(~sfBaseFee);
auto const reserveBase = sle->at(~sfReserveBase);
@@ -600,6 +658,20 @@ Ledger::setup()
assign(fees_.increment, reserveIncrementXRP);
newFees = baseFeeXRP || reserveBaseXRP || reserveIncrementXRP;
}
{
auto const gasLimit = sle->at(~sfGasLimit);
auto const bytecodeSizeLimit = sle->at(~sfBytecodeSizeLimit);
auto const gasPrice = sle->at(~sfGasPrice);
// An absent field resolves to the protocol constant.
if (rules_.enabled(featureSmartEscrow))
{
fees_.gasLimit = gasLimit.value_or(kDefaultGasLimit);
fees_.bytecodeSizeLimit = bytecodeSizeLimit.value_or(kDefaultBytecodeSizeLimit);
fees_.gasPrice = gasPrice.value_or(kDefaultGasPrice);
}
extensionFees = gasLimit || bytecodeSizeLimit || gasPrice;
}
if (oldFees && newFees)
{
// Should be all of one or the other, but not both
@@ -610,6 +682,12 @@ Ledger::setup()
// Can't populate the new fees before the amendment is enabled
ret = false;
}
if (!rules_.enabled(featureSmartEscrow) && extensionFees)
{
// Can't populate the extension fees before the amendment is
// enabled
ret = false;
}
}
}
catch (SHAMapMissingNode const&)
+29 -7
View File
@@ -511,9 +511,14 @@ doWithdraw(
AccountID const& dstAcct,
AccountID const& sourceAcct,
XRPAmount priorBalance,
STAmount const& amount,
STAmount const& destinationAmount,
STAmount const& sourceAmount,
beast::Journal j)
{
XRPL_ASSERT(
destinationAmount.asset() == sourceAmount.asset(),
"xrpl::doWithdraw : delivered and source amounts use the same asset");
auto const dstSle = ctx.view.read(keylet::account(dstAcct));
// Create a trust line or MPToken for a self-destination only when there
@@ -523,9 +528,10 @@ doWithdraw(
// create+delete MPTokens in the same transaction.
if (dstAcct == senderAcct)
{
if (amount > beast::kZero || !ctx.view.rules().enabled(fixCleanup3_4_0))
if (destinationAmount > beast::kZero || !ctx.view.rules().enabled(fixCleanup3_4_0))
{
if (auto const ter = addEmptyHolding(ctx, senderAcct, priorBalance, amount.asset(), j);
if (auto const ter =
addEmptyHolding(ctx, senderAcct, priorBalance, destinationAmount.asset(), j);
!isTesSuccess(ter) && ter != tecDUPLICATE)
return ter;
}
@@ -540,17 +546,33 @@ doWithdraw(
if (accountHolds(
ctx.view,
sourceAcct,
amount.asset(),
destinationAmount.asset(),
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
j) < amount)
j) < sourceAmount)
{
// LCOV_EXCL_START
JLOG(j.error()) << "doWithdraw: negative balance of broker cover assets.";
JLOG(j.error()) << "doWithdraw: source account holds fewer assets than the withdrawal.";
return tefINTERNAL;
// LCOV_EXCL_STOP
}
// A transfer fee applies. Redeem the gross amount with the issuer, then
// issue the net amount to the destination.
if (sourceAmount != destinationAmount)
{
auto const issuer = destinationAmount.getIssuer();
XRPL_ASSERT(
!destinationAmount.native() && sourceAcct != issuer && dstAcct != issuer,
"xrpl::doWithdraw : transfer fee applies between token holders");
if (auto const ter = directSendNoFee(ctx.view, sourceAcct, issuer, sourceAmount, false, j);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
return directSendNoFee(ctx.view, issuer, dstAcct, destinationAmount, false, j);
}
// A reserve sponsor only covers tx.Account's own objects, so resolve the
// sponsor against the destination. accountSend can auto-create a holding
// for dstAcct; keying on the destination ensures a third-party destination's
@@ -562,7 +584,7 @@ doWithdraw(
// Move the funds directly from the broker's pseudo-account to the
// dstAcct
return accountSend(
ctx.view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes);
ctx.view, sourceAcct, dstAcct, destinationAmount, j, *sponsorSle, WaiveTransferFee::Yes);
}
TER
+159 -5
View File
@@ -66,6 +66,161 @@ canApplyToBrokerCover(
return tesSUCCESS;
}
namespace detail {
[[nodiscard]] int
getPosteriorBrokerCoverScale(SLE::ConstRef vault, SLE::ConstRef broker, Number const& delta)
{
XRPL_ASSERT(
vault && vault->getType() == ltVAULT,
"xrpl::detail::getPosteriorBrokerCoverScale : valid Vault sle");
XRPL_ASSERT(
broker && broker->getType() == ltLOAN_BROKER,
"xrpl::detail::getPosteriorBrokerCoverScale : valid LoanBroker sle");
return posteriorAssetScale(
getVaultVersion(vault),
vault->at(sfAsset),
getVaultBaseScale(vault),
broker->at(sfCoverAvailable),
delta);
}
[[nodiscard]] STAmount
roundToPosteriorBrokerCoverScale(
SLE::ConstRef vault,
SLE::ConstRef broker,
STAmount const& delta,
Number::RoundingMode roundingMode)
{
XRPL_ASSERT(
vault && vault->getType() == ltVAULT,
"xrpl::detail::roundToPosteriorBrokerCoverScale : valid Vault sle");
XRPL_ASSERT(
broker && broker->getType() == ltLOAN_BROKER,
"xrpl::detail::roundToPosteriorBrokerCoverScale : valid LoanBroker sle");
XRPL_ASSERT(
delta.asset() == vault->at(sfAsset),
"xrpl::detail::roundToPosteriorBrokerCoverScale : delta and Vault asset match");
if (delta.integral())
return delta;
return roundToScale(delta, getPosteriorBrokerCoverScale(vault, broker, delta), roundingMode);
}
} // namespace detail
[[nodiscard]] STAmount
creditToPosteriorBrokerCoverScale(
SLE::ConstRef vault,
SLE::ConstRef broker,
Number const& raw,
Number::RoundingMode roundingMode)
{
XRPL_ASSERT(
vault && vault->getType() == ltVAULT,
"xrpl::creditToPosteriorBrokerCoverScale : valid Vault sle");
XRPL_ASSERT(
broker && broker->getType() == ltLOAN_BROKER,
"xrpl::creditToPosteriorBrokerCoverScale : valid LoanBroker sle");
Asset const asset = vault->at(sfAsset);
if (asset.integral())
{
NumberRoundModeGuard const rg(roundingMode);
return STAmount{asset, raw};
}
Number const reference = broker->at(sfCoverAvailable);
return detail::creditToPosteriorScale(
asset, reference, getVaultBaseScale(vault), raw, roundingMode);
}
[[nodiscard]] STAmount
debitToPosteriorBrokerCoverScale(
SLE::ConstRef vault,
SLE::ConstRef broker,
STAmount const& amount,
Number::RoundingMode roundingMode)
{
XRPL_ASSERT(
vault && vault->getType() == ltVAULT,
"xrpl::debitToPosteriorBrokerCoverScale : valid Vault sle");
XRPL_ASSERT(
broker && broker->getType() == ltLOAN_BROKER,
"xrpl::debitToPosteriorBrokerCoverScale : valid LoanBroker sle");
XRPL_ASSERT(
amount.asset() == vault->at(sfAsset),
"xrpl::debitToPosteriorBrokerCoverScale : amount and Vault asset match");
XRPL_ASSERT(!amount.negative(), "xrpl::debitToPosteriorBrokerCoverScale : non-negative amount");
return -detail::roundToPosteriorBrokerCoverScale(vault, broker, -amount, roundingMode);
}
[[nodiscard]] TER
checkOptionalBrokerCoverInflow(SLE::ConstRef vault, SLE::ConstRef broker, STAmount const& amount)
{
XRPL_ASSERT(
vault && vault->getType() == ltVAULT,
"xrpl::checkOptionalBrokerCoverInflow : valid Vault sle");
XRPL_ASSERT(
broker && broker->getType() == ltLOAN_BROKER,
"xrpl::checkOptionalBrokerCoverInflow : valid LoanBroker sle");
XRPL_ASSERT(
amount.asset() == vault->at(sfAsset),
"xrpl::checkOptionalBrokerCoverInflow : amount and Vault asset match");
XRPL_ASSERT(!amount.negative(), "xrpl::checkOptionalBrokerCoverInflow : non-negative amount");
if (getVaultVersion(vault) != VaultVersion::FixedPrecision)
return tesSUCCESS;
// amount is the effective credit (already floored on the posterior
// CoverAvailable grid by creditToPosteriorBrokerCoverScale); rounding it
// again as a standalone delta could hide a crossing.
STAmount const& rounded = amount;
int const baseScale = getVaultBaseScale(vault);
// Keep this explicit even though the Open-limit capacity check below rejects
// every coarsening transition too. The protocol defines both conditions
// independently.
if (detail::getPosteriorBrokerCoverScale(vault, broker, rounded) != baseScale)
return tecLIMIT_EXCEEDED;
Number const posterior = [&] {
NumberRoundModeGuard const rg(Number::RoundingMode::TowardsZero);
return broker->at(sfCoverAvailable) + rounded;
}();
if (posterior > getVaultOpenLimit(vault))
return tecLIMIT_EXCEEDED;
return tesSUCCESS;
}
void
adjustBrokerDebtTotal(
SLE::Ref brokerSle,
SLE::ConstRef vaultSle,
Number const& delta,
int vaultScale)
{
// On FixedPrecision Vaults, LoanSet adds principal to DebtTotal exactly, LoanPay and default
// subtract exact amounts. Thus DebtTotal must not be rounded coarser than the base scale.
if (getVaultVersion(vaultSle) == VaultVersion::FixedPrecision)
{
brokerSle->at(sfDebtTotal) += delta;
}
else
{
adjustImpreciseNumber(brokerSle->at(sfDebtTotal), delta, vaultSle->at(sfAsset), vaultScale);
}
}
Number
minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::ConstRef vaultSle)
{
XRPL_ASSERT(
vaultSle && vaultSle->getType() == ltVAULT, "xrpl::minimumBrokerCover : valid Vault sle");
NumberRoundModeGuard const mg(Number::RoundingMode::Upward);
return roundToAsset(
vaultSle->at(sfAsset),
tenthBipsOfValue(debtTotal, coverRateMinimum),
getVaultBaseScale(vaultSle));
}
bool
checkLendingProtocolDependencies(Rules const& rules, STTx const& tx)
{
@@ -250,14 +405,13 @@ loanPaymentDeltas(LoanPaymentParts const& parts)
namespace {
// Cash-basis accounting applies only when featureLendingProtocolV1_1 is
// enabled AND the specific Vault was created under it (LEVersion ==
// VaultVersion::CashBasis). Vaults created before activation keep instant
// interest recognition forever, even after the amendment later turns on.
// Cash-basis accounting applies to Vaults created under
// featureLendingProtocolV1_1 or a later version. Vaults created before
// activation keep instant interest recognition forever.
bool
cashBasisEnabled(SLE::ConstRef vaultSle)
{
return getVaultVersion(vaultSle) == VaultVersion::CashBasis;
return getVaultVersion(vaultSle) >= VaultVersion::CashBasis;
}
} // namespace
@@ -43,6 +43,9 @@ constexpr auto kProposalTxTypes = std::to_array<TxType>({
bool
isValidProposalTxnType(STObject const& proposedTx)
{
if (!proposedTx.isFieldPresent(sfTransactionType))
return false;
auto const type = proposedTx.getFieldU16(sfTransactionType);
if (std::ranges::find(kProposalTxTypes, type) != kProposalTxTypes.end())
@@ -4,6 +4,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/ledger/helpers/ProposalHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -319,6 +320,8 @@ getLedgerEntryOwnerCount(SLE const& sle)
UNREACHABLE("AccountRoots are not supported by object sponsorship.");
return 0;
// LCOV_EXCL_STOP
case ltESCROW:
return calculateAdditionalReserve(sle[~sfBytecode]);
default:
return 1;
}
+21 -6
View File
@@ -2,6 +2,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/MathUtilities.h>
#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
@@ -551,6 +552,22 @@ transferRate(ReadView const& view, STAmount const& amount)
return transferRate(view, amount.asset());
}
STAmount
addTransferFee(STAmount const& amount, Rate const& rate)
{
if (rate == kParityRate)
return amount;
return mulRatio(amount, rate.value, QUALITY_ONE, Number::RoundingMode::Upward);
}
STAmount
subtractTransferFee(STAmount const& amount, Rate const& rate)
{
if (rate == kParityRate)
return amount;
return mulRatio(amount, QUALITY_ONE, rate.value, Number::RoundingMode::Downward);
}
//------------------------------------------------------------------------------
//
// Holding operations
@@ -1347,9 +1364,8 @@ directSendNoLimitMPT(
{
// Number math loses precision on large MPT amounts, which can
// overcharge the sender. MPTs are integral, so compute the cost
// exactly and round it up, matching the payment engine.
auto const cost = mulRatio(saAmount.mpt(), rate.value, QUALITY_ONE, true);
saActual = STAmount(saAmount.asset(), cost.value());
// exactly, matching the payment engine.
saActual = addTransferFee(saAmount, rate);
}
else
{
@@ -1474,9 +1490,8 @@ directSendNoLimitMultiMPT(
{
// Number math loses precision on large MPT amounts, which can
// overcharge the sender. MPTs are integral, so compute the
// cost exactly and round it up, matching the payment engine.
auto const cost = mulRatio(amount.mpt(), rate.value, QUALITY_ONE, true);
actualSend = STAmount(amount.asset(), cost.value());
// cost exactly, matching the payment engine.
actualSend = addTransferFee(amount, rate);
}
else
{
+86 -26
View File
@@ -25,6 +25,8 @@
#include <cstdint>
#include <expected>
#include <optional>
#include <stdexcept>
#include <string_view>
#include <utility>
namespace xrpl {
@@ -46,7 +48,7 @@ fixedBaseScale(SLE::ConstRef vault)
}
// Thin, vault-specific wrapper over posteriorAssetScale: used by
// getPosteriorVaultScale and creditToPosteriorAvailableScale.
// getPosteriorVaultScale and the cash outflow rounding below.
[[nodiscard]] int
posteriorScale(SLE::ConstRef vault, Number const& reference, Number const& delta)
{
@@ -134,6 +136,18 @@ syncAssetsTotal(SLE::Ref vault)
vault->at(sfAssetsTotal) = STAmount{asset, getAssetsTotal(vault)};
}
[[nodiscard]] bool
debitIsNonZeroDust(SLE::ConstRef vault, Number const& amount)
{
if (amount == 0)
return false;
auto const total = getVaultVersion(vault) == VaultVersion::FixedPrecision
? vault->at(sfAssetsAvailable)
: getAssetsTotal(vault);
auto const asset = vault->at(sfAsset);
return STAmount{asset, total - amount} == STAmount{asset, total};
}
} // namespace
namespace detail {
@@ -181,11 +195,11 @@ posteriorAssetScale(
creditToPosteriorScale(
Asset const& asset,
Number const& reference,
int atScale,
int baseScale,
Number const& raw,
Number::RoundingMode roundingMode)
{
// Floor the SUM (reference + raw), not just raw, at atScale. See
// Floor the SUM (reference + raw), not just raw. See
// creditToPosteriorAvailableScale's doc: a delta floored on its own grid
// can still leave a 17-digit sum once the reference has crossed a power
// of ten.
@@ -194,9 +208,21 @@ creditToPosteriorScale(
// than Number holds (the sum is on the posterior grid, the reference on
// the finer one). Compute both under roundingMode, not the caller's
// ambient mode, so the credit this returns never exceeds raw.
//
// Take the grid from this same sum. A sum just under a power of ten that
// rounds up to it in another mode would pick the coarser grid, and
// flooring the unrounded sum on it would land below reference.
NumberRoundModeGuard const rg(roundingMode);
Number const flooredSum = roundToAsset(asset, reference + raw, atScale, roundingMode);
return STAmount{asset, flooredSum - reference};
Number const sum = reference + raw;
int const atScale = liveScale(sum, asset, baseScale);
Number const flooredSum = roundToAsset(asset, sum, atScale, roundingMode);
STAmount const credit{asset, flooredSum - reference};
XRPL_ASSERT_PARTS(
roundingMode != Number::RoundingMode::Downward ||
(credit >= beast::kZero && Number(credit) <= raw),
"xrpl::detail::creditToPosteriorScale",
"credit is between zero and raw");
return credit;
}
[[nodiscard]] int
@@ -302,6 +328,17 @@ adjustVaultBalances(SLE::Ref vault, VaultBalanceChange const& change, beast::Jou
return tesSUCCESS;
}
[[nodiscard]] TER
adjustVaultCash(SLE::Ref vault, STAmount const& delta, beast::Journal j)
{
if (getVaultVersion(vault) == VaultVersion::FixedPrecision)
return adjustVaultBalances(vault, {.cash = delta}, j);
vault->at(sfAssetsTotal) += delta;
vault->at(sfAssetsAvailable) += delta;
return tesSUCCESS;
}
[[nodiscard]] int
getVaultScale(SLE::ConstRef vault)
{
@@ -374,8 +411,8 @@ creditToPosteriorAvailableScale(
}
Number const reference = vault->at(sfAssetsAvailable);
int const scale = posteriorScale(vault, reference, raw);
return detail::creditToPosteriorScale(asset, reference, scale, raw, roundingMode);
return detail::creditToPosteriorScale(
asset, reference, fixedBaseScale(vault), raw, roundingMode);
}
[[nodiscard]] Number
@@ -428,6 +465,36 @@ vaultOpenZoneCapacity(SLE::ConstRef vault, Number const& roundedAmount)
return getAssetsTotal(vault) + vault->at(sfYieldUnrealized) + roundedAmount;
}
[[nodiscard]] TER
checkDebitNotDust(
SLE::ConstRef vault,
Number const& amount,
beast::Journal j,
std::string_view name)
{
try
{
if (debitIsNonZeroDust(vault, amount))
{
JLOG(j.debug()) << name << ": amount too small to change stored vault balance";
return tecPRECISION_LOSS;
}
}
// LCOV_EXCL_START
catch (std::overflow_error const&)
{
// It's easy to hit this exception from Number with large enough Scale
// so we avoid spamming the log and only use debug here.
JLOG(j.debug()) //
<< name << ": overflow error with"
<< " scale=" << (int)vault->at(sfScale) //
<< ", assetsTotal=" << vault->at(sfAssetsTotal) << ", amount=" << amount;
return tecPATH_DRY;
}
// LCOV_EXCL_STOP
return tesSUCCESS;
}
[[nodiscard]] std::optional<STAmount>
assetsToSharesDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const& assets)
{
@@ -575,6 +642,14 @@ isOnVaultBaseGrid(Asset const& asset, Number const& value, int baseScale)
roundToAsset(asset, value, baseScale, Number::RoundingMode::TowardsZero) == value;
}
[[nodiscard]] TER
checkAssetsMaximum(SLE::ConstRef vault, Number const& amount)
{
return isOnVaultBaseGrid(vault->at(sfAsset), amount, getVaultBaseScale(vault))
? TER{tesSUCCESS}
: TER{tecPRECISION_LOSS};
}
[[nodiscard]] Number
assetsTotalForWithdrawal(SLE::ConstRef vault, WaiveUnrealizedLoss waive)
{
@@ -584,22 +659,6 @@ assetsTotalForWithdrawal(SLE::ConstRef vault, WaiveUnrealizedLoss waive)
return assetTotal;
}
[[nodiscard]] bool
debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount)
{
if (amount == 0)
return false;
return STAmount{asset, total - amount} == STAmount{asset, total};
}
[[nodiscard]] Number
vaultDebitDustReference(SLE::ConstRef vault, Number const& assetsTotal)
{
return getVaultVersion(vault) == VaultVersion::FixedPrecision
? Number(vault->at(sfAssetsAvailable))
: assetsTotal;
}
[[nodiscard]] std::optional<STAmount>
assetsToSharesWithdraw(
SLE::ConstRef vault,
@@ -695,9 +754,10 @@ getVaultVersion(SLE::ConstRef vault)
[[nodiscard]] VaultVersion
getVaultDefaultVersion(Rules const& rules)
{
// FixedPrecision requires both cleanups, so a FixedPrecision Vault always
// sees them enabled.
if (rules.enabled(featureLendingProtocolV1_2) && rules.enabled(fixCleanup3_2_0) &&
// FixedPrecision requires featureLendingProtocolV1_1 (closed-ended Vaults and
// cash-basis accounting) and fixCleanup3_4_0, so a FixedPrecision Vault always
// sees both enabled. fixCleanup3_2_0 is already enabled on the network.
if (rules.enabled(featureLendingProtocolV1_1) && rules.enabled(featureLendingProtocolV1_2) &&
rules.enabled(fixCleanup3_4_0))
return VaultVersion::FixedPrecision;
if (rules.enabled(featureLendingProtocolV1_1))
+12 -5
View File
@@ -435,18 +435,25 @@ areMirrorsCurrent(SLE const& issuance, SLE const& mptoken)
}
void
setIssuerMirrorEpoch(SLE const& issuance, SLE& mptoken)
setIssuerMirrorCurrent(SLE const& issuance, SLE& mptoken)
{
XRPL_ASSERT(
issuance.getType() == ltMPTOKEN_ISSUANCE,
"xrpl::setIssuerMirrorEpoch : issuance MPTokenIssuance object");
"xrpl::setIssuerMirrorCurrent : issuance MPTokenIssuance object");
XRPL_ASSERT(
mptoken.getType() == ltMPTOKEN, "xrpl::setIssuerMirrorEpoch : mptoken MPToken object");
mptoken.getType() == ltMPTOKEN, "xrpl::setIssuerMirrorCurrent : mptoken MPToken object");
// Unlike the auditor mirror, the issuer mirror is not optional: every
// confidential MPToken carries one, so there is no existence check here.
//
// Record the key and epoch under which the mirror is now encrypted. Both fields remain absent
// at epoch 0: such mirrors use the original issuer key. The original key is stored in
// sfInitialIssuerEncryptionKey after the first rotation.
if (auto const epoch = issuance[~sfIssuerKeyEpoch].value_or(0); epoch != 0)
{
mptoken[sfIssuerKeyMirrorEpoch] = epoch;
mptoken[sfIssuerMirrorEncryptionKey] = issuance[sfIssuerEncryptionKey];
}
}
void
@@ -466,9 +473,9 @@ setAuditorMirrorEpoch(SLE const& issuance, SLE& mptoken)
}
void
setMirrorEpochs(SLE const& issuance, SLE& mptoken)
setMirrorsCurrent(SLE const& issuance, SLE& mptoken)
{
setIssuerMirrorEpoch(issuance, mptoken);
setIssuerMirrorCurrent(issuance, mptoken);
setAuditorMirrorEpoch(issuance, mptoken);
}
+3 -43
View File
@@ -12,8 +12,6 @@
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/digest.h>
@@ -105,9 +103,6 @@ enum class LedgerNameSpace : std::uint16_t {
NftokenBuyOffers = 'h',
NftokenSellOffers = 'i',
Amm = 'A',
Bridge = 'H',
XchainClaimId = 'Q',
XchainCreateAccountClaimId = 'K',
Did = 'I',
Oracle = 'R',
MPTokenIssuance = '~',
@@ -125,6 +120,9 @@ enum class LedgerNameSpace : std::uint16_t {
Contract [[deprecated]] = 'c',
Generator [[deprecated]] = 'g',
Nickname [[deprecated]] = 'n',
Bridge [[deprecated]] = 'H',
XchainClaimId [[deprecated]] = 'Q',
XchainCreateAccountClaimId [[deprecated]] = 'K',
};
template <class... Args>
@@ -527,44 +525,6 @@ delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept
return {ltDELEGATE, indexHash(LedgerNameSpace::Delegate, account, authorizedAccount)};
}
Keylet
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType)
{
// A door account can support multiple bridges. On the locking chain
// there can only be one bridge per lockingChainCurrency. On the issuing
// chain there can only be one bridge per issuingChainCurrency.
auto const& issue = bridge.issue(chainType);
return {ltBRIDGE, indexHash(LedgerNameSpace::Bridge, bridge.door(chainType), issue.currency)};
}
Keylet
xChainClaimID(STXChainBridge const& bridge, std::uint64_t const seq)
{
return {
ltXCHAIN_OWNED_CLAIM_ID,
indexHash(
LedgerNameSpace::XchainClaimId,
bridge.lockingChainDoor(),
bridge.lockingChainIssue(),
bridge.issuingChainDoor(),
bridge.issuingChainIssue(),
seq)};
}
Keylet
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t const seq)
{
return {
ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID,
indexHash(
LedgerNameSpace::XchainCreateAccountClaimId,
bridge.lockingChainDoor(),
bridge.lockingChainIssue(),
bridge.issuingChainDoor(),
bridge.issuingChainIssue(),
seq)};
}
Keylet
did(AccountID const& account) noexcept
{
@@ -65,58 +65,6 @@ InnerObjectFormats::InnerObjectFormats()
{sfAuthAccounts, SoeOptional},
});
add(sfXChainClaimAttestationCollectionElement.jsonName,
sfXChainClaimAttestationCollectionElement.getCode(),
{
{sfAttestationSignerAccount, SoeRequired},
{sfPublicKey, SoeRequired},
{sfSignature, SoeRequired},
{sfAmount, SoeRequired},
{sfAccount, SoeRequired},
{sfAttestationRewardAccount, SoeRequired},
{sfWasLockingChainSend, SoeRequired},
{sfXChainClaimID, SoeRequired},
{sfDestination, SoeOptional},
});
add(sfXChainCreateAccountAttestationCollectionElement.jsonName,
sfXChainCreateAccountAttestationCollectionElement.getCode(),
{
{sfAttestationSignerAccount, SoeRequired},
{sfPublicKey, SoeRequired},
{sfSignature, SoeRequired},
{sfAmount, SoeRequired},
{sfAccount, SoeRequired},
{sfAttestationRewardAccount, SoeRequired},
{sfWasLockingChainSend, SoeRequired},
{sfXChainAccountCreateCount, SoeRequired},
{sfDestination, SoeRequired},
{sfSignatureReward, SoeRequired},
});
add(sfXChainClaimProofSig.jsonName,
sfXChainClaimProofSig.getCode(),
{
{sfAttestationSignerAccount, SoeRequired},
{sfPublicKey, SoeRequired},
{sfAmount, SoeRequired},
{sfAttestationRewardAccount, SoeRequired},
{sfWasLockingChainSend, SoeRequired},
{sfDestination, SoeOptional},
});
add(sfXChainCreateAccountProofSig.jsonName,
sfXChainCreateAccountProofSig.getCode(),
{
{sfAttestationSignerAccount, SoeRequired},
{sfPublicKey, SoeRequired},
{sfAmount, SoeRequired},
{sfSignatureReward, SoeRequired},
{sfAttestationRewardAccount, SoeRequired},
{sfWasLockingChainSend, SoeRequired},
{sfDestination, SoeRequired},
});
add(sfAuthAccount.jsonName,
sfAuthAccount.getCode(),
{
+18
View File
@@ -1763,6 +1763,24 @@ divRoundStrict(STAmount const& num, STAmount const& den, Asset const& asset, boo
return divRoundImpl<NumberRoundModeGuard>(num, den, asset, roundUp);
}
STAmount
mulRatio(STAmount const& amt, std::uint32_t num, std::uint32_t den, Number::RoundingMode rounding)
{
XRPL_ASSERT(
rounding == Number::RoundingMode::Upward || rounding == Number::RoundingMode::Downward,
"xrpl::mulRatio(STAmount) : rounding is Upward or Downward");
bool const roundUp = rounding == Number::RoundingMode::Upward;
return amt.asset().visit(
[&](Issue const& issue) -> STAmount {
if (amt.native())
return mulRatio(amt.xrp(), num, den, roundUp);
return {mulRatio(amt.iou(), num, den, roundUp), issue};
},
[&](MPTIssue const& issue) -> STAmount {
return {mulRatio(amt.mpt(), num, den, roundUp), issue};
});
}
[[nodiscard]] bool
STAmount::isZeroAtScale(int scale) const
{
+1 -13
View File
@@ -24,9 +24,9 @@
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/STIssue.h>
#include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STPathSet.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/UintTypes.h>
@@ -956,18 +956,6 @@ parseLeaf(
}
break;
case STI_XCHAIN_BRIDGE:
try
{
ret = detail::makeStvar<STXChainBridge>(STXChainBridge(field, value));
}
catch (std::exception const&)
{
error = invalidData(jsonName, fieldName);
return ret;
}
break;
case STI_CURRENCY:
try
{
-4
View File
@@ -16,7 +16,6 @@
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STPathSet.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/Serializer.h>
#include <stdexcept>
@@ -217,9 +216,6 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args)
case STI_ISSUE:
construct<STIssue>(std::forward<Args>(args)...);
return;
case STI_XCHAIN_BRIDGE:
construct<STXChainBridge>(std::forward<Args>(args)...);
return;
case STI_CURRENCY:
construct<STCurrency>(std::forward<Args>(args)...);
return;
-204
View File
@@ -1,204 +0,0 @@
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/basics/contract.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/jss.h>
#include <cstddef>
#include <format>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
namespace xrpl {
STXChainBridge::STXChainBridge() : STBase{sfXChainBridge}
{
}
STXChainBridge::STXChainBridge(SField const& name) : STBase{name}
{
}
STXChainBridge::STXChainBridge(
AccountID const& srcChainDoor,
Issue const& srcChainIssue,
AccountID const& dstChainDoor,
Issue const& dstChainIssue)
: STBase{sfXChainBridge}
, lockingChainDoor_{sfLockingChainDoor, srcChainDoor}
, lockingChainIssue_{sfLockingChainIssue, srcChainIssue}
, issuingChainDoor_{sfIssuingChainDoor, dstChainDoor}
, issuingChainIssue_{sfIssuingChainIssue, dstChainIssue}
{
}
STXChainBridge::STXChainBridge(STObject const& o)
: STBase{sfXChainBridge}
, lockingChainDoor_{sfLockingChainDoor, o[sfLockingChainDoor]}
, lockingChainIssue_{sfLockingChainIssue, o[sfLockingChainIssue]}
, issuingChainDoor_{sfIssuingChainDoor, o[sfIssuingChainDoor]}
, issuingChainIssue_{sfIssuingChainIssue, o[sfIssuingChainIssue]}
{
}
STXChainBridge::STXChainBridge(json::Value const& v) : STXChainBridge{sfXChainBridge, v}
{
}
STXChainBridge::STXChainBridge(SField const& name, json::Value const& v) : STBase{name}
{
if (!v.isObject())
{
Throw<std::runtime_error>(
"STXChainBridge can only be specified with a 'object' Json value");
}
auto const checkExtra = [](json::Value const& v) {
static auto const kBridgeJson =
xrpl::STXChainBridge().getJson(xrpl::JsonOptions::Values::None);
for (auto it = v.begin(); it != v.end(); ++it)
{
std::string const name = it.memberName();
if (!kBridgeJson.isMember(name))
{
Throw<std::runtime_error>(
std::format("STXChainBridge extra field detected: {}", name));
}
}
return true;
};
checkExtra(v);
json::Value const& lockingChainDoorStr = v[jss::LockingChainDoor];
json::Value const& lockingChainIssue = v[jss::LockingChainIssue];
json::Value const& issuingChainDoorStr = v[jss::IssuingChainDoor];
json::Value const& issuingChainIssue = v[jss::IssuingChainIssue];
if (!lockingChainDoorStr.isString())
{
Throw<std::runtime_error>("STXChainBridge LockingChainDoor must be a string Json value");
}
if (!issuingChainDoorStr.isString())
{
Throw<std::runtime_error>("STXChainBridge IssuingChainDoor must be a string Json value");
}
auto const lockingChainDoor = parseBase58<AccountID>(lockingChainDoorStr.asString());
auto const issuingChainDoor = parseBase58<AccountID>(issuingChainDoorStr.asString());
if (!lockingChainDoor)
{
Throw<std::runtime_error>("STXChainBridge LockingChainDoor must be a valid account");
}
if (!issuingChainDoor)
{
Throw<std::runtime_error>("STXChainBridge IssuingChainDoor must be a valid account");
}
lockingChainDoor_ = STAccount{sfLockingChainDoor, *lockingChainDoor};
lockingChainIssue_ = STIssue{sfLockingChainIssue, issueFromJson(lockingChainIssue)};
issuingChainDoor_ = STAccount{sfIssuingChainDoor, *issuingChainDoor};
issuingChainIssue_ = STIssue{sfIssuingChainIssue, issueFromJson(issuingChainIssue)};
}
STXChainBridge::STXChainBridge(SerialIter& sit, SField const& name)
: STBase{name}
, lockingChainDoor_{sit, sfLockingChainDoor}
, lockingChainIssue_{sit, sfLockingChainIssue}
, issuingChainDoor_{sit, sfIssuingChainDoor}
, issuingChainIssue_{sit, sfIssuingChainIssue}
{
}
void
STXChainBridge::add(Serializer& s) const
{
lockingChainDoor_.add(s);
lockingChainIssue_.add(s);
issuingChainDoor_.add(s);
issuingChainIssue_.add(s);
}
json::Value
STXChainBridge::getJson(JsonOptions jo) const
{
json::Value v;
v[jss::LockingChainDoor] = lockingChainDoor_.getJson(jo);
v[jss::LockingChainIssue] = lockingChainIssue_.getJson(jo);
v[jss::IssuingChainDoor] = issuingChainDoor_.getJson(jo);
v[jss::IssuingChainIssue] = issuingChainIssue_.getJson(jo);
return v;
}
std::string
STXChainBridge::getText() const
{
return std::format(
"{{ {} = {}, {} = {}, {} = {}, {} = {} }}",
sfLockingChainDoor.getName(),
lockingChainDoor_.getText(),
sfLockingChainIssue.getName(),
lockingChainIssue_.getText(),
sfIssuingChainDoor.getName(),
issuingChainDoor_.getText(),
sfIssuingChainIssue.getName(),
issuingChainIssue_.getText());
}
STObject
STXChainBridge::toSTObject() const
{
STObject o{sfXChainBridge};
o[sfLockingChainDoor] = lockingChainDoor_;
o[sfLockingChainIssue] = lockingChainIssue_;
o[sfIssuingChainDoor] = issuingChainDoor_;
o[sfIssuingChainIssue] = issuingChainIssue_;
return o;
}
SerializedTypeID
STXChainBridge::getSType() const
{
return STI_XCHAIN_BRIDGE;
}
bool
STXChainBridge::isEquivalent(STBase const& t) const
{
auto const* v = dynamic_cast<STXChainBridge const*>(&t);
return (v != nullptr) && (*v == *this);
}
bool
STXChainBridge::isDefault() const
{
return lockingChainDoor_.isDefault() && lockingChainIssue_.isDefault() &&
issuingChainDoor_.isDefault() && issuingChainIssue_.isDefault();
}
std::unique_ptr<STXChainBridge>
STXChainBridge::construct(SerialIter& sit, SField const& name)
{
return std::make_unique<STXChainBridge>(sit, name);
}
STBase*
STXChainBridge::copy(std::size_t n, void* buf) const
{
return emplace(n, buf, *this);
}
STBase*
STXChainBridge::move(std::size_t n, void* buf)
{
return emplace(n, buf, std::move(*this));
}
} // namespace xrpl
+2 -1
View File
@@ -137,6 +137,8 @@ transResults()
MAKE_ERROR(tefBAD_PATH_COUNT, "Malformed: Too many paths."),
MAKE_ERROR(tefNO_BYTECODE, "There is no WASM code to run, but a WASM-specific field was included."),
MAKE_ERROR(tefBYTECODE_NOT_INCLUDED, "WASM code requires a field that was not included."),
MAKE_ERROR(tefTEMP_DISABLED, "The transaction requires logic that is currently temporarily disabled."),
MAKE_ERROR(tefEXCEEDS_LIMIT, "The transaction exceeds a limit set by fee voting."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
@@ -209,7 +211,6 @@ transResults()
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
MAKE_ERROR(temBAD_CIPHERTEXT, "Malformed: Invalid ciphertext."),
MAKE_ERROR(temINVALID_BYTECODE, "Malformed: Provided byte code is invalid."),
MAKE_ERROR(temTEMP_DISABLED, "The transaction requires logic that is currently temporarily disabled."),
MAKE_ERROR(terRETRY, "Retry transaction."),
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),
-706
View File
@@ -1,706 +0,0 @@
#include <xrpl/protocol/XChainAttestations.h>
#include <xrpl/basics/Buffer.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/contract.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/json_get_or_throw.h>
#include <xrpl/protocol/jss.h>
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <tuple>
#include <utility>
#include <vector>
namespace xrpl {
namespace attestations {
AttestationBase::AttestationBase(
AccountID attestationSignerAccount,
PublicKey const& publicKey,
Buffer signature,
AccountID const& sendingAccount,
STAmount sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend)
: attestationSignerAccount{attestationSignerAccount}
, publicKey{publicKey}
, signature{std::move(signature)}
, sendingAccount{sendingAccount}
, sendingAmount{std::move(sendingAmount)}
, rewardAccount{rewardAccount}
, wasLockingChainSend{wasLockingChainSend}
{
}
bool
AttestationBase::equalHelper(AttestationBase const& lhs, AttestationBase const& rhs)
{
return std::tie(
lhs.attestationSignerAccount,
lhs.publicKey,
lhs.signature,
lhs.sendingAccount,
lhs.sendingAmount,
lhs.rewardAccount,
lhs.wasLockingChainSend) ==
std::tie(
rhs.attestationSignerAccount,
rhs.publicKey,
rhs.signature,
rhs.sendingAccount,
rhs.sendingAmount,
rhs.rewardAccount,
rhs.wasLockingChainSend);
}
bool
AttestationBase::sameEventHelper(AttestationBase const& lhs, AttestationBase const& rhs)
{
return std::tie(lhs.sendingAccount, lhs.sendingAmount, lhs.wasLockingChainSend) ==
std::tie(rhs.sendingAccount, rhs.sendingAmount, rhs.wasLockingChainSend);
}
bool
AttestationBase::verify(STXChainBridge const& bridge) const
{
std::vector<std::uint8_t> const msg = message(bridge);
return xrpl::verify(publicKey, makeSlice(msg), signature);
}
AttestationBase::AttestationBase(STObject const& o)
: attestationSignerAccount{o[sfAttestationSignerAccount]}
, publicKey{o[sfPublicKey]}
, signature{o[sfSignature]}
, sendingAccount{o[sfAccount]}
, sendingAmount{o[sfAmount]}
, rewardAccount{o[sfAttestationRewardAccount]}
, wasLockingChainSend{bool(o[sfWasLockingChainSend])}
{
}
AttestationBase::AttestationBase(json::Value const& v)
: attestationSignerAccount{json::getOrThrow<AccountID>(v, sfAttestationSignerAccount)}
, publicKey{json::getOrThrow<PublicKey>(v, sfPublicKey)}
, signature{json::getOrThrow<Buffer>(v, sfSignature)}
, sendingAccount{json::getOrThrow<AccountID>(v, sfAccount)}
, sendingAmount{json::getOrThrow<STAmount>(v, sfAmount)}
, rewardAccount{json::getOrThrow<AccountID>(v, sfAttestationRewardAccount)}
, wasLockingChainSend{json::getOrThrow<bool>(v, sfWasLockingChainSend)}
{
}
void
AttestationBase::addHelper(STObject& o) const
{
o[sfAttestationSignerAccount] = attestationSignerAccount;
o[sfPublicKey] = publicKey;
o[sfSignature] = signature;
o[sfAmount] = sendingAmount;
o[sfAccount] = sendingAccount;
o[sfAttestationRewardAccount] = rewardAccount;
o[sfWasLockingChainSend] = wasLockingChainSend;
}
AttestationClaim::AttestationClaim(
AccountID attestationSignerAccount,
PublicKey const& publicKey,
Buffer signature,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t claimId,
std::optional<AccountID> const& dst)
: AttestationBase(
attestationSignerAccount,
publicKey,
std::move(signature),
sendingAccount,
sendingAmount,
rewardAccount,
wasLockingChainSend)
, claimID{claimId}
, dst{dst}
{
}
AttestationClaim::AttestationClaim(
STXChainBridge const& bridge,
AccountID attestationSignerAccount,
PublicKey const& publicKey,
SecretKey const& secretKey,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t claimId,
std::optional<AccountID> const& dst)
: AttestationClaim{
attestationSignerAccount,
publicKey,
Buffer{},
sendingAccount,
sendingAmount,
rewardAccount,
wasLockingChainSend,
claimId,
dst}
{
auto const toSign = message(bridge);
signature = sign(publicKey, secretKey, makeSlice(toSign));
}
AttestationClaim::AttestationClaim(STObject const& o)
: AttestationBase(o), claimID{o[sfXChainClaimID]}, dst{o[~sfDestination]}
{
}
AttestationClaim::AttestationClaim(json::Value const& v)
: AttestationBase{v}, claimID{json::getOrThrow<std::uint64_t>(v, sfXChainClaimID)}
{
if (v.isMember(sfDestination.getJsonName()))
dst = json::getOrThrow<AccountID>(v, sfDestination);
}
STObject
AttestationClaim::toSTObject() const
{
STObject o = STObject::makeInnerObject(sfXChainClaimAttestationCollectionElement);
addHelper(o);
o[sfXChainClaimID] = claimID;
if (dst)
o[sfDestination] = *dst;
return o;
}
std::vector<std::uint8_t>
AttestationClaim::message(
STXChainBridge const& bridge,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t claimID,
std::optional<AccountID> const& dst)
{
STObject o{sfGeneric};
// Serialize in SField order to make python serializers easier to write
o[sfXChainClaimID] = claimID;
o[sfAmount] = sendingAmount;
if (dst)
o[sfDestination] = *dst;
o[sfOtherChainSource] = sendingAccount;
o[sfAttestationRewardAccount] = rewardAccount;
o[sfWasLockingChainSend] = wasLockingChainSend ? 1 : 0;
o[sfXChainBridge] = bridge;
Serializer s;
o.add(s);
return std::move(s.modData());
}
std::vector<std::uint8_t>
AttestationClaim::message(STXChainBridge const& bridge) const
{
return AttestationClaim::message(
bridge, sendingAccount, sendingAmount, rewardAccount, wasLockingChainSend, claimID, dst);
}
bool
AttestationClaim::validAmounts() const
{
return isLegalNet(sendingAmount);
}
bool
AttestationClaim::sameEvent(AttestationClaim const& rhs) const
{
return AttestationClaim::sameEventHelper(*this, rhs) &&
tie(claimID, dst) == tie(rhs.claimID, rhs.dst);
}
bool
operator==(AttestationClaim const& lhs, AttestationClaim const& rhs)
{
return AttestationClaim::equalHelper(lhs, rhs) &&
tie(lhs.claimID, lhs.dst) == tie(rhs.claimID, rhs.dst);
}
AttestationCreateAccount::AttestationCreateAccount(STObject const& o)
: AttestationBase(o)
, createCount{o[sfXChainAccountCreateCount]}
, toCreate{o[sfDestination]}
, rewardAmount{o[sfSignatureReward]}
{
}
AttestationCreateAccount::AttestationCreateAccount(json::Value const& v)
: AttestationBase{v}
, createCount{json::getOrThrow<std::uint64_t>(v, sfXChainAccountCreateCount)}
, toCreate{json::getOrThrow<AccountID>(v, sfDestination)}
, rewardAmount{json::getOrThrow<STAmount>(v, sfSignatureReward)}
{
}
AttestationCreateAccount::AttestationCreateAccount(
AccountID attestationSignerAccount,
PublicKey const& publicKey,
Buffer signature,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
STAmount rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t createCount,
AccountID const& toCreate)
: AttestationBase(
attestationSignerAccount,
publicKey,
std::move(signature),
sendingAccount,
sendingAmount,
rewardAccount,
wasLockingChainSend)
, createCount{createCount}
, toCreate{toCreate}
, rewardAmount{std::move(rewardAmount)}
{
}
AttestationCreateAccount::AttestationCreateAccount(
STXChainBridge const& bridge,
AccountID attestationSignerAccount,
PublicKey const& publicKey,
SecretKey const& secretKey,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
STAmount const& rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t createCount,
AccountID const& toCreate)
: AttestationCreateAccount{
attestationSignerAccount,
publicKey,
Buffer{},
sendingAccount,
sendingAmount,
rewardAmount,
rewardAccount,
wasLockingChainSend,
createCount,
toCreate}
{
auto const toSign = message(bridge);
signature = sign(publicKey, secretKey, makeSlice(toSign));
}
STObject
AttestationCreateAccount::toSTObject() const
{
STObject o = STObject::makeInnerObject(sfXChainCreateAccountAttestationCollectionElement);
addHelper(o);
o[sfXChainAccountCreateCount] = createCount;
o[sfDestination] = toCreate;
o[sfSignatureReward] = rewardAmount;
return o;
}
std::vector<std::uint8_t>
AttestationCreateAccount::message(
STXChainBridge const& bridge,
AccountID const& sendingAccount,
STAmount const& sendingAmount,
STAmount const& rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::uint64_t createCount,
AccountID const& dst)
{
STObject o{sfGeneric};
// Serialize in SField order to make python serializers easier to write
o[sfXChainAccountCreateCount] = createCount;
o[sfAmount] = sendingAmount;
o[sfSignatureReward] = rewardAmount;
o[sfDestination] = dst;
o[sfOtherChainSource] = sendingAccount;
o[sfAttestationRewardAccount] = rewardAccount;
o[sfWasLockingChainSend] = wasLockingChainSend ? 1 : 0;
o[sfXChainBridge] = bridge;
Serializer s;
o.add(s);
return std::move(s.modData());
}
std::vector<std::uint8_t>
AttestationCreateAccount::message(STXChainBridge const& bridge) const
{
return AttestationCreateAccount::message(
bridge,
sendingAccount,
sendingAmount,
rewardAmount,
rewardAccount,
wasLockingChainSend,
createCount,
toCreate);
}
bool
AttestationCreateAccount::validAmounts() const
{
return isLegalNet(rewardAmount) && isLegalNet(sendingAmount);
}
bool
AttestationCreateAccount::sameEvent(AttestationCreateAccount const& rhs) const
{
return AttestationCreateAccount::sameEventHelper(*this, rhs) &&
std::tie(createCount, toCreate, rewardAmount) ==
std::tie(rhs.createCount, rhs.toCreate, rhs.rewardAmount);
}
bool
operator==(AttestationCreateAccount const& lhs, AttestationCreateAccount const& rhs)
{
return AttestationCreateAccount::equalHelper(lhs, rhs) &&
std::tie(lhs.createCount, lhs.toCreate, lhs.rewardAmount) ==
std::tie(rhs.createCount, rhs.toCreate, rhs.rewardAmount);
}
} // namespace attestations
SField const& XChainClaimAttestation::arrayFieldName{sfXChainClaimAttestations};
SField const& XChainCreateAccountAttestation::arrayFieldName{sfXChainCreateAccountAttestations};
XChainClaimAttestation::XChainClaimAttestation(
AccountID const& keyAccount,
PublicKey const& publicKey,
STAmount const& amount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
std::optional<AccountID> const& dst)
: keyAccount(keyAccount)
, publicKey(publicKey)
, amount(sfAmount, amount)
, rewardAccount(rewardAccount)
, wasLockingChainSend(wasLockingChainSend)
, dst(dst)
{
}
XChainClaimAttestation::XChainClaimAttestation(
STAccount const& keyAccount,
PublicKey const& publicKey,
STAmount const& amount,
STAccount const& rewardAccount,
bool wasLockingChainSend,
std::optional<STAccount> const& dst)
: XChainClaimAttestation{
keyAccount.value(),
publicKey,
amount,
rewardAccount.value(),
wasLockingChainSend,
dst ? std::optional<AccountID>{dst->value()} : std::nullopt}
{
}
XChainClaimAttestation::XChainClaimAttestation(STObject const& o)
: XChainClaimAttestation{
o[sfAttestationSignerAccount],
PublicKey{o[sfPublicKey]},
o[sfAmount],
o[sfAttestationRewardAccount],
o[sfWasLockingChainSend] != 0,
o[~sfDestination]} {};
XChainClaimAttestation::XChainClaimAttestation(json::Value const& v)
: XChainClaimAttestation{
json::getOrThrow<AccountID>(v, sfAttestationSignerAccount),
json::getOrThrow<PublicKey>(v, sfPublicKey),
json::getOrThrow<STAmount>(v, sfAmount),
json::getOrThrow<AccountID>(v, sfAttestationRewardAccount),
json::getOrThrow<bool>(v, sfWasLockingChainSend),
std::nullopt}
{
if (v.isMember(sfDestination.getJsonName()))
dst = json::getOrThrow<AccountID>(v, sfDestination);
};
XChainClaimAttestation::XChainClaimAttestation(
XChainClaimAttestation::TSignedAttestation const& claimAtt)
: XChainClaimAttestation{
claimAtt.attestationSignerAccount,
claimAtt.publicKey,
claimAtt.sendingAmount,
claimAtt.rewardAccount,
claimAtt.wasLockingChainSend,
claimAtt.dst}
{
}
STObject
XChainClaimAttestation::toSTObject() const
{
STObject o = STObject::makeInnerObject(sfXChainClaimProofSig);
o[sfAttestationSignerAccount] = STAccount{sfAttestationSignerAccount, keyAccount};
o[sfPublicKey] = publicKey;
o[sfAmount] = STAmount{sfAmount, amount};
o[sfAttestationRewardAccount] = STAccount{sfAttestationRewardAccount, rewardAccount};
o[sfWasLockingChainSend] = wasLockingChainSend;
if (dst)
o[sfDestination] = STAccount{sfDestination, *dst};
return o;
}
bool
operator==(XChainClaimAttestation const& lhs, XChainClaimAttestation const& rhs)
{
return std::tie(
lhs.keyAccount,
lhs.publicKey,
lhs.amount,
lhs.rewardAccount,
lhs.wasLockingChainSend,
lhs.dst) ==
std::tie(
rhs.keyAccount,
rhs.publicKey,
rhs.amount,
rhs.rewardAccount,
rhs.wasLockingChainSend,
rhs.dst);
}
XChainClaimAttestation::MatchFields::MatchFields(
XChainClaimAttestation::TSignedAttestation const& att)
: amount{att.sendingAmount}, wasLockingChainSend{att.wasLockingChainSend}, dst{att.dst}
{
}
AttestationMatch
XChainClaimAttestation::match(XChainClaimAttestation::MatchFields const& rhs) const
{
if (std::tie(amount, wasLockingChainSend) != std::tie(rhs.amount, rhs.wasLockingChainSend))
return AttestationMatch::NonDstMismatch;
if (dst != rhs.dst)
return AttestationMatch::MatchExceptDst;
return AttestationMatch::Match;
}
//------------------------------------------------------------------------------
XChainCreateAccountAttestation::XChainCreateAccountAttestation(
AccountID const& keyAccount,
PublicKey const& publicKey,
STAmount const& amount,
STAmount const& rewardAmount,
AccountID const& rewardAccount,
bool wasLockingChainSend,
AccountID const& dst)
: keyAccount(keyAccount)
, publicKey(publicKey)
, amount(sfAmount, amount)
, rewardAmount(sfSignatureReward, rewardAmount)
, rewardAccount(rewardAccount)
, wasLockingChainSend(wasLockingChainSend)
, dst(dst)
{
}
XChainCreateAccountAttestation::XChainCreateAccountAttestation(STObject const& o)
: XChainCreateAccountAttestation{
o[sfAttestationSignerAccount],
PublicKey{o[sfPublicKey]},
o[sfAmount],
o[sfSignatureReward],
o[sfAttestationRewardAccount],
o[sfWasLockingChainSend] != 0,
o[sfDestination]} {};
XChainCreateAccountAttestation::XChainCreateAccountAttestation(json::Value const& v)
: XChainCreateAccountAttestation{
json::getOrThrow<AccountID>(v, sfAttestationSignerAccount),
json::getOrThrow<PublicKey>(v, sfPublicKey),
json::getOrThrow<STAmount>(v, sfAmount),
json::getOrThrow<STAmount>(v, sfSignatureReward),
json::getOrThrow<AccountID>(v, sfAttestationRewardAccount),
json::getOrThrow<bool>(v, sfWasLockingChainSend),
json::getOrThrow<AccountID>(v, sfDestination)}
{
}
XChainCreateAccountAttestation::XChainCreateAccountAttestation(
XChainCreateAccountAttestation::TSignedAttestation const& createAtt)
: XChainCreateAccountAttestation{
createAtt.attestationSignerAccount,
createAtt.publicKey,
createAtt.sendingAmount,
createAtt.rewardAmount,
createAtt.rewardAccount,
createAtt.wasLockingChainSend,
createAtt.toCreate}
{
}
STObject
XChainCreateAccountAttestation::toSTObject() const
{
STObject o = STObject::makeInnerObject(sfXChainCreateAccountProofSig);
o[sfAttestationSignerAccount] = STAccount{sfAttestationSignerAccount, keyAccount};
o[sfPublicKey] = publicKey;
o[sfAmount] = STAmount{sfAmount, amount};
o[sfSignatureReward] = STAmount{sfSignatureReward, rewardAmount};
o[sfAttestationRewardAccount] = STAccount{sfAttestationRewardAccount, rewardAccount};
o[sfWasLockingChainSend] = wasLockingChainSend;
o[sfDestination] = STAccount{sfDestination, dst};
return o;
}
XChainCreateAccountAttestation::MatchFields::MatchFields(
XChainCreateAccountAttestation::TSignedAttestation const& att)
: amount{att.sendingAmount}
, rewardAmount(att.rewardAmount)
, wasLockingChainSend{att.wasLockingChainSend}
, dst{att.toCreate}
{
}
AttestationMatch
XChainCreateAccountAttestation::match(XChainCreateAccountAttestation::MatchFields const& rhs) const
{
if (std::tie(amount, rewardAmount, wasLockingChainSend) !=
std::tie(rhs.amount, rhs.rewardAmount, rhs.wasLockingChainSend))
return AttestationMatch::NonDstMismatch;
if (dst != rhs.dst)
return AttestationMatch::MatchExceptDst;
return AttestationMatch::Match;
}
bool
operator==(XChainCreateAccountAttestation const& lhs, XChainCreateAccountAttestation const& rhs)
{
return std::tie(
lhs.keyAccount,
lhs.publicKey,
lhs.amount,
lhs.rewardAmount,
lhs.rewardAccount,
lhs.wasLockingChainSend,
lhs.dst) ==
std::tie(
rhs.keyAccount,
rhs.publicKey,
rhs.amount,
rhs.rewardAmount,
rhs.rewardAccount,
rhs.wasLockingChainSend,
rhs.dst);
}
//------------------------------------------------------------------------------
//
template <class TAttestation>
XChainAttestationsBase<TAttestation>::XChainAttestationsBase(
XChainAttestationsBase<TAttestation>::AttCollection&& atts)
: attestations_{std::move(atts)}
{
}
template <class TAttestation>
XChainAttestationsBase<TAttestation>::AttCollection::const_iterator
XChainAttestationsBase<TAttestation>::begin() const
{
return attestations_.begin();
}
template <class TAttestation>
XChainAttestationsBase<TAttestation>::AttCollection::const_iterator
XChainAttestationsBase<TAttestation>::end() const
{
return attestations_.end();
}
template <class TAttestation>
XChainAttestationsBase<TAttestation>::AttCollection::iterator
XChainAttestationsBase<TAttestation>::begin()
{
return attestations_.begin();
}
template <class TAttestation>
XChainAttestationsBase<TAttestation>::AttCollection::iterator
XChainAttestationsBase<TAttestation>::end()
{
return attestations_.end();
}
template <class TAttestation>
XChainAttestationsBase<TAttestation>::XChainAttestationsBase(json::Value const& v)
{
if (!v.isObject())
{
Throw<std::runtime_error>(
"XChainAttestationsBase can only be specified with an 'object' "
"Json value");
}
attestations_ = [&] {
auto const jAtts = v[jss::attestations];
if (jAtts.size() > kMaxAttestations)
Throw<std::runtime_error>("XChainAttestationsBase exceeded max number of attestations");
std::vector<TAttestation> r;
r.reserve(jAtts.size());
for (auto const& a : jAtts)
r.emplace_back(a);
return r;
}();
}
template <class TAttestation>
XChainAttestationsBase<TAttestation>::XChainAttestationsBase(STArray const& arr)
{
if (arr.size() > kMaxAttestations)
Throw<std::runtime_error>("XChainAttestationsBase exceeded max number of attestations");
attestations_.reserve(arr.size());
for (auto const& o : arr)
attestations_.emplace_back(o);
}
template <class TAttestation>
STArray
XChainAttestationsBase<TAttestation>::toSTArray() const
{
STArray r{TAttestation::arrayFieldName, attestations_.size()};
for (auto const& e : attestations_)
r.emplaceBack(e.toSTObject());
return r;
}
template class XChainAttestationsBase<XChainClaimAttestation>;
template class XChainAttestationsBase<XChainCreateAccountAttestation>;
} // namespace xrpl
+32 -15
View File
@@ -26,6 +26,7 @@
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <atomic>
#include <cstdint>
#include <exception>
#include <functional>
@@ -77,14 +78,23 @@ SHAMap::SHAMap(SHAMap const& other, bool isMutable)
: f_(other.f_)
, journal_(other.f_.journal())
, cowid_(other.cowid_ + 1)
, ledgerSeq_(other.ledgerSeq_)
, ledgerSeq_(other.ledgerSeq())
, root_(other.root_)
, state_(isMutable ? SHAMapState::Modifying : SHAMapState::Immutable)
, type_(other.type_)
, backed_(other.backed_)
{
// A snapshot shares the source's root, so Invalid carries over. Read once into a local, since
// the source's state can change while this runs.
auto const otherState = other.state();
auto const ownState = [&] {
if (otherState == SHAMapState::Invalid)
return SHAMapState::Invalid;
return isMutable ? SHAMapState::Modifying : SHAMapState::Immutable;
}();
state_.store(ownState, std::memory_order_release);
// If either map may change, they cannot share nodes
if ((state_ != SHAMapState::Immutable) || (other.state_ != SHAMapState::Immutable))
if ((ownState != SHAMapState::Immutable) || (otherState != SHAMapState::Immutable))
{
unshare();
}
@@ -105,7 +115,7 @@ SHAMap::dirtyUp(NodePathStack& stack, UInt256 const& target, SHAMapTreeNodePtr c
// child can be an inner node or a leaf
XRPL_ASSERT(
(state_ != SHAMapState::Synching) && (state_ != SHAMapState::Immutable),
(state() != SHAMapState::Synching) && (state() != SHAMapState::Immutable),
"xrpl::SHAMap::dirtyUp : valid state");
XRPL_ASSERT(child && (child->cowid() == cowid_), "xrpl::SHAMap::dirtyUp : valid child input");
@@ -170,7 +180,7 @@ SHAMapTreeNodePtr
SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const
{
XRPL_ASSERT(backed_, "xrpl::SHAMap::fetchNodeFromDB : is backed");
auto const obj = f_.db().fetchNodeObject(hash.asUInt256(), ledgerSeq_);
auto const obj = f_.db().fetchNodeObject(hash.asUInt256(), ledgerSeq());
return finishFetch(hash, obj);
}
@@ -183,10 +193,17 @@ SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr<NodeObject> const& o
{
if (!object)
{
if (full_)
// A missing node disproves full_, so withdraw it and report the gap. The exchange
// makes exactly one reader report it, and its acquire pairs with the release store in
// setFull(). It needs no release: the only other reader of full_ is this same path,
// and a reader that loses the exchange does nothing with what the winner did. The load
// ahead of it is relaxed, since all it decides is whether to try the exchange: a stale
// true costs one exchange that loses, and a stale false means another reader won, or
// setFull() is not visible yet and the gap goes unreported.
if (full_.load(std::memory_order_relaxed) &&
full_.exchange(false, std::memory_order_acquire))
{
full_ = false;
f_.missingNodeAcquireBySeq(ledgerSeq_, hash.asUInt256());
f_.missingNodeAcquireBySeq(ledgerSeq(), hash.asUInt256());
}
return {};
}
@@ -219,7 +236,7 @@ SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter const* filter) cons
auto node = SHAMapTreeNode::makeFromPrefix(makeSlice(*nodeData), hash);
if (node)
{
filter->gotNode(true, hash, ledgerSeq_, std::move(*nodeData), node->getType());
filter->gotNode(true, hash, ledgerSeq(), std::move(*nodeData), node->getType());
if (backed_)
canonicalize(hash, node);
}
@@ -399,7 +416,7 @@ SHAMap::descendAsync(
{
f_.db().asyncFetch(
hash.asUInt256(),
ledgerSeq_,
ledgerSeq(),
[this, hash, cb{std::move(callback)}](std::shared_ptr<NodeObject> const& object) {
auto const node = finishFetch(hash, object);
cb(node, hash);
@@ -424,7 +441,7 @@ SHAMap::unshareNode(intr_ptr::SharedPtr<Node> node, SHAMapNodeID const& nodeID)
if (node->cowid() != cowid_)
{
// have a CoW
XRPL_ASSERT(state_ != SHAMapState::Immutable, "xrpl::SHAMap::unshareNode : not immutable");
XRPL_ASSERT(state() != SHAMapState::Immutable, "xrpl::SHAMap::unshareNode : not immutable");
node = intr_ptr::staticPointerCast<Node>(node->clone(cowid_));
if (nodeID.isRoot())
root_ = node;
@@ -637,7 +654,7 @@ bool
SHAMap::delItem(UInt256 const& id)
{
// delete the item with this ID
XRPL_ASSERT(state_ != SHAMapState::Immutable, "xrpl::SHAMap::delItem : not immutable");
XRPL_ASSERT(state() != SHAMapState::Immutable, "xrpl::SHAMap::delItem : not immutable");
NodePathStack stack;
walkTowardsKey(id, &stack);
@@ -719,7 +736,7 @@ SHAMap::delItem(UInt256 const& id)
bool
SHAMap::addGiveItem(SHAMapNodeType type, boost::intrusive_ptr<SHAMapItem const> item)
{
XRPL_ASSERT(state_ != SHAMapState::Immutable, "xrpl::SHAMap::addGiveItem : not immutable");
XRPL_ASSERT(state() != SHAMapState::Immutable, "xrpl::SHAMap::addGiveItem : not immutable");
XRPL_ASSERT(type != SHAMapNodeType::TnInner, "xrpl::SHAMap::addGiveItem : valid type input");
// add the specified item, does not update
@@ -810,7 +827,7 @@ SHAMap::updateGiveItem(SHAMapNodeType type, boost::intrusive_ptr<SHAMapItem cons
// can't change the tag but can change the hash
UInt256 const tag = item->key();
XRPL_ASSERT(state_ != SHAMapState::Immutable, "xrpl::SHAMap::updateGiveItem : not immutable");
XRPL_ASSERT(state() != SHAMapState::Immutable, "xrpl::SHAMap::updateGiveItem : not immutable");
NodePathStack stack;
walkTowardsKey(tag, &stack);
@@ -901,7 +918,7 @@ SHAMap::writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const
Serializer s;
node->serializeWithPrefix(s);
f_.db().store(t, std::move(s.modData()), node->getHash().asUInt256(), ledgerSeq_);
f_.db().store(t, std::move(s.modData()), node->getHash().asUInt256(), ledgerSeq());
return node;
}
+4 -1
View File
@@ -16,6 +16,7 @@
#include <xrpl/shamap/detail/TaggedPointer.h>
#include <xrpl/shamap/detail/TaggedPointer.ipp>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <mutex>
@@ -77,7 +78,9 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const
auto p = intr_ptr::makeShared<SHAMapInnerNode>(cowid, branchCount);
p->hash_ = hash_;
p->isBranch_ = isBranch_;
p->fullBelowGen_ = fullBelowGen_;
// Relaxed, as everywhere. p is not reachable by another thread until this returns.
p->fullBelowGen_.store(
fullBelowGen_.load(std::memory_order_relaxed), std::memory_order_relaxed);
SHAMapHash* cloneHashes = nullptr;
SHAMapHash* thisHashes = nullptr;
SHAMapTreeNodePtr* cloneChildren = nullptr;
+112 -40
View File
@@ -31,6 +31,25 @@
namespace xrpl {
namespace {
/**
* Whether a depth is at or past the deepest an inner node may occupy.
*
* Nibbles run out at SHAMap::kLeafDepth, so only a leaf may sit there. True for
* every deeper position too, which lies past the end of a key.
*
* @param depth The depth to judge.
* @return Whether an inner node at that depth makes the map impossible.
*/
[[nodiscard]] bool
isLeafDepth(unsigned int depth)
{
return depth >= SHAMap::kLeafDepth;
}
} // namespace
void
SHAMap::visitLeaves(
std::function<void(boost::intrusive_ptr<SHAMapItem const> const& item)> const& leafFunction)
@@ -143,13 +162,10 @@ SHAMap::visitDifferences(
if (!function(*node))
return;
// Nibbles run out at kLeafDepth, so only a leaf belongs there. A well-formed map never
// holds an inner node at that depth: addKnownNode marks the map invalid rather than hooking
// one in, and fetch-pack data is hash-verified against a validated root, so reaching this
// means a defect or a corrupt store, not something a peer can provoke. Report the node
// anyway - the wire form carries no depth, and the recipient hooks blobs in by hash - but
// skip the children rather than letting getChildNodeID throw on them.
if (nodeID.getDepth() >= kLeafDepth)
// Only a leaf belongs at kLeafDepth. The node is still reported, since the wire form
// carries no depth and the recipient hooks blobs by hash. Its children are skipped, since
// getChildNodeID has no answer past kLeafDepth.
if (isLeafDepth(nodeID.getDepth()))
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::SHAMap::visitDifferences : inner node at leaf depth");
@@ -207,7 +223,12 @@ SHAMap::gmnProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se)
// we already know this child node is missing
fullBelow = false;
}
else if (!backed_ || !f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
// The depth test precedes the cache lookup for the same reason it does in addKnownNode():
// the cache is keyed by node hash and shared across maps, and a hash covers a node's
// children but not its depth. Skipping the shortcut forgoes an optimization only.
else if (
!backed_ || isLeafDepth(nodeID.getDepth() + 1) ||
!f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
{
bool pending = false;
auto d = descendAsync(
@@ -238,6 +259,17 @@ SHAMap::gmnProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se)
if (--mn.max <= 0)
return;
}
else if (d->isInner() && isLeafDepth(nodeID.getDepth() + 1))
{
// Only a leaf belongs that deep (see isLeafDepth and SHAMap::addKnownNode). A node
// resolved locally reaches the walk without passing through addKnownNode(), so the
// walk reaches this verdict itself. Ordered ahead of the full-below test below,
// which canonicalization shares across maps.
JLOG(journal_.warn()) << "Inner node at branch " << branch << " below " << nodeID
<< " makes the map invalid";
setInvalid();
return;
}
else if (d->isInner() && !safeDowncast<SHAMapInnerNode*>(d)->isFullBelow(mn.generation))
{
mn.stack.push(se);
@@ -301,6 +333,8 @@ SHAMap::gmnProcessDeferredReads(MissingNodes& mn)
}
else if ((mn.max > 0) && mn.missingHashes.insert(nodeHash).second)
{
// getChildNodeID() is safe here: gmnProcessNodes refuses to descend into an inner node
// at kLeafDepth, so a deferred parent sits at most one level above it.
mn.missingNodes.emplace_back(parentID.getChildNodeID(branch), nodeHash.asUInt256());
--mn.max;
}
@@ -311,17 +345,20 @@ SHAMap::gmnProcessDeferredReads(MissingNodes& mn)
mn.deferred = 0;
}
/**
* Get a list of node IDs and hashes for nodes that are part of this SHAMap
* but not available locally. The filter can hold alternate sources of
* nodes that are not permanently stored locally
*/
std::vector<std::pair<SHAMapNodeID, UInt256>>
SHAMap::getMissingNodes(int max, SHAMapSyncFilter const* filter)
{
XRPL_ASSERT(root_->getHash().isNonZero(), "xrpl::SHAMap::getMissingNodes : nonzero root hash");
XRPL_ASSERT(max > 0, "xrpl::SHAMap::getMissingNodes : valid max input");
if (!isValid())
{
// The root node's own hash, since getHash() unshares the tree on a zero hash.
JLOG(journal_.warn()) << "getMissingNodes called on an invalid map, root hash "
<< root_->getHash() << " seq " << ledgerSeq();
return {};
}
MissingNodes mn(
max,
filter,
@@ -354,6 +391,11 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter const* filter)
{
gmnProcessNodes(mn, pos);
// The walk just invalidated the map. The loop stops descending here but falls through
// to the drain below, since every posted read must be drained while `mn` is alive.
if (!isValid())
break;
if (mn.max <= 0)
break;
@@ -383,6 +425,11 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter const* filter)
if (mn.deferred != 0)
gmnProcessDeferredReads(mn);
// Reads are drained, so the map can be abandoned. What was collected belongs to a tree
// that cannot exist.
if (!isValid())
return {};
if (mn.max <= 0)
return std::move(mn.missingNodes);
@@ -416,6 +463,11 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter const* filter)
} while (node != nullptr);
// addKnownNode() on another thread can write the verdict after the loop's own test, so the map
// is judged once more before the result is returned.
if (!isValid())
return {}; // LCOV_EXCL_LINE: only that other thread reaches this, so no test does
if (mn.missingNodes.empty())
clearSynching();
@@ -527,11 +579,19 @@ SHAMap::addRootNode(
XRPL_ASSERT(cowid_ >= 1, "xrpl::SHAMap::addRootNode : valid cowid");
XRPL_ASSERT(rootNode, "xrpl::SHAMap::addRootNode : non-null root node");
// we already have a root_ node
// A map syncs against one hash and installs a root once, so a root already held is a duplicate
// only once it hashes to the hash asked for.
if (root_->getHash().isNonZero())
{
JLOG(journal_.trace()) << "Got root node, already have one";
XRPL_ASSERT(root_->getHash() == hash, "xrpl::SHAMap::addRootNode : valid hash");
if (root_->getHash() != hash)
{
JLOG(journal_.warn()) << "Root node offered under hash " << hash
<< ", but the map holds " << root_->getHash();
return SHAMapAddNode::invalid();
}
return SHAMapAddNode::duplicate();
}
@@ -555,7 +615,7 @@ SHAMap::addRootNode(
Serializer s;
root_->serializeWithPrefix(s);
filter->gotNode(
false, root_->getHash(), ledgerSeq_, std::move(s.modData()), root_->getType());
false, root_->getHash(), ledgerSeq(), std::move(s.modData()), root_->getType());
}
return SHAMapAddNode::useful();
@@ -598,7 +658,13 @@ SHAMap::addKnownNode(
}
auto childHash = inner->getChildHash(branch);
if (f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
// Depth before the cache: the cache is keyed by node hash and shared across the family, and
// a hash covers a node's children but not its depth, so the same subtree can be cached as
// complete at one depth and reached at another. Skipping the shortcut only forgoes an
// optimization.
if (!isLeafDepth(currNodeID.getDepth() + 1) &&
f_.getFullBelowCache()->touchIfExists(childHash.asUInt256()))
{
return SHAMapAddNode::duplicate();
}
@@ -616,25 +682,32 @@ SHAMap::addKnownNode(
return SHAMapAddNode::invalid();
}
// Inner nodes must be at a level strictly less than 64
// but leaf nodes (while notionally at level 64) can be
// at any depth up to and including 64:
if ((currNodeID.getDepth() > kLeafDepth) ||
(treeNode->isInner() && currNodeID.getDepth() == kLeafDepth))
// Only leaves may sit at kLeafDepth (see isLeafDepth), so an inner node there makes the map
// impossible. The node is reported as bad data.
//
// Every node from the root down hash-verified to get here, so it is the requested root hash
// itself that commits to a shape no valid tree can have. The verdict belongs to that hash
// rather than to our copy of the tree.
bool const badDepth = treeNode->isInner() && isLeafDepth(currNodeID.getDepth());
SOMETIMES(badDepth, "xrpl::SHAMap::addKnownNode : map is invalid");
if (badDepth)
{
// Map is provably invalid
state_ = SHAMapState::Invalid;
return SHAMapAddNode::useful();
JLOG(journal_.warn()) << "Node " << nodeID << " makes the map invalid at "
<< currNodeID;
setInvalid();
return SHAMapAddNode::invalid();
}
if (currNodeID != nodeID)
// The data hashes to the child at currNodeID but claims to belong at nodeID, so it is not
// the node that was asked for. Only the label is wrong, so the map stays sound and the node
// is still obtainable from another sender.
bool const badPosition = (currNodeID != nodeID);
SOMETIMES(badPosition, "xrpl::SHAMap::addKnownNode : node ID does not match its position");
if (badPosition)
{
// Either this node is broken or we didn't request it (yet)
JLOG(journal_.warn()) << "unable to hook node " << nodeID;
JLOG(journal_.info()) << " stuck at " << currNodeID;
JLOG(journal_.info()) << "got depth=" << nodeID.getDepth()
<< ", walked to= " << currNodeID.getDepth();
return SHAMapAddNode::useful();
JLOG(journal_.warn()) << "Unable to hook node " << nodeID << ", stuck at "
<< currNodeID;
return SHAMapAddNode::invalid();
}
if (backed_)
@@ -647,7 +720,7 @@ SHAMap::addKnownNode(
Serializer s;
treeNode->serializeWithPrefix(s);
filter->gotNode(
false, childHash, ledgerSeq_, std::move(s.modData()), treeNode->getType());
false, childHash, ledgerSeq(), std::move(s.modData()), treeNode->getType());
}
return SHAMapAddNode::useful();
@@ -766,7 +839,7 @@ SHAMap::hasLeafNode(UInt256 const& tag, SHAMapHash const& targetNodeHash) const
// Same kLeafDepth hazard as in visitDifferences above. That guard bounds the caller's own
// traversal, not the map queried here, and the loop below descends from this map's root
// independently, so this check is what keeps a malformed map from reaching getChildNodeID.
if (nodeID.getDepth() >= kLeafDepth)
if (isLeafDepth(nodeID.getDepth()))
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::SHAMap::hasLeafNode : inner node at leaf depth");
@@ -849,8 +922,8 @@ SHAMap::verifyProofPath(UInt256 const& rootHash, UInt256 const& key, std::vector
// there. These nodes come off the wire, so a peer can still claim an inner one;
// reject it rather than passing this depth to selectBranch.
SOMETIMES(
depth >= kLeafDepth, "xrpl::SHAMap::verifyProofPath : inner at leaf depth");
if (depth >= kLeafDepth)
isLeafDepth(depth), "xrpl::SHAMap::verifyProofPath : inner at leaf depth");
if (isLeafDepth(depth))
return false;
auto const nodeId = SHAMapNodeID::createID(depth, key);
@@ -859,10 +932,9 @@ SHAMap::verifyProofPath(UInt256 const& rootHash, UInt256 const& key, std::vector
}
else
{
// The hash chain up to rootHash only proves this leaf sits where the path claims,
// not that it is the leaf for `key`: a peer could substitute any other leaf whose
// subtree hashes to the same value at every level above it. Checking the terminal
// leaf's own key is what ties the proof to `key` specifically.
// The hash chain up to rootHash proves this leaf sits where the path claims. Any
// leaf whose subtree hashes the same at every level above satisfies that chain, so
// the terminal leaf's own key is what ties the proof to `key`.
if (leafKey(*node) != key)
return false;
+2
View File
@@ -31,6 +31,8 @@
#include <xrpl/protocol/detail/transactions.macro>
#undef TRANSACTION
#undef TRANSACTION_INCLUDE
#pragma pop_macro("TRANSACTION")
// DO NOT INCLUDE TRANSACTOR HEADER FILES HERE.
@@ -0,0 +1,225 @@
#include <xrpl/tx/invariants/CosignerInvariant.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/XRPAmount.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
namespace xrpl {
namespace {
bool
validSignerArray(STArray const& signers, std::size_t limit, SField const& elementName)
{
if (signers.size() > limit)
return false;
std::optional<AccountID> previous;
for (auto const& signer : signers)
{
if (signer.getFName() != elementName || !signer.isFieldPresent(sfAccount))
return false;
auto const account = signer.getAccountID(sfAccount);
if (previous && account <= *previous)
return false;
previous = account;
}
return true;
}
bool
validNestedSigners(STObject const& signature)
{
return !signature.isFieldPresent(sfSigners) ||
validSignerArray(signature.getFieldArray(sfSigners), STTx::kMaxMultiSigners, sfSigner);
}
bool
validSignerArrays(STObject const& proposedTx)
{
if (!validNestedSigners(proposedTx))
return false;
for (auto const* field :
std::array<SField const*, 2>{&sfCounterpartySignature, &sfSponsorSignature})
{
if (proposedTx.isFieldPresent(*field) &&
!validNestedSigners(proposedTx.getFieldObject(*field)))
return false;
}
if (!proposedTx.isFieldPresent(sfBatchSigners))
return true;
auto const& batchSigners = proposedTx.getFieldArray(sfBatchSigners);
if (!validSignerArray(batchSigners, kMaxBatchSigners, sfBatchSigner))
return false;
return std::ranges::all_of(
batchSigners, [](auto const& batchSigner) { return validNestedSigners(batchSigner); });
}
// The Ticket a proposal is keyed to: the one its proposed transaction spends.
uint256
proposalTicketKey(SLE const& proposal)
{
auto const& proposedTx = proposal.getFieldObject(sfProposedTransaction);
return keylet::ticket(
proposedTx.getAccountID(sfAccount),
SeqProxy::rawTicket(proposedTx.getFieldU32(sfTicketSequence)))
.key;
}
} // namespace
void
ValidTransactionProposal::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after)
{
// `after` is always present (on deletion it holds the final state), so it
// alone determines the entry type. LedgerEntryTypesMatch owns malformed
// type transitions; skip them rather than read one side with the other's
// schema.
if (!after || (before && before->getType() != after->getType()))
return;
auto const type = after->getType();
// Consuming a Ticket deletes the proposal keyed to it (XLS-0103 §4.5).
// Remember each Ticket that went, so finalize can tell a proposal deleted
// with its Ticket from one that simply vanished, and the proposal each
// Ticket was keyed to, so finalize can check that none stayed behind.
if (type == ltTICKET)
{
if (isDelete)
{
deletedTickets_.insert(after->key());
proposalsOfDeletedTickets_.push_back(
keylet::txProposal(
after->getAccountID(sfAccount), after->getFieldU32(sfTicketSequence)));
}
return;
}
if (type != ltTRANSACTION_PROPOSAL)
return;
if (!before)
{
++created_;
}
else if (isDelete)
{
++deleted_;
ticketsOfDeletedProposals_.push_back(proposalTicketKey(*before));
}
else
{
++modified_;
}
// A live proposal's signer lists must stay submittable: within size
// limits, sorted by account, and free of duplicates.
if (!isDelete && !validSignerArrays(after->getFieldObject(sfProposedTransaction)))
invalidSignerArrays_ = true;
}
bool
ValidTransactionProposal::finalize(
STTx const& tx,
TER result,
XRPAmount,
ReadView const& view,
beast::Journal const& j) const
{
if (!view.rules().enabled(featureCosign))
return true;
if (invalidSignerArrays_)
{
JLOG(j.fatal()) << "Invariant failed: TransactionProposal signer arrays are not canonical.";
return false;
}
// A proposal leaves the ledger in two ways (XLS-0103 §4.5): a
// TransactionProposalCancel deletes the proposal it names, and consuming
// the Ticket a proposal is keyed to deletes the proposal with it, whether
// the proposed transaction executed (or failed with a claimed-fee tec) or
// AccountDelete swept the target's Tickets.
std::uint32_t deletedWithTicket = 0;
for (auto const& ticketKey : ticketsOfDeletedProposals_)
{
if (deletedTickets_.contains(ticketKey))
++deletedWithTicket;
}
bool const succeeded = isTesSuccess(result);
bool effectsMatch = false;
if (tx.getTxnType() == ttTRANSACTION_PROPOSAL_CREATE)
{
effectsMatch = created_ == (succeeded ? 1u : 0u) && modified_ == 0 && deleted_ == 0;
}
else if (tx.getTxnType() == ttTRANSACTION_PROPOSAL_CANCEL)
{
// A Cancel deletes one proposal and nothing else. It cannot
// spend a reserved Ticket, so no proposal goes with its Ticket here.
effectsMatch = created_ == 0 && modified_ == 0 && deletedWithTicket == 0 &&
deleted_ == (succeeded ? 1u : 0u);
}
else
{
// Any other transaction deletes a proposal only together with its
// Ticket, and modifies at most one, only as a successful
// SponsorshipTransfer.
// TODO: extend this when TransactionProposalSign is implemented.
std::uint32_t const modifiable =
tx.getTxnType() == ttSPONSORSHIP_TRANSFER && succeeded ? 1 : 0;
effectsMatch = created_ == 0 && modified_ <= modifiable && deleted_ == deletedWithTicket;
}
if (!effectsMatch)
{
JLOG(j.fatal())
<< "Invariant failed: TransactionProposal changes do not match transaction result.";
return false;
}
// The other direction of the deletion rule: ticketDelete deletes the
// proposal keyed to a Ticket along with it (XLS-0103 §4.5), so no
// proposal may outlive its Ticket, orphaned on a key that can no longer
// be spent.
bool const orphaned = std::ranges::any_of(
proposalsOfDeletedTickets_, [&view](Keylet const& keylet) { return view.exists(keylet); });
if (orphaned)
{
JLOG(j.fatal())
<< "Invariant failed: TransactionProposal outlived the Ticket it is keyed to.";
return false;
}
return true;
}
} // namespace xrpl
+139 -1
View File
@@ -9,6 +9,7 @@
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
@@ -22,6 +23,7 @@
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STNumber.h> // IWYU pragma: keep
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/SystemParameters.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
@@ -933,8 +935,12 @@ ValidClawback::finalize(
return !mptV2Enabled;
}
// IOU balances keep 16 significant digits, so a clawback
// much smaller than the balance changes it by a rounded
// amount, possibly zero. Compare with the same rounded
// subtraction the balance update performs.
if (*afterBalance > *beforeBalance ||
(*beforeBalance - *afterBalance) != std::min(*beforeBalance, clawAmount))
*afterBalance != *beforeBalance - std::min(*beforeBalance, clawAmount))
{
JLOG(j.fatal())
<< "Invariant failed: trustline clawback balance change is invalid";
@@ -1010,6 +1016,78 @@ ValidClawback::finalize(
//------------------------------------------------------------------------------
// Whether `entry` is the trust line or MPToken through which `account` holds
// `asset`. XRP has no such entry.
static bool
isHolding(SLE const& entry, Asset const& asset, AccountID const& account)
{
return asset.visit(
[&](Issue const& issue) {
return !isXRP(issue) && keylet::trustLine(account, issue).check(entry);
},
[&](MPTIssue const& issue) {
return keylet::mptoken(issue.getMptID(), account).check(entry);
});
}
// Whether `entry`, linked into the owner directory of `pseudo`, belongs to the
// AMM, Vault or LoanBroker that the pseudo-account was created for.
static bool
isOwnedByPseudoAccount(ReadView const& view, SLE const& pseudo, SLE const& entry)
{
auto const pseudoAcctID = pseudo[sfAccount];
if (pseudo.isFieldPresent(sfAMMID))
{
if (entry.getType() == ltAMM)
return entry.key() == pseudo[sfAMMID];
auto const amm = view.read(keylet::amm(pseudo[sfAMMID]));
if (!amm)
return false; // LCOV_EXCL_LINE
if (isHolding(entry, (*amm)[sfAsset], pseudoAcctID) ||
isHolding(entry, (*amm)[sfAsset2], pseudoAcctID))
return true;
// The AMM issues LP tokens, so each liquidity provider's trust line
// links into its directory.
if (entry.getType() != ltRIPPLE_STATE)
return false;
auto const low = entry[sfLowLimit].getIssuer();
auto const high = entry[sfHighLimit].getIssuer();
auto const lpCurrency = (*amm)[sfLPTokenBalance].get<Issue>().currency;
return (low == pseudoAcctID || high == pseudoAcctID) &&
entry.key() == keylet::trustLine(low, high, lpCurrency).key;
}
if (pseudo.isFieldPresent(sfVaultID))
{
if (entry.getType() == ltLOAN_BROKER)
return entry[sfVaultID] == pseudo[sfVaultID];
auto const vault = view.read(keylet::vault(pseudo[sfVaultID]));
if (!vault)
return false; // LCOV_EXCL_LINE
return keylet::mptokenIssuance((*vault)[sfShareMPTID]).check(entry) ||
isHolding(entry, (*vault)[sfAsset], pseudoAcctID);
}
if (pseudo.isFieldPresent(sfLoanBrokerID))
{
if (entry.getType() == ltLOAN)
return entry[sfLoanBrokerID] == pseudo[sfLoanBrokerID];
auto const broker = view.read(keylet::loanBroker(pseudo[sfLoanBrokerID]));
if (!broker)
return false; // LCOV_EXCL_LINE
auto const vault = view.read(keylet::vault((*broker)[sfVaultID]));
return vault && isHolding(entry, (*vault)[sfAsset], pseudoAcctID);
}
// A new pseudo-account kind must say what it owns.
return false; // LCOV_EXCL_LINE
}
void
ValidPseudoAccounts::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after)
{
@@ -1019,6 +1097,33 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstR
return;
}
// Only owner directory pages carry sfOwner; book directories don't.
if (after && after->getType() == ltDIR_NODE && after->isFieldPresent(sfOwner))
{
auto const& afterIndexes = after->getFieldV256(sfIndexes);
std::vector<uint256> added;
if (!before)
{
added.assign(afterIndexes.begin(), afterIndexes.end());
}
else
{
auto const& beforeIndexes = before->getFieldV256(sfIndexes);
for (auto const& index : afterIndexes)
{
if (!std::ranges::contains(beforeIndexes, index))
added.push_back(index);
}
}
if (!added.empty())
{
auto& owned = ownerDirAdditions_[after->getAccountID(sfOwner)];
owned.insert(owned.end(), added.begin(), added.end());
}
return;
}
if (after && after->getType() == ltACCOUNT_ROOT)
{
bool const isPseudo = [&] {
@@ -1100,6 +1205,39 @@ ValidPseudoAccounts::finalize(
if (enforce)
return false;
}
// Pre-fixCleanup3_5_0: what a pseudo-account owns is not checked.
// Post-fixCleanup3_5_0: it may own only what belongs to its AMM, Vault or
// LoanBroker.
if (!view.rules().enabled(fixCleanup3_5_0))
return true;
for (auto const& [owner, indexes] : ownerDirAdditions_)
{
auto const root = view.read(keylet::account(owner));
if (!root || !isPseudoAccount(root))
continue;
for (auto const& index : indexes)
{
auto const sle = view.read(keylet::unchecked(index));
if (!sle)
{
JLOG(j.fatal()) << "Invariant failed: pseudo-account " << toBase58(owner)
<< " owner directory links a missing object " << to_string(index);
return false;
}
if (!isOwnedByPseudoAccount(view, *root, *sle))
{
JLOG(j.fatal()) << "Invariant failed: pseudo-account " << toBase58(owner)
<< " may not own " << ledgerEntryTypeName(*sle) << " "
<< to_string(index);
return false;
}
}
}
return true;
}
@@ -5,8 +5,8 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/ledger/helpers/VaultHelpers.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -160,7 +160,7 @@ ValidLoanBroker::finalize(
roundToAsset(
Asset{vault->at(sfAsset)},
debtTotal,
getAssetsTotalScale(vault),
getVaultScale(vault),
Number::RoundingMode::TowardsZero) != beast::kZero)
{
JLOG(j.fatal())
Loaded 100 of 311 files, more files were not shown because too many files have changed in this diff. Show more