Compare commits

..

6 Commits

130 changed files with 753 additions and 18123 deletions

View File

@@ -10,26 +10,26 @@ Checks: "-*,
bugprone-chained-comparison,
bugprone-compare-pointer-to-member-virtual-function,
bugprone-copy-constructor-init,
# bugprone-crtp-constructor-accessibility, # has issues
bugprone-crtp-constructor-accessibility,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
# bugprone-empty-catch, # has issues
bugprone-empty-catch,
bugprone-fold-init-type,
# bugprone-forward-declaration-namespace, # has issues
# bugprone-inaccurate-erase,
# bugprone-inc-dec-in-conditions,
# bugprone-incorrect-enable-if,
# bugprone-incorrect-roundings,
# bugprone-infinite-loop,
# bugprone-integer-division,
bugprone-forward-declaration-namespace,
bugprone-inaccurate-erase,
bugprone-inc-dec-in-conditions,
bugprone-incorrect-enable-if,
bugprone-incorrect-roundings,
bugprone-infinite-loop,
bugprone-integer-division,
bugprone-lambda-function-name,
# bugprone-macro-parentheses, # has issues
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
# bugprone-multi-level-implicit-pointer-conversion, # has issues
bugprone-multi-level-implicit-pointer-conversion,
bugprone-multiple-new-in-one-expression,
bugprone-multiple-statement-macro,
bugprone-no-escape,
@@ -39,13 +39,13 @@ Checks: "-*,
bugprone-pointer-arithmetic-on-polymorphic-object,
bugprone-posix-return,
bugprone-redundant-branch-condition,
# bugprone-reserved-identifier, # has issues
# bugprone-return-const-ref-from-parameter, # has issues
bugprone-reserved-identifier,
bugprone-return-const-ref-from-parameter,
bugprone-shared-ptr-array-mismatch,
bugprone-signal-handler,
bugprone-signed-char-misuse,
bugprone-sizeof-container,
# bugprone-sizeof-expression, # has issues
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-standalone-empty,
bugprone-string-constructor,
@@ -62,7 +62,7 @@ Checks: "-*,
bugprone-suspicious-string-compare,
bugprone-suspicious-stringview-data-usage,
bugprone-swapped-arguments,
# bugprone-switch-missing-default-case, # has issues
bugprone-switch-missing-default-case,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
@@ -73,7 +73,7 @@ Checks: "-*,
bugprone-unhandled-self-assignment,
bugprone-unique-ptr-array-mismatch,
bugprone-unsafe-functions,
# bugprone-use-after-move, # has issues
bugprone-use-after-move, # has issues
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-unused-local-non-trivial-variable,

View File

@@ -153,6 +153,32 @@ jobs:
${CMAKE_ARGS} \
..
- name: Check protocol autogen files are up-to-date
working-directory: ${{ env.BUILD_DIR }}
env:
MESSAGE: |
The generated protocol wrapper classes are out of date.
This typically happens when the macro files or generator scripts
have changed but the generated files were not regenerated.
To fix this:
1. Run: cmake --build . --target setup_code_gen
2. Run: cmake --build . --target code_gen
3. Commit and push the regenerated files
run: |
set -e
cmake --build . --target setup_code_gen
cmake --build . --target code_gen
DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen)
if [ -n "${DIFF}" ]; then
echo "::error::Generated protocol files are out of date"
git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen
echo "${MESSAGE}"
exit 1
fi
- name: Build the binary
working-directory: ${{ env.BUILD_DIR }}
env:
@@ -166,29 +192,6 @@ jobs:
--parallel "${BUILD_NPROC}" \
--target "${CMAKE_TARGET}"
- name: Check protocol autogen files are up-to-date
env:
MESSAGE: |
The generated protocol wrapper classes are out of date.
This typically happens when your branch is behind develop and
the macro files or generator scripts have changed.
To fix this:
1. Update your branch from develop (merge or rebase)
2. Build with code generation enabled (XRPL_NO_CODEGEN=OFF)
3. Commit and push the regenerated files
run: |
set -e
DIFF=$(git status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen)
if [ -n "${DIFF}" ]; then
echo "::error::Generated protocol files are out of date"
git diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen
echo "${MESSAGE}"
exit 1
fi
- name: Show ccache statistics
if: ${{ inputs.ccache_enabled }}
run: |

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@
Debug/
Release/
/.build/
/.venv/
/build/
/db/
/out.txt

View File

@@ -17,7 +17,6 @@ repos:
args: [--maxkb=400, --enforce-all]
- id: trailing-whitespace
- id: end-of-file-fixer
- id: mixed-line-ending
- id: check-merge-conflict
args: [--assume-in-merge]
@@ -38,6 +37,7 @@ repos:
rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1
hooks:
- id: prettier
args: [--end-of-line=auto]
- repo: https://github.com/psf/black-pre-commit-mirror
rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0

View File

@@ -459,6 +459,21 @@ install ccache --version 4.11.3 --allow-downgrade`.
The location of `xrpld` binary in your build directory depends on your
CMake generator. Pass `--help` to see the rest of the command line options.
## Code generation
The protocol wrapper classes in `include/xrpl/protocol_autogen/` are generated
from macro definition files in `include/xrpl/protocol/detail/`. If you modify
the macro files (e.g. `transactions.macro`, `ledger_entries.macro`) or the
generation scripts/templates in `cmake/scripts/codegen/`, you need to regenerate the
files:
```
cmake --build . --target setup_code_gen # create venv and install dependencies (once)
cmake --build . --target code_gen # regenerate code
```
The regenerated files should be committed alongside your changes.
## Coverage report
The coverage report is intended for developers using compilers GCC

View File

@@ -88,7 +88,6 @@ find_package(ed25519 REQUIRED)
find_package(gRPC REQUIRED)
find_package(LibArchive REQUIRED)
find_package(lz4 REQUIRED)
find_package(mpt-crypto REQUIRED)
find_package(nudb REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(secp256k1 REQUIRED)
@@ -101,7 +100,6 @@ target_link_libraries(
INTERFACE
ed25519::ed25519
lz4::lz4
mpt-crypto::mpt-crypto
OpenSSL::Crypto
OpenSSL::SSL
secp256k1::secp256k1
@@ -134,6 +132,7 @@ if(coverage)
endif()
include(XrplCore)
include(XrplProtocolAutogen)
include(XrplInstall)
include(XrplValidatorKeys)

View File

@@ -108,24 +108,12 @@ target_link_libraries(
)
# Level 05
## Set up code generation for protocol_autogen module
include(XrplProtocolAutogen)
# Must call setup_protocol_autogen before add_module so that:
# 1. Stale generated files are cleared before GLOB runs
# 2. Output file list is known for custom commands
setup_protocol_autogen()
add_module(xrpl protocol_autogen)
target_link_libraries(
xrpl.libxrpl.protocol_autogen
PUBLIC xrpl.libxrpl.protocol
)
# Ensure code generation runs before compiling protocol_autogen
if(TARGET protocol_autogen_generate)
add_dependencies(xrpl.libxrpl.protocol_autogen protocol_autogen_generate)
endif()
# Level 06
add_module(xrpl core)
target_link_libraries(

View File

@@ -2,308 +2,145 @@
Protocol Autogen - Code generation for protocol wrapper classes
#]===================================================================]
# Options for code generation
option(
XRPL_NO_CODEGEN
"Disable code generation (use pre-generated files from repository)"
OFF
)
set(CODEGEN_VENV_DIR
""
"${CMAKE_CURRENT_SOURCE_DIR}/.venv"
CACHE PATH
"Path to Python virtual environment for code generation. If provided, automatic venv setup is skipped."
"Path to a Python virtual environment for code generation. A venv will be created here by setup_code_gen and used to run generation scripts."
)
# Function to set up code generation for protocol_autogen module
# This runs at configure time to generate C++ wrapper classes from macro files
function(setup_protocol_autogen)
# Directory paths
set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol/detail")
set(AUTOGEN_HEADER_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol_autogen"
)
set(AUTOGEN_TEST_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/src/tests/libxrpl/protocol_autogen"
)
set(SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts")
# Directory paths
set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol/detail")
set(AUTOGEN_HEADER_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl/protocol_autogen"
)
set(AUTOGEN_TEST_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/src/tests/libxrpl/protocol_autogen"
)
set(SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts/codegen")
# Input macro files
set(TRANSACTIONS_MACRO "${MACRO_DIR}/transactions.macro")
set(LEDGER_ENTRIES_MACRO "${MACRO_DIR}/ledger_entries.macro")
set(SFIELDS_MACRO "${MACRO_DIR}/sfields.macro")
# Input macro files
set(TRANSACTIONS_MACRO "${MACRO_DIR}/transactions.macro")
set(LEDGER_ENTRIES_MACRO "${MACRO_DIR}/ledger_entries.macro")
set(SFIELDS_MACRO "${MACRO_DIR}/sfields.macro")
# Python scripts and templates
set(GENERATE_TX_SCRIPT "${SCRIPTS_DIR}/generate_tx_classes.py")
set(GENERATE_LEDGER_SCRIPT "${SCRIPTS_DIR}/generate_ledger_classes.py")
set(REQUIREMENTS_FILE "${SCRIPTS_DIR}/requirements.txt")
set(MACRO_PARSER_COMMON "${SCRIPTS_DIR}/macro_parser_common.py")
set(TX_TEMPLATE "${SCRIPTS_DIR}/templates/Transaction.h.mako")
set(TX_TEST_TEMPLATE "${SCRIPTS_DIR}/templates/TransactionTests.cpp.mako")
set(LEDGER_TEMPLATE "${SCRIPTS_DIR}/templates/LedgerEntry.h.mako")
set(LEDGER_TEST_TEMPLATE
"${SCRIPTS_DIR}/templates/LedgerEntryTests.cpp.mako"
# Python scripts and templates
set(GENERATE_TX_SCRIPT "${SCRIPTS_DIR}/generate_tx_classes.py")
set(GENERATE_LEDGER_SCRIPT "${SCRIPTS_DIR}/generate_ledger_classes.py")
set(REQUIREMENTS_FILE "${SCRIPTS_DIR}/requirements.txt")
set(MACRO_PARSER_COMMON "${SCRIPTS_DIR}/macro_parser_common.py")
set(TX_TEMPLATE "${SCRIPTS_DIR}/templates/Transaction.h.mako")
set(TX_TEST_TEMPLATE "${SCRIPTS_DIR}/templates/TransactionTests.cpp.mako")
set(LEDGER_TEMPLATE "${SCRIPTS_DIR}/templates/LedgerEntry.h.mako")
set(LEDGER_TEST_TEMPLATE "${SCRIPTS_DIR}/templates/LedgerEntryTests.cpp.mako")
set(ALL_INPUT_FILES
"${TRANSACTIONS_MACRO}"
"${LEDGER_ENTRIES_MACRO}"
"${SFIELDS_MACRO}"
"${GENERATE_TX_SCRIPT}"
"${GENERATE_LEDGER_SCRIPT}"
"${REQUIREMENTS_FILE}"
"${MACRO_PARSER_COMMON}"
"${TX_TEMPLATE}"
"${TX_TEST_TEMPLATE}"
"${LEDGER_TEMPLATE}"
"${LEDGER_TEST_TEMPLATE}"
)
# Create output directories
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/transactions")
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/ledger_entries")
file(MAKE_DIRECTORY "${AUTOGEN_TEST_DIR}/ledger_entries")
file(MAKE_DIRECTORY "${AUTOGEN_TEST_DIR}/transactions")
# Find Python3
if(NOT Python3_EXECUTABLE)
find_package(Python3 COMPONENTS Interpreter QUIET)
endif()
if(NOT Python3_EXECUTABLE)
find_program(Python3_EXECUTABLE NAMES python3 python)
endif()
if(NOT Python3_EXECUTABLE)
message(
WARNING
"Python3 not found. The 'code_gen' and 'setup_code_gen' targets will not be available."
)
return()
endif()
# Check if code generation is disabled
if(XRPL_NO_CODEGEN)
# Warn if pip is configured with a non-default index (may need VPN).
execute_process(
COMMAND ${Python3_EXECUTABLE} -m pip config get global.index-url
OUTPUT_VARIABLE PIP_INDEX_URL
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE PIP_CONFIG_RESULT
)
if(PIP_CONFIG_RESULT EQUAL 0 AND PIP_INDEX_URL)
if(
NOT PIP_INDEX_URL STREQUAL "https://pypi.org/simple"
AND NOT PIP_INDEX_URL STREQUAL "https://pypi.python.org/simple"
)
message(
WARNING
"Protocol autogen: Code generation is disabled (XRPL_NO_CODEGEN=ON). "
"Generated files may be out of date."
"Private pip index URL detected: ${PIP_INDEX_URL}\n"
"You may need to connect to VPN to access this URL."
)
return()
endif()
endif()
# Create output directories
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/transactions")
file(MAKE_DIRECTORY "${AUTOGEN_HEADER_DIR}/ledger_entries")
file(MAKE_DIRECTORY "${AUTOGEN_TEST_DIR}/ledger_entries")
file(MAKE_DIRECTORY "${AUTOGEN_TEST_DIR}/transactions")
# Find Python3 - check if already found by Conan or find it ourselves
if(NOT Python3_EXECUTABLE)
find_package(Python3 COMPONENTS Interpreter QUIET)
endif()
if(NOT Python3_EXECUTABLE)
# Try finding python3 executable directly
find_program(Python3_EXECUTABLE NAMES python3 python)
endif()
if(NOT Python3_EXECUTABLE)
message(
FATAL_ERROR
"Python3 not found. Code generation cannot proceed.\n"
"Please install Python 3, or set -DXRPL_NO_CODEGEN=ON to use existing generated files."
)
return()
endif()
message(STATUS "Using Python3 for code generation: ${Python3_EXECUTABLE}")
# Set up Python virtual environment for code generation
if(CODEGEN_VENV_DIR)
# User-provided venv - skip automatic setup
set(VENV_DIR "${CODEGEN_VENV_DIR}")
message(STATUS "Using user-provided Python venv: ${VENV_DIR}")
else()
# Use default venv in build directory
set(VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv")
endif()
# Determine the Python executable path in the venv
# Determine which Python interpreter to use for code generation.
if(CODEGEN_VENV_DIR)
if(WIN32)
set(VENV_PYTHON "${VENV_DIR}/Scripts/python.exe")
set(VENV_PIP "${VENV_DIR}/Scripts/pip.exe")
set(CODEGEN_PYTHON "${CODEGEN_VENV_DIR}/Scripts/python.exe")
else()
set(VENV_PYTHON "${VENV_DIR}/bin/python")
set(VENV_PIP "${VENV_DIR}/bin/pip")
set(CODEGEN_PYTHON "${CODEGEN_VENV_DIR}/bin/python")
endif()
# Only auto-setup venv if not user-provided
if(NOT CODEGEN_VENV_DIR)
# Check if venv needs to be created or updated
set(VENV_NEEDS_UPDATE FALSE)
if(NOT EXISTS "${VENV_PYTHON}")
set(VENV_NEEDS_UPDATE TRUE)
message(
STATUS
"Creating Python virtual environment for code generation..."
)
elseif(
"${REQUIREMENTS_FILE}"
IS_NEWER_THAN
"${VENV_DIR}/.requirements_installed"
)
set(VENV_NEEDS_UPDATE TRUE)
message(
STATUS
"Updating Python virtual environment (requirements changed)..."
)
endif()
# Create/update virtual environment if needed
if(VENV_NEEDS_UPDATE)
message(
STATUS
"Setting up Python virtual environment at ${VENV_DIR}"
)
execute_process(
COMMAND ${Python3_EXECUTABLE} -m venv "${VENV_DIR}"
RESULT_VARIABLE VENV_RESULT
ERROR_VARIABLE VENV_ERROR
)
if(NOT VENV_RESULT EQUAL 0)
message(
FATAL_ERROR
"Failed to create virtual environment: ${VENV_ERROR}"
)
endif()
# Check pip index URL configuration
execute_process(
COMMAND ${VENV_PIP} config get global.index-url
OUTPUT_VARIABLE PIP_INDEX_URL
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
# Default PyPI URL
set(DEFAULT_PIP_INDEX "https://pypi.org/simple")
# Show warning if using non-default index
if(PIP_INDEX_URL AND NOT PIP_INDEX_URL STREQUAL "")
if(NOT PIP_INDEX_URL STREQUAL DEFAULT_PIP_INDEX)
message(
WARNING
"Private pip index URL detected: ${PIP_INDEX_URL}\n"
"You may need to connect to VPN to access this URL."
)
endif()
endif()
message(STATUS "Installing Python dependencies...")
execute_process(
COMMAND ${VENV_PIP} install --upgrade pip
RESULT_VARIABLE PIP_UPGRADE_RESULT
OUTPUT_QUIET
ERROR_VARIABLE PIP_UPGRADE_ERROR
)
if(NOT PIP_UPGRADE_RESULT EQUAL 0)
message(WARNING "Failed to upgrade pip: ${PIP_UPGRADE_ERROR}")
endif()
execute_process(
COMMAND ${VENV_PIP} install -r "${REQUIREMENTS_FILE}"
RESULT_VARIABLE PIP_INSTALL_RESULT
ERROR_VARIABLE PIP_INSTALL_ERROR
)
if(NOT PIP_INSTALL_RESULT EQUAL 0)
message(
FATAL_ERROR
"Failed to install Python dependencies: ${PIP_INSTALL_ERROR}"
)
endif()
# Mark requirements as installed
file(TOUCH "${VENV_DIR}/.requirements_installed")
message(STATUS "Python virtual environment ready")
endif()
endif()
# At configure time - get list of output files for transactions
execute_process(
COMMAND
${VENV_PYTHON} "${GENERATE_TX_SCRIPT}" "${TRANSACTIONS_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/transactions" --test-dir
"${AUTOGEN_TEST_DIR}/transactions" --list-outputs
OUTPUT_VARIABLE TX_OUTPUT_FILES
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE TX_LIST_RESULT
ERROR_VARIABLE TX_LIST_ERROR
else()
set(CODEGEN_PYTHON "${Python3_EXECUTABLE}")
message(
WARNING
"CODEGEN_VENV_DIR is not set. Dependencies will be installed globally.\n"
"If this is not intended, reconfigure with:\n"
" cmake . -UCODEGEN_VENV_DIR"
)
if(NOT TX_LIST_RESULT EQUAL 0)
message(
FATAL_ERROR
"Failed to list transaction output files:\n${TX_LIST_ERROR}"
)
endif()
# Convert newline-separated list to CMake list
string(REPLACE "\\" "/" TX_OUTPUT_FILES "${TX_OUTPUT_FILES}")
string(REPLACE "\n" ";" TX_OUTPUT_FILES "${TX_OUTPUT_FILES}")
endif()
# At configure time - get list of output files for ledger entries
execute_process(
COMMAND
${VENV_PYTHON} "${GENERATE_LEDGER_SCRIPT}" "${LEDGER_ENTRIES_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/ledger_entries" --test-dir
"${AUTOGEN_TEST_DIR}/ledger_entries" --list-outputs
OUTPUT_VARIABLE LEDGER_OUTPUT_FILES
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE LEDGER_LIST_RESULT
ERROR_VARIABLE LEDGER_LIST_ERROR
)
if(NOT LEDGER_LIST_RESULT EQUAL 0)
message(
FATAL_ERROR
"Failed to list ledger entry output files:\n${LEDGER_LIST_ERROR}"
)
endif()
# Convert newline-separated list to CMake list
string(REPLACE "\\" "/" LEDGER_OUTPUT_FILES "${LEDGER_OUTPUT_FILES}")
string(REPLACE "\n" ";" LEDGER_OUTPUT_FILES "${LEDGER_OUTPUT_FILES}")
# Custom command to generate transaction classes at build time
add_custom_command(
OUTPUT ${TX_OUTPUT_FILES}
COMMAND
${VENV_PYTHON} "${GENERATE_TX_SCRIPT}" "${TRANSACTIONS_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/transactions" --test-dir
"${AUTOGEN_TEST_DIR}/transactions" --sfields-macro
"${SFIELDS_MACRO}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
DEPENDS
"${TRANSACTIONS_MACRO}"
"${SFIELDS_MACRO}"
"${GENERATE_TX_SCRIPT}"
"${MACRO_PARSER_COMMON}"
"${TX_TEMPLATE}"
"${TX_TEST_TEMPLATE}"
"${REQUIREMENTS_FILE}"
COMMENT "Generating transaction classes from transactions.macro..."
VERBATIM
)
# Custom command to generate ledger entry classes at build time
add_custom_command(
OUTPUT ${LEDGER_OUTPUT_FILES}
COMMAND
${VENV_PYTHON} "${GENERATE_LEDGER_SCRIPT}" "${LEDGER_ENTRIES_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/ledger_entries" --test-dir
"${AUTOGEN_TEST_DIR}/ledger_entries" --sfields-macro
"${SFIELDS_MACRO}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
DEPENDS
"${LEDGER_ENTRIES_MACRO}"
"${SFIELDS_MACRO}"
"${GENERATE_LEDGER_SCRIPT}"
"${MACRO_PARSER_COMMON}"
"${LEDGER_TEMPLATE}"
"${LEDGER_TEST_TEMPLATE}"
"${REQUIREMENTS_FILE}"
COMMENT "Generating ledger entry classes from ledger_entries.macro..."
VERBATIM
)
# Create a custom target that depends on all generated files
# Custom target to create a venv and install Python dependencies.
# Run manually with: cmake --build . --target setup_code_gen
if(CODEGEN_VENV_DIR)
add_custom_target(
protocol_autogen_generate
DEPENDS ${TX_OUTPUT_FILES} ${LEDGER_OUTPUT_FILES}
COMMENT "Protocol autogen code generation"
setup_code_gen
COMMAND ${Python3_EXECUTABLE} -m venv "${CODEGEN_VENV_DIR}"
COMMAND ${CODEGEN_PYTHON} -m pip install -r "${REQUIREMENTS_FILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Creating venv and installing code generation dependencies..."
)
else()
add_custom_target(
setup_code_gen
COMMAND ${Python3_EXECUTABLE} -m pip install -r "${REQUIREMENTS_FILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Installing code generation dependencies..."
)
endif()
# Extract test files from output lists (files ending in Tests.cpp)
set(PROTOCOL_AUTOGEN_TEST_SOURCES "")
foreach(FILE ${TX_OUTPUT_FILES} ${LEDGER_OUTPUT_FILES})
if(FILE MATCHES "Tests\\.cpp$")
list(APPEND PROTOCOL_AUTOGEN_TEST_SOURCES "${FILE}")
endif()
endforeach()
# Export test sources to parent scope for use in test CMakeLists.txt
set(PROTOCOL_AUTOGEN_TEST_SOURCES
"${PROTOCOL_AUTOGEN_TEST_SOURCES}"
CACHE INTERNAL
"Generated protocol_autogen test sources"
)
# Register dependencies so CMake reconfigures when macro files change
# (to update the list of output files)
set_property(
DIRECTORY
APPEND
PROPERTY
CMAKE_CONFIGURE_DEPENDS
"${TRANSACTIONS_MACRO}"
"${LEDGER_ENTRIES_MACRO}"
)
endfunction()
# Custom target for code generation, excluded from ALL.
# Run manually with: cmake --build . --target code_gen
add_custom_target(
code_gen
COMMAND
${CMAKE_COMMAND} -DCODEGEN_PYTHON=${CODEGEN_PYTHON}
-DGENERATE_TX_SCRIPT=${GENERATE_TX_SCRIPT}
-DGENERATE_LEDGER_SCRIPT=${GENERATE_LEDGER_SCRIPT}
-DTRANSACTIONS_MACRO=${TRANSACTIONS_MACRO}
-DLEDGER_ENTRIES_MACRO=${LEDGER_ENTRIES_MACRO}
-DSFIELDS_MACRO=${SFIELDS_MACRO}
-DAUTOGEN_HEADER_DIR=${AUTOGEN_HEADER_DIR}
-DAUTOGEN_TEST_DIR=${AUTOGEN_TEST_DIR} -P
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/XrplProtocolAutogenRun.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Running protocol code generation..."
SOURCES ${ALL_INPUT_FILES}
)

View File

@@ -0,0 +1,39 @@
#[===================================================================[
Protocol Autogen - Run script invoked by the 'code_gen' target
#]===================================================================]
# Generate transaction classes.
execute_process(
COMMAND
${CODEGEN_PYTHON} "${GENERATE_TX_SCRIPT}" "${TRANSACTIONS_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/transactions" --test-dir
"${AUTOGEN_TEST_DIR}/transactions" --sfields-macro "${SFIELDS_MACRO}"
RESULT_VARIABLE TX_RESULT
OUTPUT_VARIABLE TX_OUTPUT
ERROR_VARIABLE TX_ERROR
)
if(NOT TX_RESULT EQUAL 0)
message(
FATAL_ERROR
"Transaction code generation failed:\n${TX_OUTPUT}\n${TX_ERROR}\n${TX_RESULT}"
)
endif()
# Generate ledger entry classes.
execute_process(
COMMAND
${CODEGEN_PYTHON} "${GENERATE_LEDGER_SCRIPT}" "${LEDGER_ENTRIES_MACRO}"
--header-dir "${AUTOGEN_HEADER_DIR}/ledger_entries" --test-dir
"${AUTOGEN_TEST_DIR}/ledger_entries" --sfields-macro "${SFIELDS_MACRO}"
RESULT_VARIABLE LEDGER_RESULT
OUTPUT_VARIABLE LEDGER_OUTPUT
ERROR_VARIABLE LEDGER_ERROR
)
if(NOT LEDGER_RESULT EQUAL 0)
message(
FATAL_ERROR
"Ledger entry code generation failed:\n${LEDGER_OUTPUT}\n${LEDGER_ERROR}\n${TX_RESULT}"
)
endif()
message(STATUS "Protocol autogen: code generation complete")

View File

@@ -138,28 +138,12 @@ def main():
"--sfields-macro",
help="Path to sfields.macro (default: auto-detect from macro_path)",
)
parser.add_argument(
"--list-outputs",
action="store_true",
help="List output files without generating (one per line)",
)
parser.add_argument("--venv-dir", help=argparse.SUPPRESS)
args = parser.parse_args()
# Parse the macro file to get ledger entry names
entries = parse_macro_file(args.macro_path)
# If --list-outputs, just print the output file paths and exit
if args.list_outputs:
header_dir = Path(args.header_dir)
for entry in entries:
print(header_dir / f"{entry['name']}.h")
if args.test_dir:
test_dir = Path(args.test_dir)
for entry in entries:
print(test_dir / f"{entry['name']}Tests.cpp")
return
# Auto-detect sfields.macro path if not provided
if args.sfields_macro:
sfields_path = Path(args.sfields_macro)

View File

@@ -147,28 +147,12 @@ def main():
"--sfields-macro",
help="Path to sfields.macro (default: auto-detect from macro_path)",
)
parser.add_argument(
"--list-outputs",
action="store_true",
help="List output files without generating (one per line)",
)
parser.add_argument("--venv-dir", help=argparse.SUPPRESS)
args = parser.parse_args()
# Parse the macro file to get transaction names
transactions = parse_macro_file(args.macro_path)
# If --list-outputs, just print the output file paths and exit
if args.list_outputs:
header_dir = Path(args.header_dir)
for tx in transactions:
print(header_dir / f"{tx['name']}.h")
if args.test_dir:
test_dir = Path(args.test_dir)
for tx in transactions:
print(test_dir / f"{tx['name']}Tests.cpp")
return
# Auto-detect sfields.macro path if not provided
if args.sfields_macro:
sfields_path = Path(args.sfields_macro)

View File

@@ -12,7 +12,6 @@
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
"openssl/3.6.1#e6399de266349245a4542fc5f6c71552%1774458290.139",
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1774883011.384",
"mpt-crypto/0.2.0-rc1#ed3f241f69d8b9ebf80069d1923d93a8%1773853481.755",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
@@ -35,7 +34,6 @@
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"m4/1.4.19#5d7a4994e5875d76faf7acf3ed056036%1774365463.87",
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
"cmake/3.31.10#313d16a1aa16bbdb2ca0792467214b76%1765850153.479",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
@@ -60,12 +58,6 @@
],
"lz4/[>=1.9.4 <2]": [
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
],
"openssl/3.5.5": [
"openssl/3.6.1"
],
"openssl/[>=3 <4]": [
"openssl/3.6.1"
]
},
"config_requires": []

View File

@@ -31,7 +31,6 @@ class Xrpl(ConanFile):
"ed25519/2015.03",
"grpc/1.78.1",
"libarchive/3.8.1",
"mpt-crypto/0.2.0-rc1",
"nudb/2.0.9",
"openssl/3.6.1",
"secp256k1/0.7.1",
@@ -215,7 +214,6 @@ class Xrpl(ConanFile):
"grpc::grpc++",
"libarchive::libarchive",
"lz4::lz4",
"mpt-crypto::mpt-crypto",
"nudb::nudb",
"openssl::crypto",
"protobuf::libprotobuf",

View File

@@ -59,7 +59,6 @@ words:
- autobridging
- bimap
- bindir
- blindings
- bookdir
- Bougalis
- Britto
@@ -92,7 +91,6 @@ words:
- daria
- dcmake
- dearmor
- decryptor
- deleteme
- demultiplexer
- deserializaton
@@ -102,7 +100,6 @@ words:
- distro
- doxyfile
- dxrpl
- elgamal
- enabled
- endmacro
- exceptioned
@@ -113,7 +110,6 @@ words:
- fmtdur
- fsanitize
- funclets
- Gamal
- gcov
- gcovr
- ghead
@@ -203,7 +199,6 @@ words:
- partitioner
- paychan
- paychans
- Pedersen
- permdex
- perminute
- permissioned
@@ -240,7 +235,6 @@ words:
- sahyadri
- Satoshi
- scons
- Schnorr
- secp
- sendq
- seqit
@@ -268,7 +262,6 @@ words:
- stvar
- stvector
- stxchainattestations
- summands
- superpeer
- superpeers
- takergets

View File

@@ -296,7 +296,7 @@ set(T& target, std::string const& name, Section const& section)
if ((found_and_valid = val.has_value()))
target = *val;
}
catch (boost::bad_lexical_cast&)
catch (boost::bad_lexical_cast const&) // NOLINT(bugprone-empty-catch)
{
}
return found_and_valid;
@@ -330,7 +330,7 @@ get(Section const& section, std::string const& name, T const& defaultValue = T{}
{
return section.value_or<T>(name, defaultValue);
}
catch (boost::bad_lexical_cast&)
catch (boost::bad_lexical_cast const&) // NOLINT(bugprone-empty-catch)
{
}
return defaultValue;
@@ -345,7 +345,7 @@ get(Section const& section, std::string const& name, char const* defaultValue)
if (val.has_value())
return *val;
}
catch (boost::bad_lexical_cast&)
catch (boost::bad_lexical_cast const&) // NOLINT(bugprone-empty-catch)
{
}
return defaultValue;

View File

@@ -112,7 +112,6 @@ private:
return c;
}
public:
CountedObject() noexcept
{
getCounter().increment();
@@ -126,10 +125,13 @@ public:
CountedObject&
operator=(CountedObject const&) noexcept = default;
public:
~CountedObject() noexcept
{
getCounter().decrement();
}
friend Object;
};
} // namespace xrpl

View File

@@ -226,7 +226,7 @@ private:
// expensive argument lists if the stream is not active.
#ifndef JLOG
#define JLOG(x) \
if (!x) \
if (!(x)) \
{ \
} \
else \
@@ -235,7 +235,7 @@ private:
#ifndef CLOG
#define CLOG(ss) \
if (!ss) \
if (!(ss)) \
; \
else \
*ss

View File

@@ -60,7 +60,7 @@ class SlabAllocator
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(data, &l_, sizeof(std::uint8_t*));
std::memcpy(data, static_cast<void const*>(&l_), sizeof(std::uint8_t*));
l_ = data;
data += item;
}
@@ -102,7 +102,7 @@ class SlabAllocator
{
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(&l_, ret, sizeof(std::uint8_t*));
std::memcpy(static_cast<void*>(&l_), ret, sizeof(std::uint8_t*));
}
}
@@ -127,7 +127,7 @@ class SlabAllocator
// Use memcpy to avoid unaligned UB
// (will optimize to equivalent code)
std::memcpy(ptr, &l_, sizeof(std::uint8_t*));
std::memcpy(ptr, static_cast<void const*>(&l_), sizeof(std::uint8_t*));
l_ = ptr;
}
};

View File

@@ -26,7 +26,7 @@ struct aged_associative_container_extract_t<false>
Value const&
operator()(Value const& value) const
{
return value;
return value; // NOLINT(bugprone-return-const-ref-from-parameter)
}
};

View File

@@ -257,7 +257,8 @@ private:
config_t(config_t&& other)
: KeyValueCompare(std::move(other.key_compare()))
, beast::detail::empty_base_optimization<ElementAllocator>(std::move(other))
, beast::detail::empty_base_optimization<ElementAllocator>(std::move(
static_cast<beast::detail::empty_base_optimization<ElementAllocator>&>(other)))
, clock(other.clock)
{
}

View File

@@ -35,9 +35,11 @@ struct CopyConst<T const, U>
template <typename T, typename Tag>
class ListNode
{
private:
ListNode() = default;
using value_type = T;
friend T;
friend class List<T, Tag>;
template <typename>

View File

@@ -203,7 +203,8 @@ template <class Hasher, class T>
inline std::enable_if_t<is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, T const& t) noexcept
{
h(std::addressof(t), sizeof(t));
// NOLINTNEXTLINE(bugprone-sizeof-expression)
h(static_cast<void const*>(std::addressof(t)), sizeof(t));
}
template <class Hasher, class T>

View File

@@ -53,8 +53,9 @@ is_white(char c)
case '\t':
case '\v':
return true;
default:
return false;
};
return false;
}
template <class FwdIter>

View File

@@ -118,18 +118,18 @@ private:
//------------------------------------------------------------------------------
template <class _>
template <class Unused>
void
reporter<_>::suite_results::add(case_results const& r)
reporter<Unused>::suite_results::add(case_results const& r)
{
++cases;
total += r.total;
failed += r.failed;
}
template <class _>
template <class Unused>
void
reporter<_>::results::add(suite_results const& r)
reporter<Unused>::results::add(suite_results const& r)
{
++suites;
total += r.total;
@@ -160,13 +160,13 @@ reporter<_>::results::add(suite_results const& r)
//------------------------------------------------------------------------------
template <class _>
reporter<_>::reporter(std::ostream& os) : os_(os)
template <class Unused>
reporter<Unused>::reporter(std::ostream& os) : os_(os)
{
}
template <class _>
reporter<_>::~reporter()
template <class Unused>
reporter<Unused>::~reporter()
{
if (results_.top.size() > 0)
{
@@ -180,9 +180,9 @@ reporter<_>::~reporter()
<< amount{results_.failed, "failure"} << std::endl;
}
template <class _>
template <class Unused>
std::string
reporter<_>::fmtdur(typename clock_type::duration const& d)
reporter<Unused>::fmtdur(typename clock_type::duration const& d)
{
using namespace std::chrono;
auto const ms = duration_cast<milliseconds>(d);
@@ -193,46 +193,46 @@ reporter<_>::fmtdur(typename clock_type::duration const& d)
return ss.str();
}
template <class _>
template <class Unused>
void
reporter<_>::on_suite_begin(suite_info const& info)
reporter<Unused>::on_suite_begin(suite_info const& info)
{
suite_results_ = suite_results{info.full_name()};
}
template <class _>
template <class Unused>
void
reporter<_>::on_suite_end()
reporter<Unused>::on_suite_end()
{
results_.add(suite_results_);
}
template <class _>
template <class Unused>
void
reporter<_>::on_case_begin(std::string const& name)
reporter<Unused>::on_case_begin(std::string const& name)
{
case_results_ = case_results(name);
os_ << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name))
<< std::endl;
}
template <class _>
template <class Unused>
void
reporter<_>::on_case_end()
reporter<Unused>::on_case_end()
{
suite_results_.add(case_results_);
}
template <class _>
template <class Unused>
void
reporter<_>::on_pass()
reporter<Unused>::on_pass()
{
++case_results_.total;
}
template <class _>
template <class Unused>
void
reporter<_>::on_fail(std::string const& reason)
reporter<Unused>::on_fail(std::string const& reason)
{
++case_results_.failed;
++case_results_.total;
@@ -240,9 +240,9 @@ reporter<_>::on_fail(std::string const& reason)
<< std::endl;
}
template <class _>
template <class Unused>
void
reporter<_>::on_log(std::string const& s)
reporter<Unused>::on_log(std::string const& s)
{
os_ << s;
}

View File

@@ -145,9 +145,9 @@ public:
void
insert(case_results&& r)
{
cont().emplace_back(std::move(r));
total_ += r.tests.total();
failed_ += r.tests.failed();
cont().emplace_back(std::move(r));
}
void

View File

@@ -36,7 +36,7 @@ make_reason(String const& reason, char const* file, int line)
} // namespace detail
class thread;
class Thread;
enum abort_t { no_abort_on_fail, abort_on_fail };
@@ -295,7 +295,7 @@ public:
}
private:
friend class thread;
friend class Thread;
static suite**
p_this_suite()
@@ -538,7 +538,7 @@ suite::run(runner& r)
{
run();
}
catch (abort_exception const&)
catch (abort_exception const&) // NOLINT(bugprone-empty-catch)
{
// ends the suite
}

View File

@@ -14,7 +14,7 @@ namespace beast {
namespace unit_test {
/** Replacement for std::thread that handles exceptions in unit tests. */
class thread
class Thread
{
private:
suite* s_ = nullptr;
@@ -24,17 +24,17 @@ public:
using id = std::thread::id;
using native_handle_type = std::thread::native_handle_type;
thread() = default;
thread(thread const&) = delete;
thread&
operator=(thread const&) = delete;
Thread() = default;
Thread(Thread const&) = delete;
Thread&
operator=(Thread const&) = delete;
thread(thread&& other) : s_(other.s_), t_(std::move(other.t_))
Thread(Thread&& other) : s_(other.s_), t_(std::move(other.t_))
{
}
thread&
operator=(thread&& other)
Thread&
operator=(Thread&& other)
{
s_ = other.s_;
t_ = std::move(other.t_);
@@ -42,10 +42,10 @@ public:
}
template <class F, class... Args>
explicit thread(suite& s, F&& f, Args&&... args) : s_(&s)
explicit Thread(suite& s, F&& f, Args&&... args) : s_(&s)
{
std::function<void(void)> b = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
t_ = std::thread(&thread::run, this, std::move(b));
t_ = std::thread(&Thread::run, this, std::move(b));
}
bool
@@ -80,7 +80,7 @@ public:
}
void
swap(thread& other)
swap(Thread& other)
{
std::swap(s_, other.s_);
std::swap(t_, other.t_);
@@ -94,7 +94,7 @@ private:
{
f();
}
catch (suite::abort_exception const&)
catch (suite::abort_exception const&) // NOLINT(bugprone-empty-catch)
{
}
catch (std::exception const& e)

View File

@@ -43,15 +43,15 @@ private:
murmurhash3(result_type x);
};
template <class _>
xor_shift_engine<_>::xor_shift_engine(result_type val)
template <class Unused>
xor_shift_engine<Unused>::xor_shift_engine(result_type val)
{
seed(val);
}
template <class _>
template <class Unused>
void
xor_shift_engine<_>::seed(result_type seed)
xor_shift_engine<Unused>::seed(result_type seed)
{
if (seed == 0)
throw std::domain_error("invalid seed");
@@ -59,9 +59,9 @@ xor_shift_engine<_>::seed(result_type seed)
s_[1] = murmurhash3(s_[0]);
}
template <class _>
template <class Unused>
auto
xor_shift_engine<_>::operator()() -> result_type
xor_shift_engine<Unused>::operator()() -> result_type
{
result_type s1 = s_[0];
result_type const s0 = s_[1];
@@ -70,9 +70,9 @@ xor_shift_engine<_>::operator()() -> result_type
return (s_[1] = (s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26))) + s0;
}
template <class _>
template <class Unused>
auto
xor_shift_engine<_>::murmurhash3(result_type x) -> result_type
xor_shift_engine<Unused>::murmurhash3(result_type x) -> result_type
{
x ^= x >> 33;
x *= 0xff51afd7ed558ccdULL;

View File

@@ -20,10 +20,6 @@ removeTokenOffersWithLimit(
Keylet const& directory,
std::size_t maxDeletableOffers);
/** Returns tesSUCCESS if NFToken has few enough offers that it can be burned */
TER
notTooManyOffers(ReadView const& view, uint256 const& nftokenID);
/** Finds the specified token in the owner's token directory. */
std::optional<STObject>
findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID);

View File

@@ -134,7 +134,7 @@ public:
{
lowest_layer().shutdown(plain_socket::shutdown_both);
}
catch (boost::system::system_error& e)
catch (boost::system::system_error const& e)
{
ec = e.code();
}

View File

@@ -138,9 +138,11 @@ forApiVersions(Fn const& fn, Args&&... args)
{
constexpr auto size = maxVer + 1 - minVer;
[&]<std::size_t... offset>(std::index_sequence<offset...>) {
// NOLINTBEGIN(bugprone-use-after-move)
(((void)fn(
std::integral_constant<unsigned int, minVer + offset>{}, std::forward<Args>(args)...)),
...);
// NOLINTEND(bugprone-use-after-move)
}(std::make_index_sequence<size>{});
}

View File

@@ -1,460 +0,0 @@
#pragma once
#include <xrpl/basics/Slice.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/detail/secp256k1.h>
#include <secp256k1_mpt.h>
namespace xrpl {
/**
* @brief Bundles an ElGamal public key with its associated encrypted amount.
*
* Used to represent a recipient in confidential transfers, containing both
* the recipient's ElGamal public key and the ciphertext encrypting the
* transfer amount under that key.
*/
struct ConfidentialRecipient
{
Slice publicKey; ///< The recipient's ElGamal public key (size=xrpl::ecPubKeyLength).
Slice encryptedAmount; ///< The encrypted amount ciphertext
///< (size=xrpl::ecGamalEncryptedTotalLength).
};
/// Holds two secp256k1 public key components representing an ElGamal ciphertext (C1, C2).
struct EcPair
{
secp256k1_pubkey c1;
secp256k1_pubkey c2;
};
/**
* @brief Increments the confidential balance version counter on an MPToken.
*
* The version counter is used to prevent replay attacks by binding proofs
* to a specific state of the account's confidential balance. Wraps to 0
* on overflow (defined behavior for unsigned integers).
*
* @param mptoken The MPToken ledger entry to update.
*/
inline void
incrementConfidentialVersion(STObject& mptoken)
{
// Retrieve current version and increment.
// Unsigned integer overflow is defined behavior in C++ (wraps to 0),
// which is acceptable here.
mptoken[sfConfidentialBalanceVersion] =
mptoken[~sfConfidentialBalanceVersion].value_or(0u) + 1u;
}
/**
* @brief Generates the context hash for ConfidentialMPTSend transactions.
*
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
* this specific send transaction, preventing proof reuse across transactions.
*
* @param account The sender's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
* @param destination The destination account ID.
* @param version The sender's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getSendContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version);
/**
* @brief Generates the context hash for ConfidentialMPTClawback transactions.
*
* Creates a unique 256-bit hash that binds the equality proof to this
* specific clawback transaction.
*
* @param account The issuer's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or ticket number.
* @param holder The holder's account ID being clawed back from.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getClawbackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& holder);
/**
* @brief Generates the context hash for ConfidentialMPTConvert transactions.
*
* Creates a unique 256-bit hash that binds the Schnorr proof (for key
* registration) to this specific convert transaction.
*
* @param account The holder's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or a ticket number.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence);
/**
* @brief Generates the context hash for ConfidentialMPTConvertBack transactions.
*
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
* this specific convert-back transaction.
*
* @param account The holder's account ID.
* @param issuanceID The MPToken Issuance ID.
* @param sequence The transaction sequence number or a ticket number.
* @param version The holder's confidential balance version.
* @return A 256-bit context hash unique to this transaction.
*/
uint256
getConvertBackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
std::uint32_t version);
/**
* @brief Parses an ElGamal ciphertext into two secp256k1 public key components.
*
* Breaks a 66-byte encrypted amount (two 33-byte compressed EC points) into
* a pair containing (C1, C2) for use in cryptographic operations.
*
* @param buffer The 66-byte buffer containing the compressed ciphertext.
* @return The parsed pair (c1, c2) if successful, std::nullopt if the buffer is invalid.
*/
std::optional<EcPair>
makeEcPair(Slice const& buffer);
/**
* @brief Serializes an EcPair into compressed form.
*
* Converts an EcPair (C1, C2) back into a 66-byte buffer containing
* two 33-byte compressed EC points.
*
* @param pair The EcPair to serialize.
* @return The 66-byte buffer, or std::nullopt if serialization fails.
*/
std::optional<Buffer>
serializeEcPair(EcPair const& pair);
/**
* @brief Verifies that a buffer contains two valid, parsable EC public keys.
*
* @param buffer The input buffer containing two concatenated components.
* @return true if both components can be parsed successfully, false otherwise.
*/
bool
isValidCiphertext(Slice const& buffer);
/**
* @brief Verifies that a buffer contains a valid, parsable compressed EC point.
*
* Can be used to validate both compressed public keys and Pedersen commitments.
* Fails early if the prefix byte is not 0x02 or 0x03.
*
* @param buffer The input buffer containing a compressed EC point (33 bytes).
* @return true if the point can be parsed successfully, false otherwise.
*/
bool
isValidCompressedECPoint(Slice const& buffer);
/**
* @brief Homomorphically adds two ElGamal ciphertexts.
*
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a + b) from Enc(a) and Enc(b) without decryption.
*
* @param a The first ciphertext (66 bytes).
* @param b The second ciphertext (66 bytes).
* @return The resulting ciphertext Enc(a + b), or std::nullopt on failure.
*/
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b);
/**
* @brief Homomorphically subtracts two ElGamal ciphertexts.
*
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a - b) from Enc(a) and Enc(b) without decryption.
*
* @param a The minuend ciphertext (66 bytes).
* @param b The subtrahend ciphertext (66 bytes).
* @return The resulting ciphertext Enc(a - b), or std::nullopt on failure.
*/
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b);
/**
* @brief Encrypts an amount using ElGamal encryption.
*
* Produces a ciphertext C = (C1, C2) where C1 = r*G and C2 = m*G + r*Pk,
* using the provided blinding factor r.
*
* @param amt The plaintext amount to encrypt.
* @param pubKeySlice The recipient's ElGamal public key (size=xrpl::ecPubKeyLength).
* @param blindingFactor The randomness used as blinding factor r
* (size=xrpl::ecBlindingFactorLength).
* @return The ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt on failure.
*/
std::optional<Buffer>
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor);
/**
* @brief Generates the canonical zero encryption for a specific MPToken.
*
* Creates a deterministic encryption of zero that is unique to the account
* and MPT issuance. Used to initialize confidential balance fields.
*
* @param pubKeySlice The holder's ElGamal public key (size=xrpl::ecPubKeyLength).
* @param account The account ID of the token holder.
* @param mptId The MPToken Issuance ID.
* @return The canonical zero ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt
* on failure.
*/
std::optional<Buffer>
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId);
/**
* @brief Verifies a Schnorr proof of knowledge of an ElGamal private key.
*
* Proves that the submitter knows the secret key corresponding to the
* provided public key, without revealing the secret key itself.
*
* @param pubKeySlice The ElGamal public key (size=xrpl::ecPubKeyLength).
* @param proofSlice The Schnorr proof (size=xrpl::ecSchnorrProofLength).
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if valid, or an error code otherwise.
*/
TER
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash);
/**
* @brief Validates the format of encrypted amount fields in a transaction.
*
* Checks that all ciphertext fields in the transaction object have the
* correct length and contain valid EC points. This function is only used
* by ConfidentialMPTConvert and ConfidentialMPTConvertBack transactions.
*
* @param object The transaction object containing encrypted amount fields.
* @return tesSUCCESS if all formats are valid, temMALFORMED if required fields
* are missing, or temBAD_CIPHERTEXT if format validation fails.
*/
NotTEC
checkEncryptedAmountFormat(STObject const& object);
/**
* @brief Verifies revealed amount encryptions for all recipients.
*
* Validates that the same amount was correctly encrypted for the holder,
* issuer, and optionally the auditor using their respective public keys.
*
* @param amount The revealed plaintext amount.
* @param blindingFactor The blinding factor used in all encryptions
* (size=xrpl::ecBlindingFactorLength).
* @param holder The holder's public key and encrypted amount.
* @param issuer The issuer's public key and encrypted amount.
* @param auditor Optional auditor's public key and encrypted amount.
* @return tesSUCCESS if all encryptions are valid, or an error code otherwise.
*/
TER
verifyRevealedAmount(
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor);
/**
* @brief Returns the number of recipients in a confidential transfer.
*
* Returns 4 if an auditor is present (sender, destination, issuer, auditor),
* or 3 if no auditor (sender, destination, issuer).
*
* @param hasAuditor Whether the issuance has an auditor configured.
* @return The number of recipients (3 or 4).
*/
constexpr std::size_t
getConfidentialRecipientCount(bool hasAuditor)
{
return hasAuditor ? 4 : 3;
}
/**
* @brief Returns the size of a multi-ciphertext equality proof.
*
* Computes the byte size required for a zero-knowledge proof that demonstrates
* multiple ciphertexts encrypt the same plaintext value. The size depends on
* the number of recipients.
*
* @param nRecipients The number of recipients (typically 3 or 4).
* @return The proof size in bytes.
*/
inline std::size_t
getEqualityProofSize(std::size_t nRecipients)
{
return secp256k1_mpt_proof_equality_shared_r_size(nRecipients);
}
/**
* @brief Verifies a clawback equality proof.
*
* Proves that the issuer knows the exact amount encrypted in the holder's
* balance ciphertext. Used in ConfidentialMPTClawback to verify the issuer
* can decrypt the balance using their private key.
*
* @param amount The revealed plaintext amount.
* @param proof The zero-knowledge proof bytes.
* @param pubKeySlice The issuer's ElGamal public key (64 bytes).
* @param ciphertext The issuer's encrypted balance on the holder's account (66 bytes).
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
*/
TER
verifyClawbackEqualityProof(
uint64_t const amount,
Slice const& proof,
Slice const& pubKeySlice,
Slice const& ciphertext,
uint256 const& contextHash);
/**
* @brief Generates a cryptographically secure 32-byte blinding factor.
*
* Produces random bytes suitable for use as an ElGamal blinding factor
* or Pedersen commitment randomness.
*
* @return A 32-byte buffer containing the random blinding factor.
*/
Buffer
generateBlindingFactor();
/**
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTSend transaction.
*
* This function calls mpt_verify_send_proof API in the mpt-crypto utility lib, which verifies the
* equality proof, amount linkage, balance linkage, and range proof.
* Equality proof: Proves the same value is encrypted for the sender, receiver, issuer, and auditor.
* Amount linkage: Proves the send amount matches the amount Pedersen commitment.
* Balance linkage: Proves the sender's balance matches the balance Pedersen
* commitment.
* Range proof: Proves the amount and the remaining balance are within range [0, 2^64-1].
*
* @param proof The full proof blob.
* @param sender The sender's public key and encrypted amount.
* @param destination The destination's public key and encrypted amount.
* @param issuer The issuer's public key and encrypted amount.
* @param auditor The auditor's public key and encrypted amount if present.
* @param spendingBalance The sender's current spending balance ciphertext.
* @param amountCommitment The Pedersen commitment to the send amount.
* @param balanceCommitment The Pedersen commitment to the sender's balance.
* @param contextHash The context hash binding the proof.
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
*/
TER
verifySendProof(
Slice const& proof,
ConfidentialRecipient const& sender,
ConfidentialRecipient const& destination,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor,
Slice const& spendingBalance,
Slice const& amountCommitment,
Slice const& balanceCommitment,
uint256 const& contextHash);
/**
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTConvertBack transaction.
*
* This function calls mpt_verify_convert_back_proof API in the mpt-crypto utility lib, which
* verifies the balance linkage proof and range proof. Balance linkage proof: proves the balance
* commitment matches the spending ciphertext. Range proof: proves the remaining balance after
* convert back is within range [0, 2^64-1].
*
* @param proof The full proof blob.
* @param pubKeySlice The holder's public key.
* @param spendingBalance The holder's spending balance ciphertext.
* @param balanceCommitment The Pedersen commitment to the balance.
* @param amount The amount being converted back to public.
* @param contextHash The context hash binding the proof.
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
*/
TER
verifyConvertBackProof(
Slice const& proof,
Slice const& pubKeySlice,
Slice const& spendingBalance,
Slice const& balanceCommitment,
uint64_t amount,
uint256 const& contextHash);
/**
* @brief Sequential reader for extracting proof components from a ZKProof blob.
*
* Encapsulates the offset-based arithmetic for slicing a concatenated proof
* blob into its individual components (equality proofs, Pedersen linkage
* proofs, bulletproofs, etc.). Performs bounds checking on every read and
* tracks whether the entire blob has been consumed.
*
* Usage:
* @code
* ProofReader reader(tx[sfZKProof]);
* auto equalityProof = reader.read(sizeEquality);
* auto pedersenProof = reader.read(ecPedersenProofLength);
* if (!equalityProof || !pedersenProof || !reader.done())
* return tecINTERNAL;
* @endcode
*/
class ProofReader
{
Slice data_;
std::size_t offset_ = 0;
public:
explicit ProofReader(Slice data) : data_(data)
{
}
/**
* @brief Read the next @p length bytes from the proof blob.
*
* @param length Number of bytes to read.
* @return A Slice of the requested bytes, or std::nullopt if there are
* not enough remaining bytes.
*/
[[nodiscard]] std::optional<Slice>
read(std::size_t length)
{
if (offset_ + length > data_.size())
return std::nullopt;
auto result = data_.substr(offset_, length);
offset_ += length;
return result;
}
/**
* @brief Returns true when every byte has been consumed.
*/
[[nodiscard]] bool
done() const
{
return offset_ == data_.size();
}
};
} // namespace xrpl

View File

@@ -125,10 +125,12 @@ namespace detail {
#pragma push_macro("XRPL_RETIRE_FIX")
#undef XRPL_RETIRE_FIX
// NOLINTBEGIN(bugprone-macro-parentheses)
#define XRPL_FEATURE(name, supported, vote) +1
#define XRPL_FIX(name, supported, vote) +1
#define XRPL_RETIRE_FEATURE(name) +1
#define XRPL_RETIRE_FIX(name) +1
// NOLINTEND(bugprone-macro-parentheses)
// This value SHOULD be equal to the number of amendments registered in
// Feature.cpp. Because it's only used to reserve storage, and determine how

View File

@@ -74,10 +74,12 @@ public:
Derived classes will load the object with all the known formats.
*/
private:
KnownFormats() : name_(beast::type_name<Derived>())
{
}
public:
/** Destroy the known formats object.
The defined formats are deleted.
@@ -181,6 +183,7 @@ private:
boost::container::flat_map<std::string, Item const*> names_;
boost::container::flat_map<KeyType, Item const*> types_;
friend Derived;
};
} // namespace xrpl

View File

@@ -173,8 +173,7 @@ enum LedgerEntryType : std::uint16_t {
LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040) \
LSF_FLAG(lsfMPTCanConfidentialAmount, 0x00000080)) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \
\
LEDGER_OBJECT(MPTokenIssuanceMutable, \
LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \
@@ -184,8 +183,7 @@ enum LedgerEntryType : std::uint16_t {
LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \
LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \
LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000) \
LSF_FLAG(lsmfMPTCannotMutateCanConfidentialAmount, 0x00040000)) \
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \
\
LEDGER_OBJECT(MPToken, \
LSF_FLAG2(lsfMPTLocked, 0x00000001) \
@@ -213,7 +211,7 @@ enum LedgerEntryType : std::uint16_t {
// lsfRequireDestTag = 0x00020000,
// ...
// };
#define TO_VALUE(name, value) name = value,
#define TO_VALUE(name, value) name = (value),
#define NULL_NAME(name, values) values
#define NULL_OUTPUT(name, value)
enum LedgerSpecificFlags : std::uint32_t { XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT) };

View File

@@ -20,7 +20,7 @@ enum GranularPermissionType : std::uint32_t {
#pragma push_macro("PERMISSION")
#undef PERMISSION
#define PERMISSION(type, txType, value) type = value,
#define PERMISSION(type, txType, value) type = (value),
#include <xrpl/protocol/detail/permissions.macro>

View File

@@ -307,46 +307,4 @@ std::size_t constexpr permissionMaxSize = 10;
/** The maximum number of transactions that can be in a batch. */
std::size_t constexpr maxBatchTxCount = 8;
/** Length of one component of EC ElGamal ciphertext */
std::size_t constexpr ecGamalEncryptedLength = 33;
/** EC ElGamal ciphertext length: two 33-byte components concatenated */
std::size_t constexpr ecGamalEncryptedTotalLength = ecGamalEncryptedLength * 2;
/** Length of equality ZKProof in bytes */
std::size_t constexpr ecEqualityProofLength = 98;
/** Length of EC point (compressed) */
std::size_t constexpr compressedECPointLength = 33;
/** Length of EC public key (compressed) */
std::size_t constexpr ecPubKeyLength = compressedECPointLength;
/** Length of EC private key in bytes */
std::size_t constexpr ecPrivKeyLength = 32;
/** Length of the EC blinding factor in bytes */
std::size_t constexpr ecBlindingFactorLength = 32;
/** Length of Schnorr ZKProof for public key registration in bytes */
std::size_t constexpr ecSchnorrProofLength = 65;
/** Length of ElGamal Pedersen linkage proof in bytes */
std::size_t constexpr ecPedersenProofLength = 195;
/** Length of Pedersen Commitment (compressed) */
std::size_t constexpr ecPedersenCommitmentLength = compressedECPointLength;
/** Length of single bulletproof (range proof for 1 commitment) in bytes */
std::size_t constexpr ecSingleBulletproofLength = 688;
/** Length of double bulletproof (range proof for 2 commitments) in bytes */
std::size_t constexpr ecDoubleBulletproofLength = 754;
/** Compressed EC point prefix for even y-coordinate */
std::uint8_t constexpr ecCompressedPrefixEvenY = 0x02;
/** Compressed EC point prefix for odd y-coordinate */
std::uint8_t constexpr ecCompressedPrefixOddY = 0x03;
} // namespace xrpl

View File

@@ -84,7 +84,7 @@ class STCurrency;
#pragma push_macro("TO_MAP")
#undef TO_MAP
#define TO_ENUM(name, value) name = value,
#define TO_ENUM(name, value) name = (value),
#define TO_MAP(name, value) {#name, value},
enum SerializedTypeID { XMACRO(TO_ENUM) };

View File

@@ -401,7 +401,7 @@ amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource);
inline STAmount const&
toSTAmount(STAmount const& a)
{
return a;
return a; // NOLINT(bugprone-return-const-ref-from-parameter)
}
//------------------------------------------------------------------------------

View File

@@ -1188,6 +1188,7 @@ STObject::getFieldByConstRef(SField const& field, V const& empty) const
SerializedTypeID const id = rf->getSType();
if (id == STI_NOTPRESENT)
// NOLINTNEXTLINE(bugprone-return-const-ref-from-parameter)
return empty; // optional field not present
T const* cf = dynamic_cast<T const*>(rf);

View File

@@ -122,7 +122,6 @@ enum TEMcodes : TERUnderlyingType {
temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH,
temBAD_MPT,
temBAD_CIPHERTEXT,
};
//------------------------------------------------------------------------------
@@ -345,11 +344,6 @@ enum TECcodes : TERUnderlyingType {
tecLIMIT_EXCEEDED = 195,
tecPSEUDO_ACCOUNT = 196,
tecPRECISION_LOSS = 197,
// DEPRECATED: This error code tecNO_DELEGATE_PERMISSION is reserved for
// backward compatibility with historical data on non-prod networks, can be
// reclaimed after those networks reset.
tecNO_DELEGATE_PERMISSION = 198,
tecBAD_PROOF = 199,
};
//------------------------------------------------------------------------------

View File

@@ -138,8 +138,7 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \
TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \
TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback) \
TF_FLAG(tfMPTCanConfidentialAmount, lsfMPTCanConfidentialAmount), \
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \
MASK_ADJ(0)) \
\
TRANSACTION(MPTokenAuthorize, \
@@ -236,7 +235,7 @@ XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT, NULL_MASK_ADJ)
// The mask adjustment (maskAdj) allows adding flags back to the mask, making them invalid.
// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) to reject tfInnerBatchTxn on outer Batch.
#define TO_MASK(name, values, maskAdj) \
inline constexpr FlagValue tf##name##Mask = ~(tfUniversal values) | maskAdj;
inline constexpr FlagValue tf##name##Mask = ~(tfUniversal values) | (maskAdj);
#define VALUE_TO_MASK(name, value) | name
#define MASK_ADJ_TO_MASK(value) value
XMACRO(TO_MASK, VALUE_TO_MASK, VALUE_TO_MASK, MASK_ADJ_TO_MASK)
@@ -348,12 +347,10 @@ inline constexpr FlagValue tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTrans
inline constexpr FlagValue tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
inline constexpr FlagValue tmfMPTCannotMutateCanConfidentialAmount =
lsmfMPTCannotMutateCanConfidentialAmount;
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask = ~(
tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee | tmfMPTCannotMutateCanConfidentialAmount);
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask =
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
// MPTokenIssuanceSet MutableFlags:
// Set or Clear flags.
@@ -370,13 +367,10 @@ inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000100;
inline constexpr FlagValue tmfMPTClearCanTransfer = 0x00000200;
inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000400;
inline constexpr FlagValue tmfMPTClearCanClawback = 0x00000800;
inline constexpr FlagValue tmfMPTSetCanConfidentialAmount = 0x00001000;
inline constexpr FlagValue tmfMPTClearCanConfidentialAmount = 0x00002000;
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask =
~(tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback |
tmfMPTClearCanClawback | tmfMPTSetCanConfidentialAmount | tmfMPTClearCanConfidentialAmount);
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = ~(
tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback | tmfMPTClearCanClawback);
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a
// TrustLine to be added to the issuer of that token without explicit permission from that issuer.

View File

@@ -15,7 +15,6 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FEATURE(ConfidentialTransfer, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(MPTokensV2, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -387,41 +387,32 @@ LEDGER_ENTRY(ltAMM, 0x0079, AMM, amm, ({
\sa keylet::mptIssuance
*/
LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfIssuer, soeREQUIRED},
{sfSequence, soeREQUIRED},
{sfTransferFee, soeDEFAULT},
{sfOwnerNode, soeREQUIRED},
{sfAssetScale, soeDEFAULT},
{sfMaximumAmount, soeOPTIONAL},
{sfOutstandingAmount, soeREQUIRED},
{sfLockedAmount, soeOPTIONAL},
{sfMPTokenMetadata, soeOPTIONAL},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
{sfDomainID, soeOPTIONAL},
{sfMutableFlags, soeDEFAULT},
{sfIssuerEncryptionKey, soeOPTIONAL},
{sfAuditorEncryptionKey, soeOPTIONAL},
{sfConfidentialOutstandingAmount, soeDEFAULT},
{sfIssuer, soeREQUIRED},
{sfSequence, soeREQUIRED},
{sfTransferFee, soeDEFAULT},
{sfOwnerNode, soeREQUIRED},
{sfAssetScale, soeDEFAULT},
{sfMaximumAmount, soeOPTIONAL},
{sfOutstandingAmount, soeREQUIRED},
{sfLockedAmount, soeOPTIONAL},
{sfMPTokenMetadata, soeOPTIONAL},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
{sfDomainID, soeOPTIONAL},
{sfMutableFlags, soeDEFAULT},
}))
/** A ledger object which tracks MPToken
\sa keylet::mptoken
*/
LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfAccount, soeREQUIRED},
{sfMPTokenIssuanceID, soeREQUIRED},
{sfMPTAmount, soeDEFAULT},
{sfLockedAmount, soeOPTIONAL},
{sfOwnerNode, soeREQUIRED},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
{sfConfidentialBalanceInbox, soeOPTIONAL},
{sfConfidentialBalanceSpending, soeOPTIONAL},
{sfConfidentialBalanceVersion, soeDEFAULT},
{sfIssuerEncryptedBalance, soeOPTIONAL},
{sfAuditorEncryptedBalance, soeOPTIONAL},
{sfHolderEncryptionKey, soeOPTIONAL},
{sfAccount, soeREQUIRED},
{sfMPTokenIssuanceID, soeREQUIRED},
{sfMPTAmount, soeDEFAULT},
{sfLockedAmount, soeOPTIONAL},
{sfOwnerNode, soeREQUIRED},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
}))
/** A ledger object which tracks Oracle

View File

@@ -11,10 +11,7 @@ secp256k1Context()
struct holder
{
secp256k1_context* impl;
// SECP256K1_CONTEXT_SIGN and SECP256K1_CONTEXT_VERIFY were
// deprecated. All contexts support both signing and verification, so
// SECP256K1_CONTEXT_NONE is the correct flag to use.
holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_NONE))
holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
{
}

View File

@@ -113,7 +113,6 @@ TYPED_SFIELD(sfInterestRate, UINT32, 65) // 1/10 basis points (bi
TYPED_SFIELD(sfLateInterestRate, UINT32, 66) // 1/10 basis points (bips)
TYPED_SFIELD(sfCloseInterestRate, UINT32, 67) // 1/10 basis points (bips)
TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 68) // 1/10 basis points (bips)
TYPED_SFIELD(sfConfidentialBalanceVersion, UINT32, 69)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)
@@ -147,7 +146,6 @@ TYPED_SFIELD(sfSubjectNode, UINT64, 28)
TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::sMD_BaseTen|SField::sMD_Default)
TYPED_SFIELD(sfVaultNode, UINT64, 30)
TYPED_SFIELD(sfLoanBrokerNode, UINT64, 31)
TYPED_SFIELD(sfConfidentialOutstandingAmount, UINT64, 32, SField::sMD_BaseTen|SField::sMD_Default)
// 128-bit
TYPED_SFIELD(sfEmailHash, UINT128, 1)
@@ -207,7 +205,6 @@ TYPED_SFIELD(sfParentBatchID, UINT256, 36)
TYPED_SFIELD(sfLoanBrokerID, UINT256, 37,
SField::sMD_PseudoAccount | SField::sMD_Default)
TYPED_SFIELD(sfLoanID, UINT256, 38)
TYPED_SFIELD(sfBlindingFactor, UINT256, 39)
// number (common)
TYPED_SFIELD(sfNumber, NUMBER, 1)
@@ -301,21 +298,6 @@ TYPED_SFIELD(sfAssetClass, VL, 28)
TYPED_SFIELD(sfProvider, VL, 29)
TYPED_SFIELD(sfMPTokenMetadata, VL, 30)
TYPED_SFIELD(sfCredentialType, VL, 31)
TYPED_SFIELD(sfConfidentialBalanceInbox, VL, 32)
TYPED_SFIELD(sfConfidentialBalanceSpending, VL, 33)
TYPED_SFIELD(sfIssuerEncryptedBalance, VL, 34)
TYPED_SFIELD(sfIssuerEncryptionKey, VL, 35)
TYPED_SFIELD(sfHolderEncryptionKey, VL, 36)
TYPED_SFIELD(sfZKProof, VL, 37)
TYPED_SFIELD(sfHolderEncryptedAmount, VL, 38)
TYPED_SFIELD(sfIssuerEncryptedAmount, VL, 39)
TYPED_SFIELD(sfSenderEncryptedAmount, VL, 40)
TYPED_SFIELD(sfDestinationEncryptedAmount, VL, 41)
TYPED_SFIELD(sfAuditorEncryptedBalance, VL, 42)
TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)

View File

@@ -734,8 +734,6 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
{sfMPTokenMetadata, soeOPTIONAL},
{sfTransferFee, soeOPTIONAL},
{sfMutableFlags, soeOPTIONAL},
{sfIssuerEncryptionKey, soeOPTIONAL},
{sfAuditorEncryptionKey, soeOPTIONAL},
}))
/** This transaction type authorizes a MPToken instance */
@@ -1078,90 +1076,6 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
{sfAmount, soeREQUIRED, soeMPTSupported},
}))
/** This transaction type converts into confidential MPT balance. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTConvert.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert,
Delegation::delegable,
featureConfidentialTransfer,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfMPTAmount, soeREQUIRED},
{sfHolderEncryptionKey, soeOPTIONAL},
{sfHolderEncryptedAmount, soeREQUIRED},
{sfIssuerEncryptedAmount, soeREQUIRED},
{sfAuditorEncryptedAmount, soeOPTIONAL},
{sfBlindingFactor, soeREQUIRED},
{sfZKProof, soeOPTIONAL},
}))
/** This transaction type merges MPT inbox. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTMergeInbox.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_MERGE_INBOX, 86, ConfidentialMPTMergeInbox,
Delegation::delegable,
featureConfidentialTransfer,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
}))
/** This transaction type converts back into public MPT balance. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTConvertBack.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT_BACK, 87, ConfidentialMPTConvertBack,
Delegation::delegable,
featureConfidentialTransfer,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfMPTAmount, soeREQUIRED},
{sfHolderEncryptedAmount, soeREQUIRED},
{sfIssuerEncryptedAmount, soeREQUIRED},
{sfAuditorEncryptedAmount, soeOPTIONAL},
{sfBlindingFactor, soeREQUIRED},
{sfZKProof, soeREQUIRED},
{sfBalanceCommitment, soeREQUIRED},
}))
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_SEND, 88, ConfidentialMPTSend,
Delegation::delegable,
featureConfidentialTransfer,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfDestination, soeREQUIRED},
{sfSenderEncryptedAmount, soeREQUIRED},
{sfDestinationEncryptedAmount, soeREQUIRED},
{sfIssuerEncryptedAmount, soeREQUIRED},
{sfAuditorEncryptedAmount, soeOPTIONAL},
{sfZKProof, soeREQUIRED},
{sfAmountCommitment, soeREQUIRED},
{sfBalanceCommitment, soeREQUIRED},
{sfCredentialIDs, soeOPTIONAL},
}))
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTClawback.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback,
Delegation::delegable,
featureConfidentialTransfer,
noPriv,
({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfHolder, soeREQUIRED},
{sfMPTAmount, soeREQUIRED},
{sfZKProof, soeREQUIRED},
}))
/** This system-generated transaction type is used to update the status of the various amendments.
For details, see: https://xrpl.org/amendments.html

View File

@@ -146,7 +146,7 @@ getOptional(Json::Value const& v, xrpl::SField const& field)
{
return getOrThrow<T>(v, field);
}
catch (...)
catch (...) // NOLINT(bugprone-empty-catch)
{
}
return {};

View File

@@ -137,7 +137,6 @@ JSS(authorized_credentials); // in: ledger_entry DepositPreauth
JSS(auth_accounts); // out: amm_info
JSS(auth_change); // out: AccountInfo
JSS(auth_change_queued); // out: AccountInfo
JSS(auditor_encrypted_balance); // out: mpt_holders (confidential MPT)
JSS(available); // out: ValidatorList
JSS(avg_bps_recv); // out: Peers
JSS(avg_bps_sent); // out: Peers
@@ -162,6 +161,9 @@ JSS(build_path); // in: TransactionSign
JSS(build_version); // out: NetworkOPs
JSS(cancel_after); // out: AccountChannels
JSS(can_delete); // out: CanDelete
JSS(mpt_amount); // out: mpt_holders
JSS(mpt_issuance_id); // in: Payment, mpt_holders
JSS(mptoken_index); // out: mpt_holders
JSS(changes); // out: BookChanges
JSS(channel_id); // out: AccountChannels
JSS(channels); // out: AccountChannels
@@ -183,170 +185,165 @@ JSS(command); // in: RPCHandler
JSS(common); // out: RPC server_definitions
JSS(complete); // out: NetworkOPs, InboundLedger
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
JSS(confidential_balance_inbox); // out: mpt_holders (confidential MPT)
JSS(confidential_balance_spending); // out: mpt_holders (confidential MPT)
JSS(confidential_balance_version); // out: mpt_holders (confidential MPT)
JSS(consensus); // out: NetworkOPs, LedgerConsensus
JSS(converge_time); // out: NetworkOPs
JSS(converge_time_s); // out: NetworkOPs
JSS(cookie); // out: NetworkOPs
JSS(count); // in: AccountTx*, ValidatorList
JSS(counters); // in/out: retrieve counters
JSS(credentials); // in: deposit_authorized
JSS(credential_type); // in: LedgerEntry DepositPreauth
JSS(ctid); // in/out: Tx RPC
JSS(currency_a); // out: BookChanges
JSS(currency_b); // out: BookChanges
JSS(currency); // in: paths/PathRequest, STAmount
// out: STPathSet, STAmount, AccountLines
JSS(current); // out: OwnerInfo
JSS(current_activities); //
JSS(current_ledger_size); // out: TxQ
JSS(current_queue_size); // out: TxQ
JSS(data); // out: LedgerData
JSS(date); // out: tx/Transaction, NetworkOPs
JSS(dbKBLedger); // out: getCounts
JSS(dbKBTotal); // out: getCounts
JSS(dbKBTransaction); // out: getCounts
JSS(debug_signing); // in: TransactionSign
JSS(deletion_blockers_only); // in: AccountObjects
JSS(delivered_amount); // out: insertDeliveredAmount
JSS(deposit_authorized); // out: deposit_authorized
JSS(deprecated); //
JSS(descending); // in: AccountTx*
JSS(description); // in/out: Reservations
JSS(destination); // in: nft_buy_offers, nft_sell_offers
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
// out: AccountChannels
JSS(destination_amount); // in: PathRequest, RipplePathFind
JSS(destination_currencies); // in: PathRequest, RipplePathFind
JSS(destination_tag); // in: PathRequest
// out: AccountChannels
JSS(details); // out: Manifest, server_info
JSS(dir_entry); // out: DirectoryEntryIterator
JSS(dir_index); // out: DirectoryEntryIterator
JSS(dir_root); // out: DirectoryEntryIterator
JSS(discounted_fee); // out: amm_info
JSS(domain); // out: ValidatorInfo, Manifest
JSS(drops); // out: TxQ
JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(enabled); // out: AmendmentTable
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
JSS(entire_set); // out: get_aggregate_price
JSS(ephemeral_key); // out: ValidatorInfo
// in/out: Manifest
JSS(error); // out: error
JSS(errored); //
JSS(error_code); // out: error
JSS(error_exception); // out: Submit
JSS(error_message); // out: error
JSS(expand); // in: handler/Ledger
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(fail_hard); // in: Sign, Submit
JSS(failed); // out: InboundLedger
JSS(feature); // in: Feature
JSS(features); // out: Feature
JSS(fee_base); // out: NetworkOPs
JSS(fee_div_max); // in: TransactionSign
JSS(fee_level); // out: AccountInfo
JSS(fee_mult_max); // in: TransactionSign
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
JSS(fetch_pack); // out: NetworkOPs
JSS(FIELDS); // out: RPC server_definitions
// matches definitions.json format
JSS(first); // out: rpc/Version
JSS(finished); //
JSS(fix_txns); // in: LedgerCleaner
JSS(flags); // out: AccountOffers, NetworkOPs
JSS(forward); // in: AccountTx
JSS(freeze); // out: AccountLines
JSS(freeze_peer); // out: AccountLines
JSS(deep_freeze); // out: AccountLines
JSS(deep_freeze_peer); // out: AccountLines
JSS(frozen_balances); // out: GatewayBalances
JSS(full); // in: LedgerClearer, handlers/Ledger
JSS(full_reply); // out: PathFind
JSS(fullbelow_size); // out: GetCounts
JSS(git); // out: server_info
JSS(good); // out: RPCVersion
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
JSS(have_header); // out: InboundLedger
JSS(have_state); // out: InboundLedger
JSS(have_transactions); // out: InboundLedger
JSS(high); // out: BookChanges
JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
JSS(historical_perminute); // historical_perminute.
JSS(holders); // out: MPTHolders
JSS(holder_encryption_key); // out: mpt_holders (confidential MPT)
JSS(hostid); // out: NetworkOPs
JSS(hotwallet); // in: GatewayBalances
JSS(id); // websocket.
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(in); // out: OverlayImpl
JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us); //
JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid version
JSS(io_latency_ms); // out: NetworkOPs
JSS(ip); // in: Connect, out: OverlayImpl
JSS(is_burned); // out: nft_info (clio)
JSS(isSerialized); // out: RPC server_definitions
// matches definitions.json format
JSS(isSigningField); // out: RPC server_definitions
// matches definitions.json format
JSS(isVLEncoded); // out: RPC server_definitions
// matches definitions.json format
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
// out: STPathSet, STAmount
JSS(issuer_encrypted_balance); // out: mpt_holders (confidential MPT)
JSS(job); //
JSS(job_queue); //
JSS(jobs); //
JSS(jsonrpc); // json version
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
JSS(kept); // out: SubmitTransaction
JSS(key); // out
JSS(key_type); // in/out: WalletPropose, TransactionSign
JSS(latency); // out: PeerImp
JSS(last); // out: RPCVersion
JSS(last_close); // out: NetworkOPs
JSS(last_refresh_time); // out: ValidatorSite
JSS(last_refresh_status); // out: ValidatorSite
JSS(last_refresh_message); // out: ValidatorSite
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
// out: NetworkOPs, PeerImp
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
// AccountLines
JSS(ledger_data); // out: LedgerHeader
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
// TransactionEntry, handlers/Ledger
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
// AccountLines
JSS(ledger_hit_rate); // out: GetCounts
JSS(ledger_index); // in/out: many
JSS(ledger_index_max); // in, out: AccountTx*
JSS(ledger_index_min); // in, out: AccountTx*
JSS(ledger_max); // in, out: AccountTx*
JSS(ledger_min); // in, out: AccountTx*
JSS(ledger_time); // out: NetworkOPs
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
// matches definitions.json format
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
JSS(levels); // LogLevels
JSS(consensus); // out: NetworkOPs, LedgerConsensus
JSS(converge_time); // out: NetworkOPs
JSS(converge_time_s); // out: NetworkOPs
JSS(cookie); // out: NetworkOPs
JSS(count); // in: AccountTx*, ValidatorList
JSS(counters); // in/out: retrieve counters
JSS(credentials); // in: deposit_authorized
JSS(credential_type); // in: LedgerEntry DepositPreauth
JSS(ctid); // in/out: Tx RPC
JSS(currency_a); // out: BookChanges
JSS(currency_b); // out: BookChanges
JSS(currency); // in: paths/PathRequest, STAmount
// out: STPathSet, STAmount, AccountLines
JSS(current); // out: OwnerInfo
JSS(current_activities); //
JSS(current_ledger_size); // out: TxQ
JSS(current_queue_size); // out: TxQ
JSS(data); // out: LedgerData
JSS(date); // out: tx/Transaction, NetworkOPs
JSS(dbKBLedger); // out: getCounts
JSS(dbKBTotal); // out: getCounts
JSS(dbKBTransaction); // out: getCounts
JSS(debug_signing); // in: TransactionSign
JSS(deletion_blockers_only); // in: AccountObjects
JSS(delivered_amount); // out: insertDeliveredAmount
JSS(deposit_authorized); // out: deposit_authorized
JSS(deprecated); //
JSS(descending); // in: AccountTx*
JSS(description); // in/out: Reservations
JSS(destination); // in: nft_buy_offers, nft_sell_offers
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
// out: AccountChannels
JSS(destination_amount); // in: PathRequest, RipplePathFind
JSS(destination_currencies); // in: PathRequest, RipplePathFind
JSS(destination_tag); // in: PathRequest
// out: AccountChannels
JSS(details); // out: Manifest, server_info
JSS(dir_entry); // out: DirectoryEntryIterator
JSS(dir_index); // out: DirectoryEntryIterator
JSS(dir_root); // out: DirectoryEntryIterator
JSS(discounted_fee); // out: amm_info
JSS(domain); // out: ValidatorInfo, Manifest
JSS(drops); // out: TxQ
JSS(duration_us); // out: NetworkOPs
JSS(effective); // out: ValidatorList
// in: UNL
JSS(enabled); // out: AmendmentTable
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
JSS(entire_set); // out: get_aggregate_price
JSS(ephemeral_key); // out: ValidatorInfo
// in/out: Manifest
JSS(error); // out: error
JSS(errored); //
JSS(error_code); // out: error
JSS(error_exception); // out: Submit
JSS(error_message); // out: error
JSS(expand); // in: handler/Ledger
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(fail_hard); // in: Sign, Submit
JSS(failed); // out: InboundLedger
JSS(feature); // in: Feature
JSS(features); // out: Feature
JSS(fee_base); // out: NetworkOPs
JSS(fee_div_max); // in: TransactionSign
JSS(fee_level); // out: AccountInfo
JSS(fee_mult_max); // in: TransactionSign
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
JSS(fetch_pack); // out: NetworkOPs
JSS(FIELDS); // out: RPC server_definitions
// matches definitions.json format
JSS(first); // out: rpc/Version
JSS(finished); //
JSS(fix_txns); // in: LedgerCleaner
JSS(flags); // out: AccountOffers, NetworkOPs
JSS(forward); // in: AccountTx
JSS(freeze); // out: AccountLines
JSS(freeze_peer); // out: AccountLines
JSS(deep_freeze); // out: AccountLines
JSS(deep_freeze_peer); // out: AccountLines
JSS(frozen_balances); // out: GatewayBalances
JSS(full); // in: LedgerClearer, handlers/Ledger
JSS(full_reply); // out: PathFind
JSS(fullbelow_size); // out: GetCounts
JSS(git); // out: server_info
JSS(good); // out: RPCVersion
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
JSS(have_header); // out: InboundLedger
JSS(have_state); // out: InboundLedger
JSS(have_transactions); // out: InboundLedger
JSS(high); // out: BookChanges
JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
JSS(historical_perminute); // historical_perminute.
JSS(holders); // out: MPTHolders
JSS(hostid); // out: NetworkOPs
JSS(hotwallet); // in: GatewayBalances
JSS(id); // websocket.
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(in); // out: OverlayImpl
JSS(inLedger); // out: tx/Transaction
JSS(inbound); // out: PeerImp
JSS(index); // in: LedgerEntry
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
JSS(initial_sync_duration_us); //
JSS(internal_command); // in: Internal
JSS(invalid_API_version); // out: Many, when a request has an invalid version
JSS(io_latency_ms); // out: NetworkOPs
JSS(ip); // in: Connect, out: OverlayImpl
JSS(is_burned); // out: nft_info (clio)
JSS(isSerialized); // out: RPC server_definitions
// matches definitions.json format
JSS(isSigningField); // out: RPC server_definitions
// matches definitions.json format
JSS(isVLEncoded); // out: RPC server_definitions
// matches definitions.json format
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
// out: STPathSet, STAmount
JSS(job); //
JSS(job_queue); //
JSS(jobs); //
JSS(jsonrpc); // json version
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
JSS(kept); // out: SubmitTransaction
JSS(key); // out
JSS(key_type); // in/out: WalletPropose, TransactionSign
JSS(latency); // out: PeerImp
JSS(last); // out: RPCVersion
JSS(last_close); // out: NetworkOPs
JSS(last_refresh_time); // out: ValidatorSite
JSS(last_refresh_status); // out: ValidatorSite
JSS(last_refresh_message); // out: ValidatorSite
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
// out: NetworkOPs, PeerImp
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
// AccountLines
JSS(ledger_data); // out: LedgerHeader
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
// TransactionEntry, handlers/Ledger
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
// AccountLines
JSS(ledger_hit_rate); // out: GetCounts
JSS(ledger_index); // in/out: many
JSS(ledger_index_max); // in, out: AccountTx*
JSS(ledger_index_min); // in, out: AccountTx*
JSS(ledger_max); // in, out: AccountTx*
JSS(ledger_min); // in, out: AccountTx*
JSS(ledger_time); // out: NetworkOPs
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
// matches definitions.json format
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
JSS(levels); // LogLevels
JSS(limit); // in/out: AccountTx*, AccountOffers, AccountLines, AccountObjects
// in: LedgerData, BookOffers
JSS(limit_peer); // out: AccountLines
@@ -404,9 +401,6 @@ JSS(min_ledger); // in: LedgerCleaner
JSS(minimum_fee); // out: TxQ
JSS(minimum_level); // out: TxQ
JSS(missingCommand); // error
JSS(mpt_amount); // out: mpt_holders
JSS(mpt_issuance_id); // in: Payment, mpt_holders
JSS(mptoken_index); // out: mpt_holders
JSS(mpt_issuance_id_a); // out: BookChanges
JSS(mpt_issuance_id_b); // out: BookChanges
JSS(name); // out: AmendmentTableImpl, PeerImp

View File

@@ -4,42 +4,33 @@ This directory contains auto-generated C++ wrapper classes for XRP Ledger protoc
## Generated Files
The files in this directory are automatically generated at **CMake configure time** from macro definition files:
The files in this directory are generated from macro definition files:
- **Transaction classes** (in `transactions/`): Generated from `include/xrpl/protocol/detail/transactions.macro` by `scripts/generate_tx_classes.py`
- **Ledger entry classes** (in `ledger_entries/`): Generated from `include/xrpl/protocol/detail/ledger_entries.macro` by `scripts/generate_ledger_classes.py`
- **Transaction classes** (in `transactions/`): Generated from `include/xrpl/protocol/detail/transactions.macro` by `cmake/scripts/codegen/generate_tx_classes.py`
- **Ledger entry classes** (in `ledger_entries/`): Generated from `include/xrpl/protocol/detail/ledger_entries.macro` by `cmake/scripts/codegen/generate_ledger_classes.py`
## Generation Process
The generation happens automatically when you **configure** the project (not during build). When you run CMake, the system:
Generation requires a one-time setup step to create a virtual environment
and install Python dependencies, followed by running the generation target:
1. Creates a Python virtual environment in the build directory (`codegen_venv`)
2. Installs Python dependencies from `scripts/requirements.txt` into the venv (only if needed)
3. Runs the Python generation scripts using the venv Python interpreter
4. Parses the macro files to extract type definitions
5. Generates type-safe C++ wrapper classes using Mako templates
6. Places the generated headers in this directory
```bash
cmake --build . --target setup_code_gen # create venv and install dependencies (once)
cmake --build . --target code_gen # generate code
```
### When Regeneration Happens
The code is regenerated when:
- You run CMake configure for the first time
- The Python virtual environment doesn't exist
- `scripts/requirements.txt` has been modified
To force regeneration, delete the build directory and reconfigure.
By default, `CODEGEN_VENV_DIR` points to `.venv` in the project root. The
`setup_code_gen` target creates a venv there and installs the required packages.
The `code_gen` target then uses the venv's Python interpreter to run generation.
### Python Dependencies
The code generation requires the following Python packages (automatically installed):
The code generation requires the following Python packages (installed by `setup_code_gen`):
- `pcpp` - C preprocessor for Python
- `pyparsing` - Parser combinator library
- `Mako` - Template engine
These are isolated in a virtual environment and won't affect your system Python installation.
## Version Control
The generated `.h` files **are checked into version control**. This means:
@@ -50,15 +41,15 @@ The generated `.h` files **are checked into version control**. This means:
## Modifying Generated Code
**Do not manually edit generated files.** Any changes will be overwritten the next time CMake configure runs.
**Do not manually edit generated files.** Any changes will be overwritten the next time `code_gen` is run.
To modify the generated classes:
- Edit the macro files in `include/xrpl/protocol/detail/`
- Edit the Mako templates in `scripts/templates/`
- Edit the generation scripts in `scripts/`
- Update Python dependencies in `scripts/requirements.txt`
- Run CMake configure to regenerate
- Edit the Mako templates in `cmake/scripts/codegen/templates/`
- Edit the generation scripts in `cmake/scripts/codegen/`
- Update Python dependencies in `cmake/scripts/codegen/requirements.txt`
- Run `cmake --build . --target code_gen` to regenerate
## Adding Common Fields
@@ -73,7 +64,7 @@ Base classes:
Templates (update to pass required common fields to base class constructors):
- `scripts/templates/Transaction.h.mako`
- `scripts/templates/LedgerEntry.h.mako`
- `cmake/scripts/codegen/templates/Transaction.h.mako`
- `cmake/scripts/codegen/templates/LedgerEntry.h.mako`
These files are **not auto-generated** and must be updated by hand.

View File

@@ -147,150 +147,6 @@ public:
{
return this->sle_->at(sfPreviousTxnLgrSeq);
}
/**
* @brief Get sfConfidentialBalanceInbox (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceInbox() const
{
if (hasConfidentialBalanceInbox())
return this->sle_->at(sfConfidentialBalanceInbox);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceInbox is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceInbox() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceInbox);
}
/**
* @brief Get sfConfidentialBalanceSpending (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceSpending() const
{
if (hasConfidentialBalanceSpending())
return this->sle_->at(sfConfidentialBalanceSpending);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceSpending is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceSpending() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceSpending);
}
/**
* @brief Get sfConfidentialBalanceVersion (soeDEFAULT)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getConfidentialBalanceVersion() const
{
if (hasConfidentialBalanceVersion())
return this->sle_->at(sfConfidentialBalanceVersion);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceVersion is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceVersion() const
{
return this->sle_->isFieldPresent(sfConfidentialBalanceVersion);
}
/**
* @brief Get sfIssuerEncryptedBalance (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptedBalance() const
{
if (hasIssuerEncryptedBalance())
return this->sle_->at(sfIssuerEncryptedBalance);
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptedBalance is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptedBalance() const
{
return this->sle_->isFieldPresent(sfIssuerEncryptedBalance);
}
/**
* @brief Get sfAuditorEncryptedBalance (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedBalance() const
{
if (hasAuditorEncryptedBalance())
return this->sle_->at(sfAuditorEncryptedBalance);
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedBalance is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedBalance() const
{
return this->sle_->isFieldPresent(sfAuditorEncryptedBalance);
}
/**
* @brief Get sfHolderEncryptionKey (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getHolderEncryptionKey() const
{
if (hasHolderEncryptionKey())
return this->sle_->at(sfHolderEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfHolderEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasHolderEncryptionKey() const
{
return this->sle_->isFieldPresent(sfHolderEncryptionKey);
}
};
/**
@@ -414,72 +270,6 @@ public:
return *this;
}
/**
* @brief Set sfConfidentialBalanceInbox (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceInbox(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceInbox] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceSpending (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceSpending(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceSpending] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceVersion (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setConfidentialBalanceVersion(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfConfidentialBalanceVersion] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedBalance (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setIssuerEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedBalance] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedBalance (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setAuditorEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedBalance] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptionKey] = value;
return *this;
}
/**
* @brief Build and return the completed MPToken wrapper.
* @param index The ledger entry index.

View File

@@ -278,78 +278,6 @@ public:
{
return this->sle_->isFieldPresent(sfMutableFlags);
}
/**
* @brief Get sfIssuerEncryptionKey (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptionKey() const
{
if (hasIssuerEncryptionKey())
return this->sle_->at(sfIssuerEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptionKey() const
{
return this->sle_->isFieldPresent(sfIssuerEncryptionKey);
}
/**
* @brief Get sfAuditorEncryptionKey (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptionKey() const
{
if (hasAuditorEncryptionKey())
return this->sle_->at(sfAuditorEncryptionKey);
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptionKey() const
{
return this->sle_->isFieldPresent(sfAuditorEncryptionKey);
}
/**
* @brief Get sfConfidentialOutstandingAmount (soeDEFAULT)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT64::type::value_type>
getConfidentialOutstandingAmount() const
{
if (hasConfidentialOutstandingAmount())
return this->sle_->at(sfConfidentialOutstandingAmount);
return std::nullopt;
}
/**
* @brief Check if sfConfidentialOutstandingAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialOutstandingAmount() const
{
return this->sle_->isFieldPresent(sfConfidentialOutstandingAmount);
}
};
/**
@@ -541,39 +469,6 @@ public:
return *this;
}
/**
* @brief Set sfIssuerEncryptionKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptionKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfConfidentialOutstandingAmount (soeDEFAULT)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setConfidentialOutstandingAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfConfidentialOutstandingAmount] = value;
return *this;
}
/**
* @brief Build and return the completed MPTokenIssuance wrapper.
* @param index The ledger entry index.

View File

@@ -1,201 +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 ConfidentialMPTClawbackBuilder;
/**
* @brief Transaction: ConfidentialMPTClawback
*
* Type: ttCONFIDENTIAL_MPT_CLAWBACK (89)
* Delegable: Delegation::delegable
* Amendment: featureConfidentialTransfer
* Privileges: noPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTClawbackBuilder to construct new transactions.
*/
class ConfidentialMPTClawback : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CLAWBACK;
/**
* @brief Construct a ConfidentialMPTClawback transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTClawback(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 ConfidentialMPTClawback");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfHolder (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getHolder() const
{
return this->tx_->at(sfHolder);
}
/**
* @brief Get sfMPTAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfZKProof (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
};
/**
* @brief Builder for ConfidentialMPTClawback transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses Json::Value internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTClawbackBuilder : public TransactionBuilderBase<ConfidentialMPTClawbackBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTClawbackBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param holder The sfHolder field value.
* @param mPTAmount The sfMPTAmount field value.
* @param zKProof The sfZKProof field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTClawbackBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& holder, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTClawbackBuilder>(ttCONFIDENTIAL_MPT_CLAWBACK, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setHolder(holder);
setMPTAmount(mPTAmount);
setZKProof(zKProof);
}
/**
* @brief Construct a ConfidentialMPTClawbackBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTClawbackBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CLAWBACK)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawbackBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfHolder (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setHolder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfHolder] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfZKProof (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTClawbackBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTClawback wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTClawback
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTClawback{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -1,336 +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 ConfidentialMPTConvertBuilder;
/**
* @brief Transaction: ConfidentialMPTConvert
*
* Type: ttCONFIDENTIAL_MPT_CONVERT (85)
* Delegable: Delegation::delegable
* Amendment: featureConfidentialTransfer
* Privileges: noPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTConvertBuilder to construct new transactions.
*/
class ConfidentialMPTConvert : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT;
/**
* @brief Construct a ConfidentialMPTConvert transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTConvert(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 ConfidentialMPTConvert");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfMPTAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfHolderEncryptionKey (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getHolderEncryptionKey() const
{
if (hasHolderEncryptionKey())
{
return this->tx_->at(sfHolderEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfHolderEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasHolderEncryptionKey() const
{
return this->tx_->isFieldPresent(sfHolderEncryptionKey);
}
/**
* @brief Get sfHolderEncryptedAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getHolderEncryptedAmount() const
{
return this->tx_->at(sfHolderEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfBlindingFactor (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getBlindingFactor() const
{
return this->tx_->at(sfBlindingFactor);
}
/**
* @brief Get sfZKProof (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getZKProof() const
{
if (hasZKProof())
{
return this->tx_->at(sfZKProof);
}
return std::nullopt;
}
/**
* @brief Check if sfZKProof is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasZKProof() const
{
return this->tx_->isFieldPresent(sfZKProof);
}
};
/**
* @brief Builder for ConfidentialMPTConvert transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses Json::Value internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTConvertBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTConvertBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param mPTAmount The sfMPTAmount field value.
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param blindingFactor The sfBlindingFactor field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTConvertBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTConvertBuilder>(ttCONFIDENTIAL_MPT_CONVERT, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setMPTAmount(mPTAmount);
setHolderEncryptedAmount(holderEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setBlindingFactor(blindingFactor);
}
/**
* @brief Construct a ConfidentialMPTConvertBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTConvertBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptedAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfBlindingFactor (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBlindingFactor] = value;
return *this;
}
/**
* @brief Set sfZKProof (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTConvert wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTConvert
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTConvert{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -1,310 +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 ConfidentialMPTConvertBackBuilder;
/**
* @brief Transaction: ConfidentialMPTConvertBack
*
* Type: ttCONFIDENTIAL_MPT_CONVERT_BACK (87)
* Delegable: Delegation::delegable
* Amendment: featureConfidentialTransfer
* Privileges: noPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTConvertBackBuilder to construct new transactions.
*/
class ConfidentialMPTConvertBack : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT_BACK;
/**
* @brief Construct a ConfidentialMPTConvertBack transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTConvertBack(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 ConfidentialMPTConvertBack");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfMPTAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT64::type::value_type
getMPTAmount() const
{
return this->tx_->at(sfMPTAmount);
}
/**
* @brief Get sfHolderEncryptedAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getHolderEncryptedAmount() const
{
return this->tx_->at(sfHolderEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfBlindingFactor (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT256::type::value_type
getBlindingFactor() const
{
return this->tx_->at(sfBlindingFactor);
}
/**
* @brief Get sfZKProof (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
/**
* @brief Get sfBalanceCommitment (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getBalanceCommitment() const
{
return this->tx_->at(sfBalanceCommitment);
}
};
/**
* @brief Builder for ConfidentialMPTConvertBack transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses Json::Value internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTConvertBackBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTConvertBackBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param mPTAmount The sfMPTAmount field value.
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param blindingFactor The sfBlindingFactor field value.
* @param zKProof The sfZKProof field value.
* @param balanceCommitment The sfBalanceCommitment field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTConvertBackBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>(ttCONFIDENTIAL_MPT_CONVERT_BACK, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setMPTAmount(mPTAmount);
setHolderEncryptedAmount(holderEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setBlindingFactor(blindingFactor);
setZKProof(zKProof);
setBalanceCommitment(balanceCommitment);
}
/**
* @brief Construct a ConfidentialMPTConvertBackBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTConvertBackBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT_BACK)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBackBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfMPTAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
{
object_[sfMPTAmount] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptedAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfBlindingFactor (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBlindingFactor] = value;
return *this;
}
/**
* @brief Set sfZKProof (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Set sfBalanceCommitment (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTConvertBackBuilder&
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfBalanceCommitment] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTConvertBack wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTConvertBack
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTConvertBack{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -1,129 +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 ConfidentialMPTMergeInboxBuilder;
/**
* @brief Transaction: ConfidentialMPTMergeInbox
*
* Type: ttCONFIDENTIAL_MPT_MERGE_INBOX (86)
* Delegable: Delegation::delegable
* Amendment: featureConfidentialTransfer
* Privileges: noPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTMergeInboxBuilder to construct new transactions.
*/
class ConfidentialMPTMergeInbox : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_MERGE_INBOX;
/**
* @brief Construct a ConfidentialMPTMergeInbox transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTMergeInbox(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 ConfidentialMPTMergeInbox");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
};
/**
* @brief Builder for ConfidentialMPTMergeInbox transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses Json::Value internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTMergeInboxBuilder : public TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTMergeInboxBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTMergeInboxBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>(ttCONFIDENTIAL_MPT_MERGE_INBOX, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
}
/**
* @brief Construct a ConfidentialMPTMergeInboxBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTMergeInboxBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_MERGE_INBOX)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInboxBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTMergeInboxBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTMergeInbox wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTMergeInbox
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTMergeInbox{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -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 ConfidentialMPTSendBuilder;
/**
* @brief Transaction: ConfidentialMPTSend
*
* Type: ttCONFIDENTIAL_MPT_SEND (88)
* Delegable: Delegation::delegable
* Amendment: featureConfidentialTransfer
* Privileges: noPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTSendBuilder to construct new transactions.
*/
class ConfidentialMPTSend : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_SEND;
/**
* @brief Construct a ConfidentialMPTSend transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTSend(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 ConfidentialMPTSend");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfDestination (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_ACCOUNT::type::value_type
getDestination() const
{
return this->tx_->at(sfDestination);
}
/**
* @brief Get sfSenderEncryptedAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getSenderEncryptedAmount() const
{
return this->tx_->at(sfSenderEncryptedAmount);
}
/**
* @brief Get sfDestinationEncryptedAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getDestinationEncryptedAmount() const
{
return this->tx_->at(sfDestinationEncryptedAmount);
}
/**
* @brief Get sfIssuerEncryptedAmount (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getIssuerEncryptedAmount() const
{
return this->tx_->at(sfIssuerEncryptedAmount);
}
/**
* @brief Get sfAuditorEncryptedAmount (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptedAmount() const
{
if (hasAuditorEncryptedAmount())
{
return this->tx_->at(sfAuditorEncryptedAmount);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptedAmount is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptedAmount() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfZKProof (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getZKProof() const
{
return this->tx_->at(sfZKProof);
}
/**
* @brief Get sfAmountCommitment (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getAmountCommitment() const
{
return this->tx_->at(sfAmountCommitment);
}
/**
* @brief Get sfBalanceCommitment (soeREQUIRED)
* @return The field value.
*/
[[nodiscard]]
SF_VL::type::value_type
getBalanceCommitment() const
{
return this->tx_->at(sfBalanceCommitment);
}
/**
* @brief Get sfCredentialIDs (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VECTOR256::type::value_type>
getCredentialIDs() const
{
if (hasCredentialIDs())
{
return this->tx_->at(sfCredentialIDs);
}
return std::nullopt;
}
/**
* @brief Check if sfCredentialIDs is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasCredentialIDs() const
{
return this->tx_->isFieldPresent(sfCredentialIDs);
}
};
/**
* @brief Builder for ConfidentialMPTSend transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses Json::Value internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTSendBuilder : public TransactionBuilderBase<ConfidentialMPTSendBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTSendBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param destination The sfDestination field value.
* @param senderEncryptedAmount The sfSenderEncryptedAmount field value.
* @param destinationEncryptedAmount The sfDestinationEncryptedAmount field value.
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
* @param zKProof The sfZKProof field value.
* @param amountCommitment The sfAmountCommitment field value.
* @param balanceCommitment The sfBalanceCommitment field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTSendBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination, std::decay_t<typename SF_VL::type::value_type> const& senderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& destinationEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& amountCommitment, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTSendBuilder>(ttCONFIDENTIAL_MPT_SEND, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
setDestination(destination);
setSenderEncryptedAmount(senderEncryptedAmount);
setDestinationEncryptedAmount(destinationEncryptedAmount);
setIssuerEncryptedAmount(issuerEncryptedAmount);
setZKProof(zKProof);
setAmountCommitment(amountCommitment);
setBalanceCommitment(balanceCommitment);
}
/**
* @brief Construct a ConfidentialMPTSendBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTSendBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_SEND)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTSendBuilder");
}
object_ = *tx;
}
/** @brief Transaction-specific field setters */
/**
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfDestination (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
{
object_[sfDestination] = value;
return *this;
}
/**
* @brief Set sfSenderEncryptedAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setSenderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfSenderEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfDestinationEncryptedAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setDestinationEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfDestinationEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfIssuerEncryptedAmount (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptedAmount (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptedAmount] = value;
return *this;
}
/**
* @brief Set sfZKProof (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Set sfAmountCommitment (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setAmountCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAmountCommitment] = value;
return *this;
}
/**
* @brief Set sfBalanceCommitment (soeREQUIRED)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfBalanceCommitment] = value;
return *this;
}
/**
* @brief Set sfCredentialIDs (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTSendBuilder&
setCredentialIDs(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfCredentialIDs] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTSend wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTSend
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTSend{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions

View File

@@ -187,58 +187,6 @@ public:
{
return this->tx_->isFieldPresent(sfMutableFlags);
}
/**
* @brief Get sfIssuerEncryptionKey (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getIssuerEncryptionKey() const
{
if (hasIssuerEncryptionKey())
{
return this->tx_->at(sfIssuerEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasIssuerEncryptionKey() const
{
return this->tx_->isFieldPresent(sfIssuerEncryptionKey);
}
/**
* @brief Get sfAuditorEncryptionKey (soeOPTIONAL)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getAuditorEncryptionKey() const
{
if (hasAuditorEncryptionKey())
{
return this->tx_->at(sfAuditorEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfAuditorEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasAuditorEncryptionKey() const
{
return this->tx_->isFieldPresent(sfAuditorEncryptionKey);
}
};
/**
@@ -349,28 +297,6 @@ public:
return *this;
}
/**
* @brief Set sfIssuerEncryptionKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceSetBuilder&
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfAuditorEncryptionKey (soeOPTIONAL)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceSetBuilder&
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfAuditorEncryptionKey] = value;
return *this;
}
/**
* @brief Build and return the MPTokenIssuanceSet wrapper.
* @param publicKey The public key for signing.

View File

@@ -34,6 +34,7 @@ protected:
boost::asio::strand<boost::asio::executor> strand_;
public:
// NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
BasePeer(
Port const& port,
Handler& handler,

View File

@@ -392,7 +392,7 @@ BaseWSPeer<Handler, Impl>::cancel_timer()
{
timer_.cancel();
}
catch (boost::system::system_error const&)
catch (boost::system::system_error const&) // NOLINT(bugprone-empty-catch)
{
// ignored
}

View File

@@ -399,7 +399,6 @@ using InvariantChecks = std::tuple<
ValidLoanBroker,
ValidLoan,
ValidVault,
ValidConfidentialMPToken,
ValidMPTPayment>;
/**

View File

@@ -56,47 +56,4 @@ public:
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
/**
* @brief Invariants: Confidential MPToken consistency
*
* - Convert/ConvertBack symmetry:
* Regular MPToken balance change (±X) == COA (Confidential Outstanding Amount) change (∓X)
* - Cannot delete MPToken with non-zero confidential state:
* Cannot delete if sfIssuerEncryptedBalance exists
* Cannot delete if sfConfidentialBalanceInbox and sfConfidentialBalanceSpending exist
* - Privacy flag consistency:
* MPToken can only have encrypted fields if lsfMPTCanConfidentialAmount is set on
* issuance.
* - Encrypted field existence consistency:
* If sfConfidentialBalanceSpending/sfConfidentialBalanceInbox exists, then
* sfIssuerEncryptedBalance must also exist (and vice versa).
* - COA <= OutstandingAmount:
* Confidential outstanding balance cannot exceed total outstanding.
* - Verifies sfConfidentialBalanceVersion is changed whenever sfConfidentialBalanceSpending is
* modified on an MPToken.
*/
class ValidConfidentialMPToken
{
struct Changes
{
std::int64_t mptAmountDelta = 0;
std::int64_t coaDelta = 0;
std::int64_t outstandingDelta = 0;
SLE::const_pointer issuance;
bool deletedWithEncrypted = false;
bool badConsistency = false;
bool badCOA = false;
bool requiresPrivacyFlag = false;
bool badVersion = false;
};
std::map<uint192, Changes> changes_;
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
} // namespace xrpl

View File

@@ -220,6 +220,7 @@ struct FlowDebugInfo
write_list(amts, get_val, delim);
};
auto writeIntList = [&write_list](std::vector<size_t> const& vals, char delim = ';') {
// NOLINTNEXTLINE(bugprone-return-const-ref-from-parameter)
auto get_val = [](size_t const& v) -> size_t const& { return v; };
write_list(vals, get_val);
};

View File

@@ -429,8 +429,10 @@ toStrands(
template <StepAmount TIn, StepAmount TOut, class TDerived>
struct StepImp : public Step
{
private:
explicit StepImp() = default;
public:
std::pair<EitherAmount, EitherAmount>
rev(PaymentSandbox& sb,
ApplyView& afView,
@@ -470,6 +472,7 @@ struct StepImp : public Step
{
return get<TIn>(lhs) == get<TIn>(rhs);
}
friend TDerived;
};
/// @endcond

View File

@@ -33,22 +33,23 @@ public:
TER
doApply() override;
static constexpr auto disabledTxTypes = std::to_array<TxType>(
{ttVAULT_CREATE,
ttVAULT_SET,
ttVAULT_DELETE,
ttVAULT_DEPOSIT,
ttVAULT_WITHDRAW,
ttVAULT_CLAWBACK,
ttLOAN_BROKER_SET,
ttLOAN_BROKER_DELETE,
ttLOAN_BROKER_COVER_DEPOSIT,
ttLOAN_BROKER_COVER_WITHDRAW,
ttLOAN_BROKER_COVER_CLAWBACK,
ttLOAN_SET,
ttLOAN_DELETE,
ttLOAN_MANAGE,
ttLOAN_PAY});
static constexpr auto disabledTxTypes = std::to_array<TxType>({
ttVAULT_CREATE,
ttVAULT_SET,
ttVAULT_DELETE,
ttVAULT_DEPOSIT,
ttVAULT_WITHDRAW,
ttVAULT_CLAWBACK,
ttLOAN_BROKER_SET,
ttLOAN_BROKER_DELETE,
ttLOAN_BROKER_COVER_DEPOSIT,
ttLOAN_BROKER_COVER_WITHDRAW,
ttLOAN_BROKER_COVER_CLAWBACK,
ttLOAN_SET,
ttLOAN_DELETE,
ttLOAN_MANAGE,
ttLOAN_PAY,
});
};
} // namespace xrpl

View File

@@ -1,42 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Allows an MPT issuer to clawback confidential balances from a holder.
*
* This transaction enables the issuer of an MPToken Issuance (with clawback
* enabled) to reclaim confidential tokens from a holder's account. Unlike
* regular clawback, the issuer cannot see the holder's balance directly.
* Instead, the issuer must provide a zero-knowledge proof that demonstrates
* they know the exact encrypted balance amount.
*
* @par Cryptographic Operations:
* - **Equality Proof Verification**: Verifies that the issuer's revealed
* amount matches the holder's encrypted balance using the issuer's
* ElGamal private key.
*
* @see ConfidentialMPTSend, ConfidentialMPTConvert
*/
class ConfidentialMPTClawback : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTClawback(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl

View File

@@ -1,44 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Converts public (plaintext) MPT balance to confidential (encrypted)
* balance.
*
* This transaction allows a token holder to convert their publicly visible
* MPToken balance into an encrypted confidential balance. Once converted,
* the balance can only be spent using ConfidentialMPTSend transactions and
* remains hidden from public view on the ledger.
*
* @par Cryptographic Operations:
* - **Schnorr Proof Verification**: When registering a new ElGamal public key,
* verifies proof of knowledge of the corresponding private key.
* - **Revealed Amount Verification**: Verifies that the provided encrypted
* amounts (for holder, issuer, and optionally auditor) all encrypt the
* same plaintext amount using the provided blinding factor.
*
* @see ConfidentialMPTConvertBack, ConfidentialMPTSend
*/
class ConfidentialMPTConvert : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTConvert(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl

View File

@@ -1,45 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Converts confidential (encrypted) MPT balance back to public
* (plaintext) balance.
*
* This transaction allows a token holder to convert their encrypted
* confidential balance back into a publicly visible MPToken balance. The
* holder must prove they have sufficient confidential balance without
* revealing the actual balance amount.
*
* @par Cryptographic Operations:
* - **Revealed Amount Verification**: Verifies that the provided encrypted
* amounts correctly encrypt the conversion amount.
* - **Pedersen Linkage Proof**: Verifies that the provided balance commitment
* correctly links to the holder's encrypted spending balance.
* - **Bulletproof Range Proof**: Verifies that the remaining balance (after
* conversion) is non-negative, ensuring the holder has sufficient funds.
*
* @see ConfidentialMPTConvert, ConfidentialMPTSend
*/
class ConfidentialMPTConvertBack : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTConvertBack(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl

View File

@@ -1,46 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Merges the confidential inbox balance into the spending balance.
*
* In the confidential transfer system, incoming funds are deposited into an
* "inbox" balance that the recipient cannot immediately spend. This prevents
* front-running attacks where an attacker could invalidate a pending
* transaction by sending funds to the sender. This transaction merges the
* inbox into the spending balance, making those funds available for spending.
*
* @par Cryptographic Operations:
* - **Homomorphic Addition**: Adds the encrypted inbox balance to the
* encrypted spending balance using ElGamal homomorphic properties.
* - **Zero Encryption**: Resets the inbox to an encryption of zero.
*
* @note This transaction requires no zero-knowledge proofs because it only
* combines encrypted values that the holder already owns. The
* homomorphic properties of ElGamal encryption ensure correctness.
*
* @see ConfidentialMPTSend, ConfidentialMPTConvert
*/
class ConfidentialMPTMergeInbox : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTMergeInbox(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl

View File

@@ -1,55 +0,0 @@
#pragma once
#include <xrpl/tx/Transactor.h>
namespace xrpl {
/**
* @brief Transfers confidential MPT tokens between holders privately.
*
* This transaction enables private token transfers where the transfer amount
* is hidden from public view. Both sender and recipient must have initialized
* confidential balances. The transaction provides encrypted amounts for all
* parties (sender, destination, issuer, and optionally auditor) along with
* zero-knowledge proofs that verify correctness without revealing the amount.
*
* @par Cryptographic Operations:
* - **Multi-Ciphertext Equality Proof**: Verifies that all encrypted amounts
* (sender, destination, issuer, auditor) encrypt the same plaintext value.
* - **Amount Pedersen Linkage Proof**: Verifies that the amount commitment
* correctly links to the sender's encrypted amount.
* - **Balance Pedersen Linkage Proof**: Verifies that the balance commitment
* correctly links to the sender's encrypted spending balance.
* - **Bulletproof Range Proof**: Verifies remaining balance and
* transfer amount are non-negative.
*
* @note Funds are deposited into the destination's inbox, not spending
* balance. The recipient must call ConfidentialMPTMergeInbox to make
* received funds spendable.
*
* @see ConfidentialMPTMergeInbox, ConfidentialMPTConvert,
* ConfidentialMPTConvertBack
*/
class ConfidentialMPTSend : public Transactor
{
/// Size of two Pedersen linkage proofs (amount + balance)
static constexpr std::size_t doublePedersenProofLength = 2 * ecPedersenProofLength;
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit ConfidentialMPTSend(ApplyContext& ctx) : Transactor(ctx)
{
}
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace xrpl

View File

@@ -59,8 +59,7 @@ Logs::File::open(boost::filesystem::path const& path)
bool wasOpened = false;
// VFALCO TODO Make this work with Unicode file paths
std::unique_ptr<std::ofstream> stream =
std::make_unique<std::ofstream>(path.c_str(), std::fstream::app);
std::unique_ptr<std::ofstream> stream(new std::ofstream(path.c_str(), std::fstream::app));
if (stream->good())
{

View File

@@ -5,6 +5,8 @@
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/st.h>
#include <stdexcept>
namespace xrpl {
namespace detail {
@@ -374,14 +376,14 @@ ApplyStateTable::erase(ReadView const& base, std::shared_ptr<SLE> const& sle)
{
auto const iter = items_.find(sle->key());
if (iter == items_.end())
LogicError("ApplyStateTable::erase: missing key");
Throw<std::logic_error>("ApplyStateTable::erase: missing key");
auto& item = iter->second;
if (item.second != sle)
LogicError("ApplyStateTable::erase: unknown SLE");
Throw<std::logic_error>("ApplyStateTable::erase: unknown SLE");
switch (item.first)
{
case Action::erase:
LogicError("ApplyStateTable::erase: double erase");
Throw<std::logic_error>("ApplyStateTable::erase: double erase");
break;
case Action::insert:
items_.erase(iter);
@@ -405,7 +407,7 @@ ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr<SLE> const& sle)
switch (item.first)
{
case Action::erase:
LogicError("ApplyStateTable::rawErase: double erase");
Throw<std::logic_error>("ApplyStateTable::rawErase: double erase");
break;
case Action::insert:
items_.erase(result.first);
@@ -436,11 +438,11 @@ ApplyStateTable::insert(ReadView const& base, std::shared_ptr<SLE> const& sle)
switch (item.first)
{
case Action::cache:
LogicError("ApplyStateTable::insert: already cached");
Throw<std::logic_error>("ApplyStateTable::insert: already cached");
case Action::insert:
LogicError("ApplyStateTable::insert: already inserted");
Throw<std::logic_error>("ApplyStateTable::insert: already inserted");
case Action::modify:
LogicError("ApplyStateTable::insert: already modified");
Throw<std::logic_error>("ApplyStateTable::insert: already modified");
case Action::erase:
break;
}
@@ -466,7 +468,7 @@ ApplyStateTable::replace(ReadView const& base, std::shared_ptr<SLE> const& sle)
switch (item.first)
{
case Action::erase:
LogicError("ApplyStateTable::replace: already erased");
Throw<std::logic_error>("ApplyStateTable::replace: already erased");
case Action::cache:
item.first = Action::modify;
break;
@@ -482,14 +484,14 @@ ApplyStateTable::update(ReadView const& base, std::shared_ptr<SLE> const& sle)
{
auto const iter = items_.find(sle->key());
if (iter == items_.end())
LogicError("ApplyStateTable::update: missing key");
Throw<std::logic_error>("ApplyStateTable::update: missing key");
auto& item = iter->second;
if (item.second != sle)
LogicError("ApplyStateTable::update: unknown SLE");
Throw<std::logic_error>("ApplyStateTable::update: unknown SLE");
switch (item.first)
{
case Action::erase:
LogicError("ApplyStateTable::update: erased");
Throw<std::logic_error>("ApplyStateTable::update: erased");
break;
case Action::cache:
item.first = Action::modify;

View File

@@ -4,6 +4,7 @@
#include <xrpl/protocol/Protocol.h>
#include <limits>
#include <stdexcept>
#include <type_traits>
namespace xrpl {
@@ -40,10 +41,8 @@ findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start)
{
node = view.peek(keylet::page(directory, page));
if (!node)
{ // LCOV_EXCL_START
LogicError("Directory chain: root back-pointer broken.");
// LCOV_EXCL_STOP
}
Throw<std::logic_error>(
"Directory chain: root back-pointer broken."); // LCOV_EXCL_LINE
}
auto indexes = node->getFieldV256(sfIndexes);
@@ -62,21 +61,20 @@ insertKey(
if (preserveOrder)
{
if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
Throw<std::logic_error>("dirInsert: double insertion"); // LCOV_EXCL_LINE
indexes.push_back(key);
}
else
{
// We can't be sure if this page is already sorted because
// it may be a legacy page we haven't yet touched. Take
// the time to sort it.
// We can't be sure if this page is already sorted because it may be a
// legacy page we haven't yet touched. Take the time to sort it.
std::sort(indexes.begin(), indexes.end());
auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
if (pos != indexes.end() && key == *pos)
LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
Throw<std::logic_error>("dirInsert: double insertion"); // LCOV_EXCL_LINE
indexes.insert(pos, key);
}
@@ -129,8 +127,7 @@ insertPage(
node->setFieldH256(sfRootIndex, directory.key);
node->setFieldV256(sfIndexes, indexes);
// Save some space by not specifying the value 0 since
// it's the default.
// Save some space by not specifying the value 0 since it's the default.
if (page != 1)
node->setFieldU64(sfIndexPrevious, page - 1);
XRPL_ASSERT_PARTS(!nextPage, "xrpl::directory::insertPage", "nextPage has default value");
@@ -199,28 +196,24 @@ ApplyView::emptyDirDelete(Keylet const& directory)
auto nextPage = node->getFieldU64(sfIndexNext);
if (nextPage == rootPage && prevPage != rootPage)
LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
if (prevPage == rootPage && nextPage != rootPage)
LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
// Older versions of the code would, in some cases, allow the last
// page to be empty. Remove such pages:
// Older versions of the code would, in some cases, allow the last page to
// be empty. Remove such pages:
if (nextPage == prevPage && nextPage != rootPage)
{
auto last = peek(keylet::page(directory, nextPage));
if (!last)
{ // LCOV_EXCL_START
LogicError("Directory chain: fwd link broken.");
// LCOV_EXCL_STOP
}
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
if (!last->getFieldV256(sfIndexes).empty())
return false;
// Update the first page's linked list and
// mark it as updated.
// Update the first page's linked list and mark it as updated.
node->setFieldU64(sfIndexNext, rootPage);
node->setFieldU64(sfIndexPrevious, rootPage);
update(node);
@@ -228,8 +221,7 @@ ApplyView::emptyDirDelete(Keylet const& directory)
// And erase the empty last page:
erase(last);
// Make sure our local values reflect the
// updated information:
// Make sure our local values reflect the updated information:
nextPage = rootPage;
prevPage = rootPage;
}
@@ -269,46 +261,33 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
return true;
}
// The current page is now empty; check if it can be
// deleted, and, if so, whether the entire directory
// can now be removed.
// The current page is now empty; check if it can be deleted, and, if so,
// whether the entire directory can now be removed.
auto prevPage = node->getFieldU64(sfIndexPrevious);
auto nextPage = node->getFieldU64(sfIndexNext);
// The first page is the directory's root node and is
// treated specially: it can never be deleted even if
// it is empty, unless we plan on removing the entire
// directory.
// The first page is the directory's root node and is treated specially: it
// can never be deleted even if it is empty, unless we plan on removing the
// entire directory.
if (page == rootPage)
{
if (nextPage == page && prevPage != page)
{ // LCOV_EXCL_START
LogicError("Directory chain: fwd link broken");
// LCOV_EXCL_STOP
}
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
if (prevPage == page && nextPage != page)
{ // LCOV_EXCL_START
LogicError("Directory chain: rev link broken");
// LCOV_EXCL_STOP
}
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
// Older versions of the code would, in some cases,
// allow the last page to be empty. Remove such
// pages if we stumble on them:
// Older versions of the code would, in some cases, allow the last page
// to be empty. Remove such pages if we stumble on them:
if (nextPage == prevPage && nextPage != page)
{
auto last = peek(keylet::page(directory, nextPage));
if (!last)
{ // LCOV_EXCL_START
LogicError("Directory chain: fwd link broken.");
// LCOV_EXCL_STOP
}
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
if (last->getFieldV256(sfIndexes).empty())
{
// Update the first page's linked list and
// mark it as updated.
// Update the first page's linked list and mark it as updated.
node->setFieldU64(sfIndexNext, page);
node->setFieldU64(sfIndexPrevious, page);
update(node);
@@ -316,8 +295,7 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// And erase the empty last page:
erase(last);
// Make sure our local values reflect the
// updated information:
// Make sure our local values reflect the updated information:
nextPage = page;
prevPage = page;
}
@@ -335,25 +313,24 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// This can never happen for nodes other than the root:
if (nextPage == page)
LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
if (prevPage == page)
LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
// This node isn't the root, so it can either be in the
// middle of the list, or at the end. Unlink it first
// and then check if that leaves the list with only a
// root:
// This node isn't the root, so it can either be in the middle of the list,
// or at the end. Unlink it first and then check if that leaves the list
// with only a root:
auto prev = peek(keylet::page(directory, prevPage));
if (!prev)
LogicError("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
// Fix previous to point to its new next.
prev->setFieldU64(sfIndexNext, nextPage);
update(prev);
auto next = peek(keylet::page(directory, nextPage));
if (!next)
LogicError("Directory chain: rev link broken."); // LCOV_EXCL_LINE
Throw<std::logic_error>("Directory chain: rev link broken."); // LCOV_EXCL_LINE
// Fix next to point to its new previous.
next->setFieldU64(sfIndexPrevious, prevPage);
update(next);
@@ -361,13 +338,12 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// The page is no longer linked. Delete it.
erase(node);
// Check whether the next page is the last page and, if
// so, whether it's empty. If it is, delete it.
// Check whether the next page is the last page and, if so, whether it's
// empty. If it is, delete it.
if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
next->getFieldV256(sfIndexes).empty())
{
// Since next doesn't point to the root, it
// can't be pointing to prev.
// Since next doesn't point to the root, it can't be pointing to prev.
erase(next);
// The previous page is now the last page:
@@ -377,18 +353,16 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
// And the root points to the last page:
auto root = peek(keylet::page(directory, rootPage));
if (!root)
{ // LCOV_EXCL_START
LogicError("Directory chain: root link broken.");
// LCOV_EXCL_STOP
}
Throw<std::logic_error>("Directory chain: root link broken."); // LCOV_EXCL_LINE
root->setFieldU64(sfIndexPrevious, prevPage);
update(root);
nextPage = rootPage;
}
// If we're not keeping the root, then check to see if
// it's left empty. If so, delete it as well.
// If we're not keeping the root, then check to see if it's left empty.
// If so, delete it as well.
if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
{
if (prev->getFieldV256(sfIndexes).empty())

View File

@@ -12,6 +12,7 @@
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <stdexcept>
#include <utility>
#include <vector>
@@ -461,14 +462,14 @@ void
Ledger::rawErase(std::shared_ptr<SLE> const& sle)
{
if (!stateMap_.delItem(sle->key()))
LogicError("Ledger::rawErase: key not found");
Throw<std::logic_error>("Ledger::rawErase: key not found");
}
void
Ledger::rawErase(uint256 const& key)
{
if (!stateMap_.delItem(key))
LogicError("Ledger::rawErase: key not found");
Throw<std::logic_error>("Ledger::rawErase: key not found");
}
void
@@ -478,7 +479,7 @@ Ledger::rawInsert(std::shared_ptr<SLE> const& sle)
sle->add(ss);
if (!stateMap_.addGiveItem(
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
LogicError("Ledger::rawInsert: key already exists");
Throw<std::logic_error>("Ledger::rawInsert: key already exists");
}
void
@@ -488,7 +489,7 @@ Ledger::rawReplace(std::shared_ptr<SLE> const& sle)
sle->add(ss);
if (!stateMap_.updateGiveItem(
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
LogicError("Ledger::rawReplace: key not found");
Throw<std::logic_error>("Ledger::rawReplace: key not found");
}
void
@@ -504,7 +505,7 @@ Ledger::rawTxInsert(
s.addVL(txn->peekData());
s.addVL(metaData->peekData());
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, make_shamapitem(key, s.slice())))
LogicError("duplicate_tx: " + to_string(key));
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
}
uint256
@@ -522,7 +523,7 @@ Ledger::rawTxInsertWithHash(
auto item = make_shamapitem(key, s.slice());
auto hash = sha512Half(HashPrefix::txNode, item->slice(), item->key());
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, std::move(item)))
LogicError("duplicate_tx: " + to_string(key));
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
return hash;
}

View File

@@ -1,6 +1,8 @@
#include <xrpl/basics/contract.h>
#include <xrpl/ledger/OpenView.h>
#include <stdexcept>
namespace xrpl {
class OpenView::txs_iter_impl : public txs_type::iter_base
@@ -247,7 +249,7 @@ OpenView::rawTxInsert(
auto const result = txs_.emplace(
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(txn, metaData));
if (!result.second)
LogicError("rawTxInsert: duplicate TX id: " + to_string(key));
Throw<std::logic_error>("rawTxInsert: duplicate TX id: " + to_string(key));
}
} // namespace xrpl

View File

@@ -1,6 +1,8 @@
#include <xrpl/basics/contract.h>
#include <xrpl/ledger/detail/RawStateTable.h>
#include <stdexcept>
namespace xrpl {
namespace detail {
@@ -241,7 +243,7 @@ RawStateTable::erase(std::shared_ptr<SLE> const& sle)
switch (item.action)
{
case Action::erase:
LogicError("RawStateTable::erase: already erased");
Throw<std::logic_error>("RawStateTable::erase: already erased");
break;
case Action::insert:
items_.erase(result.first);
@@ -270,10 +272,10 @@ RawStateTable::insert(std::shared_ptr<SLE> const& sle)
item.sle = sle;
break;
case Action::insert:
LogicError("RawStateTable::insert: already inserted");
Throw<std::logic_error>("RawStateTable::insert: already inserted");
break;
case Action::replace:
LogicError("RawStateTable::insert: already exists");
Throw<std::logic_error>("RawStateTable::insert: already exists");
break;
}
}
@@ -291,7 +293,7 @@ RawStateTable::replace(std::shared_ptr<SLE> const& sle)
switch (item.action)
{
case Action::erase:
LogicError("RawStateTable::replace: was erased");
Throw<std::logic_error>("RawStateTable::replace: was erased");
break;
case Action::insert:
case Action::replace:

View File

@@ -8,6 +8,7 @@
#include <algorithm>
#include <limits>
#include <stdexcept>
namespace xrpl {
@@ -153,7 +154,7 @@ getPseudoAccountFields()
if (!ar)
{
// LCOV_EXCL_START
LogicError(
Throw<std::logic_error>(
"xrpl::getPseudoAccountFields : unable to find account root "
"ledger format");
// LCOV_EXCL_STOP

View File

@@ -258,13 +258,6 @@ removeEmptyHolding(
(view.rules().enabled(fixSecurity3_1_3) && (*mptoken)[~sfLockedAmount].value_or(0) != 0))
return tecHAS_OBLIGATIONS;
// Don't delete if the token still has confidential balances
if (mptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
mptoken->isFieldPresent(sfConfidentialBalanceSpending))
{
return tecHAS_OBLIGATIONS;
}
return authorizeMPToken(
view,
{}, // priorBalance

View File

@@ -607,33 +607,6 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
return deletedOffersCount;
}
TER
notTooManyOffers(ReadView const& view, uint256 const& nftokenID)
{
std::size_t totalOffers = 0;
{
Dir const buys(view, keylet::nft_buys(nftokenID));
for (auto iter = buys.begin(); iter != buys.end(); iter.next_page())
{
totalOffers += iter.page_size();
if (totalOffers > maxDeletableTokenOfferEntries)
return tefTOO_BIG;
}
}
{
Dir const sells(view, keylet::nft_sells(nftokenID));
for (auto iter = sells.begin(); iter != sells.end(); iter.next_page())
{
totalOffers += iter.page_size();
if (totalOffers > maxDeletableTokenOfferEntries)
return tefTOO_BIG;
}
}
return tesSUCCESS;
}
bool
deleteTokenOffer(ApplyView& view, std::shared_ptr<SLE> const& offer)
{

View File

@@ -313,8 +313,7 @@ accountHolds(
// Only if auth check is needed, as it needs to do an additional read
// operation. Note featureSingleAssetVault will affect error codes.
if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
(view.rules().enabled(featureSingleAssetVault) ||
view.rules().enabled(featureConfidentialTransfer)))
view.rules().enabled(featureSingleAssetVault))
{
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth);
!isTesSuccess(err))

View File

@@ -1,445 +0,0 @@
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Protocol.h>
#include <openssl/rand.h>
#include <utility/mpt_utility.h>
namespace xrpl {
/**
* @brief Converts an XRPL AccountID to mpt-crypto lib C struct.
*
* @param account The AccountID.
* @return The equivalent mpt-crypto lib account_id struct.
*/
account_id
toAccountId(AccountID const& account)
{
account_id res;
std::memcpy(res.bytes, account.data(), kMPT_ACCOUNT_ID_SIZE);
return res;
}
/**
* @brief Converts an XRPL uint192 to mpt-crypto lib C struct.
*
* @param i The XRPL MPTokenIssuance ID.
* @return The equivalent mpt-crypto lib mpt_issuance_id struct.
*/
mpt_issuance_id
toIssuanceId(uint192 const& issuance)
{
mpt_issuance_id res;
std::memcpy(res.bytes, issuance.data(), kMPT_ISSUANCE_ID_SIZE);
return res;
}
uint256
getSendContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& destination,
std::uint32_t version)
{
uint256 result;
mpt_get_send_context_hash(
toAccountId(account),
toIssuanceId(issuanceID),
sequence,
toAccountId(destination),
version,
result.data());
return result;
}
uint256
getClawbackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
AccountID const& holder)
{
uint256 result;
mpt_get_clawback_context_hash(
toAccountId(account),
toIssuanceId(issuanceID),
sequence,
toAccountId(holder),
result.data());
return result;
}
uint256
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence)
{
uint256 result;
mpt_get_convert_context_hash(
toAccountId(account), toIssuanceId(issuanceID), sequence, result.data());
return result;
}
uint256
getConvertBackContextHash(
AccountID const& account,
uint192 const& issuanceID,
std::uint32_t sequence,
std::uint32_t version)
{
uint256 result;
mpt_get_convert_back_context_hash(
toAccountId(account), toIssuanceId(issuanceID), sequence, version, result.data());
return result;
}
std::optional<EcPair>
makeEcPair(Slice const& buffer)
{
if (buffer.length() != 2 * ecGamalEncryptedLength)
return std::nullopt; // LCOV_EXCL_LINE
auto parsePubKey = [](Slice const& slice, secp256k1_pubkey& out) {
return secp256k1_ec_pubkey_parse(
secp256k1Context(),
&out,
reinterpret_cast<unsigned char const*>(slice.data()),
slice.length());
};
Slice const s1{buffer.data(), ecGamalEncryptedLength};
Slice const s2{buffer.data() + ecGamalEncryptedLength, ecGamalEncryptedLength};
EcPair pair{};
if (parsePubKey(s1, pair.c1) != 1 || parsePubKey(s2, pair.c2) != 1)
return std::nullopt;
return pair;
}
std::optional<Buffer>
serializeEcPair(EcPair const& pair)
{
auto serializePubKey = [](secp256k1_pubkey const& pub, unsigned char* out) {
size_t outLen = ecGamalEncryptedLength; // 33 bytes
int const ret = secp256k1_ec_pubkey_serialize(
secp256k1Context(), out, &outLen, &pub, SECP256K1_EC_COMPRESSED);
return ret == 1 && outLen == ecGamalEncryptedLength;
};
Buffer buffer(ecGamalEncryptedTotalLength);
unsigned char* ptr = buffer.data();
bool const res1 = serializePubKey(pair.c1, ptr);
bool const res2 = serializePubKey(pair.c2, ptr + ecGamalEncryptedLength);
if (!res1 || !res2)
return std::nullopt;
return buffer;
}
bool
isValidCiphertext(Slice const& buffer)
{
return makeEcPair(buffer).has_value();
}
bool
isValidCompressedECPoint(Slice const& buffer)
{
if (buffer.size() != compressedECPointLength)
return false;
// Compressed EC points must start with 0x02 or 0x03
if (buffer[0] != ecCompressedPrefixEvenY && buffer[0] != ecCompressedPrefixOddY)
return false;
secp256k1_pubkey point;
return secp256k1_ec_pubkey_parse(secp256k1Context(), &point, buffer.data(), buffer.size()) == 1;
}
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b)
{
if (a.length() != ecGamalEncryptedTotalLength || b.length() != ecGamalEncryptedTotalLength)
return std::nullopt;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!pairA || !pairB)
return std::nullopt;
EcPair sum{};
if (auto res = secp256k1_elgamal_add(
secp256k1Context(), &sum.c1, &sum.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
return serializeEcPair(sum);
}
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b)
{
if (a.length() != ecGamalEncryptedTotalLength || b.length() != ecGamalEncryptedTotalLength)
return std::nullopt;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!pairA || !pairB)
return std::nullopt;
EcPair diff{};
if (auto res = secp256k1_elgamal_subtract(
secp256k1Context(), &diff.c1, &diff.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
return serializeEcPair(diff);
}
Buffer
generateBlindingFactor()
{
unsigned char blindingFactor[ecBlindingFactorLength];
// todo: might need to be updated using another RNG
if (RAND_bytes(blindingFactor, ecBlindingFactorLength) != 1)
Throw<std::runtime_error>("Failed to generate random number");
return Buffer(blindingFactor, ecBlindingFactorLength);
}
std::optional<Buffer>
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor)
{
if (blindingFactor.size() != ecBlindingFactorLength || pubKeySlice.size() != ecPubKeyLength)
return std::nullopt;
Buffer out(ecGamalEncryptedTotalLength);
if (mpt_encrypt_amount(amt, pubKeySlice.data(), blindingFactor.data(), out.data()) != 0)
return std::nullopt;
return out;
}
std::optional<Buffer>
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId)
{
if (pubKeySlice.size() != ecPubKeyLength)
return std::nullopt; // LCOV_EXCL_LINE
EcPair pair{};
secp256k1_pubkey pubKey;
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
if (auto res = generate_canonical_encrypted_zero(
secp256k1Context(), &pair.c1, &pair.c2, &pubKey, account.data(), mptId.data());
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
return serializeEcPair(pair);
}
TER
verifyRevealedAmount(
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor)
{
if (blindingFactor.size() != ecBlindingFactorLength ||
holder.publicKey.size() != ecPubKeyLength ||
holder.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
issuer.publicKey.size() != ecPubKeyLength ||
issuer.encryptedAmount.size() != ecGamalEncryptedTotalLength)
return tecINTERNAL; // LCOV_EXCL_LINE
auto toParticipant = [](ConfidentialRecipient const& r) {
mpt_confidential_participant p;
std::memcpy(p.pubkey, r.publicKey.data(), kMPT_PUBKEY_SIZE);
std::memcpy(p.ciphertext, r.encryptedAmount.data(), kMPT_ELGAMAL_TOTAL_SIZE);
return p;
};
auto const holderP = toParticipant(holder);
auto const issuerP = toParticipant(issuer);
mpt_confidential_participant auditorP;
mpt_confidential_participant const* auditorPtr = nullptr;
if (auditor)
{
if (auditor->publicKey.size() != ecPubKeyLength ||
auditor->encryptedAmount.size() != ecGamalEncryptedTotalLength)
return tecINTERNAL; // LCOV_EXCL_LINE
auditorP = toParticipant(*auditor);
auditorPtr = &auditorP;
}
if (mpt_verify_revealed_amount(amount, blindingFactor.data(), &holderP, &issuerP, auditorPtr) !=
0)
return tecBAD_PROOF;
return tesSUCCESS;
}
NotTEC
checkEncryptedAmountFormat(STObject const& object)
{
// Current usage of this function is only for ConfidentialMPTConvert and
// ConfidentialMPTConvertBack transactions, which already enforce that these fields
// are present.
if (!object.isFieldPresent(sfHolderEncryptedAmount) ||
!object.isFieldPresent(sfIssuerEncryptedAmount))
return temMALFORMED; // LCOV_EXCL_LINE
if (object[sfHolderEncryptedAmount].length() != ecGamalEncryptedTotalLength ||
object[sfIssuerEncryptedAmount].length() != ecGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
bool const hasAuditor = object.isFieldPresent(sfAuditorEncryptedAmount);
if (hasAuditor && object[sfAuditorEncryptedAmount].length() != ecGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
if (!isValidCiphertext(object[sfHolderEncryptedAmount]) ||
!isValidCiphertext(object[sfIssuerEncryptedAmount]))
return temBAD_CIPHERTEXT;
if (hasAuditor && !isValidCiphertext(object[sfAuditorEncryptedAmount]))
return temBAD_CIPHERTEXT;
return tesSUCCESS;
}
TER
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash)
{
if (proofSlice.size() != ecSchnorrProofLength || pubKeySlice.size() != ecPubKeyLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (mpt_verify_convert_proof(proofSlice.data(), pubKeySlice.data(), contextHash.data()) != 0)
return tecBAD_PROOF;
return tesSUCCESS;
}
TER
verifyClawbackEqualityProof(
uint64_t const amount,
Slice const& proof,
Slice const& pubKeySlice,
Slice const& ciphertext,
uint256 const& contextHash)
{
if (ciphertext.size() != ecGamalEncryptedTotalLength || pubKeySlice.size() != ecPubKeyLength ||
proof.size() != ecEqualityProofLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (mpt_verify_clawback_proof(
proof.data(), amount, pubKeySlice.data(), ciphertext.data(), contextHash.data()) != 0)
return tecBAD_PROOF;
return tesSUCCESS;
}
TER
verifySendProof(
Slice const& proof,
ConfidentialRecipient const& sender,
ConfidentialRecipient const& destination,
ConfidentialRecipient const& issuer,
std::optional<ConfidentialRecipient> const& auditor,
Slice const& spendingBalance,
Slice const& amountCommitment,
Slice const& balanceCommitment,
uint256 const& contextHash)
{
auto const recipientCount = getConfidentialRecipientCount(auditor.has_value());
auto const expectedProofSize = getEqualityProofSize(recipientCount) +
2 * ecPedersenProofLength + ecDoubleBulletproofLength;
if (proof.size() != expectedProofSize || sender.publicKey.size() != ecPubKeyLength ||
sender.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
destination.publicKey.size() != ecPubKeyLength ||
destination.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
issuer.publicKey.size() != ecPubKeyLength ||
issuer.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
spendingBalance.size() != ecGamalEncryptedTotalLength ||
amountCommitment.size() != ecPedersenCommitmentLength ||
balanceCommitment.size() != ecPedersenCommitmentLength)
return tecINTERNAL; // LCOV_EXCL_LINE
auto makeParticipant = [](ConfidentialRecipient const& r) {
mpt_confidential_participant p;
std::memcpy(p.pubkey, r.publicKey.data(), kMPT_PUBKEY_SIZE);
std::memcpy(p.ciphertext, r.encryptedAmount.data(), kMPT_ELGAMAL_TOTAL_SIZE);
return p;
};
std::vector<mpt_confidential_participant> participants(recipientCount);
participants[0] = makeParticipant(sender);
participants[1] = makeParticipant(destination);
participants[2] = makeParticipant(issuer);
if (auditor)
{
if (auditor->publicKey.size() != ecPubKeyLength ||
auditor->encryptedAmount.size() != ecGamalEncryptedTotalLength)
return tecINTERNAL;
participants[3] = makeParticipant(*auditor);
}
if (mpt_verify_send_proof(
proof.data(),
proof.size(),
participants.data(),
static_cast<uint8_t>(recipientCount),
spendingBalance.data(),
amountCommitment.data(),
balanceCommitment.data(),
contextHash.data()) != 0)
return tecBAD_PROOF;
return tesSUCCESS;
}
TER
verifyConvertBackProof(
Slice const& proof,
Slice const& pubKeySlice,
Slice const& spendingBalance,
Slice const& balanceCommitment,
uint64_t amount,
uint256 const& contextHash)
{
if (proof.size() != ecPedersenProofLength + ecSingleBulletproofLength ||
pubKeySlice.size() != ecPubKeyLength ||
spendingBalance.size() != ecGamalEncryptedTotalLength ||
balanceCommitment.size() != ecPedersenCommitmentLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (mpt_verify_convert_back_proof(
proof.data(),
pubKeySlice.data(),
spendingBalance.data(),
balanceCommitment.data(),
amount,
contextHash.data()) != 0)
return tecBAD_PROOF;
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -3,7 +3,6 @@
#include <xrpl/basics/contract.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/detail/secp256k1.h>
@@ -209,7 +208,7 @@ publicKeyType(Slice const& slice)
if (slice[0] == 0xED)
return KeyType::ed25519;
if (slice[0] == ecCompressedPrefixEvenY || slice[0] == ecCompressedPrefixOddY)
if (slice[0] == 0x02 || slice[0] == 0x03)
return KeyType::secp256k1;
}

View File

@@ -106,7 +106,6 @@ transResults()
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
MAKE_ERROR(tecBAD_PROOF, "Proof cannot be verified"),
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
@@ -200,7 +199,6 @@ transResults()
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
MAKE_ERROR(temBAD_CIPHERTEXT, "Malformed: Invalid ciphertext."),
MAKE_ERROR(terRETRY, "Retry transaction."),
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),

View File

@@ -9,19 +9,8 @@
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
#include <algorithm>
#include <array>
namespace xrpl {
static constexpr auto confidentialMPTTxTypes = std::to_array<TxType>({
ttCONFIDENTIAL_MPT_SEND,
ttCONFIDENTIAL_MPT_CONVERT,
ttCONFIDENTIAL_MPT_CONVERT_BACK,
ttCONFIDENTIAL_MPT_MERGE_INBOX,
ttCONFIDENTIAL_MPT_CLAWBACK,
});
void
ValidMPTIssuance::visitEntry(
bool isDelete,
@@ -352,14 +341,6 @@ ValidMPTPayment::finalize(
{
if (isTesSuccess(result))
{
// Confidential transactions are validated by ValidConfidentialMPToken.
// They modify encrypted fields and sfConfidentialOutstandingAmount
// rather than sfMPTAmount/sfOutstandingAmount in the standard way,
// so ValidMPTPayment's accounting does not apply to them.
if (std::ranges::find(confidentialMPTTxTypes, tx.getTxnType()) !=
confidentialMPTTxTypes.end())
return true;
bool const enforce = view.rules().enabled(featureMPTokensV2);
if (overflow_)
{
@@ -388,208 +369,4 @@ ValidMPTPayment::finalize(
return true;
}
void
ValidConfidentialMPToken::visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
{
// Helper to get MPToken Issuance ID safely
auto const getMptID = [](std::shared_ptr<SLE const> const& sle) -> uint192 {
if (!sle)
return beast::zero;
if (sle->getType() == ltMPTOKEN)
return sle->getFieldH192(sfMPTokenIssuanceID);
if (sle->getType() == ltMPTOKEN_ISSUANCE)
return makeMptID(sle->getFieldU32(sfSequence), sle->getAccountID(sfIssuer));
return beast::zero;
};
if (before && before->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(before);
changes_[id].mptAmountDelta -= before->getFieldU64(sfMPTAmount);
// Cannot delete MPToken with non-zero confidential state or non-zero public amount
if (isDelete)
{
bool const hasPublicBalance = before->getFieldU64(sfMPTAmount) > 0;
bool const hasEncryptedFields = before->isFieldPresent(sfConfidentialBalanceSpending) ||
before->isFieldPresent(sfConfidentialBalanceInbox) ||
before->isFieldPresent(sfIssuerEncryptedBalance);
if (hasPublicBalance || hasEncryptedFields)
changes_[id].deletedWithEncrypted = true;
}
}
if (after && after->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(after);
changes_[id].mptAmountDelta += after->getFieldU64(sfMPTAmount);
// Encrypted field existence consistency
bool const hasIssuerBalance = after->isFieldPresent(sfIssuerEncryptedBalance);
bool const hasHolderInbox = after->isFieldPresent(sfConfidentialBalanceInbox);
bool const hasHolderSpending = after->isFieldPresent(sfConfidentialBalanceSpending);
bool const hasAnyHolder = hasHolderInbox || hasHolderSpending;
if (hasAnyHolder != hasIssuerBalance)
{
changes_[id].badConsistency = true;
}
// Privacy flag consistency
bool const hasEncrypted = hasAnyHolder || hasIssuerBalance;
if (hasEncrypted)
changes_[id].requiresPrivacyFlag = true;
}
if (before && before->getType() == ltMPTOKEN_ISSUANCE)
{
uint192 const id = getMptID(before);
if (before->isFieldPresent(sfConfidentialOutstandingAmount))
changes_[id].coaDelta -= before->getFieldU64(sfConfidentialOutstandingAmount);
changes_[id].outstandingDelta -= before->getFieldU64(sfOutstandingAmount);
}
if (after && after->getType() == ltMPTOKEN_ISSUANCE)
{
uint192 const id = getMptID(after);
auto& change = changes_[id];
bool const hasCOA = after->isFieldPresent(sfConfidentialOutstandingAmount);
std::uint64_t const coa = (*after)[~sfConfidentialOutstandingAmount].value_or(0);
std::uint64_t const oa = after->getFieldU64(sfOutstandingAmount);
if (hasCOA)
change.coaDelta += coa;
change.outstandingDelta += oa;
change.issuance = after;
// COA <= OutstandingAmount
if (coa > oa)
change.badCOA = true;
}
if (before && after && before->getType() == ltMPTOKEN && after->getType() == ltMPTOKEN)
{
uint192 const id = getMptID(after);
// sfConfidentialBalanceVersion must change when spending changes
auto const spendingBefore = (*before)[~sfConfidentialBalanceSpending];
auto const spendingAfter = (*after)[~sfConfidentialBalanceSpending];
auto const versionBefore = (*before)[~sfConfidentialBalanceVersion];
auto const versionAfter = (*after)[~sfConfidentialBalanceVersion];
if (spendingBefore.has_value() && spendingBefore != spendingAfter)
{
if (versionBefore == versionAfter)
{
changes_[id].badVersion = true;
}
}
}
}
bool
ValidConfidentialMPToken::finalize(
STTx const& tx,
TER const result,
XRPAmount const,
ReadView const& view,
beast::Journal const& j)
{
if (result != tesSUCCESS)
return true;
for (auto const& [id, checks] : changes_)
{
// Find the MPTokenIssuance
auto const issuance = [&]() -> std::shared_ptr<SLE const> {
if (checks.issuance)
return checks.issuance;
return view.read(keylet::mptIssuance(id));
}();
// Skip all invariance checks if issuance doesn't exist because that means the MPT has been
// deleted
if (!issuance)
continue;
// Cannot delete MPToken with non-zero confidential state
if (checks.deletedWithEncrypted)
{
if ((*issuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0)
{
JLOG(j.fatal())
<< "Invariant failed: MPToken deleted with encrypted fields while COA > 0";
return false;
}
}
// Encrypted field existence consistency
if (checks.badConsistency)
{
JLOG(j.fatal()) << "Invariant failed: MPToken encrypted field "
"existence inconsistency";
return false;
}
// COA <= OutstandingAmount
if (checks.badCOA)
{
JLOG(j.fatal()) << "Invariant failed: Confidential outstanding amount "
"exceeds total outstanding amount";
return false;
}
// Privacy flag consistency
if (checks.requiresPrivacyFlag)
{
if (!issuance->isFlag(lsfMPTCanConfidentialAmount))
{
JLOG(j.fatal()) << "Invariant failed: MPToken has encrypted "
"fields but Issuance does not have "
"lsfMPTCanConfidentialAmount set";
return false;
}
}
// We only enforce this when Confidential Outstanding Amount changes (Convert, ConvertBack,
// ConfidentialClawback). This avoids falsely failing on Escrow or AMM operations that lock
// public tokens outside of ltMPTOKEN. Convert / ConvertBack:
// - COA and MPTAmount must have opposite deltas, which cancel each other out to zero.
// - OA remains unchanged.
// - Therefore, the net delta on both sides of the equation is zero.
//
// Clawback:
// - MPTAmount remains unchanged.
// - COA and OA must have identical deltas (mirrored on each side).
// - The equation remains balanced as both sides have equal offsets.
if (checks.coaDelta != 0)
{
if (checks.mptAmountDelta + checks.coaDelta != checks.outstandingDelta)
{
JLOG(j.fatal()) << "Invariant failed: Token conservation "
"violation for MPT "
<< to_string(id);
return false;
}
}
if (checks.badVersion)
{
JLOG(j.fatal())
<< "Invariant failed: MPToken sfConfidentialBalanceVersion not updated when "
"sfConfidentialBalanceSpending changed";
return false;
}
}
return true;
}
} // namespace xrpl

View File

@@ -1,168 +0,0 @@
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/tx/transactors/token/ConfidentialMPTClawback.h>
namespace xrpl {
NotTEC
ConfidentialMPTClawback::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
auto const account = ctx.tx[sfAccount];
// Only issuer can clawback
if (account != MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer())
return temMALFORMED;
// Cannot clawback from self
if (account == ctx.tx[sfHolder])
return temMALFORMED;
// Check invalid claw amount
auto const clawAmount = ctx.tx[sfMPTAmount];
if (clawAmount == 0 || clawAmount > maxMPTokenAmount)
return temBAD_AMOUNT;
// Verify proof length
if (ctx.tx[sfZKProof].length() != ecEqualityProofLength)
return temMALFORMED;
return tesSUCCESS;
}
TER
ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx)
{
// Check if sender account exists
auto const account = ctx.tx[sfAccount];
if (!ctx.view.exists(keylet::account(account)))
return terNO_ACCOUNT;
// Check if holder account exists
auto const holder = ctx.tx[sfHolder];
if (!ctx.view.exists(keylet::account(holder)))
return tecNO_TARGET;
// Check if MPT issuance exists
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
// Sanity check: account must be the same as issuer
if (sleIssuance->getAccountID(sfIssuer) != account)
return tefINTERNAL; // LCOV_EXCL_LINE
// Check if issuance has issuer ElGamal public key
if (!sleIssuance->isFieldPresent(sfIssuerEncryptionKey))
return tecNO_PERMISSION;
// Check if clawback is allowed
if (!sleIssuance->isFlag(lsfMPTCanClawback))
return tecNO_PERMISSION;
// Check if issuance allows confidential transfer
if (!sleIssuance->isFlag(lsfMPTCanConfidentialAmount))
return tecNO_PERMISSION; // LCOV_EXCL_LINE
// Check holder's MPToken
auto const sleHolderMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, holder));
if (!sleHolderMPToken)
return tecOBJECT_NOT_FOUND;
// Check if holder has confidential balances to claw back
if (!sleHolderMPToken->isFieldPresent(sfIssuerEncryptedBalance))
return tecNO_PERMISSION; // LCOV_EXCL_LINE
// Check if Holder has ElGamal public Key
if (!sleHolderMPToken->isFieldPresent(sfHolderEncryptionKey))
return tecNO_PERMISSION; // LCOV_EXCL_LINE
// Sanity check: claw amount can not exceed confidential outstanding amount
auto const amount = ctx.tx[sfMPTAmount];
if (amount > (*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0))
return tecINSUFFICIENT_FUNDS;
auto const contextHash =
getClawbackContextHash(account, mptIssuanceID, ctx.tx.getSeqProxy().value(), holder);
// Verify the revealed confidential amount by the issuer matches the exact
// confidential balance of the holder.
return verifyClawbackEqualityProof(
amount,
ctx.tx[sfZKProof],
(*sleIssuance)[sfIssuerEncryptionKey],
(*sleHolderMPToken)[sfIssuerEncryptedBalance],
contextHash);
}
TER
ConfidentialMPTClawback::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto const holder = ctx_.tx[sfHolder];
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
auto sleHolderMPToken = view().peek(keylet::mptoken(mptIssuanceID, holder));
if (!sleIssuance || !sleHolderMPToken)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const clawAmount = ctx_.tx[sfMPTAmount];
Slice const holderPubKey = (*sleHolderMPToken)[sfHolderEncryptionKey];
Slice const issuerPubKey = (*sleIssuance)[sfIssuerEncryptionKey];
// After clawback, the balance should be encrypted zero.
auto const encZeroForHolder = encryptCanonicalZeroAmount(holderPubKey, holder, mptIssuanceID);
if (!encZeroForHolder)
return tecINTERNAL; // LCOV_EXCL_LINE
auto encZeroForIssuer = encryptCanonicalZeroAmount(issuerPubKey, holder, mptIssuanceID);
if (!encZeroForIssuer)
return tecINTERNAL; // LCOV_EXCL_LINE
// Set holder's confidential balances to encrypted zero
(*sleHolderMPToken)[sfConfidentialBalanceInbox] = *encZeroForHolder;
(*sleHolderMPToken)[sfConfidentialBalanceSpending] = *encZeroForHolder;
(*sleHolderMPToken)[sfIssuerEncryptedBalance] = std::move(*encZeroForIssuer);
incrementConfidentialVersion(*sleHolderMPToken);
if (sleHolderMPToken->isFieldPresent(sfAuditorEncryptedBalance))
{
// Sanity check: the issuance must have an auditor public key if
// auditing is enabled.
if (!sleIssuance->isFieldPresent(sfAuditorEncryptionKey))
return tecINTERNAL; // LCOV_EXCL_LINE
Slice const auditorPubKey = (*sleIssuance)[sfAuditorEncryptionKey];
auto encZeroForAuditor = encryptCanonicalZeroAmount(auditorPubKey, holder, mptIssuanceID);
if (!encZeroForAuditor)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleHolderMPToken)[sfAuditorEncryptedBalance] = std::move(*encZeroForAuditor);
}
// Decrease Global Confidential Outstanding Amount
auto const oldCOA = (*sleIssuance)[sfConfidentialOutstandingAmount];
(*sleIssuance)[sfConfidentialOutstandingAmount] = oldCOA - clawAmount;
// Decrease Global Total Outstanding Amount
auto const oldOA = (*sleIssuance)[sfOutstandingAmount];
(*sleIssuance)[sfOutstandingAmount] = oldOA - clawAmount;
view().update(sleHolderMPToken);
view().update(sleIssuance);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -1,281 +0,0 @@
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/tx/transactors/token/ConfidentialMPTConvert.h>
namespace xrpl {
NotTEC
ConfidentialMPTConvert::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
// issuer cannot convert
if (MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer() == ctx.tx[sfAccount])
return temMALFORMED;
if (ctx.tx[sfMPTAmount] > maxMPTokenAmount)
return temBAD_AMOUNT;
if (ctx.tx.isFieldPresent(sfHolderEncryptionKey))
{
if (!isValidCompressedECPoint(ctx.tx[sfHolderEncryptionKey]))
return temMALFORMED;
// proof of knowledge of the secret key corresponding to the provided
// public key is needed when holder ec public key is being set.
if (!ctx.tx.isFieldPresent(sfZKProof))
return temMALFORMED;
// verify schnorr proof length when registering holder ec public key
if (ctx.tx[sfZKProof].size() != ecSchnorrProofLength)
return temMALFORMED;
}
else
{
// Either both sfHolderEncryptionKey and sfZKProof should be present, or both should be
// absent.
if (ctx.tx.isFieldPresent(sfZKProof))
return temMALFORMED;
}
// check encrypted amount format after the above basic checks
// this check is more expensive so put it at the end
if (auto const res = checkEncryptedAmountFormat(ctx.tx); !isTesSuccess(res))
return res;
return tesSUCCESS;
}
TER
ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx)
{
auto const account = ctx.tx[sfAccount];
auto const issuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const amount = ctx.tx[sfMPTAmount];
// ensure that issuance exists
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(issuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanConfidentialAmount) ||
!sleIssuance->isFieldPresent(sfIssuerEncryptionKey))
return tecNO_PERMISSION;
// already checked in preflight, but should also check that issuer on the
// issuance isn't the account either
if (sleIssuance->getAccountID(sfIssuer) == account)
return tefINTERNAL; // LCOV_EXCL_LINE
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey);
// tx must include auditor ciphertext if the issuance has enabled
// auditing, and must not include it if auditing is not enabled
if (requiresAuditor != hasAuditor)
return tecNO_PERMISSION;
auto const sleMptoken = ctx.view.read(keylet::mptoken(issuanceID, account));
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
auto const mptIssue = MPTIssue{issuanceID};
// Explicit freeze and auth checks are required because accountHolds
// with fhZERO_IF_FROZEN/ahZERO_IF_UNAUTHORIZED only implicitly rejects
// non-zero amounts. A zero-amount convert would bypass those implicit
// checks, allowing frozen or unauthorized accounts to register ElGamal
// keys and initialize confidential balance fields.
// Check lock
if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter))
return ter;
// Check auth
if (auto const ter = requireAuth(ctx.view, mptIssue, account); !isTesSuccess(ter))
return ter;
STAmount const mptAmount =
STAmount(MPTAmount{static_cast<MPTAmount::value_type>(amount)}, mptIssue);
if (accountHolds(
ctx.view,
account,
mptIssue,
FreezeHandling::fhZERO_IF_FROZEN,
AuthHandling::ahZERO_IF_UNAUTHORIZED,
ctx.j) < mptAmount)
{
return tecINSUFFICIENT_FUNDS;
}
auto const hasHolderKeyOnLedger = sleMptoken->isFieldPresent(sfHolderEncryptionKey);
auto const hasHolderKeyInTx = ctx.tx.isFieldPresent(sfHolderEncryptionKey);
// must have pk to convert
if (!hasHolderKeyOnLedger && !hasHolderKeyInTx)
return tecNO_PERMISSION;
// can't update if there's already a pk
if (hasHolderKeyOnLedger && hasHolderKeyInTx)
return tecDUPLICATE;
// Run all verifications before returning any error to prevent timing attacks
// that could reveal which proof failed.
bool valid = true;
Slice holderPubKey;
if (hasHolderKeyInTx)
{
holderPubKey = ctx.tx[sfHolderEncryptionKey];
auto const contextHash =
getConvertContextHash(account, issuanceID, ctx.tx.getSeqProxy().value());
if (auto const ter = verifySchnorrProof(holderPubKey, ctx.tx[sfZKProof], contextHash);
!isTesSuccess(ter))
{
valid = false;
}
}
else
{
holderPubKey = (*sleMptoken)[sfHolderEncryptionKey];
}
std::optional<ConfidentialRecipient> auditor;
if (hasAuditor)
{
auditor.emplace(
ConfidentialRecipient{
(*sleIssuance)[sfAuditorEncryptionKey], ctx.tx[sfAuditorEncryptedAmount]});
}
auto const blindingFactor = ctx.tx[sfBlindingFactor];
if (auto const ter = verifyRevealedAmount(
amount,
Slice(blindingFactor.data(), blindingFactor.size()),
{holderPubKey, ctx.tx[sfHolderEncryptedAmount]},
{(*sleIssuance)[sfIssuerEncryptionKey], ctx.tx[sfIssuerEncryptedAmount]},
auditor);
!isTesSuccess(ter))
{
valid = false;
}
if (!valid)
return tecBAD_PROOF;
return tesSUCCESS;
}
TER
ConfidentialMPTConvert::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
if (!sleMptoken)
return tecINTERNAL; // LCOV_EXCL_LINE
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const amtToConvert = ctx_.tx[sfMPTAmount];
auto const amt = (*sleMptoken)[~sfMPTAmount].value_or(0);
if (ctx_.tx.isFieldPresent(sfHolderEncryptionKey))
(*sleMptoken)[sfHolderEncryptionKey] = ctx_.tx[sfHolderEncryptionKey];
// Converting decreases regular balance and increases confidential outstanding.
// The confidential outstanding tracks total tokens in confidential form globally.
auto const currentCOA = (*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0);
if (amtToConvert > maxMPTokenAmount - currentCOA)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfMPTAmount] = amt - amtToConvert;
(*sleIssuance)[sfConfidentialOutstandingAmount] = currentCOA + amtToConvert;
Slice const holderEc = ctx_.tx[sfHolderEncryptedAmount];
Slice const issuerEc = ctx_.tx[sfIssuerEncryptedAmount];
auto const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
// Two cases for Convert:
// 1. Holder already has confidential balances -> homomorphically add to inbox
// 2. First-time convert -> initialize all confidential balance fields
if (sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
{
// Case 1: Add to existing inbox balance (holder will merge later)
{
auto sum = homomorphicAdd(holderEc, (*sleMptoken)[sfConfidentialBalanceInbox]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceInbox] = std::move(*sum);
}
// homomorphically add issuer's encrypted balance
{
auto sum = homomorphicAdd(issuerEc, (*sleMptoken)[sfIssuerEncryptedBalance]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfIssuerEncryptedBalance] = std::move(*sum);
}
// homomorphically add auditor's encrypted balance
if (auditorEc)
{
auto sum = homomorphicAdd(*auditorEc, (*sleMptoken)[sfAuditorEncryptedBalance]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfAuditorEncryptedBalance] = std::move(*sum);
}
}
else if (
!sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
{
// Case 2: First-time convert - initialize all confidential fields
(*sleMptoken)[sfConfidentialBalanceInbox] = holderEc;
(*sleMptoken)[sfIssuerEncryptedBalance] = issuerEc;
(*sleMptoken)[sfConfidentialBalanceVersion] = 0;
if (auditorEc)
(*sleMptoken)[sfAuditorEncryptedBalance] = *auditorEc;
// Spending balance starts at zero. Must use canonical zero encryption
// (deterministic ciphertext) so the ledger state is reproducible.
auto zeroBalance = encryptCanonicalZeroAmount(
(*sleMptoken)[sfHolderEncryptionKey], account_, mptIssuanceID);
if (!zeroBalance)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*zeroBalance);
}
else
{
// both sfIssuerEncryptedBalance and sfConfidentialBalanceInbox should
// exist together
return tecINTERNAL; // LCOV_EXCL_LINE
}
view().update(sleIssuance);
view().update(sleMptoken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -1,247 +0,0 @@
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/tx/transactors/token/ConfidentialMPTConvertBack.h>
#include <cstddef>
namespace xrpl {
NotTEC
ConfidentialMPTConvertBack::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
// issuer cannot convert back
if (MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer() == ctx.tx[sfAccount])
return temMALFORMED;
if (ctx.tx[sfMPTAmount] == 0 || ctx.tx[sfMPTAmount] > maxMPTokenAmount)
return temBAD_AMOUNT;
if (!isValidCompressedECPoint(ctx.tx[sfBalanceCommitment]))
return temMALFORMED;
// check encrypted amount format after the above basic checks
// this check is more expensive so put it at the end
if (auto const res = checkEncryptedAmountFormat(ctx.tx); !isTesSuccess(res))
return res;
// ConvertBack proof = pedersen linkage proof + single bulletproof
if (ctx.tx[sfZKProof].size() != ecPedersenProofLength + ecSingleBulletproofLength)
return temMALFORMED;
return tesSUCCESS;
}
/**
* Verifies the cryptographic proofs for a ConvertBack transaction.
*
* This function verifies three proofs:
* 1. Revealed amount proof: verifies the encrypted amounts (holder, issuer,
* auditor) all encrypt the same revealed amount using the blinding factor.
* 2. Pedersen linkage proof: verifies the balance commitment is derived from
* the holder's encrypted spending balance.
* 3. Bulletproof (range proof): verifies the remaining balance (balance - amount)
* is non-negative, preventing overdrafts.
*
* All proofs are verified before returning any error to prevent timing attacks.
*/
static TER
verifyProofs(
STTx const& tx,
std::shared_ptr<SLE const> const& issuance,
std::shared_ptr<SLE const> const& mptoken)
{
if (!mptoken->isFieldPresent(sfHolderEncryptionKey))
return tecINTERNAL; // LCOV_EXCL_LINE
auto const mptIssuanceID = tx[sfMPTokenIssuanceID];
auto const account = tx[sfAccount];
auto const amount = tx[sfMPTAmount];
auto const blindingFactor = tx[sfBlindingFactor];
auto const holderPubKey = (*mptoken)[sfHolderEncryptionKey];
auto const contextHash = getConvertBackContextHash(
account,
mptIssuanceID,
tx.getSeqProxy().value(),
(*mptoken)[~sfConfidentialBalanceVersion].value_or(0));
// Prepare Auditor Info
std::optional<ConfidentialRecipient> auditor;
bool const hasAuditor = issuance->isFieldPresent(sfAuditorEncryptionKey);
if (hasAuditor)
{
auditor.emplace(
ConfidentialRecipient{
(*issuance)[sfAuditorEncryptionKey], tx[sfAuditorEncryptedAmount]});
}
// Run all verifications before returning any error to prevent timing attacks
// that could reveal which proof failed.
bool valid = true;
if (auto const ter = verifyRevealedAmount(
amount,
Slice(blindingFactor.data(), blindingFactor.size()),
{holderPubKey, tx[sfHolderEncryptedAmount]},
{(*issuance)[sfIssuerEncryptionKey], tx[sfIssuerEncryptedAmount]},
auditor);
!isTesSuccess(ter))
{
valid = false;
}
if (auto const ter = verifyConvertBackProof(
tx[sfZKProof],
holderPubKey,
(*mptoken)[sfConfidentialBalanceSpending],
tx[sfBalanceCommitment],
amount,
contextHash);
!isTesSuccess(ter))
{
valid = false;
}
if (!valid)
return tecBAD_PROOF;
return tesSUCCESS;
}
TER
ConfidentialMPTConvertBack::preclaim(PreclaimContext const& ctx)
{
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const account = ctx.tx[sfAccount];
auto const amount = ctx.tx[sfMPTAmount];
// ensure that issuance exists
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanConfidentialAmount))
return tecNO_PERMISSION;
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey);
// tx must include auditor ciphertext if the issuance has enabled
// auditing
if (requiresAuditor && !hasAuditor)
return tecNO_PERMISSION;
// if auditing is not supported then user should not upload auditor
// ciphertext
if (!requiresAuditor && hasAuditor)
return tecNO_PERMISSION;
// already checked in preflight, but should also check that issuer on
// the issuance isn't the account either
if (sleIssuance->getAccountID(sfIssuer) == account)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sleMptoken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
if (!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleMptoken->isFieldPresent(sfHolderEncryptionKey))
{
return tecNO_PERMISSION;
}
// if the total circulating confidential balance is smaller than what the
// holder is trying to convert back, we know for sure this txn should
// fail
if ((*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0) < amount)
{
return tecINSUFFICIENT_FUNDS;
}
// Check lock
MPTIssue const mptIssue(mptIssuanceID);
if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter))
return ter;
// Check auth
if (auto const ter = requireAuth(ctx.view, mptIssue, account); !isTesSuccess(ter))
return ter;
if (TER const res = verifyProofs(ctx.tx, sleIssuance, sleMptoken); !isTesSuccess(res))
return res;
return tesSUCCESS;
}
TER
ConfidentialMPTConvertBack::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
if (!sleMptoken)
return tecINTERNAL; // LCOV_EXCL_LINE
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const amtToConvertBack = ctx_.tx[sfMPTAmount];
auto const amt = (*sleMptoken)[~sfMPTAmount].value_or(0);
// Converting back increases regular balance and decreases confidential
// outstanding. This is the inverse of Convert.
(*sleMptoken)[sfMPTAmount] = amt + amtToConvertBack;
(*sleIssuance)[sfConfidentialOutstandingAmount] =
(*sleIssuance)[sfConfidentialOutstandingAmount] - amtToConvertBack;
std::optional<Slice> const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
// homomorphically subtract holder's encrypted balance
{
auto res = homomorphicSubtract(
(*sleMptoken)[sfConfidentialBalanceSpending], ctx_.tx[sfHolderEncryptedAmount]);
if (!res)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*res);
}
// homomorphically subtract issuer's encrypted balance
{
auto res = homomorphicSubtract(
(*sleMptoken)[sfIssuerEncryptedBalance], ctx_.tx[sfIssuerEncryptedAmount]);
if (!res)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfIssuerEncryptedBalance] = std::move(*res);
}
if (auditorEc)
{
auto res = homomorphicSubtract(
(*sleMptoken)[sfAuditorEncryptedBalance], ctx_.tx[sfAuditorEncryptedAmount]);
if (!res)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfAuditorEncryptedBalance] = std::move(*res);
}
incrementConfidentialVersion(*sleMptoken);
view().update(sleIssuance);
view().update(sleMptoken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -1,106 +0,0 @@
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/tx/transactors/token/ConfidentialMPTMergeInbox.h>
namespace xrpl {
NotTEC
ConfidentialMPTMergeInbox::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
// issuer cannot merge
if (MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer() == ctx.tx[sfAccount])
return temMALFORMED;
return tesSUCCESS;
}
TER
ConfidentialMPTMergeInbox::preclaim(PreclaimContext const& ctx)
{
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanConfidentialAmount))
return tecNO_PERMISSION;
// already checked in preflight, but should also check that issuer on the
// issuance isn't the account either
if (sleIssuance->getAccountID(sfIssuer) == ctx.tx[sfAccount])
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sleMptoken =
ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], ctx.tx[sfAccount]));
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
if (!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleMptoken->isFieldPresent(sfHolderEncryptionKey))
return tecNO_PERMISSION;
// Check lock
auto const account = ctx.tx[sfAccount];
MPTIssue const mptIssue(ctx.tx[sfMPTokenIssuanceID]);
if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter))
return ter;
// Check auth
if (auto const ter = requireAuth(ctx.view, mptIssue, account); !isTesSuccess(ter))
return ter;
return tesSUCCESS;
}
TER
ConfidentialMPTMergeInbox::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
if (!sleMptoken)
return tecINTERNAL; // LCOV_EXCL_LINE
// sanity check
if (!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
!sleMptoken->isFieldPresent(sfHolderEncryptionKey))
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// Merge inbox into spending: spending = spending + inbox
// This allows holder to use received funds. Without merging, incoming
// transfers sit in inbox and cannot be spent or converted back.
auto sum = homomorphicAdd(
(*sleMptoken)[sfConfidentialBalanceSpending], (*sleMptoken)[sfConfidentialBalanceInbox]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*sum);
// Reset inbox to encrypted zero. Must use canonical zero encryption
// (deterministic ciphertext) so the ledger state is reproducible.
auto zeroEncryption =
encryptCanonicalZeroAmount((*sleMptoken)[sfHolderEncryptionKey], account_, mptIssuanceID);
if (!zeroEncryption)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceInbox] = std::move(*zeroEncryption);
incrementConfidentialVersion(*sleMptoken);
view().update(sleMptoken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -1,298 +0,0 @@
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
namespace xrpl {
NotTEC
ConfidentialMPTSend::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
auto const account = ctx.tx[sfAccount];
auto const issuer = MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer();
// ConfidentialMPTSend only allows holder to holder, holder to second account,
// and second account to holder transfers. So issuer cannot be the sender.
if (account == issuer)
return temMALFORMED;
// Can not send to self
if (account == ctx.tx[sfDestination])
return temMALFORMED;
// Check the length of the encrypted amounts
if (ctx.tx[sfSenderEncryptedAmount].length() != ecGamalEncryptedTotalLength ||
ctx.tx[sfDestinationEncryptedAmount].length() != ecGamalEncryptedTotalLength ||
ctx.tx[sfIssuerEncryptedAmount].length() != ecGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
if (hasAuditor && ctx.tx[sfAuditorEncryptedAmount].length() != ecGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
// Check the length of the ZKProof
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
auto const sizeEquality = getEqualityProofSize(recipientCount);
if (ctx.tx[sfZKProof].length() !=
sizeEquality + doublePedersenProofLength + ecDoubleBulletproofLength)
return temMALFORMED;
// Check the Pedersen commitments are valid
if (!isValidCompressedECPoint(ctx.tx[sfBalanceCommitment]) ||
!isValidCompressedECPoint(ctx.tx[sfAmountCommitment]))
return temMALFORMED;
// Check the encrypted amount formats, this is more expensive so put it at
// the end
if (!isValidCiphertext(ctx.tx[sfSenderEncryptedAmount]) ||
!isValidCiphertext(ctx.tx[sfDestinationEncryptedAmount]) ||
!isValidCiphertext(ctx.tx[sfIssuerEncryptedAmount]))
return temBAD_CIPHERTEXT;
if (hasAuditor && !isValidCiphertext(ctx.tx[sfAuditorEncryptedAmount]))
return temBAD_CIPHERTEXT;
if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err))
return err;
return tesSUCCESS;
}
TER
verifySendProofs(
PreclaimContext const& ctx,
std::shared_ptr<SLE const> const& sleSenderMPToken,
std::shared_ptr<SLE const> const& sleDestinationMPToken,
std::shared_ptr<SLE const> const& sleIssuance)
{
// Sanity check
if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
std::optional<ConfidentialRecipient> auditor;
if (hasAuditor)
auditor.emplace(
ConfidentialRecipient{
(*sleIssuance)[sfAuditorEncryptionKey], ctx.tx[sfAuditorEncryptedAmount]});
auto const contextHash = getSendContextHash(
ctx.tx[sfAccount],
ctx.tx[sfMPTokenIssuanceID],
ctx.tx.getSeqProxy().value(),
ctx.tx[sfDestination],
(*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));
return verifySendProof(
ctx.tx[sfZKProof],
{(*sleSenderMPToken)[sfHolderEncryptionKey], ctx.tx[sfSenderEncryptedAmount]},
{(*sleDestinationMPToken)[sfHolderEncryptionKey], ctx.tx[sfDestinationEncryptedAmount]},
{(*sleIssuance)[sfIssuerEncryptionKey], ctx.tx[sfIssuerEncryptedAmount]},
auditor,
(*sleSenderMPToken)[sfConfidentialBalanceSpending],
ctx.tx[sfAmountCommitment],
ctx.tx[sfBalanceCommitment],
contextHash);
}
TER
ConfidentialMPTSend::preclaim(PreclaimContext const& ctx)
{
// Check if sender account exists
auto const account = ctx.tx[sfAccount];
if (!ctx.view.exists(keylet::account(account)))
return terNO_ACCOUNT;
// Check if destination account exists
auto const destination = ctx.tx[sfDestination];
if (!ctx.view.exists(keylet::account(destination)))
return tecNO_TARGET;
// Check if MPT issuance exists
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(mptIssuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
// Check if the issuance allows transfer
if (!sleIssuance->isFlag(lsfMPTCanTransfer))
return tecNO_AUTH;
// Check if issuance allows confidential transfer
if (!sleIssuance->isFlag(lsfMPTCanConfidentialAmount))
return tecNO_PERMISSION;
// Check if issuance has issuer ElGamal public key
if (!sleIssuance->isFieldPresent(sfIssuerEncryptionKey))
return tecNO_PERMISSION;
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey);
// Tx must include auditor ciphertext if the issuance has enabled
// auditing, and must not include it if auditing is not enabled
if (requiresAuditor != hasAuditor)
return tecNO_PERMISSION;
// Sanity check: issuer isn't the sender
if (sleIssuance->getAccountID(sfIssuer) == ctx.tx[sfAccount])
return tefINTERNAL; // LCOV_EXCL_LINE
// Check sender's MPToken existence
auto const sleSenderMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
if (!sleSenderMPToken)
return tecOBJECT_NOT_FOUND;
// Check sender's MPToken has necessary fields for confidential send
if (!sleSenderMPToken->isFieldPresent(sfHolderEncryptionKey) ||
!sleSenderMPToken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleSenderMPToken->isFieldPresent(sfIssuerEncryptedBalance))
return tecNO_PERMISSION;
// Check destination's MPToken existence
auto const sleDestinationMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, destination));
if (!sleDestinationMPToken)
return tecOBJECT_NOT_FOUND;
// Check destination's MPToken has necessary fields for confidential send
if (!sleDestinationMPToken->isFieldPresent(sfHolderEncryptionKey) ||
!sleDestinationMPToken->isFieldPresent(sfConfidentialBalanceInbox) ||
!sleDestinationMPToken->isFieldPresent(sfIssuerEncryptedBalance))
return tecNO_PERMISSION;
// Sanity check: Both MPTokens' auditor fields must be present if auditing
// is enabled
if (requiresAuditor &&
(!sleSenderMPToken->isFieldPresent(sfAuditorEncryptedBalance) ||
!sleDestinationMPToken->isFieldPresent(sfAuditorEncryptedBalance)))
return tefINTERNAL; // LCOV_EXCL_LINE
// Check lock
MPTIssue const mptIssue(mptIssuanceID);
if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter))
return ter;
if (auto const ter = checkFrozen(ctx.view, destination, mptIssue); !isTesSuccess(ter))
return ter;
// Check auth
if (auto const ter = requireAuth(ctx.view, mptIssue, account); !isTesSuccess(ter))
return ter;
if (auto const ter = requireAuth(ctx.view, mptIssue, destination); !isTesSuccess(ter))
return ter;
if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
!isTesSuccess(err))
return err;
return verifySendProofs(ctx, sleSenderMPToken, sleDestinationMPToken, sleIssuance);
}
TER
ConfidentialMPTSend::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto const destination = ctx_.tx[sfDestination];
auto sleSenderMPToken = view().peek(keylet::mptoken(mptIssuanceID, account_));
auto sleDestinationMPToken = view().peek(keylet::mptoken(mptIssuanceID, destination));
auto sleDestAcct = view().peek(keylet::account(destination));
if (!sleSenderMPToken || !sleDestinationMPToken || !sleDestAcct)
return tecINTERNAL; // LCOV_EXCL_LINE
if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), account_, destination, sleDestAcct, ctx_.journal);
!isTesSuccess(err))
return err;
Slice const senderEc = ctx_.tx[sfSenderEncryptedAmount];
Slice const destEc = ctx_.tx[sfDestinationEncryptedAmount];
Slice const issuerEc = ctx_.tx[sfIssuerEncryptedAmount];
auto const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
// Subtract from sender's spending balance
{
Slice const curSpending = (*sleSenderMPToken)[sfConfidentialBalanceSpending];
auto newSpending = homomorphicSubtract(curSpending, senderEc);
if (!newSpending)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleSenderMPToken)[sfConfidentialBalanceSpending] = std::move(*newSpending);
}
// Subtract from issuer's balance
{
Slice const curIssuerEnc = (*sleSenderMPToken)[sfIssuerEncryptedBalance];
auto newIssuerEnc = homomorphicSubtract(curIssuerEnc, issuerEc);
if (!newIssuerEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleSenderMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
}
// Subtract from auditor's balance if present
if (auditorEc)
{
Slice const curAuditorEnc = (*sleSenderMPToken)[sfAuditorEncryptedBalance];
auto newAuditorEnc = homomorphicSubtract(curAuditorEnc, *auditorEc);
if (!newAuditorEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleSenderMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
}
// Add to destination's inbox balance
{
Slice const curInbox = (*sleDestinationMPToken)[sfConfidentialBalanceInbox];
auto newInbox = homomorphicAdd(curInbox, destEc);
if (!newInbox)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleDestinationMPToken)[sfConfidentialBalanceInbox] = std::move(*newInbox);
}
// Add to issuer's balance
{
Slice const curIssuerEnc = (*sleDestinationMPToken)[sfIssuerEncryptedBalance];
auto newIssuerEnc = homomorphicAdd(curIssuerEnc, issuerEc);
if (!newIssuerEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleDestinationMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
}
// Add to auditor's balance if present
if (auditorEc)
{
Slice const curAuditorEnc = (*sleDestinationMPToken)[sfAuditorEncryptedBalance];
auto newAuditorEnc = homomorphicAdd(curAuditorEnc, *auditorEc);
if (!newAuditorEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleDestinationMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
}
// increment sender version only; receiver version is not modified by incoming sends
incrementConfidentialVersion(*sleSenderMPToken);
view().update(sleSenderMPToken);
view().update(sleDestinationMPToken);
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -75,25 +75,6 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureSingleAssetVault) && sleMpt->isFlag(lsfMPTLocked))
return tecNO_PERMISSION;
if (ctx.view.rules().enabled(featureConfidentialTransfer))
{
auto const sleMptIssuance =
ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
// if there still existing encrypted balances of MPT in
// circulation
if (sleMptIssuance &&
(*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0) != 0)
{
// this MPT still has encrypted balance, since we don't know
// if it's non-zero or not, we won't allow deletion of
// MPToken
if (sleMpt->isFieldPresent(sfConfidentialBalanceInbox) ||
sleMpt->isFieldPresent(sfConfidentialBalanceSpending))
return tecHAS_OBLIGATIONS;
}
}
return tesSUCCESS;
}

View File

@@ -18,16 +18,6 @@ MPTokenIssuanceCreate::checkExtraFeatures(PreflightContext const& ctx)
if (ctx.tx.isFieldPresent(sfMutableFlags) && !ctx.rules.enabled(featureDynamicMPT))
return false;
if (ctx.tx.isFlag(tfMPTCanConfidentialAmount) &&
!ctx.rules.enabled(featureConfidentialTransfer))
return false;
// can not set tmfMPTCannotMutateCanConfidentialAmount without featureConfidentialTransfer
auto const mutableFlags = ctx.tx[~sfMutableFlags];
if (mutableFlags && (*mutableFlags & tmfMPTCannotMutateCanConfidentialAmount) &&
!ctx.rules.enabled(featureConfidentialTransfer))
return false;
return true;
}

View File

@@ -1,5 +1,4 @@
#include <xrpl/ledger/helpers/DelegateHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TxFlags.h>
@@ -26,25 +25,18 @@ MPTokenIssuanceSet::getFlagsMask(PreflightContext const& ctx)
// allowed.
struct MPTMutabilityFlags
{
std::uint32_t setFlag{};
std::uint32_t clearFlag{};
std::uint32_t mutabilityFlag{};
std::uint32_t targetFlag{};
bool isCannotMutate = false; // if true, cannot mutate by default.
std::uint32_t setFlag;
std::uint32_t clearFlag;
std::uint32_t canMutateFlag;
};
static constexpr std::array<MPTMutabilityFlags, 7> mptMutabilityFlags = {
{{tmfMPTSetCanLock, tmfMPTClearCanLock, lsmfMPTCanMutateCanLock, lsfMPTCanLock},
{tmfMPTSetRequireAuth, tmfMPTClearRequireAuth, lsmfMPTCanMutateRequireAuth, lsfMPTRequireAuth},
{tmfMPTSetCanEscrow, tmfMPTClearCanEscrow, lsmfMPTCanMutateCanEscrow, lsfMPTCanEscrow},
{tmfMPTSetCanTrade, tmfMPTClearCanTrade, lsmfMPTCanMutateCanTrade, lsfMPTCanTrade},
{tmfMPTSetCanTransfer, tmfMPTClearCanTransfer, lsmfMPTCanMutateCanTransfer, lsfMPTCanTransfer},
{tmfMPTSetCanClawback, tmfMPTClearCanClawback, lsmfMPTCanMutateCanClawback, lsfMPTCanClawback},
{tmfMPTSetCanConfidentialAmount,
tmfMPTClearCanConfidentialAmount,
lsmfMPTCannotMutateCanConfidentialAmount,
lsfMPTCanConfidentialAmount,
true}}};
static constexpr std::array<MPTMutabilityFlags, 6> mptMutabilityFlags = {
{{tmfMPTSetCanLock, tmfMPTClearCanLock, lsmfMPTCanMutateCanLock},
{tmfMPTSetRequireAuth, tmfMPTClearRequireAuth, lsmfMPTCanMutateRequireAuth},
{tmfMPTSetCanEscrow, tmfMPTClearCanEscrow, lsmfMPTCanMutateCanEscrow},
{tmfMPTSetCanTrade, tmfMPTClearCanTrade, lsmfMPTCanMutateCanTrade},
{tmfMPTSetCanTransfer, tmfMPTClearCanTransfer, lsmfMPTCanMutateCanTransfer},
{tmfMPTSetCanClawback, tmfMPTClearCanClawback, lsmfMPTCanMutateCanClawback}}};
NotTEC
MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
@@ -53,28 +45,14 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
auto const metadata = ctx.tx[~sfMPTokenMetadata];
auto const transferFee = ctx.tx[~sfTransferFee];
auto const isMutate = mutableFlags || metadata || transferFee;
auto const hasIssuerElGamalKey = ctx.tx.isFieldPresent(sfIssuerEncryptionKey);
auto const hasAuditorElGamalKey = ctx.tx.isFieldPresent(sfAuditorEncryptionKey);
auto const txFlags = ctx.tx.getFlags();
auto const mutatePrivacy = mutableFlags &&
((*mutableFlags & (tmfMPTSetCanConfidentialAmount | tmfMPTClearCanConfidentialAmount)));
auto const hasDomain = ctx.tx.isFieldPresent(sfDomainID);
auto const hasHolder = ctx.tx.isFieldPresent(sfHolder);
if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
return temDISABLED;
if ((hasIssuerElGamalKey || hasAuditorElGamalKey || mutatePrivacy) &&
!ctx.rules.enabled(featureConfidentialTransfer))
return temDISABLED;
if (hasDomain && hasHolder)
if (ctx.tx.isFieldPresent(sfDomainID) && ctx.tx.isFieldPresent(sfHolder))
return temMALFORMED;
if (mutatePrivacy && hasHolder)
return temMALFORMED;
auto const txFlags = ctx.tx.getFlags();
// fails if both flags are set
if (((txFlags & tfMPTLock) != 0u) && ((txFlags & tfMPTUnlock) != 0u))
@@ -85,12 +63,10 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
if (holderID && accountID == holderID)
return temMALFORMED;
if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT) ||
ctx.rules.enabled(featureConfidentialTransfer))
if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT))
{
// Is this transaction actually changing anything ?
if (txFlags == 0 && !hasDomain && !hasIssuerElGamalKey && !hasAuditorElGamalKey &&
!isMutate)
if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID) && !isMutate)
return temMALFORMED;
}
@@ -131,23 +107,6 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
}
}
if (hasHolder && (hasIssuerElGamalKey || hasAuditorElGamalKey))
return temMALFORMED;
if (hasAuditorElGamalKey && !hasIssuerElGamalKey)
return temMALFORMED;
// Cannot set keys while clearing confidential amount
if ((hasIssuerElGamalKey || hasAuditorElGamalKey) && mutableFlags &&
(*mutableFlags & tmfMPTClearCanConfidentialAmount))
return temINVALID_FLAG;
if (hasIssuerElGamalKey && !isValidCompressedECPoint(ctx.tx[sfIssuerEncryptionKey]))
return temMALFORMED;
if (hasAuditorElGamalKey && !isValidCompressedECPoint(ctx.tx[sfAuditorEncryptionKey]))
return temMALFORMED;
return tesSUCCESS;
}
@@ -244,30 +203,16 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
return currentMutableFlags & mutableFlag;
};
auto const mutableFlags = ctx.tx[~sfMutableFlags];
if (mutableFlags)
if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
{
if (std::any_of(
mptMutabilityFlags.begin(),
mptMutabilityFlags.end(),
[mutableFlags, &isMutableFlag](auto const& f) {
bool const canMutate = f.isCannotMutate ? isMutableFlag(f.mutabilityFlag)
: !isMutableFlag(f.mutabilityFlag);
return canMutate && (*mutableFlags & (f.setFlag | f.clearFlag));
return !isMutableFlag(f.canMutateFlag) &&
((*mutableFlags & (f.setFlag | f.clearFlag)));
}))
return tecNO_PERMISSION;
if ((*mutableFlags & tmfMPTSetCanConfidentialAmount) ||
(*mutableFlags & tmfMPTClearCanConfidentialAmount))
{
std::uint64_t const confidentialOA =
(*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0);
// If there's any confidential outstanding amount, disallow toggling
// the lsfMPTCanConfidentialAmount flag
if (confidentialOA > 0)
return tecNO_PERMISSION;
}
}
if (!isMutableFlag(lsmfMPTCanMutateMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata))
@@ -286,46 +231,6 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
return tecNO_PERMISSION;
}
// cannot update issuer public key
if (ctx.tx.isFieldPresent(sfIssuerEncryptionKey) &&
sleMptIssuance->isFieldPresent(sfIssuerEncryptionKey))
{
return tecNO_PERMISSION;
}
// cannot update auditor public key
if (ctx.tx.isFieldPresent(sfAuditorEncryptionKey) &&
sleMptIssuance->isFieldPresent(sfAuditorEncryptionKey))
{
return tecNO_PERMISSION; // LCOV_EXCL_LINE
}
// Check if the transaction is enabling confidential amounts
bool const enablesConfidentialAmount =
mutableFlags && (*mutableFlags & tmfMPTSetCanConfidentialAmount);
// Encryption keys can only be set if confidential amounts are already
// enabled on the issuance OR if the transaction is enabling it
if (ctx.tx.isFieldPresent(sfIssuerEncryptionKey) &&
!sleMptIssuance->isFlag(lsfMPTCanConfidentialAmount) && !enablesConfidentialAmount)
{
return tecNO_PERMISSION;
}
if (ctx.tx.isFieldPresent(sfAuditorEncryptionKey) &&
!sleMptIssuance->isFlag(lsfMPTCanConfidentialAmount) && !enablesConfidentialAmount)
{
return tecNO_PERMISSION;
}
// cannot upload key if there's circulating supply of COA
if ((ctx.tx.isFieldPresent(sfIssuerEncryptionKey) ||
ctx.tx.isFieldPresent(sfAuditorEncryptionKey)) &&
sleMptIssuance->isFieldPresent(sfConfidentialOutstandingAmount))
{
return tecNO_PERMISSION; // LCOV_EXCL_LINE
}
return tesSUCCESS;
}
@@ -368,11 +273,11 @@ MPTokenIssuanceSet::doApply()
{
if ((mutableFlags & f.setFlag) != 0u)
{
flagsOut |= f.targetFlag;
flagsOut |= f.canMutateFlag;
}
else if ((mutableFlags & f.clearFlag) != 0u)
{
flagsOut &= ~f.targetFlag;
flagsOut &= ~f.canMutateFlag;
}
}
@@ -433,26 +338,6 @@ MPTokenIssuanceSet::doApply()
}
}
if (auto const pubKey = ctx_.tx[~sfIssuerEncryptionKey])
{
// This is enforced in preflight.
XRPL_ASSERT(
sle->getType() == ltMPTOKEN_ISSUANCE,
"MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
sle->setFieldVL(sfIssuerEncryptionKey, *pubKey);
}
if (auto const pubKey = ctx_.tx[~sfAuditorEncryptionKey])
{
// This is enforced in preflight.
XRPL_ASSERT(
sle->getType() == ltMPTOKEN_ISSUANCE,
"MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
sle->setFieldVL(sfAuditorEncryptionKey, *pubKey);
}
view().update(sle);
return tesSUCCESS;

File diff suppressed because it is too large Load Diff

View File

@@ -1846,7 +1846,7 @@ class Delegate_test : public beast::unit_test::suite
// DO NOT modify expectedDelegableCount unless all scenarios, including
// edge cases, have been fully tested and verified.
// ====================================================================
std::size_t const expectedDelegableCount = 80;
std::size_t const expectedDelegableCount = 75;
BEAST_EXPECTS(
delegableCount == expectedDelegableCount,

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