mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-02 21:15:25 +00:00
Compare commits
59 Commits
bthomee/ke
...
ripple/con
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3941283438 | ||
|
|
86af28d91d | ||
|
|
41f7102fb8 | ||
|
|
66ed0fa452 | ||
|
|
cad8fb328a | ||
|
|
31346425f0 | ||
|
|
40bfaa25d2 | ||
|
|
c4916f1251 | ||
|
|
fc8b7898c5 | ||
|
|
a0e09187b9 | ||
|
|
446f9fbe6d | ||
|
|
1297385b7e | ||
|
|
114adc0c57 | ||
|
|
1d349c32c5 | ||
|
|
a5f20c129d | ||
|
|
75d143a2a0 | ||
|
|
e3da98e310 | ||
|
|
ec6d7cb91d | ||
|
|
fa055c2bd5 | ||
|
|
6c38086f17 | ||
|
|
3e9dc276ed | ||
|
|
abf7a62b1f | ||
|
|
bd3a6e1631 | ||
|
|
7c0bd419a4 | ||
|
|
d3126959e7 | ||
|
|
67e8e89e0f | ||
|
|
4e4326a174 | ||
|
|
5397bd6d6e | ||
|
|
6dece25cc3 | ||
|
|
d9da8733be | ||
|
|
f6f51451e7 | ||
|
|
b94c95b3e9 | ||
|
|
8365148b5c | ||
|
|
c03866bf0f | ||
|
|
389afc5f06 | ||
|
|
7b04eaae81 | ||
|
|
1343019509 | ||
|
|
cd75e630a2 | ||
|
|
ec57fbdc5f | ||
|
|
4fe67f5715 | ||
|
|
44d885e39b | ||
|
|
3af758145c | ||
|
|
f3d4d4341b | ||
|
|
ddb518ad09 | ||
|
|
3899e3f36c | ||
|
|
e4a8ba51f9 | ||
|
|
35e4fad557 | ||
|
|
8e9cb3c1da | ||
|
|
18d92058e3 | ||
|
|
f24d584f29 | ||
|
|
da3fbcd25b | ||
|
|
daa1303b5a | ||
|
|
a636fe5871 | ||
|
|
bbc3071fd1 | ||
|
|
8fdc639206 | ||
|
|
5a89641d98 | ||
|
|
beefa248a6 | ||
|
|
e919a25ecb | ||
|
|
c3fdbc0430 |
236
.cmake-format.yaml
Normal file
236
.cmake-format.yaml
Normal file
@@ -0,0 +1,236 @@
|
||||
_help_parse: Options affecting listfile parsing
|
||||
parse:
|
||||
_help_additional_commands:
|
||||
- Specify structure for custom cmake functions
|
||||
_help_override_spec:
|
||||
- Override configurations per-command where available
|
||||
override_spec: {}
|
||||
_help_vartags:
|
||||
- Specify variable tags.
|
||||
vartags: []
|
||||
_help_proptags:
|
||||
- Specify property tags.
|
||||
proptags: []
|
||||
_help_format: Options affecting formatting.
|
||||
format:
|
||||
_help_disable:
|
||||
- Disable formatting entirely, making cmake-format a no-op
|
||||
disable: false
|
||||
_help_line_width:
|
||||
- How wide to allow formatted cmake files
|
||||
line_width: 120
|
||||
_help_tab_size:
|
||||
- How many spaces to tab for indent
|
||||
tab_size: 4
|
||||
_help_use_tabchars:
|
||||
- If true, lines are indented using tab characters (utf-8
|
||||
- 0x09) instead of <tab_size> space characters (utf-8 0x20).
|
||||
- In cases where the layout would require a fractional tab
|
||||
- character, the behavior of the fractional indentation is
|
||||
- governed by <fractional_tab_policy>
|
||||
use_tabchars: false
|
||||
_help_fractional_tab_policy:
|
||||
- If <use_tabchars> is True, then the value of this variable
|
||||
- indicates how fractional indentions are handled during
|
||||
- whitespace replacement. If set to 'use-space', fractional
|
||||
- indentation is left as spaces (utf-8 0x20). If set to
|
||||
- "`round-up` fractional indentation is replaced with a single"
|
||||
- tab character (utf-8 0x09) effectively shifting the column
|
||||
- to the next tabstop
|
||||
fractional_tab_policy: use-space
|
||||
_help_max_subgroups_hwrap:
|
||||
- If an argument group contains more than this many sub-groups
|
||||
- (parg or kwarg groups) then force it to a vertical layout.
|
||||
max_subgroups_hwrap: 4
|
||||
_help_max_pargs_hwrap:
|
||||
- If a positional argument group contains more than this many
|
||||
- arguments, then force it to a vertical layout.
|
||||
max_pargs_hwrap: 5
|
||||
_help_max_rows_cmdline:
|
||||
- If a cmdline positional group consumes more than this many
|
||||
- lines without nesting, then invalidate the layout (and nest)
|
||||
max_rows_cmdline: 2
|
||||
_help_separate_ctrl_name_with_space:
|
||||
- If true, separate flow control names from their parentheses
|
||||
- with a space
|
||||
separate_ctrl_name_with_space: true
|
||||
_help_separate_fn_name_with_space:
|
||||
- If true, separate function names from parentheses with a
|
||||
- space
|
||||
separate_fn_name_with_space: false
|
||||
_help_dangle_parens:
|
||||
- If a statement is wrapped to more than one line, than dangle
|
||||
- the closing parenthesis on its own line.
|
||||
dangle_parens: false
|
||||
_help_dangle_align:
|
||||
- If the trailing parenthesis must be 'dangled' on its on
|
||||
- "line, then align it to this reference: `prefix`: the start"
|
||||
- "of the statement, `prefix-indent`: the start of the"
|
||||
- "statement, plus one indentation level, `child`: align to"
|
||||
- the column of the arguments
|
||||
dangle_align: prefix
|
||||
_help_min_prefix_chars:
|
||||
- If the statement spelling length (including space and
|
||||
- parenthesis) is smaller than this amount, then force reject
|
||||
- nested layouts.
|
||||
min_prefix_chars: 18
|
||||
_help_max_prefix_chars:
|
||||
- If the statement spelling length (including space and
|
||||
- parenthesis) is larger than the tab width by more than this
|
||||
- amount, then force reject un-nested layouts.
|
||||
max_prefix_chars: 10
|
||||
_help_max_lines_hwrap:
|
||||
- If a candidate layout is wrapped horizontally but it exceeds
|
||||
- this many lines, then reject the layout.
|
||||
max_lines_hwrap: 2
|
||||
_help_line_ending:
|
||||
- What style line endings to use in the output.
|
||||
line_ending: unix
|
||||
_help_command_case:
|
||||
- Format command names consistently as 'lower' or 'upper' case
|
||||
command_case: canonical
|
||||
_help_keyword_case:
|
||||
- Format keywords consistently as 'lower' or 'upper' case
|
||||
keyword_case: unchanged
|
||||
_help_always_wrap:
|
||||
- A list of command names which should always be wrapped
|
||||
always_wrap: []
|
||||
_help_enable_sort:
|
||||
- If true, the argument lists which are known to be sortable
|
||||
- will be sorted lexicographicall
|
||||
enable_sort: true
|
||||
_help_autosort:
|
||||
- If true, the parsers may infer whether or not an argument
|
||||
- list is sortable (without annotation).
|
||||
autosort: true
|
||||
_help_require_valid_layout:
|
||||
- By default, if cmake-format cannot successfully fit
|
||||
- everything into the desired linewidth it will apply the
|
||||
- last, most aggressive attempt that it made. If this flag is
|
||||
- True, however, cmake-format will print error, exit with non-
|
||||
- zero status code, and write-out nothing
|
||||
require_valid_layout: false
|
||||
_help_layout_passes:
|
||||
- A dictionary mapping layout nodes to a list of wrap
|
||||
- decisions. See the documentation for more information.
|
||||
layout_passes: {}
|
||||
_help_markup: Options affecting comment reflow and formatting.
|
||||
markup:
|
||||
_help_bullet_char:
|
||||
- What character to use for bulleted lists
|
||||
bullet_char: "-"
|
||||
_help_enum_char:
|
||||
- What character to use as punctuation after numerals in an
|
||||
- enumerated list
|
||||
enum_char: .
|
||||
_help_first_comment_is_literal:
|
||||
- If comment markup is enabled, don't reflow the first comment
|
||||
- block in each listfile. Use this to preserve formatting of
|
||||
- your copyright/license statements.
|
||||
first_comment_is_literal: false
|
||||
_help_literal_comment_pattern:
|
||||
- If comment markup is enabled, don't reflow any comment block
|
||||
- which matches this (regex) pattern. Default is `None`
|
||||
- (disabled).
|
||||
literal_comment_pattern: null
|
||||
_help_fence_pattern:
|
||||
- Regular expression to match preformat fences in comments
|
||||
- default= ``r'^\s*([`~]{3}[`~]*)(.*)$'``
|
||||
fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$
|
||||
_help_ruler_pattern:
|
||||
- Regular expression to match rulers in comments default=
|
||||
- '``r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''``'
|
||||
ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$
|
||||
_help_explicit_trailing_pattern:
|
||||
- If a comment line matches starts with this pattern then it
|
||||
- is explicitly a trailing comment for the preceding
|
||||
- argument. Default is '#<'
|
||||
explicit_trailing_pattern: "#<"
|
||||
_help_hashruler_min_length:
|
||||
- If a comment line starts with at least this many consecutive
|
||||
- hash characters, then don't lstrip() them off. This allows
|
||||
- for lazy hash rulers where the first hash char is not
|
||||
- separated by space
|
||||
hashruler_min_length: 10
|
||||
_help_canonicalize_hashrulers:
|
||||
- If true, then insert a space between the first hash char and
|
||||
- remaining hash chars in a hash ruler, and normalize its
|
||||
- length to fill the column
|
||||
canonicalize_hashrulers: true
|
||||
_help_enable_markup:
|
||||
- enable comment markup parsing and reflow
|
||||
enable_markup: true
|
||||
_help_lint: Options affecting the linter
|
||||
lint:
|
||||
_help_disabled_codes:
|
||||
- a list of lint codes to disable
|
||||
disabled_codes: []
|
||||
_help_function_pattern:
|
||||
- regular expression pattern describing valid function names
|
||||
function_pattern: "[0-9a-z_]+"
|
||||
_help_macro_pattern:
|
||||
- regular expression pattern describing valid macro names
|
||||
macro_pattern: "[0-9A-Z_]+"
|
||||
_help_global_var_pattern:
|
||||
- regular expression pattern describing valid names for
|
||||
- variables with global (cache) scope
|
||||
global_var_pattern: "[A-Z][0-9A-Z_]+"
|
||||
_help_internal_var_pattern:
|
||||
- regular expression pattern describing valid names for
|
||||
- variables with global scope (but internal semantic)
|
||||
internal_var_pattern: _[A-Z][0-9A-Z_]+
|
||||
_help_local_var_pattern:
|
||||
- regular expression pattern describing valid names for
|
||||
- variables with local scope
|
||||
local_var_pattern: "[a-z][a-z0-9_]+"
|
||||
_help_private_var_pattern:
|
||||
- regular expression pattern describing valid names for
|
||||
- privatedirectory variables
|
||||
private_var_pattern: _[0-9a-z_]+
|
||||
_help_public_var_pattern:
|
||||
- regular expression pattern describing valid names for public
|
||||
- directory variables
|
||||
public_var_pattern: "[A-Z][0-9A-Z_]+"
|
||||
_help_argument_var_pattern:
|
||||
- regular expression pattern describing valid names for
|
||||
- function/macro arguments and loop variables.
|
||||
argument_var_pattern: "[a-z][a-z0-9_]+"
|
||||
_help_keyword_pattern:
|
||||
- regular expression pattern describing valid names for
|
||||
- keywords used in functions or macros
|
||||
keyword_pattern: "[A-Z][0-9A-Z_]+"
|
||||
_help_max_conditionals_custom_parser:
|
||||
- In the heuristic for C0201, how many conditionals to match
|
||||
- within a loop in before considering the loop a parser.
|
||||
max_conditionals_custom_parser: 2
|
||||
_help_min_statement_spacing:
|
||||
- Require at least this many newlines between statements
|
||||
min_statement_spacing: 1
|
||||
_help_max_statement_spacing:
|
||||
- Require no more than this many newlines between statements
|
||||
max_statement_spacing: 2
|
||||
max_returns: 6
|
||||
max_branches: 12
|
||||
max_arguments: 5
|
||||
max_localvars: 15
|
||||
max_statements: 50
|
||||
_help_encode: Options affecting file encoding
|
||||
encode:
|
||||
_help_emit_byteorder_mark:
|
||||
- If true, emit the unicode byte-order mark (BOM) at the start
|
||||
- of the file
|
||||
emit_byteorder_mark: false
|
||||
_help_input_encoding:
|
||||
- Specify the encoding of the input file. Defaults to utf-8
|
||||
input_encoding: utf-8
|
||||
_help_output_encoding:
|
||||
- Specify the encoding of the output file. Defaults to utf-8.
|
||||
- Note that cmake only claims to support utf-8 so be careful
|
||||
- when using anything else
|
||||
output_encoding: utf-8
|
||||
_help_misc: Miscellaneous configurations options.
|
||||
misc:
|
||||
_help_per_command:
|
||||
- A dictionary containing any per-command configuration
|
||||
- overrides. Currently only `command_case` is supported.
|
||||
per_command: {}
|
||||
@@ -101,6 +101,7 @@ words:
|
||||
- gpgcheck
|
||||
- gpgkey
|
||||
- hotwallet
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
- insuf
|
||||
@@ -114,6 +115,8 @@ words:
|
||||
- keylet
|
||||
- keylets
|
||||
- keyvadb
|
||||
- kwarg
|
||||
- kwargs
|
||||
- ledgerentry
|
||||
- ledgerhash
|
||||
- ledgerindex
|
||||
@@ -163,6 +166,7 @@ words:
|
||||
- nunl
|
||||
- Nyffenegger
|
||||
- ostr
|
||||
- pargs
|
||||
- partitioner
|
||||
- paychan
|
||||
- paychans
|
||||
|
||||
@@ -26,6 +26,24 @@ repos:
|
||||
args: [--style=file]
|
||||
"types_or": [c++, c, proto]
|
||||
|
||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
rev: e2c2116d86a80e72e7146a06e68b7c228afc6319 # frozen: v0.6.13
|
||||
hooks:
|
||||
- id: cmake-format
|
||||
additional_dependencies: [PyYAML]
|
||||
exclude: |
|
||||
(?x)^(
|
||||
cmake/CodeCoverage.cmake|
|
||||
cmake/XrplCore.cmake|
|
||||
cmake/XrplDocs.cmake|
|
||||
cmake/XrplInstall.cmake|
|
||||
cmake/add_module.cmake|
|
||||
cmake/isolate_headers.cmake|
|
||||
cmake/target_link_modules.cmake|
|
||||
cmake/target_protobuf_sources.cmake|
|
||||
tests/conan/CMakeLists.txt
|
||||
)$
|
||||
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
|
||||
hooks:
|
||||
|
||||
136
CMakeLists.txt
136
CMakeLists.txt
@@ -1,16 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if(POLICY CMP0074)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0077)
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
endif()
|
||||
if (POLICY CMP0074)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
endif ()
|
||||
if (POLICY CMP0077)
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
endif ()
|
||||
|
||||
# Fix "unrecognized escape" issues when passing CMAKE_MODULE_PATH on Windows.
|
||||
if(DEFINED CMAKE_MODULE_PATH)
|
||||
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
|
||||
endif()
|
||||
if (DEFINED CMAKE_MODULE_PATH)
|
||||
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
|
||||
endif ()
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
project(xrpl)
|
||||
@@ -20,65 +20,65 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
include(CompilationEnv)
|
||||
|
||||
if(is_gcc)
|
||||
if (is_gcc)
|
||||
# GCC-specific fixes
|
||||
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
|
||||
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
|
||||
elseif(is_clang)
|
||||
elseif (is_clang)
|
||||
# Clang-specific fixes
|
||||
add_compile_options(-Wno-unknown-warning-option) # Ignore unknown warning options
|
||||
elseif(is_msvc)
|
||||
elseif (is_msvc)
|
||||
# MSVC-specific fixes
|
||||
add_compile_options(/wd4068) # Ignore unknown pragmas
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Enable ccache to speed up builds.
|
||||
include(Ccache)
|
||||
|
||||
# make GIT_COMMIT_HASH define available to all sources
|
||||
find_package(Git)
|
||||
if(Git_FOUND)
|
||||
if (Git_FOUND)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
|
||||
if(gch)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
|
||||
if (gch)
|
||||
set(GIT_COMMIT_HASH "${gch}")
|
||||
message(STATUS gch: ${GIT_COMMIT_HASH})
|
||||
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git rev-parse --abbrev-ref HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
|
||||
if(gb)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gb)
|
||||
if (gb)
|
||||
set(GIT_BRANCH "${gb}")
|
||||
message(STATUS gb: ${GIT_BRANCH})
|
||||
add_definitions(-DGIT_BRANCH="${GIT_BRANCH}")
|
||||
endif()
|
||||
endif() #git
|
||||
endif ()
|
||||
endif () # git
|
||||
|
||||
if(thread_safety_analysis)
|
||||
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DXRPL_ENABLE_THREAD_SAFETY_ANNOTATIONS)
|
||||
add_compile_options("-stdlib=libc++")
|
||||
add_link_options("-stdlib=libc++")
|
||||
endif()
|
||||
if (thread_safety_analysis)
|
||||
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
|
||||
-DXRPL_ENABLE_THREAD_SAFETY_ANNOTATIONS)
|
||||
add_compile_options("-stdlib=libc++")
|
||||
add_link_options("-stdlib=libc++")
|
||||
endif ()
|
||||
|
||||
include (CheckCXXCompilerFlag)
|
||||
include (FetchContent)
|
||||
include (ExternalProject)
|
||||
include (CMakeFuncs) # must come *after* ExternalProject b/c it overrides one function in EP
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(FetchContent)
|
||||
include(ExternalProject)
|
||||
include(CMakeFuncs) # must come *after* ExternalProject b/c it overrides one function in EP
|
||||
if (target)
|
||||
message (FATAL_ERROR "The target option has been removed - use native cmake options to control build")
|
||||
message(FATAL_ERROR "The target option has been removed - use native cmake options to control build")
|
||||
endif ()
|
||||
|
||||
include(XrplSanity)
|
||||
include(XrplVersion)
|
||||
include(XrplSettings)
|
||||
# this check has to remain in the top-level cmake
|
||||
# because of the early return statement
|
||||
# this check has to remain in the top-level cmake because of the early return statement
|
||||
if (packages_only)
|
||||
if (NOT TARGET rpm)
|
||||
message (FATAL_ERROR "packages_only requested, but targets were not created - is docker installed?")
|
||||
endif()
|
||||
return ()
|
||||
if (NOT TARGET rpm)
|
||||
message(FATAL_ERROR "packages_only requested, but targets were not created - is docker installed?")
|
||||
endif ()
|
||||
return()
|
||||
endif ()
|
||||
include(XrplCompiler)
|
||||
include(XrplSanitizers)
|
||||
@@ -86,11 +86,9 @@ include(XrplInterface)
|
||||
|
||||
option(only_docs "Include only the docs target?" FALSE)
|
||||
include(XrplDocs)
|
||||
if(only_docs)
|
||||
return()
|
||||
endif()
|
||||
|
||||
###
|
||||
if (only_docs)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
include(deps/Boost)
|
||||
|
||||
@@ -107,45 +105,43 @@ find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
|
||||
target_link_libraries(xrpl_libs INTERFACE
|
||||
ed25519::ed25519
|
||||
lz4::lz4
|
||||
OpenSSL::Crypto
|
||||
OpenSSL::SSL
|
||||
secp256k1::secp256k1
|
||||
soci::soci
|
||||
SQLite::SQLite3
|
||||
)
|
||||
target_link_libraries(
|
||||
xrpl_libs
|
||||
INTERFACE ed25519::ed25519
|
||||
lz4::lz4
|
||||
OpenSSL::Crypto
|
||||
OpenSSL::SSL
|
||||
secp256k1::secp256k1
|
||||
soci::soci
|
||||
SQLite::SQLite3)
|
||||
|
||||
option(rocksdb "Enable RocksDB" ON)
|
||||
if(rocksdb)
|
||||
if (rocksdb)
|
||||
find_package(RocksDB REQUIRED)
|
||||
set_target_properties(RocksDB::rocksdb PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS XRPL_ROCKSDB_AVAILABLE=1
|
||||
)
|
||||
set_target_properties(RocksDB::rocksdb PROPERTIES INTERFACE_COMPILE_DEFINITIONS XRPL_ROCKSDB_AVAILABLE=1)
|
||||
target_link_libraries(xrpl_libs INTERFACE RocksDB::rocksdb)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Work around changes to Conan recipe for now.
|
||||
if(TARGET nudb::core)
|
||||
set(nudb nudb::core)
|
||||
elseif(TARGET NuDB::nudb)
|
||||
set(nudb NuDB::nudb)
|
||||
else()
|
||||
message(FATAL_ERROR "unknown nudb target")
|
||||
endif()
|
||||
if (TARGET nudb::core)
|
||||
set(nudb nudb::core)
|
||||
elseif (TARGET NuDB::nudb)
|
||||
set(nudb NuDB::nudb)
|
||||
else ()
|
||||
message(FATAL_ERROR "unknown nudb target")
|
||||
endif ()
|
||||
target_link_libraries(xrpl_libs INTERFACE ${nudb})
|
||||
|
||||
if(coverage)
|
||||
include(XrplCov)
|
||||
endif()
|
||||
if (coverage)
|
||||
include(XrplCov)
|
||||
endif ()
|
||||
|
||||
set(PROJECT_EXPORT_SET XrplExports)
|
||||
include(XrplCore)
|
||||
include(XrplInstall)
|
||||
include(XrplValidatorKeys)
|
||||
|
||||
if(tests)
|
||||
include(CTest)
|
||||
add_subdirectory(src/tests/libxrpl)
|
||||
endif()
|
||||
if (tests)
|
||||
include(CTest)
|
||||
add_subdirectory(src/tests/libxrpl)
|
||||
endif ()
|
||||
|
||||
@@ -940,7 +940,23 @@
|
||||
#
|
||||
# path Location to store the database
|
||||
#
|
||||
# Optional keys for NuDB and RocksDB:
|
||||
# Optional keys
|
||||
#
|
||||
# cache_size Size of cache for database records. Default is 16384.
|
||||
# Setting this value to 0 will use the default value.
|
||||
#
|
||||
# cache_age Length of time in minutes to keep database records
|
||||
# cached. Default is 5 minutes. Setting this value to
|
||||
# 0 will use the default value.
|
||||
#
|
||||
# Note: if neither cache_size nor cache_age is
|
||||
# specified, the cache for database records will not
|
||||
# be created. If only one of cache_size or cache_age
|
||||
# is specified, the cache will be created using the
|
||||
# default value for the unspecified parameter.
|
||||
#
|
||||
# Note: the cache will not be created if online_delete
|
||||
# is specified.
|
||||
#
|
||||
# fast_load Boolean. If set, load the last persisted ledger
|
||||
# from disk upon process start before syncing to
|
||||
@@ -948,6 +964,8 @@
|
||||
# if sufficient IOPS capacity is available.
|
||||
# Default 0.
|
||||
#
|
||||
# Optional keys for NuDB or RocksDB:
|
||||
#
|
||||
# earliest_seq The default is 32570 to match the XRP ledger
|
||||
# network's earliest allowed sequence. Alternate
|
||||
# networks may set this value. Minimum value of 1.
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
macro (exclude_from_default target_)
|
||||
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)
|
||||
set_target_properties(${target_} PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||
set_target_properties(${target_} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)
|
||||
endmacro ()
|
||||
|
||||
macro (exclude_if_included target_)
|
||||
get_directory_property(has_parent PARENT_DIRECTORY)
|
||||
if (has_parent)
|
||||
exclude_from_default (${target_})
|
||||
endif ()
|
||||
get_directory_property(has_parent PARENT_DIRECTORY)
|
||||
if (has_parent)
|
||||
exclude_from_default(${target_})
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
function (git_branch branch_val)
|
||||
if (NOT GIT_FOUND)
|
||||
return ()
|
||||
endif ()
|
||||
set (_branch "")
|
||||
execute_process (COMMAND ${GIT_EXECUTABLE} "rev-parse" "--abbrev-ref" "HEAD"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE _git_exit_code
|
||||
OUTPUT_VARIABLE _temp_branch
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET)
|
||||
if (_git_exit_code EQUAL 0)
|
||||
set (_branch ${_temp_branch})
|
||||
endif ()
|
||||
set (${branch_val} "${_branch}" PARENT_SCOPE)
|
||||
if (NOT GIT_FOUND)
|
||||
return()
|
||||
endif ()
|
||||
set(_branch "")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} "rev-parse" "--abbrev-ref" "HEAD"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE _git_exit_code
|
||||
OUTPUT_VARIABLE _temp_branch
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
|
||||
if (_git_exit_code EQUAL 0)
|
||||
set(_branch ${_temp_branch})
|
||||
endif ()
|
||||
set(${branch_val} "${_branch}" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
@@ -15,18 +15,17 @@ endif ()
|
||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio#usage-with-cmake.
|
||||
if ("${CCACHE_PATH}" MATCHES "chocolatey")
|
||||
message(DEBUG "Ccache path: ${CCACHE_PATH}")
|
||||
# Chocolatey uses a shim executable that we cannot use directly, in which
|
||||
# case we have to find the executable it points to. If we cannot find the
|
||||
# target executable then we cannot use ccache.
|
||||
# Chocolatey uses a shim executable that we cannot use directly, in which case we have to find the executable it
|
||||
# points to. If we cannot find the target executable then we cannot use ccache.
|
||||
find_program(BASH_PATH "bash")
|
||||
if (NOT BASH_PATH)
|
||||
message(WARNING "Could not find bash.")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
execute_process(
|
||||
COMMAND bash -c "export LC_ALL='en_US.UTF-8'; ${CCACHE_PATH} --shimgen-noop | grep -oP 'path to executable: \\K.+' | head -c -1"
|
||||
OUTPUT_VARIABLE CCACHE_PATH)
|
||||
execute_process(COMMAND bash -c
|
||||
"export LC_ALL='en_US.UTF-8'; ${CCACHE_PATH} --shimgen-noop | grep -oP 'path to executable: \\K.+' | head -c -1"
|
||||
OUTPUT_VARIABLE CCACHE_PATH)
|
||||
|
||||
if (NOT CCACHE_PATH)
|
||||
message(WARNING "Could not find ccache target.")
|
||||
@@ -37,21 +36,14 @@ endif ()
|
||||
message(STATUS "Found ccache: ${CCACHE_PATH}")
|
||||
|
||||
# Tell cmake to use ccache for compiling with Visual Studio.
|
||||
file(COPY_FILE
|
||||
${CCACHE_PATH} ${CMAKE_BINARY_DIR}/cl.exe
|
||||
ONLY_IF_DIFFERENT)
|
||||
set(CMAKE_VS_GLOBALS
|
||||
"CLToolExe=cl.exe"
|
||||
"CLToolPath=${CMAKE_BINARY_DIR}"
|
||||
"TrackFileAccess=false"
|
||||
"UseMultiToolTask=true")
|
||||
file(COPY_FILE ${CCACHE_PATH} ${CMAKE_BINARY_DIR}/cl.exe ONLY_IF_DIFFERENT)
|
||||
set(CMAKE_VS_GLOBALS "CLToolExe=cl.exe" "CLToolPath=${CMAKE_BINARY_DIR}" "TrackFileAccess=false"
|
||||
"UseMultiToolTask=true")
|
||||
|
||||
# By default Visual Studio generators will use /Zi to capture debug information,
|
||||
# which is not compatible with ccache, so tell it to use /Z7 instead.
|
||||
# By default Visual Studio generators will use /Zi to capture debug information, which is not compatible with ccache, so
|
||||
# tell it to use /Z7 instead.
|
||||
if (MSVC)
|
||||
foreach (var_
|
||||
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
|
||||
string (REPLACE "/Zi" "/Z7" ${var_} "${${var_}}")
|
||||
foreach (var_ CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
|
||||
string(REPLACE "/Zi" "/Z7" ${var_} "${${var_}}")
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# Shared detection of compiler, operating system, and architecture.
|
||||
#
|
||||
# This module centralizes environment detection so that other
|
||||
# CMake modules can use the same variables instead of repeating
|
||||
# checks on CMAKE_* and built-in platform variables.
|
||||
# Shared detection of compiler, operating system, and architecture.
|
||||
#
|
||||
# This module centralizes environment detection so that other CMake modules can use the same variables instead of
|
||||
# repeating checks on CMAKE_* and built-in platform variables.
|
||||
|
||||
# Only run once per configure step.
|
||||
include_guard(GLOBAL)
|
||||
@@ -15,21 +14,20 @@ set(is_gcc FALSE)
|
||||
set(is_msvc FALSE)
|
||||
set(is_xcode FALSE)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
|
||||
set(is_clang TRUE)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(is_gcc TRUE)
|
||||
elseif(MSVC)
|
||||
set(is_msvc TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
|
||||
set(is_clang TRUE)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(is_gcc TRUE)
|
||||
elseif (MSVC)
|
||||
set(is_msvc TRUE)
|
||||
else ()
|
||||
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif ()
|
||||
|
||||
# Xcode generator detection
|
||||
if(CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(is_xcode TRUE)
|
||||
endif()
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(is_xcode TRUE)
|
||||
endif ()
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Operating system detection
|
||||
@@ -38,23 +36,23 @@ set(is_linux FALSE)
|
||||
set(is_windows FALSE)
|
||||
set(is_macos FALSE)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(is_linux TRUE)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(is_windows TRUE)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(is_macos TRUE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(is_linux TRUE)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(is_windows TRUE)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(is_macos TRUE)
|
||||
endif ()
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Architecture
|
||||
# --------------------------------------------------------------------
|
||||
set(is_amd64 FALSE)
|
||||
set(is_arm64 FALSE)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
||||
set(is_amd64 TRUE)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
|
||||
set(is_arm64 TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
||||
set(is_amd64 TRUE)
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
|
||||
set(is_arm64 TRUE)
|
||||
else ()
|
||||
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif ()
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
include(isolate_headers)
|
||||
|
||||
function(xrpl_add_test name)
|
||||
set(target ${PROJECT_NAME}.test.${name})
|
||||
function (xrpl_add_test name)
|
||||
set(target ${PROJECT_NAME}.test.${name})
|
||||
|
||||
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
|
||||
)
|
||||
add_executable(${target} ${ARGN} ${sources})
|
||||
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp")
|
||||
add_executable(${target} ${ARGN} ${sources})
|
||||
|
||||
isolate_headers(
|
||||
${target}
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/tests/${name}"
|
||||
PRIVATE
|
||||
)
|
||||
isolate_headers(${target} "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/tests/${name}" PRIVATE)
|
||||
|
||||
# Make sure the test isn't optimized away in unity builds
|
||||
set_target_properties(${target} PROPERTIES
|
||||
UNITY_BUILD_MODE GROUP
|
||||
UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
|
||||
# Make sure the test isn't optimized away in unity builds
|
||||
set_target_properties(${target} PROPERTIES UNITY_BUILD_MODE GROUP UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
|
||||
|
||||
add_test(NAME ${target} COMMAND ${target})
|
||||
endfunction()
|
||||
add_test(NAME ${target} COMMAND ${target})
|
||||
endfunction ()
|
||||
|
||||
@@ -8,153 +8,155 @@ include(CompilationEnv)
|
||||
TODO some/most of these common settings belong in a
|
||||
toolchain file, especially the ABI-impacting ones
|
||||
#]=========================================================]
|
||||
add_library (common INTERFACE)
|
||||
add_library (Xrpl::common ALIAS common)
|
||||
add_library(common INTERFACE)
|
||||
add_library(Xrpl::common ALIAS common)
|
||||
include(XrplSanitizers)
|
||||
# add a single global dependency on this interface lib
|
||||
link_libraries (Xrpl::common)
|
||||
link_libraries(Xrpl::common)
|
||||
# Respect CMAKE_POSITION_INDEPENDENT_CODE setting (may be set by Conan toolchain)
|
||||
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
set_target_properties (common
|
||||
PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
|
||||
if (NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif ()
|
||||
set_target_properties(common PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
target_compile_definitions (common
|
||||
INTERFACE
|
||||
$<$<CONFIG:Debug>:DEBUG _DEBUG>
|
||||
#[===[
|
||||
target_compile_definitions(
|
||||
common
|
||||
INTERFACE $<$<CONFIG:Debug>:DEBUG
|
||||
_DEBUG>
|
||||
#[===[
|
||||
NOTE: CMAKE release builds already have NDEBUG defined, so no need to add it
|
||||
explicitly except for the special case of (profile ON) and (assert OFF).
|
||||
Presumably this is because we don't want profile builds asserting unless
|
||||
asserts were specifically requested.
|
||||
]===]
|
||||
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>
|
||||
# TODO: Remove once we have migrated functions from OpenSSL 1.x to 3.x.
|
||||
OPENSSL_SUPPRESS_DEPRECATED
|
||||
)
|
||||
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>
|
||||
# TODO: Remove once we have migrated functions from OpenSSL 1.x to 3.x.
|
||||
OPENSSL_SUPPRESS_DEPRECATED)
|
||||
|
||||
if (MSVC)
|
||||
# remove existing exception flag since we set it to -EHa
|
||||
string (REGEX REPLACE "[-/]EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
# remove existing exception flag since we set it to -EHa
|
||||
string(REGEX REPLACE "[-/]EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
|
||||
foreach (var_
|
||||
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
|
||||
foreach (var_ CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
|
||||
|
||||
# also remove dynamic runtime
|
||||
string (REGEX REPLACE "[-/]MD[d]*" " " ${var_} "${${var_}}")
|
||||
# also remove dynamic runtime
|
||||
string(REGEX REPLACE "[-/]MD[d]*" " " ${var_} "${${var_}}")
|
||||
|
||||
# /ZI (Edit & Continue debugging information) is incompatible with Gy-
|
||||
string (REPLACE "/ZI" "/Zi" ${var_} "${${var_}}")
|
||||
# /ZI (Edit & Continue debugging information) is incompatible with Gy-
|
||||
string(REPLACE "/ZI" "/Zi" ${var_} "${${var_}}")
|
||||
|
||||
# omit debug info completely under CI (not needed)
|
||||
if (is_ci)
|
||||
string (REPLACE "/Zi" " " ${var_} "${${var_}}")
|
||||
string (REPLACE "/Z7" " " ${var_} "${${var_}}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
# omit debug info completely under CI (not needed)
|
||||
if (is_ci)
|
||||
string(REPLACE "/Zi" " " ${var_} "${${var_}}")
|
||||
string(REPLACE "/Z7" " " ${var_} "${${var_}}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
target_compile_options (common
|
||||
INTERFACE
|
||||
-bigobj # Increase object file max size
|
||||
-fp:precise # Floating point behavior
|
||||
-Gd # __cdecl calling convention
|
||||
-Gm- # Minimal rebuild: disabled
|
||||
-Gy- # Function level linking: disabled
|
||||
-MP # Multiprocessor compilation
|
||||
-openmp- # pragma omp: disabled
|
||||
-errorReport:none # No error reporting to Internet
|
||||
-nologo # Suppress login banner
|
||||
-wd4018 # Disable signed/unsigned comparison warnings
|
||||
-wd4244 # Disable float to int possible loss of data warnings
|
||||
-wd4267 # Disable size_t to T possible loss of data warnings
|
||||
-wd4800 # Disable C4800(int to bool performance)
|
||||
-wd4503 # Decorated name length exceeded, name was truncated
|
||||
$<$<COMPILE_LANGUAGE:CXX>:
|
||||
-EHa
|
||||
-GR
|
||||
>
|
||||
$<$<CONFIG:Release>:-Ox>
|
||||
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:
|
||||
-GS
|
||||
-Zc:forScope
|
||||
>
|
||||
# static runtime
|
||||
$<$<CONFIG:Debug>:-MTd>
|
||||
$<$<NOT:$<CONFIG:Debug>>:-MT>
|
||||
$<$<BOOL:${werr}>:-WX>
|
||||
)
|
||||
target_compile_definitions (common
|
||||
INTERFACE
|
||||
_WIN32_WINNT=0x6000
|
||||
_SCL_SECURE_NO_WARNINGS
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
WIN32_CONSOLE
|
||||
WIN32_LEAN_AND_MEAN
|
||||
NOMINMAX
|
||||
# TODO: Resolve these warnings, don't just silence them
|
||||
_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
|
||||
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:_CRTDBG_MAP_ALLOC>)
|
||||
target_link_libraries (common
|
||||
INTERFACE
|
||||
-errorreport:none
|
||||
-machine:X64)
|
||||
target_compile_options(
|
||||
common
|
||||
INTERFACE # Increase object file max size
|
||||
-bigobj
|
||||
# Floating point behavior
|
||||
-fp:precise
|
||||
# __cdecl calling convention
|
||||
-Gd
|
||||
# Minimal rebuild: disabled
|
||||
-Gm-
|
||||
# Function level linking: disabled
|
||||
-Gy-
|
||||
# Multiprocessor compilation
|
||||
-MP
|
||||
# pragma omp: disabled
|
||||
-openmp-
|
||||
# No error reporting to Internet
|
||||
-errorReport:none
|
||||
# Suppress login banner
|
||||
-nologo
|
||||
# Disable signed/unsigned comparison warnings
|
||||
-wd4018
|
||||
# Disable float to int possible loss of data warnings
|
||||
-wd4244
|
||||
# Disable size_t to T possible loss of data warnings
|
||||
-wd4267
|
||||
# Disable C4800(int to bool performance)
|
||||
-wd4800
|
||||
# Decorated name length exceeded, name was truncated
|
||||
-wd4503
|
||||
$<$<COMPILE_LANGUAGE:CXX>:
|
||||
-EHa
|
||||
-GR
|
||||
>
|
||||
$<$<CONFIG:Release>:-Ox>
|
||||
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:
|
||||
-GS
|
||||
-Zc:forScope
|
||||
>
|
||||
# static runtime
|
||||
$<$<CONFIG:Debug>:-MTd>
|
||||
$<$<NOT:$<CONFIG:Debug>>:-MT>
|
||||
$<$<BOOL:${werr}>:-WX>)
|
||||
target_compile_definitions(
|
||||
common
|
||||
INTERFACE _WIN32_WINNT=0x6000
|
||||
_SCL_SECURE_NO_WARNINGS
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
WIN32_CONSOLE
|
||||
WIN32_LEAN_AND_MEAN
|
||||
NOMINMAX
|
||||
# TODO: Resolve these warnings, don't just silence them
|
||||
_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
|
||||
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:_CRTDBG_MAP_ALLOC>)
|
||||
target_link_libraries(common INTERFACE -errorreport:none -machine:X64)
|
||||
else ()
|
||||
target_compile_options (common
|
||||
INTERFACE
|
||||
-Wall
|
||||
-Wdeprecated
|
||||
$<$<BOOL:${is_clang}>:-Wno-deprecated-declarations>
|
||||
$<$<BOOL:${wextra}>:-Wextra -Wno-unused-parameter>
|
||||
$<$<BOOL:${werr}>:-Werror>
|
||||
-fstack-protector
|
||||
-Wno-sign-compare
|
||||
-Wno-unused-but-set-variable
|
||||
$<$<NOT:$<CONFIG:Debug>>:-fno-strict-aliasing>
|
||||
# tweak gcc optimization for debug
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<CONFIG:Debug>>:-O0>
|
||||
# Add debug symbols to release config
|
||||
$<$<CONFIG:Release>:-g>)
|
||||
target_link_libraries (common
|
||||
INTERFACE
|
||||
-rdynamic
|
||||
$<$<BOOL:${is_linux}>:-Wl,-z,relro,-z,now,--build-id>
|
||||
# link to static libc/c++ iff:
|
||||
# * static option set and
|
||||
# * NOT APPLE (AppleClang does not support static libc/c++) and
|
||||
# * NOT SANITIZERS (sanitizers typically don't work with static libc/c++)
|
||||
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${SANITIZERS_ENABLED}>>>:
|
||||
-static-libstdc++
|
||||
-static-libgcc
|
||||
>)
|
||||
target_compile_options(
|
||||
common
|
||||
INTERFACE -Wall
|
||||
-Wdeprecated
|
||||
$<$<BOOL:${is_clang}>:-Wno-deprecated-declarations>
|
||||
$<$<BOOL:${wextra}>:-Wextra
|
||||
-Wno-unused-parameter>
|
||||
$<$<BOOL:${werr}>:-Werror>
|
||||
-fstack-protector
|
||||
-Wno-sign-compare
|
||||
-Wno-unused-but-set-variable
|
||||
$<$<NOT:$<CONFIG:Debug>>:-fno-strict-aliasing>
|
||||
# tweak gcc optimization for debug
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<CONFIG:Debug>>:-O0>
|
||||
# Add debug symbols to release config
|
||||
$<$<CONFIG:Release>:-g>)
|
||||
target_link_libraries(
|
||||
common
|
||||
INTERFACE -rdynamic
|
||||
$<$<BOOL:${is_linux}>:-Wl,-z,relro,-z,now,--build-id>
|
||||
# link to static libc/c++ iff: * static option set and * NOT APPLE (AppleClang does not support static
|
||||
# libc/c++) and * NOT SANITIZERS (sanitizers typically don't work with static libc/c++)
|
||||
$<$<AND:$<BOOL:${static}>,$<NOT:$<BOOL:${APPLE}>>,$<NOT:$<BOOL:${SANITIZERS_ENABLED}>>>:
|
||||
-static-libstdc++
|
||||
-static-libgcc
|
||||
>)
|
||||
endif ()
|
||||
|
||||
# Antithesis instrumentation will only be built and deployed using machines running Linux.
|
||||
if (voidstar)
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(FATAL_ERROR "Antithesis instrumentation requires Debug build type, aborting...")
|
||||
elseif (NOT is_linux)
|
||||
message(FATAL_ERROR "Antithesis instrumentation requires Linux, aborting...")
|
||||
elseif (NOT (is_clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0))
|
||||
message(FATAL_ERROR "Antithesis instrumentation requires Clang version 16 or later, aborting...")
|
||||
endif ()
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(FATAL_ERROR "Antithesis instrumentation requires Debug build type, aborting...")
|
||||
elseif (NOT is_linux)
|
||||
message(FATAL_ERROR "Antithesis instrumentation requires Linux, aborting...")
|
||||
elseif (NOT (is_clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0))
|
||||
message(FATAL_ERROR "Antithesis instrumentation requires Clang version 16 or later, aborting...")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (use_mold)
|
||||
# use mold linker if available
|
||||
execute_process (
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=mold -Wl,--version
|
||||
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
if ("${LD_VERSION}" MATCHES "mold")
|
||||
target_link_libraries (common INTERFACE -fuse-ld=mold)
|
||||
endif ()
|
||||
unset (LD_VERSION)
|
||||
# use mold linker if available
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=mold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
if ("${LD_VERSION}" MATCHES "mold")
|
||||
target_link_libraries(common INTERFACE -fuse-ld=mold)
|
||||
endif ()
|
||||
unset(LD_VERSION)
|
||||
elseif (use_gold AND is_gcc)
|
||||
# use gold linker if available
|
||||
execute_process (
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version
|
||||
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
# use gold linker if available
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
#[=========================================================[
|
||||
NOTE: THE gold linker inserts -rpath as DT_RUNPATH by
|
||||
default instead of DT_RPATH, so you might have slightly
|
||||
@@ -168,34 +170,31 @@ elseif (use_gold AND is_gcc)
|
||||
disabling would be to figure out all the settings
|
||||
required to make gold play nicely with jemalloc.
|
||||
#]=========================================================]
|
||||
if (("${LD_VERSION}" MATCHES "GNU gold") AND (NOT jemalloc))
|
||||
target_link_libraries (common
|
||||
INTERFACE
|
||||
-fuse-ld=gold
|
||||
-Wl,--no-as-needed
|
||||
#[=========================================================[
|
||||
if (("${LD_VERSION}" MATCHES "GNU gold") AND (NOT jemalloc))
|
||||
target_link_libraries(
|
||||
common
|
||||
INTERFACE -fuse-ld=gold
|
||||
-Wl,--no-as-needed
|
||||
#[=========================================================[
|
||||
see https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/1253638/comments/5
|
||||
DT_RUNPATH does not work great for transitive
|
||||
dependencies (of which boost has a few) - so just
|
||||
switch to DT_RPATH if doing dynamic linking with gold
|
||||
#]=========================================================]
|
||||
$<$<NOT:$<BOOL:${static}>>:-Wl,--disable-new-dtags>)
|
||||
endif ()
|
||||
unset (LD_VERSION)
|
||||
$<$<NOT:$<BOOL:${static}>>:-Wl,--disable-new-dtags>)
|
||||
endif ()
|
||||
unset(LD_VERSION)
|
||||
elseif (use_lld)
|
||||
# use lld linker if available
|
||||
execute_process (
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=lld -Wl,--version
|
||||
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
if ("${LD_VERSION}" MATCHES "LLD")
|
||||
target_link_libraries (common INTERFACE -fuse-ld=lld)
|
||||
endif ()
|
||||
unset (LD_VERSION)
|
||||
endif()
|
||||
|
||||
# use lld linker if available
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=lld -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
if ("${LD_VERSION}" MATCHES "LLD")
|
||||
target_link_libraries(common INTERFACE -fuse-ld=lld)
|
||||
endif ()
|
||||
unset(LD_VERSION)
|
||||
endif ()
|
||||
|
||||
if (assert)
|
||||
foreach (var_ CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE)
|
||||
STRING (REGEX REPLACE "[-/]DNDEBUG" "" ${var_} "${${var_}}")
|
||||
endforeach ()
|
||||
foreach (var_ CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE)
|
||||
string(REGEX REPLACE "[-/]DNDEBUG" "" ${var_} "${${var_}}")
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
@@ -1,54 +1,52 @@
|
||||
include (CMakeFindDependencyMacro)
|
||||
include(CMakeFindDependencyMacro)
|
||||
# need to represent system dependencies of the lib here
|
||||
#[=========================================================[
|
||||
Boost
|
||||
#]=========================================================]
|
||||
if (static OR APPLE OR MSVC)
|
||||
set (Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif ()
|
||||
set (Boost_USE_MULTITHREADED ON)
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
if (static OR MSVC)
|
||||
set (Boost_USE_STATIC_RUNTIME ON)
|
||||
set(Boost_USE_STATIC_RUNTIME ON)
|
||||
else ()
|
||||
set (Boost_USE_STATIC_RUNTIME OFF)
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
endif ()
|
||||
find_dependency (Boost
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
context
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread)
|
||||
find_dependency(Boost
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
context
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread)
|
||||
#[=========================================================[
|
||||
OpenSSL
|
||||
#]=========================================================]
|
||||
if (NOT DEFINED OPENSSL_ROOT_DIR)
|
||||
if (DEFINED ENV{OPENSSL_ROOT})
|
||||
set (OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT})
|
||||
elseif (APPLE)
|
||||
find_program (homebrew brew)
|
||||
if (homebrew)
|
||||
execute_process (COMMAND ${homebrew} --prefix openssl
|
||||
OUTPUT_VARIABLE OPENSSL_ROOT_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (DEFINED ENV{OPENSSL_ROOT})
|
||||
set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT})
|
||||
elseif (APPLE)
|
||||
find_program(homebrew brew)
|
||||
if (homebrew)
|
||||
execute_process(COMMAND ${homebrew} --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
file (TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" OPENSSL_ROOT_DIR)
|
||||
file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" OPENSSL_ROOT_DIR)
|
||||
endif ()
|
||||
|
||||
if (static OR APPLE OR MSVC)
|
||||
set (OPENSSL_USE_STATIC_LIBS ON)
|
||||
set(OPENSSL_USE_STATIC_LIBS ON)
|
||||
endif ()
|
||||
set (OPENSSL_MSVC_STATIC_RT ON)
|
||||
find_dependency (OpenSSL REQUIRED)
|
||||
find_dependency (ZLIB)
|
||||
find_dependency (date)
|
||||
set(OPENSSL_MSVC_STATIC_RT ON)
|
||||
find_dependency(OpenSSL REQUIRED)
|
||||
find_dependency(ZLIB)
|
||||
find_dependency(date)
|
||||
if (TARGET ZLIB::ZLIB)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
|
||||
endif ()
|
||||
|
||||
@@ -2,40 +2,51 @@
|
||||
coverage report target
|
||||
#]===================================================================]
|
||||
|
||||
if(NOT coverage)
|
||||
message(FATAL_ERROR "Code coverage not enabled! Aborting ...")
|
||||
endif()
|
||||
if (NOT coverage)
|
||||
message(FATAL_ERROR "Code coverage not enabled! Aborting ...")
|
||||
endif ()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
message(WARNING "Code coverage on Windows is not supported, ignoring 'coverage' flag")
|
||||
return()
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
message(WARNING "Code coverage on Windows is not supported, ignoring 'coverage' flag")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(PROCESSOR_COUNT)
|
||||
|
||||
include(CodeCoverage)
|
||||
|
||||
# The instructions for these commands come from the `CodeCoverage` module,
|
||||
# which was copied from https://github.com/bilke/cmake-modules, commit fb7d2a3,
|
||||
# then locally changed (see CHANGES: section in `CodeCoverage.cmake`)
|
||||
# The instructions for these commands come from the `CodeCoverage` module, which was copied from
|
||||
# https://github.com/bilke/cmake-modules, commit fb7d2a3, then locally changed (see CHANGES: section in
|
||||
# `CodeCoverage.cmake`)
|
||||
|
||||
set(GCOVR_ADDITIONAL_ARGS ${coverage_extra_args})
|
||||
if(NOT GCOVR_ADDITIONAL_ARGS STREQUAL "")
|
||||
separate_arguments(GCOVR_ADDITIONAL_ARGS)
|
||||
endif()
|
||||
if (NOT GCOVR_ADDITIONAL_ARGS STREQUAL "")
|
||||
separate_arguments(GCOVR_ADDITIONAL_ARGS)
|
||||
endif ()
|
||||
|
||||
list(APPEND GCOVR_ADDITIONAL_ARGS
|
||||
--exclude-throw-branches
|
||||
--exclude-noncode-lines
|
||||
--exclude-unreachable-branches -s
|
||||
-j ${PROCESSOR_COUNT})
|
||||
list(APPEND
|
||||
GCOVR_ADDITIONAL_ARGS
|
||||
--exclude-throw-branches
|
||||
--exclude-noncode-lines
|
||||
--exclude-unreachable-branches
|
||||
-s
|
||||
-j
|
||||
${PROCESSOR_COUNT})
|
||||
|
||||
setup_target_for_coverage_gcovr(
|
||||
NAME coverage
|
||||
FORMAT ${coverage_format}
|
||||
EXCLUDE "src/test" "src/tests" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
|
||||
DEPENDENCIES xrpld xrpl.tests
|
||||
)
|
||||
NAME
|
||||
coverage
|
||||
FORMAT
|
||||
${coverage_format}
|
||||
EXCLUDE
|
||||
"src/test"
|
||||
"src/tests"
|
||||
"include/xrpl/beast/test"
|
||||
"include/xrpl/beast/unit_test"
|
||||
"${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
|
||||
DEPENDENCIES
|
||||
xrpld
|
||||
xrpl.tests)
|
||||
|
||||
add_code_coverage_to_target(opts INTERFACE)
|
||||
|
||||
@@ -5,84 +5,79 @@
|
||||
include(CompilationEnv)
|
||||
|
||||
# Set defaults for optional variables to avoid uninitialized variable warnings
|
||||
if(NOT DEFINED voidstar)
|
||||
set(voidstar OFF)
|
||||
endif()
|
||||
if (NOT DEFINED voidstar)
|
||||
set(voidstar OFF)
|
||||
endif ()
|
||||
|
||||
add_library (opts INTERFACE)
|
||||
add_library (Xrpl::opts ALIAS opts)
|
||||
target_compile_definitions (opts
|
||||
INTERFACE
|
||||
BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
|
||||
BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
|
||||
BOOST_CONTAINER_FWD_BAD_DEQUE
|
||||
HAS_UNCAUGHT_EXCEPTIONS=1
|
||||
$<$<BOOL:${boost_show_deprecated}>:
|
||||
BOOST_ASIO_NO_DEPRECATED
|
||||
BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
>
|
||||
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
|
||||
BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||
BOOST_BEAST_ALLOW_DEPRECATED
|
||||
BOOST_FILESYSTEM_DEPRECATED
|
||||
>
|
||||
$<$<BOOL:${beast_no_unit_test_inline}>:BEAST_NO_UNIT_TEST_INLINE=1>
|
||||
$<$<BOOL:${beast_disable_autolink}>:BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES=1>
|
||||
$<$<BOOL:${single_io_service_thread}>:XRPL_SINGLE_IO_SERVICE_THREAD=1>
|
||||
$<$<BOOL:${voidstar}>:ENABLE_VOIDSTAR>)
|
||||
target_compile_options (opts
|
||||
INTERFACE
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
|
||||
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized>
|
||||
$<$<BOOL:${perf}>:-fno-omit-frame-pointer>
|
||||
$<$<BOOL:${profile}>:-pg>
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
|
||||
add_library(opts INTERFACE)
|
||||
add_library(Xrpl::opts ALIAS opts)
|
||||
target_compile_definitions(
|
||||
opts
|
||||
INTERFACE BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
|
||||
BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
|
||||
BOOST_CONTAINER_FWD_BAD_DEQUE
|
||||
HAS_UNCAUGHT_EXCEPTIONS=1
|
||||
$<$<BOOL:${boost_show_deprecated}>:
|
||||
BOOST_ASIO_NO_DEPRECATED
|
||||
BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
>
|
||||
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
|
||||
BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||
BOOST_BEAST_ALLOW_DEPRECATED
|
||||
BOOST_FILESYSTEM_DEPRECATED
|
||||
>
|
||||
$<$<BOOL:${beast_no_unit_test_inline}>:BEAST_NO_UNIT_TEST_INLINE=1>
|
||||
$<$<BOOL:${beast_disable_autolink}>:BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES=1>
|
||||
$<$<BOOL:${single_io_service_thread}>:XRPL_SINGLE_IO_SERVICE_THREAD=1>
|
||||
$<$<BOOL:${voidstar}>:ENABLE_VOIDSTAR>)
|
||||
target_compile_options(
|
||||
opts
|
||||
INTERFACE $<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
|
||||
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized> $<$<BOOL:${perf}>:-fno-omit-frame-pointer>
|
||||
$<$<BOOL:${profile}>:-pg> $<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
|
||||
|
||||
target_link_libraries (opts
|
||||
INTERFACE
|
||||
$<$<BOOL:${profile}>:-pg>
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
|
||||
target_link_libraries(opts INTERFACE $<$<BOOL:${profile}>:-pg> $<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
|
||||
|
||||
if(jemalloc)
|
||||
find_package(jemalloc REQUIRED)
|
||||
target_compile_definitions(opts INTERFACE PROFILE_JEMALLOC)
|
||||
target_link_libraries(opts INTERFACE jemalloc::jemalloc)
|
||||
if (jemalloc)
|
||||
find_package(jemalloc REQUIRED)
|
||||
target_compile_definitions(opts INTERFACE PROFILE_JEMALLOC)
|
||||
target_link_libraries(opts INTERFACE jemalloc::jemalloc)
|
||||
endif ()
|
||||
|
||||
#[===================================================================[
|
||||
xrpld transitive library deps via an interface library
|
||||
#]===================================================================]
|
||||
|
||||
add_library (xrpl_syslibs INTERFACE)
|
||||
add_library (Xrpl::syslibs ALIAS xrpl_syslibs)
|
||||
target_link_libraries (xrpl_syslibs
|
||||
INTERFACE
|
||||
$<$<BOOL:${is_msvc}>:
|
||||
legacy_stdio_definitions.lib
|
||||
Shlwapi
|
||||
kernel32
|
||||
user32
|
||||
gdi32
|
||||
winspool
|
||||
comdlg32
|
||||
advapi32
|
||||
shell32
|
||||
ole32
|
||||
oleaut32
|
||||
uuid
|
||||
odbc32
|
||||
odbccp32
|
||||
crypt32
|
||||
>
|
||||
$<$<NOT:$<BOOL:${is_msvc}>>:dl>
|
||||
$<$<NOT:$<OR:$<BOOL:${is_msvc}>,$<BOOL:${is_macos}>>>:rt>)
|
||||
add_library(xrpl_syslibs INTERFACE)
|
||||
add_library(Xrpl::syslibs ALIAS xrpl_syslibs)
|
||||
target_link_libraries(
|
||||
xrpl_syslibs
|
||||
INTERFACE $<$<BOOL:${is_msvc}>:
|
||||
legacy_stdio_definitions.lib
|
||||
Shlwapi
|
||||
kernel32
|
||||
user32
|
||||
gdi32
|
||||
winspool
|
||||
comdlg32
|
||||
advapi32
|
||||
shell32
|
||||
ole32
|
||||
oleaut32
|
||||
uuid
|
||||
odbc32
|
||||
odbccp32
|
||||
crypt32
|
||||
>
|
||||
$<$<NOT:$<BOOL:${is_msvc}>>:dl>
|
||||
$<$<NOT:$<OR:$<BOOL:${is_msvc}>,$<BOOL:${is_macos}>>>:rt>)
|
||||
|
||||
if (NOT is_msvc)
|
||||
set (THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package (Threads)
|
||||
target_link_libraries (xrpl_syslibs INTERFACE Threads::Threads)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads)
|
||||
target_link_libraries(xrpl_syslibs INTERFACE Threads::Threads)
|
||||
endif ()
|
||||
|
||||
add_library (xrpl_libs INTERFACE)
|
||||
add_library (Xrpl::libs ALIAS xrpl_libs)
|
||||
target_link_libraries (xrpl_libs INTERFACE Xrpl::syslibs)
|
||||
add_library(xrpl_libs INTERFACE)
|
||||
add_library(Xrpl::libs ALIAS xrpl_libs)
|
||||
target_link_libraries(xrpl_libs INTERFACE Xrpl::syslibs)
|
||||
|
||||
@@ -44,23 +44,23 @@ include(CompilationEnv)
|
||||
|
||||
# Read environment variable
|
||||
set(SANITIZERS "")
|
||||
if(DEFINED ENV{SANITIZERS})
|
||||
set(SANITIZERS "$ENV{SANITIZERS}")
|
||||
endif()
|
||||
if (DEFINED ENV{SANITIZERS})
|
||||
set(SANITIZERS "$ENV{SANITIZERS}")
|
||||
endif ()
|
||||
|
||||
# Set SANITIZERS_ENABLED flag for use in other modules
|
||||
if(SANITIZERS MATCHES "address|thread|undefinedbehavior")
|
||||
if (SANITIZERS MATCHES "address|thread|undefinedbehavior")
|
||||
set(SANITIZERS_ENABLED TRUE)
|
||||
else()
|
||||
else ()
|
||||
set(SANITIZERS_ENABLED FALSE)
|
||||
return()
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Sanitizers are not supported on Windows/MSVC
|
||||
if(is_msvc)
|
||||
if (is_msvc)
|
||||
message(FATAL_ERROR "Sanitizers are not supported on Windows/MSVC. "
|
||||
"Please unset the SANITIZERS environment variable.")
|
||||
endif()
|
||||
"Please unset the SANITIZERS environment variable.")
|
||||
endif ()
|
||||
|
||||
message(STATUS "Configuring sanitizers: ${SANITIZERS}")
|
||||
|
||||
@@ -74,24 +74,24 @@ set(san_list "${SANITIZERS}")
|
||||
string(REPLACE "," ";" san_list "${san_list}")
|
||||
separate_arguments(san_list)
|
||||
|
||||
foreach(san IN LISTS san_list)
|
||||
if(san STREQUAL "address")
|
||||
foreach (san IN LISTS san_list)
|
||||
if (san STREQUAL "address")
|
||||
set(enable_asan TRUE)
|
||||
elseif(san STREQUAL "thread")
|
||||
elseif (san STREQUAL "thread")
|
||||
set(enable_tsan TRUE)
|
||||
elseif(san STREQUAL "undefinedbehavior")
|
||||
elseif (san STREQUAL "undefinedbehavior")
|
||||
set(enable_ubsan TRUE)
|
||||
else()
|
||||
else ()
|
||||
message(FATAL_ERROR "Unsupported sanitizer type: ${san}"
|
||||
"Supported: address, thread, undefinedbehavior and their combinations.")
|
||||
endif()
|
||||
endforeach()
|
||||
"Supported: address, thread, undefinedbehavior and their combinations.")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# Validate sanitizer compatibility
|
||||
if(enable_asan AND enable_tsan)
|
||||
if (enable_asan AND enable_tsan)
|
||||
message(FATAL_ERROR "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously. "
|
||||
"Use 'address' or 'thread', optionally with 'undefinedbehavior'.")
|
||||
endif()
|
||||
"Use 'address' or 'thread', optionally with 'undefinedbehavior'.")
|
||||
endif ()
|
||||
|
||||
# Frame pointer is required for meaningful stack traces. Sanitizers recommend minimum of -O1 for reasonable performance
|
||||
set(SANITIZERS_COMPILE_FLAGS "-fno-omit-frame-pointer" "-O1")
|
||||
@@ -99,31 +99,31 @@ set(SANITIZERS_COMPILE_FLAGS "-fno-omit-frame-pointer" "-O1")
|
||||
# Build the sanitizer flags list
|
||||
set(SANITIZER_TYPES)
|
||||
|
||||
if(enable_asan)
|
||||
if (enable_asan)
|
||||
list(APPEND SANITIZER_TYPES "address")
|
||||
elseif(enable_tsan)
|
||||
elseif (enable_tsan)
|
||||
list(APPEND SANITIZER_TYPES "thread")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if(enable_ubsan)
|
||||
if (enable_ubsan)
|
||||
# UB sanitizer flags
|
||||
list(APPEND SANITIZER_TYPES "undefined" "float-divide-by-zero")
|
||||
if(is_clang)
|
||||
# Clang supports additional UB checks. More info here https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
|
||||
if (is_clang)
|
||||
# Clang supports additional UB checks. More info here
|
||||
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
|
||||
list(APPEND SANITIZER_TYPES "unsigned-integer-overflow")
|
||||
endif()
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Configure code model for GCC on amd64
|
||||
# Use large code model for ASAN to avoid relocation errors
|
||||
# Use medium code model for TSAN (large is not compatible with TSAN)
|
||||
# Configure code model for GCC on amd64 Use large code model for ASAN to avoid relocation errors Use medium code model
|
||||
# for TSAN (large is not compatible with TSAN)
|
||||
set(SANITIZERS_RELOCATION_FLAGS)
|
||||
|
||||
# Compiler-specific configuration
|
||||
if(is_gcc)
|
||||
# Disable mold, gold and lld linkers for GCC with sanitizers
|
||||
# Use default linker (bfd/ld) which is more lenient with mixed code models
|
||||
# This is needed since the size of instrumented binary exceeds the limits set by mold, lld and gold linkers
|
||||
if (is_gcc)
|
||||
# Disable mold, gold and lld linkers for GCC with sanitizers Use default linker (bfd/ld) which is more lenient with
|
||||
# mixed code models This is needed since the size of instrumented binary exceeds the limits set by mold, lld and
|
||||
# gold linkers
|
||||
set(use_mold OFF CACHE BOOL "Use mold linker" FORCE)
|
||||
set(use_gold OFF CACHE BOOL "Use gold linker" FORCE)
|
||||
set(use_lld OFF CACHE BOOL "Use lld linker" FORCE)
|
||||
@@ -132,17 +132,17 @@ if(is_gcc)
|
||||
# Suppress false positive warnings in GCC with stringop-overflow
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-stringop-overflow")
|
||||
|
||||
if(is_amd64 AND enable_asan)
|
||||
if (is_amd64 AND enable_asan)
|
||||
message(STATUS " Using large code model (-mcmodel=large)")
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=large")
|
||||
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=large")
|
||||
elseif(enable_tsan)
|
||||
elseif (enable_tsan)
|
||||
# GCC doesn't support atomic_thread_fence with tsan. Suppress warnings.
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-Wno-tsan")
|
||||
message(STATUS " Using medium code model (-mcmodel=medium)")
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-mcmodel=medium")
|
||||
list(APPEND SANITIZERS_RELOCATION_FLAGS "-mcmodel=medium")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Join sanitizer flags with commas for -fsanitize option
|
||||
list(JOIN SANITIZER_TYPES "," SANITIZER_TYPES_STR)
|
||||
@@ -151,13 +151,12 @@ if(is_gcc)
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
set(SANITIZERS_LINK_FLAGS "${SANITIZERS_RELOCATION_FLAGS}" "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
|
||||
elseif(is_clang)
|
||||
# Add ignorelist for Clang (GCC doesn't support this)
|
||||
# Use CMAKE_SOURCE_DIR to get the path to the ignorelist
|
||||
elseif (is_clang)
|
||||
# Add ignorelist for Clang (GCC doesn't support this) Use CMAKE_SOURCE_DIR to get the path to the ignorelist
|
||||
set(IGNORELIST_PATH "${CMAKE_SOURCE_DIR}/sanitizers/suppressions/sanitizer-ignorelist.txt")
|
||||
if(NOT EXISTS "${IGNORELIST_PATH}")
|
||||
if (NOT EXISTS "${IGNORELIST_PATH}")
|
||||
message(FATAL_ERROR "Sanitizer ignorelist not found: ${IGNORELIST_PATH}")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize-ignorelist=${IGNORELIST_PATH}")
|
||||
message(STATUS " Using sanitizer ignorelist: ${IGNORELIST_PATH}")
|
||||
@@ -168,34 +167,31 @@ elseif(is_clang)
|
||||
# Add sanitizer to compile and link flags
|
||||
list(APPEND SANITIZERS_COMPILE_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
set(SANITIZERS_LINK_FLAGS "-fsanitize=${SANITIZER_TYPES_STR}")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
message(STATUS " Compile flags: ${SANITIZERS_COMPILE_FLAGS}")
|
||||
message(STATUS " Link flags: ${SANITIZERS_LINK_FLAGS}")
|
||||
|
||||
# Apply the sanitizer flags to the 'common' interface library
|
||||
# This is the same library used by XrplCompiler.cmake
|
||||
target_compile_options(common INTERFACE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:${SANITIZERS_COMPILE_FLAGS}>
|
||||
$<$<COMPILE_LANGUAGE:C>:${SANITIZERS_COMPILE_FLAGS}>
|
||||
)
|
||||
# Apply the sanitizer flags to the 'common' interface library This is the same library used by XrplCompiler.cmake
|
||||
target_compile_options(common INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${SANITIZERS_COMPILE_FLAGS}>
|
||||
$<$<COMPILE_LANGUAGE:C>:${SANITIZERS_COMPILE_FLAGS}>)
|
||||
|
||||
# Apply linker flags
|
||||
target_link_options(common INTERFACE ${SANITIZERS_LINK_FLAGS})
|
||||
|
||||
# Define SANITIZERS macro for BuildInfo.cpp
|
||||
set(sanitizers_list)
|
||||
if(enable_asan)
|
||||
if (enable_asan)
|
||||
list(APPEND sanitizers_list "ASAN")
|
||||
endif()
|
||||
if(enable_tsan)
|
||||
endif ()
|
||||
if (enable_tsan)
|
||||
list(APPEND sanitizers_list "TSAN")
|
||||
endif()
|
||||
if(enable_ubsan)
|
||||
endif ()
|
||||
if (enable_ubsan)
|
||||
list(APPEND sanitizers_list "UBSAN")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if(sanitizers_list)
|
||||
if (sanitizers_list)
|
||||
list(JOIN sanitizers_list "." sanitizers_str)
|
||||
target_compile_definitions(common INTERFACE SANITIZERS=${sanitizers_str})
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
@@ -6,40 +6,39 @@ include(CompilationEnv)
|
||||
|
||||
get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||
if (NOT is_multiconfig)
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
message (STATUS "Build type not specified - defaulting to Release")
|
||||
set (CMAKE_BUILD_TYPE Release CACHE STRING "build type" FORCE)
|
||||
elseif (NOT (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL Release))
|
||||
# for simplicity, these are the only two config types we care about. Limiting
|
||||
# the build types simplifies dealing with external project builds especially
|
||||
message (FATAL_ERROR " *** Only Debug or Release build types are currently supported ***")
|
||||
endif ()
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "Build type not specified - defaulting to Release")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "build type" FORCE)
|
||||
elseif (NOT (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL Release))
|
||||
# for simplicity, these are the only two config types we care about. Limiting the build types simplifies dealing
|
||||
# with external project builds especially
|
||||
message(FATAL_ERROR " *** Only Debug or Release build types are currently supported ***")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (is_clang) # both Clang and AppleClang
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
|
||||
message (FATAL_ERROR "This project requires clang 16 or later")
|
||||
endif ()
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
|
||||
message(FATAL_ERROR "This project requires clang 16 or later")
|
||||
endif ()
|
||||
elseif (is_gcc)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
|
||||
message (FATAL_ERROR "This project requires GCC 12 or later")
|
||||
endif ()
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
|
||||
message(FATAL_ERROR "This project requires GCC 12 or later")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# check for in-source build and fail
|
||||
if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
|
||||
message (FATAL_ERROR "Builds (in-source) are not allowed in "
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}. Please remove CMakeCache.txt and the CMakeFiles "
|
||||
"directory from ${CMAKE_CURRENT_SOURCE_DIR} and try building in a separate directory.")
|
||||
message(FATAL_ERROR "Builds (in-source) are not allowed in "
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}. Please remove CMakeCache.txt and the CMakeFiles "
|
||||
"directory from ${CMAKE_CURRENT_SOURCE_DIR} and try building in a separate directory.")
|
||||
endif ()
|
||||
|
||||
if (MSVC AND CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
|
||||
message (FATAL_ERROR "Visual Studio 32-bit build is not supported.")
|
||||
message(FATAL_ERROR "Visual Studio 32-bit build is not supported.")
|
||||
endif ()
|
||||
|
||||
if (APPLE AND NOT HOMEBREW)
|
||||
find_program (HOMEBREW brew)
|
||||
find_program(HOMEBREW brew)
|
||||
endif ()
|
||||
|
||||
@@ -5,125 +5,110 @@
|
||||
include(CompilationEnv)
|
||||
|
||||
set(is_ci FALSE)
|
||||
if(DEFINED ENV{CI})
|
||||
if("$ENV{CI}" STREQUAL "true")
|
||||
set(is_ci TRUE)
|
||||
endif()
|
||||
endif()
|
||||
if (DEFINED ENV{CI})
|
||||
if ("$ENV{CI}" STREQUAL "true")
|
||||
set(is_ci TRUE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_directory_property(has_parent PARENT_DIRECTORY)
|
||||
if(has_parent)
|
||||
set(is_root_project OFF)
|
||||
else()
|
||||
set(is_root_project ON)
|
||||
endif()
|
||||
if (has_parent)
|
||||
set(is_root_project OFF)
|
||||
else ()
|
||||
set(is_root_project ON)
|
||||
endif ()
|
||||
|
||||
option(assert "Enables asserts, even in release builds" OFF)
|
||||
|
||||
option(xrpld "Build xrpld" ON)
|
||||
|
||||
option(tests "Build tests" ON)
|
||||
if(tests)
|
||||
# This setting allows making a separate workflow to test fees other than default 10
|
||||
if(NOT UNIT_TEST_REFERENCE_FEE)
|
||||
set(UNIT_TEST_REFERENCE_FEE "10" CACHE STRING "")
|
||||
endif()
|
||||
endif()
|
||||
if (tests)
|
||||
# This setting allows making a separate workflow to test fees other than default 10
|
||||
if (NOT UNIT_TEST_REFERENCE_FEE)
|
||||
set(UNIT_TEST_REFERENCE_FEE "10" CACHE STRING "")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
option(unity "Creates a build using UNITY support in cmake." OFF)
|
||||
if(unity)
|
||||
if(NOT is_ci)
|
||||
set(CMAKE_UNITY_BUILD_BATCH_SIZE 15 CACHE STRING "")
|
||||
endif()
|
||||
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
|
||||
endif()
|
||||
if (unity)
|
||||
if (NOT is_ci)
|
||||
set(CMAKE_UNITY_BUILD_BATCH_SIZE 15 CACHE STRING "")
|
||||
endif ()
|
||||
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
|
||||
endif ()
|
||||
|
||||
if(is_clang AND is_linux)
|
||||
option(voidstar "Enable Antithesis instrumentation." OFF)
|
||||
endif()
|
||||
if (is_clang AND is_linux)
|
||||
option(voidstar "Enable Antithesis instrumentation." OFF)
|
||||
endif ()
|
||||
|
||||
if(is_gcc OR is_clang)
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(PROCESSOR_COUNT)
|
||||
if (is_gcc OR is_clang)
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(PROCESSOR_COUNT)
|
||||
|
||||
option(coverage "Generates coverage info." OFF)
|
||||
option(profile "Add profiling flags" OFF)
|
||||
set(coverage_format "html-details" CACHE STRING
|
||||
"Output format of the coverage report.")
|
||||
set(coverage_extra_args "" CACHE STRING
|
||||
"Additional arguments to pass to gcovr.")
|
||||
option(wextra "compile with extra gcc/clang warnings enabled" ON)
|
||||
else()
|
||||
set(profile OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||
set(coverage OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||
endif()
|
||||
option(coverage "Generates coverage info." OFF)
|
||||
option(profile "Add profiling flags" OFF)
|
||||
set(coverage_format "html-details" CACHE STRING "Output format of the coverage report.")
|
||||
set(coverage_extra_args "" CACHE STRING "Additional arguments to pass to gcovr.")
|
||||
option(wextra "compile with extra gcc/clang warnings enabled" ON)
|
||||
else ()
|
||||
set(profile OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||
set(coverage OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||
set(wextra OFF CACHE BOOL "gcc/clang only" FORCE)
|
||||
endif ()
|
||||
|
||||
if(is_linux AND NOT SANITIZER)
|
||||
option(BUILD_SHARED_LIBS "build shared xrpl libraries" OFF)
|
||||
option(static "link protobuf, openssl, libc++, and boost statically" ON)
|
||||
option(perf "Enables flags that assist with perf recording" OFF)
|
||||
option(use_gold "enables detection of gold (binutils) linker" ON)
|
||||
option(use_mold "enables detection of mold (binutils) linker" ON)
|
||||
# Set a default value for the log flag based on the build type.
|
||||
# This provides a sensible default (on for debug, off for release)
|
||||
# while still allowing the user to override it for any build.
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(TRUNCATED_LOGS_DEFAULT ON)
|
||||
else()
|
||||
set(TRUNCATED_LOGS_DEFAULT OFF)
|
||||
endif()
|
||||
option(TRUNCATED_THREAD_NAME_LOGS
|
||||
"Show warnings about truncated thread names on Linux."
|
||||
${TRUNCATED_LOGS_DEFAULT}
|
||||
)
|
||||
if(TRUNCATED_THREAD_NAME_LOGS)
|
||||
add_compile_definitions(TRUNCATED_THREAD_NAME_LOGS)
|
||||
endif()
|
||||
else()
|
||||
# we are not ready to allow shared-libs on windows because it would require
|
||||
# export declarations. On macos it's more feasible, but static openssl
|
||||
# produces odd linker errors, thus we disable shared lib builds for now.
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "build shared xrpl libraries - OFF for win/macos" FORCE)
|
||||
set(static ON CACHE BOOL "static link, linux only. ON for WIN/macos" FORCE)
|
||||
set(perf OFF CACHE BOOL "perf flags, linux only" FORCE)
|
||||
set(use_gold OFF CACHE BOOL "gold linker, linux only" FORCE)
|
||||
set(use_mold OFF CACHE BOOL "mold linker, linux only" FORCE)
|
||||
endif()
|
||||
if (is_linux AND NOT SANITIZER)
|
||||
option(BUILD_SHARED_LIBS "build shared xrpl libraries" OFF)
|
||||
option(static "link protobuf, openssl, libc++, and boost statically" ON)
|
||||
option(perf "Enables flags that assist with perf recording" OFF)
|
||||
option(use_gold "enables detection of gold (binutils) linker" ON)
|
||||
option(use_mold "enables detection of mold (binutils) linker" ON)
|
||||
# Set a default value for the log flag based on the build type. This provides a sensible default (on for debug, off
|
||||
# for release) while still allowing the user to override it for any build.
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(TRUNCATED_LOGS_DEFAULT ON)
|
||||
else ()
|
||||
set(TRUNCATED_LOGS_DEFAULT OFF)
|
||||
endif ()
|
||||
option(TRUNCATED_THREAD_NAME_LOGS "Show warnings about truncated thread names on Linux." ${TRUNCATED_LOGS_DEFAULT})
|
||||
if (TRUNCATED_THREAD_NAME_LOGS)
|
||||
add_compile_definitions(TRUNCATED_THREAD_NAME_LOGS)
|
||||
endif ()
|
||||
else ()
|
||||
# we are not ready to allow shared-libs on windows because it would require export declarations. On macos it's more
|
||||
# feasible, but static openssl produces odd linker errors, thus we disable shared lib builds for now.
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "build shared xrpl libraries - OFF for win/macos" FORCE)
|
||||
set(static ON CACHE BOOL "static link, linux only. ON for WIN/macos" FORCE)
|
||||
set(perf OFF CACHE BOOL "perf flags, linux only" FORCE)
|
||||
set(use_gold OFF CACHE BOOL "gold linker, linux only" FORCE)
|
||||
set(use_mold OFF CACHE BOOL "mold linker, linux only" FORCE)
|
||||
endif ()
|
||||
|
||||
if(is_clang)
|
||||
option(use_lld "enables detection of lld linker" ON)
|
||||
else()
|
||||
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)
|
||||
endif()
|
||||
if (is_clang)
|
||||
option(use_lld "enables detection of lld linker" ON)
|
||||
else ()
|
||||
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)
|
||||
endif ()
|
||||
|
||||
option(jemalloc "Enables jemalloc for heap profiling" OFF)
|
||||
option(werr "treat warnings as errors" OFF)
|
||||
option(local_protobuf
|
||||
"Force a local build of protobuf instead of looking for an installed version." OFF)
|
||||
option(local_grpc
|
||||
"Force a local build of gRPC instead of looking for an installed version." OFF)
|
||||
option(local_protobuf "Force a local build of protobuf instead of looking for an installed version." OFF)
|
||||
option(local_grpc "Force a local build of gRPC instead of looking for an installed version." OFF)
|
||||
|
||||
# the remaining options are obscure and rarely used
|
||||
option(beast_no_unit_test_inline
|
||||
"Prevents unit test definitions from being inserted into global table"
|
||||
OFF)
|
||||
option(single_io_service_thread
|
||||
"Restricts the number of threads calling io_context::run to one. \
|
||||
This can be useful when debugging."
|
||||
OFF)
|
||||
option(boost_show_deprecated
|
||||
"Allow boost to fail on deprecated usage. Only useful if you're trying\
|
||||
to find deprecated calls."
|
||||
OFF)
|
||||
option(beast_no_unit_test_inline "Prevents unit test definitions from being inserted into global table" OFF)
|
||||
option(single_io_service_thread "Restricts the number of threads calling io_context::run to one. \
|
||||
This can be useful when debugging." OFF)
|
||||
option(boost_show_deprecated "Allow boost to fail on deprecated usage. Only useful if you're trying\
|
||||
to find deprecated calls." OFF)
|
||||
|
||||
if(WIN32)
|
||||
option(beast_disable_autolink "Disables autolinking of system libraries on WIN32" OFF)
|
||||
else()
|
||||
set(beast_disable_autolink OFF CACHE BOOL "WIN32 only" FORCE)
|
||||
endif()
|
||||
if (WIN32)
|
||||
option(beast_disable_autolink "Disables autolinking of system libraries on WIN32" OFF)
|
||||
else ()
|
||||
set(beast_disable_autolink OFF CACHE BOOL "WIN32 only" FORCE)
|
||||
endif ()
|
||||
|
||||
if(coverage)
|
||||
message(STATUS "coverage build requested - forcing Debug build")
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "build type" FORCE)
|
||||
endif()
|
||||
if (coverage)
|
||||
message(STATUS "coverage build requested - forcing Debug build")
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "build type" FORCE)
|
||||
endif ()
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
option (validator_keys "Enables building of validator-keys tool as a separate target (imported via FetchContent)" OFF)
|
||||
option(validator_keys "Enables building of validator-keys tool as a separate target (imported via FetchContent)" OFF)
|
||||
|
||||
if (validator_keys)
|
||||
git_branch (current_branch)
|
||||
# default to tracking VK master branch unless we are on release
|
||||
if (NOT (current_branch STREQUAL "release"))
|
||||
set (current_branch "master")
|
||||
endif ()
|
||||
message (STATUS "Tracking ValidatorKeys branch: ${current_branch}")
|
||||
git_branch(current_branch)
|
||||
# default to tracking VK master branch unless we are on release
|
||||
if (NOT (current_branch STREQUAL "release"))
|
||||
set(current_branch "master")
|
||||
endif ()
|
||||
message(STATUS "Tracking ValidatorKeys branch: ${current_branch}")
|
||||
|
||||
FetchContent_Declare (
|
||||
validator_keys
|
||||
GIT_REPOSITORY https://github.com/ripple/validator-keys-tool.git
|
||||
GIT_TAG "${current_branch}"
|
||||
)
|
||||
FetchContent_MakeAvailable(validator_keys)
|
||||
set_target_properties(validator-keys PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
install(TARGETS validator-keys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
FetchContent_Declare(validator_keys GIT_REPOSITORY https://github.com/ripple/validator-keys-tool.git
|
||||
GIT_TAG "${current_branch}")
|
||||
FetchContent_MakeAvailable(validator_keys)
|
||||
set_target_properties(validator-keys PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
install(TARGETS validator-keys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
endif ()
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
#]===================================================================]
|
||||
|
||||
file(STRINGS src/libxrpl/protocol/BuildInfo.cpp BUILD_INFO)
|
||||
foreach(line_ ${BUILD_INFO})
|
||||
if(line_ MATCHES "versionString[ ]*=[ ]*\"(.+)\"")
|
||||
set(xrpld_version ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
endforeach()
|
||||
if(xrpld_version)
|
||||
message(STATUS "xrpld version: ${xrpld_version}")
|
||||
else()
|
||||
message(FATAL_ERROR "unable to determine xrpld version")
|
||||
endif()
|
||||
foreach (line_ ${BUILD_INFO})
|
||||
if (line_ MATCHES "versionString[ ]*=[ ]*\"(.+)\"")
|
||||
set(xrpld_version ${CMAKE_MATCH_1})
|
||||
endif ()
|
||||
endforeach ()
|
||||
if (xrpld_version)
|
||||
message(STATUS "xrpld version: ${xrpld_version}")
|
||||
else ()
|
||||
message(FATAL_ERROR "unable to determine xrpld version")
|
||||
endif ()
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
# file(CREATE_SYMLINK) only works on Windows with administrator privileges.
|
||||
# https://stackoverflow.com/a/61244115/618906
|
||||
function(create_symbolic_link target link)
|
||||
if(WIN32)
|
||||
if(NOT IS_SYMLINK "${link}")
|
||||
if(NOT IS_ABSOLUTE "${target}")
|
||||
# Relative links work do not work on Windows.
|
||||
set(target "${link}/../${target}")
|
||||
endif()
|
||||
file(TO_NATIVE_PATH "${target}" target)
|
||||
file(TO_NATIVE_PATH "${link}" link)
|
||||
execute_process(COMMAND cmd.exe /c mklink /J "${link}" "${target}")
|
||||
endif()
|
||||
else()
|
||||
file(CREATE_LINK "${target}" "${link}" SYMBOLIC)
|
||||
endif()
|
||||
if(NOT IS_SYMLINK "${link}")
|
||||
message(ERROR "failed to create symlink: <${link}>")
|
||||
endif()
|
||||
endfunction()
|
||||
# file(CREATE_SYMLINK) only works on Windows with administrator privileges. https://stackoverflow.com/a/61244115/618906
|
||||
function (create_symbolic_link target link)
|
||||
if (WIN32)
|
||||
if (NOT IS_SYMLINK "${link}")
|
||||
if (NOT IS_ABSOLUTE "${target}")
|
||||
# Relative links work do not work on Windows.
|
||||
set(target "${link}/../${target}")
|
||||
endif ()
|
||||
file(TO_NATIVE_PATH "${target}" target)
|
||||
file(TO_NATIVE_PATH "${link}" link)
|
||||
execute_process(COMMAND cmd.exe /c mklink /J "${link}" "${target}")
|
||||
endif ()
|
||||
else ()
|
||||
file(CREATE_LINK "${target}" "${link}" SYMBOLIC)
|
||||
endif ()
|
||||
if (NOT IS_SYMLINK "${link}")
|
||||
message(ERROR "failed to create symlink: <${link}>")
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
@@ -2,48 +2,43 @@ include(CompilationEnv)
|
||||
include(XrplSanitizers)
|
||||
|
||||
find_package(Boost REQUIRED
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
json
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread
|
||||
)
|
||||
COMPONENTS chrono
|
||||
container
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
json
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread)
|
||||
|
||||
add_library(xrpl_boost INTERFACE)
|
||||
add_library(Xrpl::boost ALIAS xrpl_boost)
|
||||
|
||||
target_link_libraries(xrpl_boost
|
||||
INTERFACE
|
||||
Boost::headers
|
||||
Boost::chrono
|
||||
Boost::container
|
||||
Boost::coroutine
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::json
|
||||
Boost::process
|
||||
Boost::program_options
|
||||
Boost::regex
|
||||
Boost::thread)
|
||||
if(Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif()
|
||||
if(SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else
|
||||
# for gcc ?
|
||||
if(NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
get_target_property(Boost_INCLUDE_DIRS Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif()
|
||||
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt "src:${Boost_INCLUDE_DIRS}/*")
|
||||
target_compile_options(opts
|
||||
INTERFACE
|
||||
# ignore boost headers for sanitizing
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt)
|
||||
endif()
|
||||
target_link_libraries(
|
||||
xrpl_boost
|
||||
INTERFACE Boost::headers
|
||||
Boost::chrono
|
||||
Boost::container
|
||||
Boost::coroutine
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::json
|
||||
Boost::process
|
||||
Boost::program_options
|
||||
Boost::regex
|
||||
Boost::thread)
|
||||
if (Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif ()
|
||||
if (SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
|
||||
if (NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
get_target_property(Boost_INCLUDE_DIRS Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif ()
|
||||
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt "src:${Boost_INCLUDE_DIRS}/*")
|
||||
target_compile_options(opts INTERFACE # ignore boost headers for sanitizing
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt)
|
||||
endif ()
|
||||
|
||||
@@ -77,16 +77,16 @@ public:
|
||||
If the object is not found or an error is encountered, the
|
||||
result will indicate the condition.
|
||||
@note This will be called concurrently.
|
||||
@param hash The hash of the object.
|
||||
@param key A pointer to the key data.
|
||||
@param pObject [out] The created object if successful.
|
||||
@return The result of the operation.
|
||||
*/
|
||||
virtual Status
|
||||
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) = 0;
|
||||
fetch(void const* key, std::shared_ptr<NodeObject>* pObject) = 0;
|
||||
|
||||
/** Fetch a batch synchronously. */
|
||||
virtual std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
|
||||
fetchBatch(std::vector<uint256> const& hashes) = 0;
|
||||
fetchBatch(std::vector<uint256 const*> const& hashes) = 0;
|
||||
|
||||
/** Store a single object.
|
||||
Depending on the implementation this may happen immediately
|
||||
|
||||
@@ -24,6 +24,32 @@ public:
|
||||
beast::Journal j)
|
||||
: Database(scheduler, readThreads, config, j), backend_(std::move(backend))
|
||||
{
|
||||
std::optional<int> cacheSize, cacheAge;
|
||||
|
||||
if (config.exists("cache_size"))
|
||||
{
|
||||
cacheSize = get<int>(config, "cache_size");
|
||||
if (cacheSize.value() < 0)
|
||||
{
|
||||
Throw<std::runtime_error>("Specified negative value for cache_size");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.exists("cache_age"))
|
||||
{
|
||||
cacheAge = get<int>(config, "cache_age");
|
||||
if (cacheAge.value() < 0)
|
||||
{
|
||||
Throw<std::runtime_error>("Specified negative value for cache_age");
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheSize != 0 || cacheAge != 0)
|
||||
{
|
||||
cache_ = std::make_shared<TaggedCache<uint256, NodeObject>>(
|
||||
"DatabaseNodeImp", cacheSize.value_or(0), std::chrono::minutes(cacheAge.value_or(0)), stopwatch(), j);
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
backend_,
|
||||
"xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null "
|
||||
@@ -82,6 +108,9 @@ public:
|
||||
sweep() override;
|
||||
|
||||
private:
|
||||
// Cache for database objects. This cache is not always initialized. Check
|
||||
// for null before using.
|
||||
std::shared_ptr<TaggedCache<uint256, NodeObject>> cache_;
|
||||
// Persistent key/value storage
|
||||
std::shared_ptr<Backend> backend_;
|
||||
|
||||
|
||||
489
include/xrpl/protocol/ConfidentialTransfer.h
Normal file
489
include/xrpl/protocol/ConfidentialTransfer.h
Normal file
@@ -0,0 +1,489 @@
|
||||
#ifndef XRPL_PROTOCOL_CONFIDENTIALTRANSFER_H_INCLUDED
|
||||
#define XRPL_PROTOCOL_CONFIDENTIALTRANSFER_H_INCLUDED
|
||||
|
||||
#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.h>
|
||||
|
||||
namespace xrpl {
|
||||
// Helper struct to bundle the ElGamal Public Key and the associated Ciphertext
|
||||
struct ConfidentialRecipient
|
||||
{
|
||||
Slice const publicKey;
|
||||
Slice const encryptedAmount;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
addCommonZKPFields(
|
||||
Serializer& s,
|
||||
std::uint16_t txType,
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID);
|
||||
|
||||
uint256
|
||||
getSendContextHash(
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID,
|
||||
AccountID const& destination,
|
||||
std::uint32_t version);
|
||||
|
||||
uint256
|
||||
getClawbackContextHash(
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID,
|
||||
std::uint64_t amount,
|
||||
AccountID const& holder);
|
||||
|
||||
uint256
|
||||
getConvertContextHash(
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID,
|
||||
std::uint64_t amount);
|
||||
|
||||
uint256
|
||||
getConvertBackContextHash(
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID,
|
||||
std::uint64_t amount,
|
||||
std::uint32_t version);
|
||||
|
||||
// breaks a 66-byte encrypted amount into two 33-byte components
|
||||
// then parses each 33-byte component into 64-byte secp256k1_pubkey format
|
||||
bool
|
||||
makeEcPair(Slice const& buffer, secp256k1_pubkey& out1, secp256k1_pubkey& out2);
|
||||
|
||||
// serialize two secp256k1_pubkey components back into compressed 66-byte form
|
||||
bool
|
||||
serializeEcPair(secp256k1_pubkey const& in1, secp256k1_pubkey const& in2, Buffer& buffer);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
TER
|
||||
homomorphicAdd(Slice const& a, Slice const& b, Buffer& out);
|
||||
|
||||
TER
|
||||
homomorphicSubtract(Slice const& a, Slice const& b, Buffer& out);
|
||||
|
||||
// returns ciphertext and the blinding factor used
|
||||
std::optional<Buffer>
|
||||
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor);
|
||||
|
||||
std::optional<Buffer>
|
||||
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId);
|
||||
|
||||
TER
|
||||
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash);
|
||||
|
||||
TER
|
||||
verifyElGamalEncryption(
|
||||
std::uint64_t const amount,
|
||||
Slice const& blindingFactor,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& ciphertext);
|
||||
|
||||
NotTEC
|
||||
checkEncryptedAmountFormat(STObject const& object);
|
||||
|
||||
TER
|
||||
verifyRevealedAmount(
|
||||
std::uint64_t const amount,
|
||||
Slice const& blindingFactor,
|
||||
ConfidentialRecipient const& holder,
|
||||
ConfidentialRecipient const& issuer,
|
||||
std::optional<ConfidentialRecipient> const& auditor);
|
||||
|
||||
constexpr std::size_t
|
||||
getConfidentialRecipientCount(bool hasAuditor)
|
||||
{
|
||||
return hasAuditor ? 4 : 3;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
getMultiCiphertextEqualityProofSize(std::size_t nRecipients);
|
||||
|
||||
TER
|
||||
verifyMultiCiphertextEqualityProof(
|
||||
Slice const& proof,
|
||||
std::vector<ConfidentialRecipient> const& recipients,
|
||||
std::size_t const nRecipients,
|
||||
uint256 const& contextHash);
|
||||
|
||||
TER
|
||||
verifyClawbackEqualityProof(
|
||||
uint64_t const amount,
|
||||
Slice const& proof,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& ciphertext,
|
||||
uint256 const& contextHash);
|
||||
|
||||
// generates a 32 byte randomness factor to be used in encryption and proofs
|
||||
Buffer
|
||||
generateBlindingFactor();
|
||||
|
||||
/**
|
||||
* @brief Verifies the cryptographic link between an ElGamal Ciphertext and a
|
||||
* Pedersen Commitment for a transaction Amount.
|
||||
*
|
||||
* It proves that the ElGamal ciphertext `encAmt` encrypts the same value `m`
|
||||
* as the Pedersen Commitment `pcmSlice`, using the randomness `r`.
|
||||
* Proves Enc(m) <-> Pcm(m)
|
||||
*
|
||||
* @param proof The Zero Knowledge Proof bytes.
|
||||
* @param encAmt The ElGamal ciphertext of the amount (C1, C2).
|
||||
* @param pubKeySlice The sender's public key.
|
||||
* @param pcmSlice The Pedersen Commitment to the amount.
|
||||
* @param contextHash The unique context hash for this transaction.
|
||||
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyAmountPcmLinkage(
|
||||
Slice const& proof,
|
||||
Slice const& encAmt,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& pcmSlice,
|
||||
uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Verifies the cryptographic link between an ElGamal Ciphertext and a
|
||||
* Pedersen Commitment for an account Balance.
|
||||
*
|
||||
* It proves that the ElGamal ciphertext `encAmt` encrypts the same value `b`
|
||||
* as the Pedersen Commitment `pcmSlice`, using the secret key `s`.
|
||||
* Proves Enc(b) <-> Pcm(b)
|
||||
*
|
||||
* Note: Swaps arguments (Pk <-> C1) to accommodate the different algebraic
|
||||
* structure.
|
||||
*
|
||||
* @param proof The Zero Knowledge Proof bytes.
|
||||
* @param encAmt The ElGamal ciphertext of the balance (C1, C2).
|
||||
* @param pubKeySlice The sender's public key.
|
||||
* @param pcmSlice The Pedersen Commitment to the balance.
|
||||
* @param contextHash The unique context hash for this transaction.
|
||||
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyBalancePcmLinkage(
|
||||
Slice const& proof,
|
||||
Slice const& encAmt,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& pcmSlice,
|
||||
uint256 const& contextHash);
|
||||
|
||||
// The following functions belong to the mpt-crypto library,
|
||||
// they will be finally removed and we will use conan2 to manage the dependency.
|
||||
/**
|
||||
* @brief Generates a new secp256k1 key pair.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
secp256k1_elgamal_generate_keypair(secp256k1_context const* ctx, unsigned char* privkey, secp256k1_pubkey* pubkey);
|
||||
|
||||
/**
|
||||
* @brief Encrypts a 64-bit amount using ElGamal.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
secp256k1_elgamal_encrypt(
|
||||
secp256k1_context const* ctx,
|
||||
secp256k1_pubkey* c1,
|
||||
secp256k1_pubkey* c2,
|
||||
secp256k1_pubkey const* pubkey_Q,
|
||||
uint64_t amount,
|
||||
unsigned char const* blinding_factor);
|
||||
|
||||
/**
|
||||
* @brief Decrypts an ElGamal ciphertext to recover the amount.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
secp256k1_elgamal_decrypt(
|
||||
secp256k1_context const* ctx,
|
||||
uint64_t* amount,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
unsigned char const* privkey);
|
||||
|
||||
/**
|
||||
* @brief Homomorphically adds two ElGamal ciphertexts.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
secp256k1_elgamal_add(
|
||||
secp256k1_context const* ctx,
|
||||
secp256k1_pubkey* sum_c1,
|
||||
secp256k1_pubkey* sum_c2,
|
||||
secp256k1_pubkey const* a_c1,
|
||||
secp256k1_pubkey const* a_c2,
|
||||
secp256k1_pubkey const* b_c1,
|
||||
secp256k1_pubkey const* b_c2);
|
||||
|
||||
/**
|
||||
* @brief Homomorphically subtracts two ElGamal ciphertexts.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
secp256k1_elgamal_subtract(
|
||||
secp256k1_context const* ctx,
|
||||
secp256k1_pubkey* diff_c1,
|
||||
secp256k1_pubkey* diff_c2,
|
||||
secp256k1_pubkey const* a_c1,
|
||||
secp256k1_pubkey const* a_c2,
|
||||
secp256k1_pubkey const* b_c1,
|
||||
secp256k1_pubkey const* b_c2);
|
||||
|
||||
/**
|
||||
* @brief Generates the canonical encrypted zero for a given MPT token instance.
|
||||
*
|
||||
* This ciphertext represents a zero balance for a specific account's holding
|
||||
* of a token defined by its MPTokenIssuanceID.
|
||||
*
|
||||
* @param[in] ctx A pointer to a valid secp256k1 context.
|
||||
* @param[out] enc_zero_c1 The C1 component of the canonical ciphertext.
|
||||
* @param[out] enc_zero_c2 The C2 component of the canonical ciphertext.
|
||||
* @param[in] pubkey The ElGamal public key of the account holder.
|
||||
* @param[in] account_id A pointer to the 20-byte AccountID.
|
||||
* @param[in] mpt_issuance_id A pointer to the 24-byte MPTokenIssuanceID.
|
||||
*
|
||||
* @return 1 on success, 0 on failure.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
generate_canonical_encrypted_zero(
|
||||
secp256k1_context const* ctx,
|
||||
secp256k1_pubkey* enc_zero_c1,
|
||||
secp256k1_pubkey* enc_zero_c2,
|
||||
secp256k1_pubkey const* pubkey,
|
||||
unsigned char const* account_id, // 20 bytes
|
||||
unsigned char const* mpt_issuance_id // 24 bytes
|
||||
);
|
||||
|
||||
/**
|
||||
* Generates a cryptographically secure 32-byte scalar (private key).
|
||||
* @return 1 on success, 0 on failure.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
generate_random_scalar(secp256k1_context const* ctx, unsigned char* scalar_bytes);
|
||||
|
||||
/**
|
||||
* Computes the point M = amount * G.
|
||||
* IMPORTANT: This function MUST NOT be called with amount = 0.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
compute_amount_point(secp256k1_context const* ctx, secp256k1_pubkey* mG, uint64_t amount);
|
||||
|
||||
/**
|
||||
* Builds the challenge hash input for the NON-ZERO amount case.
|
||||
* Output buffer must be 253 bytes.
|
||||
*/
|
||||
SECP256K1_API void
|
||||
build_challenge_hash_input_nonzero(
|
||||
unsigned char* hash_input,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
secp256k1_pubkey const* pk,
|
||||
secp256k1_pubkey const* mG,
|
||||
secp256k1_pubkey const* T1,
|
||||
secp256k1_pubkey const* T2,
|
||||
unsigned char const* tx_context_id);
|
||||
|
||||
/**
|
||||
* Builds the challenge hash input for the ZERO amount case.
|
||||
* Output buffer must be 220 bytes.
|
||||
*/
|
||||
SECP256K1_API void
|
||||
build_challenge_hash_input_zero(
|
||||
unsigned char* hash_input,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
secp256k1_pubkey const* pk,
|
||||
secp256k1_pubkey const* T1,
|
||||
secp256k1_pubkey const* T2,
|
||||
unsigned char const* tx_context_id);
|
||||
|
||||
/**
|
||||
* @brief Proves that a commitment (C1, C2) encrypts a specific plaintext
|
||||
* 'amount'.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
secp256k1_equality_plaintext_prove(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char* proof,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
secp256k1_pubkey const* pk_recipient,
|
||||
uint64_t amount,
|
||||
unsigned char const* randomness_r,
|
||||
unsigned char const* tx_context_id);
|
||||
|
||||
/**
|
||||
* @brief Verifies the proof generated by secp256k1_equality_plaintext_prove.
|
||||
*/
|
||||
SECP256K1_API int
|
||||
secp256k1_equality_plaintext_verify(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char const* proof,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
secp256k1_pubkey const* pk_recipient,
|
||||
uint64_t amount,
|
||||
unsigned char const* tx_context_id);
|
||||
|
||||
void
|
||||
build_pok_challenge(
|
||||
unsigned char* e,
|
||||
secp256k1_context const* ctx,
|
||||
secp256k1_pubkey const* pk,
|
||||
secp256k1_pubkey const* T,
|
||||
unsigned char const* context_id);
|
||||
|
||||
/** Proof of Knowledge of Secret Key for Registration */
|
||||
int
|
||||
secp256k1_mpt_pok_sk_prove(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char* proof, /* Expected size: 65 bytes */
|
||||
secp256k1_pubkey const* pk,
|
||||
unsigned char const* sk,
|
||||
unsigned char const* context_id);
|
||||
|
||||
int
|
||||
secp256k1_mpt_pok_sk_verify(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char const* proof, /* Expected size: 65 bytes */
|
||||
secp256k1_pubkey const* pk,
|
||||
unsigned char const* context_id);
|
||||
|
||||
/**
|
||||
* Verifies that (c1, c2) is a valid ElGamal encryption of 'amount'
|
||||
* for 'pubkey_Q' using the revealed 'blinding_factor'.
|
||||
*/
|
||||
int
|
||||
secp256k1_elgamal_verify_encryption(
|
||||
secp256k1_context const* ctx,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
secp256k1_pubkey const* pubkey_Q,
|
||||
uint64_t amount,
|
||||
unsigned char const* blinding_factor);
|
||||
|
||||
/**
|
||||
* @brief Proves the link between an ElGamal ciphertext and a Pedersen
|
||||
* commitment.
|
||||
* * Formal Statement: Knowledge of (m, r, rho) such that:
|
||||
* C1 = r*G, C2 = m*G + r*Pk, and PCm = m*G + rho*H.
|
||||
* * @param ctx Pointer to a secp256k1 context object.
|
||||
* @param proof [OUT] Pointer to 195-byte buffer for the proof output.
|
||||
* @param c1 Pointer to the ElGamal C1 point (r*G).
|
||||
* @param c2 Pointer to the ElGamal C2 point (m*G + r*Pk).
|
||||
* @param pk Pointer to the recipient's public key.
|
||||
* @param pcm Pointer to the Pedersen Commitment (m*G + rho*H).
|
||||
* @param amount The plaintext amount (m).
|
||||
* @param r The 32-byte secret ElGamal blinding factor.
|
||||
* @param rho The 32-byte secret Pedersen blinding factor.
|
||||
* @param context_id 32-byte unique transaction context identifier.
|
||||
* @return 1 on success, 0 on failure.
|
||||
*/
|
||||
int
|
||||
secp256k1_elgamal_pedersen_link_prove(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char* proof,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
secp256k1_pubkey const* pk,
|
||||
secp256k1_pubkey const* pcm,
|
||||
uint64_t amount,
|
||||
unsigned char const* r,
|
||||
unsigned char const* rho,
|
||||
unsigned char const* context_id);
|
||||
|
||||
/**
|
||||
* @brief Verifies the link proof between ElGamal and Pedersen commitments.
|
||||
* * @return 1 if the proof is valid, 0 otherwise.
|
||||
*/
|
||||
int
|
||||
secp256k1_elgamal_pedersen_link_verify(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char const* proof,
|
||||
secp256k1_pubkey const* c1,
|
||||
secp256k1_pubkey const* c2,
|
||||
secp256k1_pubkey const* pk,
|
||||
secp256k1_pubkey const* pcm,
|
||||
unsigned char const* context_id);
|
||||
|
||||
/**
|
||||
* Compute a Pedersen Commitment: PC = m*G + rho*H
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
int
|
||||
secp256k1_mpt_pedersen_commit(
|
||||
secp256k1_context const* ctx,
|
||||
secp256k1_pubkey* commitment,
|
||||
uint64_t amount,
|
||||
unsigned char const* blinding_factor_rho /* 32 bytes */
|
||||
);
|
||||
|
||||
// Multi-proof for same plaintexts
|
||||
void
|
||||
build_hash_input(
|
||||
unsigned char* hash_out, // Output: 32-byte hash
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
secp256k1_pubkey const* T_m,
|
||||
secp256k1_pubkey const* T_rG,
|
||||
secp256k1_pubkey const* T_rP,
|
||||
unsigned char const* tx_id);
|
||||
|
||||
size_t
|
||||
secp256k1_mpt_prove_same_plaintext_multi_size(size_t n);
|
||||
|
||||
int
|
||||
secp256k1_mpt_prove_same_plaintext_multi(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char* proof_out,
|
||||
size_t* proof_len,
|
||||
uint64_t amount_m,
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
unsigned char const* r_array,
|
||||
unsigned char const* tx_id);
|
||||
|
||||
int
|
||||
secp256k1_mpt_verify_same_plaintext_multi(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char const* proof,
|
||||
size_t proof_len,
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
unsigned char const* tx_id);
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
@@ -168,6 +168,7 @@ enum LedgerSpecificFlags {
|
||||
lsfMPTCanTrade = 0x00000010,
|
||||
lsfMPTCanTransfer = 0x00000020,
|
||||
lsfMPTCanClawback = 0x00000040,
|
||||
lsfMPTCanPrivacy = 0x00000080,
|
||||
|
||||
lsmfMPTCanMutateCanLock = 0x00000002,
|
||||
lsmfMPTCanMutateRequireAuth = 0x00000004,
|
||||
@@ -177,6 +178,8 @@ enum LedgerSpecificFlags {
|
||||
lsmfMPTCanMutateCanClawback = 0x00000040,
|
||||
lsmfMPTCanMutateMetadata = 0x00010000,
|
||||
lsmfMPTCanMutateTransferFee = 0x00020000,
|
||||
// if set, lsfMPTCanPrivacy can not be mutated
|
||||
lsmfMPTCannotMutatePrivacy = 0x00040000,
|
||||
|
||||
// ltMPTOKEN
|
||||
lsfMPTAuthorized = 0x00000002,
|
||||
|
||||
@@ -297,6 +297,36 @@ std::size_t constexpr permissionMaxSize = 10;
|
||||
/** The maximum number of transactions that can be in a batch. */
|
||||
std::size_t constexpr maxBatchTxCount = 8;
|
||||
|
||||
/** EC ElGamal ciphertext length 33-byte */
|
||||
std::size_t constexpr ecGamalEncryptedLength = 33;
|
||||
|
||||
/** EC ElGamal ciphertext length: two 33-byte components concatenated */
|
||||
std::size_t constexpr ecGamalEncryptedTotalLength = 66;
|
||||
|
||||
/** Length of equality ZKProof */
|
||||
std::size_t constexpr ecEqualityProofLength = 98;
|
||||
|
||||
/** Length of EC public key */
|
||||
std::size_t constexpr ecPubKeyLength = 64;
|
||||
|
||||
/** Length of EC private key */
|
||||
std::size_t constexpr ecPrivKeyLength = 32;
|
||||
|
||||
/** Length of the EC blinding factor */
|
||||
std::size_t constexpr ecBlindingFactorLength = 32;
|
||||
|
||||
/** Length of Schnorr ZKProof for public key registration */
|
||||
std::size_t constexpr ecSchnorrProofLength = 65;
|
||||
|
||||
/** Length of ElGamal ciphertext equality proof */
|
||||
std::size_t constexpr ecCiphertextEqualityProofLength = 261;
|
||||
|
||||
/** Length of ElGamal Pedersen linkage proof */
|
||||
std::size_t constexpr ecPedersenProofLength = 195;
|
||||
|
||||
/** Length of Pedersen Commitment proof */
|
||||
std::size_t constexpr ecPedersenCommitmentLength = 64;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -122,6 +122,7 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
temBAD_CIPHERTEXT,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -347,6 +348,7 @@ enum TECcodes : TERUnderlyingType {
|
||||
// backward compatibility with historical data on non-prod networks, can be
|
||||
// reclaimed after those networks reset.
|
||||
tecNO_DELEGATE_PERMISSION = 198,
|
||||
tecBAD_PROOF = 199
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -132,8 +132,9 @@ constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow;
|
||||
constexpr std::uint32_t const tfMPTCanTrade = lsfMPTCanTrade;
|
||||
constexpr std::uint32_t const tfMPTCanTransfer = lsfMPTCanTransfer;
|
||||
constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback;
|
||||
constexpr std::uint32_t const tfMPTCanPrivacy = lsfMPTCanPrivacy;
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
|
||||
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback);
|
||||
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback | tfMPTCanPrivacy);
|
||||
|
||||
// MPTokenIssuanceCreate MutableFlags:
|
||||
// Indicating specific fields or flags may be changed after issuance.
|
||||
@@ -145,9 +146,13 @@ constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTr
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
|
||||
|
||||
// Issuer can mutate lsfMPTPrivacy by default unless lsmfMPTCannotMutatePrivacy is set.
|
||||
constexpr std::uint32_t const tmfMPTCannotMutatePrivacy = lsmfMPTCannotMutatePrivacy;
|
||||
constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask =
|
||||
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade
|
||||
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
|
||||
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee
|
||||
| tmfMPTCannotMutatePrivacy);
|
||||
|
||||
// MPTokenAuthorize flags:
|
||||
constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001;
|
||||
@@ -173,10 +178,12 @@ constexpr std::uint32_t const tmfMPTSetCanTransfer = 0x00000100;
|
||||
constexpr std::uint32_t const tmfMPTClearCanTransfer = 0x00000200;
|
||||
constexpr std::uint32_t const tmfMPTSetCanClawback = 0x00000400;
|
||||
constexpr std::uint32_t const tmfMPTClearCanClawback = 0x00000800;
|
||||
constexpr std::uint32_t const tmfMPTSetPrivacy = 0x00001000;
|
||||
constexpr std::uint32_t const tmfMPTClearPrivacy = 0x00002000;
|
||||
constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock |
|
||||
tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow |
|
||||
tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer |
|
||||
tmfMPTSetCanClawback | tmfMPTClearCanClawback);
|
||||
tmfMPTSetCanClawback | tmfMPTClearCanClawback | tmfMPTSetPrivacy | tmfMPTClearPrivacy);
|
||||
|
||||
// MPTokenIssuanceDestroy flags:
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FEATURE(ConfidentialTransfer, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -398,6 +398,9 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfMutableFlags, soeDEFAULT},
|
||||
{sfIssuerElGamalPublicKey, soeOPTIONAL},
|
||||
{sfAuditorElGamalPublicKey, soeOPTIONAL},
|
||||
{sfConfidentialOutstandingAmount, soeDEFAULT},
|
||||
}))
|
||||
|
||||
/** A ledger object which tracks MPToken
|
||||
@@ -411,6 +414,12 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
{sfConfidentialBalanceInbox, soeOPTIONAL},
|
||||
{sfConfidentialBalanceSpending, soeOPTIONAL},
|
||||
{sfConfidentialBalanceVersion, soeDEFAULT},
|
||||
{sfIssuerEncryptedBalance, soeOPTIONAL},
|
||||
{sfAuditorEncryptedBalance, soeOPTIONAL},
|
||||
{sfHolderElGamalPublicKey, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** A ledger object which tracks Oracle
|
||||
|
||||
@@ -114,6 +114,7 @@ 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,6 +148,7 @@ 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)
|
||||
@@ -297,6 +299,22 @@ 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(sfIssuerElGamalPublicKey, VL, 35)
|
||||
TYPED_SFIELD(sfHolderElGamalPublicKey, 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(sfAuditorElGamalPublicKey, VL, 44)
|
||||
TYPED_SFIELD(sfBlindingFactor, VL, 45)
|
||||
TYPED_SFIELD(sfAmountCommitment, VL, 46)
|
||||
TYPED_SFIELD(sfBalanceCommitment, VL, 47)
|
||||
|
||||
// account (common)
|
||||
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
|
||||
|
||||
@@ -722,6 +722,8 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfTransferFee, soeOPTIONAL},
|
||||
{sfMutableFlags, soeOPTIONAL},
|
||||
{sfIssuerElGamalPublicKey, soeOPTIONAL},
|
||||
{sfAuditorElGamalPublicKey, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type authorizes a MPToken instance */
|
||||
@@ -1058,6 +1060,90 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
}))
|
||||
|
||||
/** This transaction type converts into confidential MPT balance. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
#include <xrpld/app/tx/detail/ConfidentialMPTConvert.h>
|
||||
#endif
|
||||
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert,
|
||||
Delegation::delegable,
|
||||
featureConfidentialTransfer,
|
||||
noPriv,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfMPTAmount, soeREQUIRED},
|
||||
{sfHolderElGamalPublicKey, soeOPTIONAL},
|
||||
{sfHolderEncryptedAmount, soeREQUIRED},
|
||||
{sfIssuerEncryptedAmount, soeREQUIRED},
|
||||
{sfAuditorEncryptedAmount, soeOPTIONAL},
|
||||
{sfBlindingFactor, soeREQUIRED},
|
||||
{sfZKProof, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type merges MPT inbox. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
#include <xrpld/app/tx/detail/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 <xrpld/app/tx/detail/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 <xrpld/app/tx/detail/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 <xrpld/app/tx/detail/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
|
||||
|
||||
@@ -468,7 +468,8 @@ 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))
|
||||
if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
|
||||
(view.rules().enabled(featureSingleAssetVault) || view.rules().enabled(featureConfidentialTransfer)))
|
||||
{
|
||||
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); !isTesSuccess(err))
|
||||
amount.clear(mptIssue);
|
||||
|
||||
@@ -10,6 +10,11 @@ DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, st
|
||||
|
||||
auto obj = NodeObject::createObject(type, std::move(data), hash);
|
||||
backend_->store(obj);
|
||||
if (cache_)
|
||||
{
|
||||
// After the store, replace a negative cache entry if there is one
|
||||
cache_->canonicalize(hash, obj, [](std::shared_ptr<NodeObject> const& n) { return n->getType() == hotDUMMY; });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -18,41 +23,77 @@ DatabaseNodeImp::asyncFetch(
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback)
|
||||
{
|
||||
if (cache_)
|
||||
{
|
||||
std::shared_ptr<NodeObject> obj = cache_->fetch(hash);
|
||||
if (obj)
|
||||
{
|
||||
callback(obj->getType() == hotDUMMY ? nullptr : obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Database::asyncFetch(hash, ledgerSeq, std::move(callback));
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseNodeImp::sweep()
|
||||
{
|
||||
if (cache_)
|
||||
cache_->sweep();
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeObject>
|
||||
DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nodeObject = nullptr;
|
||||
Status status;
|
||||
std::shared_ptr<NodeObject> nodeObject = cache_ ? cache_->fetch(hash) : nullptr;
|
||||
|
||||
try
|
||||
if (!nodeObject)
|
||||
{
|
||||
status = backend_->fetch(hash, &nodeObject);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
|
||||
Rethrow();
|
||||
}
|
||||
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record not " << (cache_ ? "cached" : "found");
|
||||
|
||||
switch (status)
|
||||
Status status;
|
||||
|
||||
try
|
||||
{
|
||||
status = backend_->fetch(hash.data(), &nodeObject);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
|
||||
Rethrow();
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case ok:
|
||||
if (cache_)
|
||||
{
|
||||
if (nodeObject)
|
||||
cache_->canonicalize_replace_client(hash, nodeObject);
|
||||
else
|
||||
{
|
||||
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
|
||||
cache_->canonicalize_replace_client(hash, notFound);
|
||||
if (notFound->getType() != hotDUMMY)
|
||||
nodeObject = notFound;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case notFound:
|
||||
break;
|
||||
case dataCorrupt:
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
|
||||
break;
|
||||
default:
|
||||
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
case ok:
|
||||
case notFound:
|
||||
break;
|
||||
case dataCorrupt:
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
|
||||
break;
|
||||
default:
|
||||
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
|
||||
break;
|
||||
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record found in cache";
|
||||
if (nodeObject->getType() == hotDUMMY)
|
||||
nodeObject.reset();
|
||||
}
|
||||
|
||||
if (nodeObject)
|
||||
@@ -64,22 +105,66 @@ DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport
|
||||
std::vector<std::shared_ptr<NodeObject>>
|
||||
DatabaseNodeImp::fetchBatch(std::vector<uint256> const& hashes)
|
||||
{
|
||||
std::vector<std::shared_ptr<NodeObject>> results{hashes.size()};
|
||||
using namespace std::chrono;
|
||||
auto const before = steady_clock::now();
|
||||
|
||||
std::vector<std::shared_ptr<NodeObject>> results{hashes.size()};
|
||||
results = backend_->fetchBatch(hashes).first;
|
||||
for (size_t i = 0; i < results.size(); ++i)
|
||||
std::unordered_map<uint256 const*, size_t> indexMap;
|
||||
std::vector<uint256 const*> cacheMisses;
|
||||
uint64_t hits = 0;
|
||||
uint64_t fetches = 0;
|
||||
for (size_t i = 0; i < hashes.size(); ++i)
|
||||
{
|
||||
if (!results[i])
|
||||
auto const& hash = hashes[i];
|
||||
// See if the object already exists in the cache
|
||||
auto nObj = cache_ ? cache_->fetch(hash) : nullptr;
|
||||
++fetches;
|
||||
if (!nObj)
|
||||
{
|
||||
JLOG(j_.error()) << "fetchBatch - "
|
||||
<< "record not found in db. hash = " << strHex(hashes[i]);
|
||||
// Try the database
|
||||
indexMap[&hash] = i;
|
||||
cacheMisses.push_back(&hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
results[i] = nObj->getType() == hotDUMMY ? nullptr : nObj;
|
||||
// It was in the cache.
|
||||
++hits;
|
||||
}
|
||||
}
|
||||
|
||||
JLOG(j_.debug()) << "fetchBatch - cache hits = " << (hashes.size() - cacheMisses.size())
|
||||
<< " - cache misses = " << cacheMisses.size();
|
||||
auto dbResults = backend_->fetchBatch(cacheMisses).first;
|
||||
|
||||
for (size_t i = 0; i < dbResults.size(); ++i)
|
||||
{
|
||||
auto nObj = std::move(dbResults[i]);
|
||||
size_t index = indexMap[cacheMisses[i]];
|
||||
auto const& hash = hashes[index];
|
||||
|
||||
if (nObj)
|
||||
{
|
||||
// Ensure all threads get the same object
|
||||
if (cache_)
|
||||
cache_->canonicalize_replace_client(hash, nObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.error()) << "fetchBatch - "
|
||||
<< "record not found in db or cache. hash = " << strHex(hash);
|
||||
if (cache_)
|
||||
{
|
||||
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
|
||||
cache_->canonicalize_replace_client(hash, notFound);
|
||||
if (notFound->getType() != hotDUMMY)
|
||||
nObj = std::move(notFound);
|
||||
}
|
||||
}
|
||||
results[index] = std::move(nObj);
|
||||
}
|
||||
|
||||
auto fetchDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - before).count();
|
||||
updateFetchMetrics(hashes.size(), 0, fetchDurationUs);
|
||||
updateFetchMetrics(fetches, hits, fetchDurationUs);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ DatabaseRotatingImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchRe
|
||||
std::shared_ptr<NodeObject> nodeObject;
|
||||
try
|
||||
{
|
||||
status = backend->fetch(hash, &nodeObject);
|
||||
status = backend->fetch(hash.data(), &nodeObject);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
|
||||
@@ -115,9 +115,10 @@ public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
Status
|
||||
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) override
|
||||
fetch(void const* key, std::shared_ptr<NodeObject>* pObject) override
|
||||
{
|
||||
XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::fetch : non-null database");
|
||||
uint256 const hash(uint256::fromVoid(key));
|
||||
|
||||
std::lock_guard _(db_->mutex);
|
||||
|
||||
@@ -132,14 +133,14 @@ public:
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
|
||||
fetchBatch(std::vector<uint256> const& hashes) override
|
||||
fetchBatch(std::vector<uint256 const*> const& hashes) override
|
||||
{
|
||||
std::vector<std::shared_ptr<NodeObject>> results;
|
||||
results.reserve(hashes.size());
|
||||
for (auto const& h : hashes)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nObj;
|
||||
Status status = fetch(h, &nObj);
|
||||
Status status = fetch(h->begin(), &nObj);
|
||||
if (status != ok)
|
||||
results.push_back({});
|
||||
else
|
||||
|
||||
@@ -177,17 +177,17 @@ public:
|
||||
}
|
||||
|
||||
Status
|
||||
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pno) override
|
||||
fetch(void const* key, std::shared_ptr<NodeObject>* pno) override
|
||||
{
|
||||
Status status;
|
||||
pno->reset();
|
||||
nudb::error_code ec;
|
||||
db_.fetch(
|
||||
hash.data(),
|
||||
[hash, pno, &status](void const* data, std::size_t size) {
|
||||
key,
|
||||
[key, pno, &status](void const* data, std::size_t size) {
|
||||
nudb::detail::buffer bf;
|
||||
auto const result = nodeobject_decompress(data, size, bf);
|
||||
DecodedBlob decoded(hash.data(), result.first, result.second);
|
||||
DecodedBlob decoded(key, result.first, result.second);
|
||||
if (!decoded.wasOk())
|
||||
{
|
||||
status = dataCorrupt;
|
||||
@@ -205,14 +205,14 @@ public:
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
|
||||
fetchBatch(std::vector<uint256> const& hashes) override
|
||||
fetchBatch(std::vector<uint256 const*> const& hashes) override
|
||||
{
|
||||
std::vector<std::shared_ptr<NodeObject>> results;
|
||||
results.reserve(hashes.size());
|
||||
for (auto const& h : hashes)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nObj;
|
||||
Status status = fetch(h, &nObj);
|
||||
Status status = fetch(h->begin(), &nObj);
|
||||
if (status != ok)
|
||||
results.push_back({});
|
||||
else
|
||||
|
||||
@@ -36,13 +36,13 @@ public:
|
||||
}
|
||||
|
||||
Status
|
||||
fetch(uint256 const&, std::shared_ptr<NodeObject>*) override
|
||||
fetch(void const*, std::shared_ptr<NodeObject>*) override
|
||||
{
|
||||
return notFound;
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
|
||||
fetchBatch(std::vector<uint256> const& hashes) override
|
||||
fetchBatch(std::vector<uint256 const*> const& hashes) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
Status
|
||||
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) override
|
||||
fetch(void const* key, std::shared_ptr<NodeObject>* pObject) override
|
||||
{
|
||||
XRPL_ASSERT(m_db, "xrpl::NodeStore::RocksDBBackend::fetch : non-null database");
|
||||
pObject->reset();
|
||||
@@ -245,7 +245,7 @@ public:
|
||||
Status status(ok);
|
||||
|
||||
rocksdb::ReadOptions const options;
|
||||
rocksdb::Slice const slice(reinterpret_cast<char const*>(hash.data()), m_keyBytes);
|
||||
rocksdb::Slice const slice(static_cast<char const*>(key), m_keyBytes);
|
||||
|
||||
std::string string;
|
||||
|
||||
@@ -253,7 +253,7 @@ public:
|
||||
|
||||
if (getStatus.ok())
|
||||
{
|
||||
DecodedBlob decoded(hash.data(), string.data(), string.size());
|
||||
DecodedBlob decoded(key, string.data(), string.size());
|
||||
|
||||
if (decoded.wasOk())
|
||||
{
|
||||
@@ -288,14 +288,14 @@ public:
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
|
||||
fetchBatch(std::vector<uint256> const& hashes) override
|
||||
fetchBatch(std::vector<uint256 const*> const& hashes) override
|
||||
{
|
||||
std::vector<std::shared_ptr<NodeObject>> results;
|
||||
results.reserve(hashes.size());
|
||||
for (auto const& h : hashes)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nObj;
|
||||
Status status = fetch(h, &nObj);
|
||||
Status status = fetch(h->begin(), &nObj);
|
||||
if (status != ok)
|
||||
results.push_back({});
|
||||
else
|
||||
|
||||
1824
src/libxrpl/protocol/ConfidentialTransfer.cpp
Normal file
1824
src/libxrpl/protocol/ConfidentialTransfer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -106,6 +106,7 @@ 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."),
|
||||
@@ -198,6 +199,7 @@ 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."),
|
||||
|
||||
2991
src/test/app/ConfidentialTransfer_test.cpp
Normal file
2991
src/test/app/ConfidentialTransfer_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -507,7 +507,8 @@ class MPToken_test : public beast::unit_test::suite
|
||||
// (2)
|
||||
mptAlice.set({.account = alice, .flags = 0x00000008, .err = temINVALID_FLAG});
|
||||
|
||||
if (!features[featureSingleAssetVault] && !features[featureDynamicMPT])
|
||||
if (!features[featureSingleAssetVault] && !features[featureDynamicMPT] &&
|
||||
!features[featureConfidentialTransfer])
|
||||
{
|
||||
// test invalid flags - nothing is being changed
|
||||
mptAlice.set({.account = alice, .flags = 0x00000000, .err = tecNO_PERMISSION});
|
||||
@@ -2550,6 +2551,7 @@ class MPToken_test : public beast::unit_test::suite
|
||||
tmfMPTSetCanTrade | tmfMPTClearCanTrade,
|
||||
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer,
|
||||
tmfMPTSetCanClawback | tmfMPTClearCanClawback,
|
||||
tmfMPTSetPrivacy | tmfMPTClearPrivacy,
|
||||
tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTClearCanTrade,
|
||||
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanEscrow | tmfMPTClearCanClawback};
|
||||
|
||||
|
||||
@@ -490,8 +490,19 @@ public:
|
||||
Env env(*this, envconfig(onlineDelete));
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Create NodeStore with two backends to allow online deletion of data.
|
||||
// Normally, SHAMapStoreImp handles all these details.
|
||||
// Create the backend. Normally, SHAMapStoreImp handles all these
|
||||
// details
|
||||
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
|
||||
|
||||
// Provide default values:
|
||||
if (!nscfg.exists("cache_size"))
|
||||
nscfg.set(
|
||||
"cache_size", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
|
||||
|
||||
if (!nscfg.exists("cache_age"))
|
||||
nscfg.set(
|
||||
"cache_age", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
|
||||
|
||||
NodeStoreScheduler scheduler(env.app().getJobQueue());
|
||||
|
||||
std::string const writableDb = "write";
|
||||
@@ -499,8 +510,9 @@ public:
|
||||
auto writableBackend = makeBackendRotating(env, scheduler, writableDb);
|
||||
auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
|
||||
|
||||
// Create NodeStore with two backends to allow online deletion of
|
||||
// data
|
||||
constexpr int readThreads = 4;
|
||||
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
|
||||
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
|
||||
scheduler,
|
||||
readThreads,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,11 @@
|
||||
#include <test/jtx/ter.h>
|
||||
#include <test/jtx/txflags.h>
|
||||
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
@@ -94,6 +97,7 @@ struct MPTCreate
|
||||
struct MPTInit
|
||||
{
|
||||
Holders holders = {};
|
||||
std::optional<Account> auditor = std::nullopt;
|
||||
PrettyAmount const xrp = XRP(10'000);
|
||||
PrettyAmount const xrpHolders = XRP(10'000);
|
||||
bool fund = true;
|
||||
@@ -108,6 +112,7 @@ struct MPTInitDef
|
||||
Env& env;
|
||||
Account issuer;
|
||||
Holders holders = {};
|
||||
std::optional<Account> auditor = std::nullopt;
|
||||
std::uint16_t transferFee = 0;
|
||||
std::optional<std::uint64_t> pay = std::nullopt;
|
||||
std::uint32_t flags = MPTDEXFlags;
|
||||
@@ -152,18 +157,131 @@ struct MPTSet
|
||||
std::optional<std::string> metadata = std::nullopt;
|
||||
std::optional<Account> delegate = std::nullopt;
|
||||
std::optional<uint256> domainID = std::nullopt;
|
||||
std::optional<Buffer> issuerPubKey = std::nullopt;
|
||||
std::optional<Buffer> auditorPubKey = std::nullopt;
|
||||
std::optional<TER> err = std::nullopt;
|
||||
};
|
||||
|
||||
struct MPTConvert
|
||||
{
|
||||
std::optional<Account> account = std::nullopt;
|
||||
std::optional<MPTID> id = std::nullopt;
|
||||
std::optional<std::uint64_t> amt = std::nullopt;
|
||||
std::optional<std::string> proof = std::nullopt;
|
||||
std::optional<bool> fillAuditorEncryptedAmt = true;
|
||||
// indicates whether to autofill schnorr proof.
|
||||
// default : auto generate proof if holderPubKey is present.
|
||||
// true: force proof generation.
|
||||
// false: force proof omission.
|
||||
std::optional<bool> fillSchnorrProof = std::nullopt;
|
||||
std::optional<Buffer> holderPubKey = std::nullopt;
|
||||
std::optional<Buffer> holderEncryptedAmt = std::nullopt;
|
||||
std::optional<Buffer> issuerEncryptedAmt = std::nullopt;
|
||||
std::optional<Buffer> auditorEncryptedAmt = std::nullopt;
|
||||
|
||||
std::optional<Buffer> blindingFactor = std::nullopt;
|
||||
std::optional<std::uint32_t> ownerCount = std::nullopt;
|
||||
std::optional<std::uint32_t> holderCount = std::nullopt;
|
||||
std::optional<std::uint32_t> flags = std::nullopt;
|
||||
std::optional<TER> err = std::nullopt;
|
||||
};
|
||||
|
||||
struct MPTMergeInbox
|
||||
{
|
||||
std::optional<Account> account = std::nullopt;
|
||||
std::optional<MPTID> id = std::nullopt;
|
||||
std::optional<std::uint32_t> ownerCount = std::nullopt;
|
||||
std::optional<std::uint32_t> holderCount = std::nullopt;
|
||||
std::optional<std::uint32_t> flags = std::nullopt;
|
||||
std::optional<TER> err = std::nullopt;
|
||||
};
|
||||
|
||||
struct MPTConfidentialSend
|
||||
{
|
||||
std::optional<Account> account = std::nullopt;
|
||||
std::optional<Account> dest = std::nullopt;
|
||||
std::optional<MPTID> id = std::nullopt;
|
||||
// amt is to generate encrypted amounts for testing purposes
|
||||
std::optional<std::uint64_t> amt = std::nullopt;
|
||||
std::optional<std::string> proof = std::nullopt;
|
||||
std::optional<Buffer> senderEncryptedAmt = std::nullopt;
|
||||
std::optional<Buffer> destEncryptedAmt = std::nullopt;
|
||||
std::optional<Buffer> issuerEncryptedAmt = std::nullopt;
|
||||
std::optional<Buffer> auditorEncryptedAmt = std::nullopt;
|
||||
std::optional<std::vector<std::string>> credentials = std::nullopt;
|
||||
// not an txn param, only used for autofilling
|
||||
std::optional<Buffer> blindingFactor = std::nullopt;
|
||||
std::optional<Buffer> amountCommitment = std::nullopt;
|
||||
std::optional<Buffer> balanceCommitment = std::nullopt;
|
||||
std::optional<std::uint32_t> ownerCount = std::nullopt;
|
||||
std::optional<std::uint32_t> holderCount = std::nullopt;
|
||||
std::optional<std::uint32_t> flags = std::nullopt;
|
||||
std::optional<TER> err = std::nullopt;
|
||||
};
|
||||
|
||||
struct MPTConvertBack
|
||||
{
|
||||
std::optional<Account> account = std::nullopt;
|
||||
std::optional<MPTID> id = std::nullopt;
|
||||
std::optional<std::uint64_t> amt = std::nullopt;
|
||||
std::optional<Buffer> proof = std::nullopt;
|
||||
std::optional<Buffer> holderEncryptedAmt = std::nullopt;
|
||||
std::optional<Buffer> issuerEncryptedAmt = std::nullopt;
|
||||
std::optional<Buffer> auditorEncryptedAmt = std::nullopt;
|
||||
std::optional<bool> fillAuditorEncryptedAmt = true;
|
||||
// not an txn param, only used for autofilling
|
||||
std::optional<Buffer> blindingFactor = std::nullopt;
|
||||
std::optional<Buffer> pedersenCommitment = std::nullopt;
|
||||
std::optional<std::uint32_t> ownerCount = std::nullopt;
|
||||
std::optional<std::uint32_t> holderCount = std::nullopt;
|
||||
std::optional<std::uint32_t> flags = std::nullopt;
|
||||
std::optional<TER> err = std::nullopt;
|
||||
};
|
||||
|
||||
struct MPTConfidentialClawback
|
||||
{
|
||||
std::optional<Account> account = std::nullopt;
|
||||
std::optional<Account> holder = std::nullopt;
|
||||
std::optional<MPTID> id = std::nullopt;
|
||||
std::optional<std::uint64_t> amt = std::nullopt;
|
||||
std::optional<std::string> proof = std::nullopt;
|
||||
std::optional<std::uint32_t> ownerCount = std::nullopt;
|
||||
std::optional<std::uint32_t> holderCount = std::nullopt;
|
||||
std::optional<std::uint32_t> flags = std::nullopt;
|
||||
std::optional<TER> err = std::nullopt;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores the parameterss that are exclusively used to generate a
|
||||
* pedersen linkage proof
|
||||
*/
|
||||
struct PedersenProofParams
|
||||
{
|
||||
Buffer const pedersenCommitment;
|
||||
uint64_t const amt; // either spending balance or value to be transferred
|
||||
Buffer const encryptedAmt;
|
||||
Buffer const blindingFactor;
|
||||
};
|
||||
|
||||
class MPTTester
|
||||
{
|
||||
Env& env_;
|
||||
Account const issuer_;
|
||||
std::unordered_map<std::string, Account> const holders_;
|
||||
std::optional<Account> const auditor_;
|
||||
std::optional<MPTID> id_;
|
||||
bool close_;
|
||||
std::unordered_map<AccountID, Buffer> pubKeys;
|
||||
std::unordered_map<AccountID, Buffer> privKeys;
|
||||
|
||||
public:
|
||||
enum EncryptedBalanceType {
|
||||
ISSUER_ENCRYPTED_BALANCE,
|
||||
HOLDER_ENCRYPTED_INBOX,
|
||||
HOLDER_ENCRYPTED_SPENDING,
|
||||
AUDITOR_ENCRYPTED_BALANCE,
|
||||
};
|
||||
|
||||
MPTTester(Env& env, Account const& issuer, MPTInit const& constr = {});
|
||||
MPTTester(MPTInitDef const& constr);
|
||||
MPTTester(
|
||||
@@ -201,6 +319,21 @@ public:
|
||||
static Json::Value
|
||||
setjv(MPTSet const& set = {});
|
||||
|
||||
void
|
||||
convert(MPTConvert const& arg = MPTConvert{});
|
||||
|
||||
void
|
||||
mergeInbox(MPTMergeInbox const& arg = MPTMergeInbox{});
|
||||
|
||||
void
|
||||
send(MPTConfidentialSend const& arg = MPTConfidentialSend{});
|
||||
|
||||
void
|
||||
convertBack(MPTConvertBack const& arg = MPTConvertBack{});
|
||||
|
||||
void
|
||||
confidentialClaw(MPTConfidentialClawback const& arg = MPTConfidentialClawback{});
|
||||
|
||||
[[nodiscard]] bool
|
||||
checkDomainID(std::optional<uint256> expected) const;
|
||||
|
||||
@@ -210,6 +343,9 @@ public:
|
||||
[[nodiscard]] bool
|
||||
checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
checkIssuanceConfidentialBalance(std::int64_t expectedAmount) const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
checkFlags(uint32_t const expectedFlags, std::optional<Account> const& holder = std::nullopt) const;
|
||||
|
||||
@@ -230,6 +366,7 @@ public:
|
||||
{
|
||||
return issuer_;
|
||||
}
|
||||
|
||||
Account const&
|
||||
holder(std::string const& h) const;
|
||||
|
||||
@@ -257,6 +394,12 @@ public:
|
||||
std::int64_t
|
||||
getBalance(Account const& account) const;
|
||||
|
||||
std::int64_t
|
||||
getIssuanceConfidentialBalance() const;
|
||||
|
||||
std::optional<Buffer>
|
||||
getEncryptedBalance(Account const& account, EncryptedBalanceType option = HOLDER_ENCRYPTED_INBOX) const;
|
||||
|
||||
MPT
|
||||
operator[](std::string const& name) const;
|
||||
|
||||
@@ -265,6 +408,79 @@ public:
|
||||
|
||||
operator Asset() const;
|
||||
|
||||
bool
|
||||
printMPT(Account const& holder_) const;
|
||||
|
||||
void
|
||||
generateKeyPair(Account const& account);
|
||||
|
||||
std::optional<Buffer>
|
||||
getPubKey(Account const& account) const;
|
||||
|
||||
std::optional<Buffer>
|
||||
getPrivKey(Account const& account) const;
|
||||
|
||||
Buffer
|
||||
encryptAmount(Account const& account, uint64_t const amt, Buffer const& blindingFactor) const;
|
||||
|
||||
std::optional<uint64_t>
|
||||
decryptAmount(Account const& account, Buffer const& amt) const;
|
||||
|
||||
std::optional<uint64_t>
|
||||
getDecryptedBalance(Account const& account, EncryptedBalanceType balanceType) const;
|
||||
|
||||
std::int64_t
|
||||
getIssuanceOutstandingBalance() const;
|
||||
|
||||
std::optional<Buffer>
|
||||
getClawbackProof(Account const& holder, std::uint64_t amount, Buffer const& privateKey, uint256 const& txHash)
|
||||
const;
|
||||
|
||||
std::optional<Buffer>
|
||||
getSchnorrProof(Account const& account, uint256 const& ctxHash) const;
|
||||
|
||||
std::optional<Buffer>
|
||||
getConfidentialSendProof(
|
||||
Account const& sender,
|
||||
std::uint64_t const amount,
|
||||
std::vector<ConfidentialRecipient> const& recipients,
|
||||
Slice const& blindingFactor,
|
||||
std::size_t const nRecipients,
|
||||
uint256 const& contextHash,
|
||||
PedersenProofParams const& amountParams,
|
||||
PedersenProofParams const& balanceParams) const;
|
||||
|
||||
Buffer
|
||||
getConvertBackProof(
|
||||
Account const& holder,
|
||||
std::uint64_t const amount,
|
||||
uint256 const& contextHash,
|
||||
Buffer const& holderCiphertext,
|
||||
Buffer const& issuerCiphertext,
|
||||
std::optional<Buffer> const& auditorCiphertext,
|
||||
Buffer const& blindingFactor,
|
||||
PedersenProofParams const& pcParams) const;
|
||||
|
||||
std::uint32_t
|
||||
getMPTokenVersion(Account const account) const;
|
||||
|
||||
Buffer
|
||||
getAmountLinkageProof(
|
||||
Buffer const& pubKey,
|
||||
Buffer const& blindingFactor,
|
||||
uint256 const& contextHash,
|
||||
PedersenProofParams const& params) const;
|
||||
|
||||
Buffer
|
||||
getBalanceLinkageProof(
|
||||
Account const& account,
|
||||
uint256 const& contextHash,
|
||||
Buffer const& pubKey,
|
||||
PedersenProofParams const& params) const;
|
||||
|
||||
Buffer
|
||||
getPedersenCommitment(std::uint64_t const amount, Buffer const& pedersenBlindingFactor);
|
||||
|
||||
private:
|
||||
using SLEP = SLE::const_pointer;
|
||||
bool
|
||||
@@ -294,6 +510,16 @@ private:
|
||||
|
||||
std::uint32_t
|
||||
getFlags(std::optional<Account> const& holder) const;
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
fillConversionCiphertexts(
|
||||
T const& arg,
|
||||
Json::Value& jv,
|
||||
Buffer& holderCiphertext,
|
||||
Buffer& issuerCiphertext,
|
||||
std::optional<Buffer>& auditorCiphertext,
|
||||
Buffer& blindingFactor) const;
|
||||
};
|
||||
|
||||
} // namespace jtx
|
||||
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
{
|
||||
std::shared_ptr<NodeObject> object;
|
||||
|
||||
Status const status = backend.fetch(batch[i]->getHash(), &object);
|
||||
Status const status = backend.fetch(batch[i]->getHash().cbegin(), &object);
|
||||
|
||||
BEAST_EXPECT(status == ok);
|
||||
|
||||
@@ -158,7 +158,7 @@ public:
|
||||
{
|
||||
std::shared_ptr<NodeObject> object;
|
||||
|
||||
Status const status = backend.fetch(batch[i]->getHash(), &object);
|
||||
Status const status = backend.fetch(batch[i]->getHash().cbegin(), &object);
|
||||
|
||||
BEAST_EXPECT(status == notFound);
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ public:
|
||||
std::shared_ptr<NodeObject> obj;
|
||||
std::shared_ptr<NodeObject> result;
|
||||
obj = seq1_.obj(dist_(gen_));
|
||||
backend_.fetch(obj->getHash(), &result);
|
||||
backend_.fetch(obj->getHash().data(), &result);
|
||||
suite_.expect(result && isSame(result, obj));
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
@@ -371,9 +371,9 @@ public:
|
||||
{
|
||||
try
|
||||
{
|
||||
auto const hash = seq2_.key(i);
|
||||
auto const key = seq2_.key(i);
|
||||
std::shared_ptr<NodeObject> result;
|
||||
backend_.fetch(hash, &result);
|
||||
backend_.fetch(key.data(), &result);
|
||||
suite_.expect(!result);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
@@ -438,9 +438,9 @@ public:
|
||||
{
|
||||
if (rand_(gen_) < missingNodePercent)
|
||||
{
|
||||
auto const hash = seq2_.key(dist_(gen_));
|
||||
auto const key = seq2_.key(dist_(gen_));
|
||||
std::shared_ptr<NodeObject> result;
|
||||
backend_.fetch(hash, &result);
|
||||
backend_.fetch(key.data(), &result);
|
||||
suite_.expect(!result);
|
||||
}
|
||||
else
|
||||
@@ -448,7 +448,7 @@ public:
|
||||
std::shared_ptr<NodeObject> obj;
|
||||
std::shared_ptr<NodeObject> result;
|
||||
obj = seq1_.obj(dist_(gen_));
|
||||
backend_.fetch(obj->getHash(), &result);
|
||||
backend_.fetch(obj->getHash().data(), &result);
|
||||
suite_.expect(result && isSame(result, obj));
|
||||
}
|
||||
}
|
||||
@@ -525,7 +525,7 @@ public:
|
||||
auto const j = older_(gen_);
|
||||
obj = seq1_.obj(j);
|
||||
std::shared_ptr<NodeObject> result1;
|
||||
backend_.fetch(obj->getHash(), &result);
|
||||
backend_.fetch(obj->getHash().data(), &result);
|
||||
suite_.expect(result != nullptr);
|
||||
suite_.expect(isSame(result, obj));
|
||||
}
|
||||
@@ -543,7 +543,7 @@ public:
|
||||
std::shared_ptr<NodeObject> result;
|
||||
auto const j = recent_(gen_);
|
||||
obj = seq1_.obj(j);
|
||||
backend_.fetch(obj->getHash(), &result);
|
||||
backend_.fetch(obj->getHash().data(), &result);
|
||||
suite_.expect(!result || isSame(result, obj));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,12 +8,8 @@ add_custom_target(xrpl.tests)
|
||||
|
||||
# Test helpers
|
||||
add_library(xrpl.helpers.test STATIC)
|
||||
target_sources(xrpl.helpers.test PRIVATE
|
||||
helpers/TestSink.cpp
|
||||
)
|
||||
target_include_directories(xrpl.helpers.test PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
target_sources(xrpl.helpers.test PRIVATE helpers/TestSink.cpp)
|
||||
target_include_directories(xrpl.helpers.test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(xrpl.helpers.test PRIVATE xrpl.libxrpl)
|
||||
|
||||
# Common library dependencies for the rest of the tests.
|
||||
@@ -34,8 +30,8 @@ target_link_libraries(xrpl.test.json PRIVATE xrpl.imports.test)
|
||||
add_dependencies(xrpl.tests xrpl.test.json)
|
||||
|
||||
# Network unit tests are currently not supported on Windows
|
||||
if(NOT WIN32)
|
||||
xrpl_add_test(net)
|
||||
target_link_libraries(xrpl.test.net PRIVATE xrpl.imports.test)
|
||||
add_dependencies(xrpl.tests xrpl.test.net)
|
||||
endif()
|
||||
if (NOT WIN32)
|
||||
xrpl_add_test(net)
|
||||
target_link_libraries(xrpl.test.net PRIVATE xrpl.imports.test)
|
||||
add_dependencies(xrpl.tests xrpl.test.net)
|
||||
endif ()
|
||||
|
||||
@@ -130,6 +130,14 @@ std::unique_ptr<NodeStore::Database>
|
||||
SHAMapStoreImp::makeNodeStore(int readThreads)
|
||||
{
|
||||
auto nscfg = app_.config().section(ConfigSection::nodeDatabase());
|
||||
|
||||
// Provide default values:
|
||||
if (!nscfg.exists("cache_size"))
|
||||
nscfg.set("cache_size", std::to_string(app_.config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
|
||||
|
||||
if (!nscfg.exists("cache_age"))
|
||||
nscfg.set("cache_age", std::to_string(app_.config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
|
||||
|
||||
std::unique_ptr<NodeStore::Database> db;
|
||||
|
||||
if (deleteInterval_)
|
||||
@@ -218,6 +226,8 @@ SHAMapStoreImp::run()
|
||||
LedgerIndex lastRotated = state_db_.getState().lastRotated;
|
||||
netOPs_ = &app_.getOPs();
|
||||
ledgerMaster_ = &app_.getLedgerMaster();
|
||||
fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache());
|
||||
treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache());
|
||||
|
||||
if (advisoryDelete_)
|
||||
canDelete_ = state_db_.getCanDelete();
|
||||
@@ -480,12 +490,16 @@ void
|
||||
SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
|
||||
{
|
||||
ledgerMaster_->clearLedgerCachePrior(validatedSeq);
|
||||
fullBelowCache_->clear();
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapStoreImp::freshenCaches()
|
||||
{
|
||||
freshenCache(*app_.getNodeFamily().getTreeNodeCache()) && freshenCache(app_.getMasterTransaction().getCache());
|
||||
if (freshenCache(*treeNodeCache_))
|
||||
return;
|
||||
if (freshenCache(app_.getMasterTransaction().getCache()))
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -94,6 +94,8 @@ private:
|
||||
// as of run() or before
|
||||
NetworkOPs* netOPs_ = nullptr;
|
||||
LedgerMaster* ledgerMaster_ = nullptr;
|
||||
FullBelowCache* fullBelowCache_ = nullptr;
|
||||
TreeNodeCache* treeNodeCache_ = nullptr;
|
||||
|
||||
static constexpr auto nodeStoreName_ = "NodeStore";
|
||||
|
||||
|
||||
160
src/xrpld/app/tx/detail/ConfidentialMPTClawback.cpp
Normal file
160
src/xrpld/app/tx/detail/ConfidentialMPTClawback.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
#include <xrpld/app/tx/detail/ConfidentialMPTClawback.h>
|
||||
|
||||
#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>
|
||||
|
||||
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: issuer must be the same as account
|
||||
if (sleIssuance->getAccountID(sfIssuer) != account)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Check if issuance has issuer ElGamal public key
|
||||
if (!sleIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// Check if clawback is allowed
|
||||
if (!sleIssuance->isFlag(lsfMPTCanClawback))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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, ctx.tx[sfSequence], mptIssuanceID, amount, holder);
|
||||
|
||||
// Verify the revealed confidential amount by the issuer matches the exact
|
||||
// confidential balance of the holder.
|
||||
return verifyClawbackEqualityProof(
|
||||
amount,
|
||||
ctx.tx[sfZKProof],
|
||||
(*sleIssuance)[sfIssuerElGamalPublicKey],
|
||||
(*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;
|
||||
|
||||
auto const clawAmount = ctx_.tx[sfMPTAmount];
|
||||
|
||||
Slice const holderPubKey = (*sleHolderMPToken)[sfHolderElGamalPublicKey];
|
||||
Slice const issuerPubKey = (*sleIssuance)[sfIssuerElGamalPublicKey];
|
||||
|
||||
// After clawback, the balance should be encrypted zero.
|
||||
auto const encZeroForHolder = encryptCanonicalZeroAmount(holderPubKey, holder, mptIssuanceID);
|
||||
if (!encZeroForHolder)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const 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] = *encZeroForIssuer;
|
||||
(*sleHolderMPToken)[sfConfidentialBalanceVersion] = 0;
|
||||
|
||||
if (sleHolderMPToken->isFieldPresent(sfAuditorEncryptedBalance))
|
||||
{
|
||||
// Sanity check: the issuance must have an auditor public key if
|
||||
// auditing is enabled.
|
||||
if (!sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
Slice const auditorPubKey = (*sleIssuance)[sfAuditorElGamalPublicKey];
|
||||
|
||||
auto const encZeroForAuditor = encryptCanonicalZeroAmount(auditorPubKey, holder, mptIssuanceID);
|
||||
|
||||
if (!encZeroForAuditor)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
(*sleHolderMPToken)[sfAuditorEncryptedBalance] = *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
|
||||
29
src/xrpld/app/tx/detail/ConfidentialMPTClawback.h
Normal file
29
src/xrpld/app/tx/detail/ConfidentialMPTClawback.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef XRPL_TX_CONFIDENTIALCLAWSBACK_H_INCLUDED
|
||||
#define XRPL_TX_CONFIDENTIALCLAWSBACK_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
249
src/xrpld/app/tx/detail/ConfidentialMPTConvert.cpp
Normal file
249
src/xrpld/app/tx/detail/ConfidentialMPTConvert.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
#include <xrpld/app/tx/detail/ConfidentialMPTConvert.h>
|
||||
|
||||
#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>
|
||||
|
||||
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[sfBlindingFactor].size() != ecBlindingFactorLength)
|
||||
return temMALFORMED;
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfHolderElGamalPublicKey))
|
||||
{
|
||||
if (ctx.tx[sfHolderElGamalPublicKey].length() != ecPubKeyLength)
|
||||
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 registerring holder ec public key
|
||||
if (ctx.tx[sfZKProof].size() != ecSchnorrProofLength)
|
||||
return temMALFORMED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// zkp should not be present if public key was already set
|
||||
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(lsfMPTCanPrivacy))
|
||||
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
|
||||
|
||||
// issuer has not uploaded their pub key yet
|
||||
if (!sleIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
|
||||
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey);
|
||||
|
||||
// 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};
|
||||
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(sfHolderElGamalPublicKey);
|
||||
auto const hasHolderKeyInTx = ctx.tx.isFieldPresent(sfHolderElGamalPublicKey);
|
||||
|
||||
// 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;
|
||||
|
||||
Slice holderPubKey;
|
||||
if (hasHolderKeyInTx)
|
||||
{
|
||||
holderPubKey = ctx.tx[sfHolderElGamalPublicKey];
|
||||
|
||||
auto const contextHash = getConvertContextHash(account, ctx.tx[sfSequence], issuanceID, amount);
|
||||
|
||||
// when register new pk, verify through schnorr proof
|
||||
if (!isTesSuccess(verifySchnorrProof(holderPubKey, ctx.tx[sfZKProof], contextHash)))
|
||||
{
|
||||
return tecBAD_PROOF;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
holderPubKey = (*sleMptoken)[sfHolderElGamalPublicKey];
|
||||
}
|
||||
|
||||
std::optional<ConfidentialRecipient> auditor;
|
||||
if (hasAuditor)
|
||||
{
|
||||
auditor.emplace(
|
||||
ConfidentialRecipient{(*sleIssuance)[sfAuditorElGamalPublicKey], ctx.tx[sfAuditorEncryptedAmount]});
|
||||
}
|
||||
|
||||
return verifyRevealedAmount(
|
||||
amount,
|
||||
ctx.tx[sfBlindingFactor],
|
||||
{holderPubKey, ctx.tx[sfHolderEncryptedAmount]},
|
||||
{(*sleIssuance)[sfIssuerElGamalPublicKey], ctx.tx[sfIssuerEncryptedAmount]},
|
||||
auditor);
|
||||
}
|
||||
|
||||
TER
|
||||
ConfidentialMPTConvert::doApply()
|
||||
{
|
||||
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
|
||||
|
||||
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
|
||||
if (!sleMptoken)
|
||||
return tecINTERNAL;
|
||||
|
||||
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
|
||||
if (!sleIssuance)
|
||||
return tecINTERNAL;
|
||||
|
||||
auto const amtToConvert = ctx_.tx[sfMPTAmount];
|
||||
auto const amt = (*sleMptoken)[~sfMPTAmount].value_or(0);
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfHolderElGamalPublicKey))
|
||||
(*sleMptoken)[sfHolderElGamalPublicKey] = ctx_.tx[sfHolderElGamalPublicKey];
|
||||
|
||||
(*sleMptoken)[sfMPTAmount] = amt - amtToConvert;
|
||||
(*sleIssuance)[sfConfidentialOutstandingAmount] =
|
||||
(*sleIssuance)[~sfConfidentialOutstandingAmount].value_or(0) + amtToConvert;
|
||||
|
||||
Slice const holderEc = ctx_.tx[sfHolderEncryptedAmount];
|
||||
Slice const issuerEc = ctx_.tx[sfIssuerEncryptedAmount];
|
||||
|
||||
auto const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
|
||||
|
||||
// todo: we should check sfConfidentialBalanceSpending depending on
|
||||
// if we encrypt zero amount
|
||||
if (sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
|
||||
sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
|
||||
sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
|
||||
{
|
||||
// homomorphically add holder's encrypted balance
|
||||
{
|
||||
Buffer sum(ecGamalEncryptedTotalLength);
|
||||
if (TER const ter = homomorphicAdd(holderEc, (*sleMptoken)[sfConfidentialBalanceInbox], sum);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleMptoken)[sfConfidentialBalanceInbox] = sum;
|
||||
}
|
||||
|
||||
// homomorphically add issuer's encrypted balance
|
||||
{
|
||||
Buffer sum(ecGamalEncryptedTotalLength);
|
||||
if (TER const ter = homomorphicAdd(issuerEc, (*sleMptoken)[sfIssuerEncryptedBalance], sum);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleMptoken)[sfIssuerEncryptedBalance] = sum;
|
||||
}
|
||||
|
||||
// homomorphically add auditor's encrypted balance
|
||||
if (auditorEc)
|
||||
{
|
||||
Buffer sum(ecGamalEncryptedTotalLength);
|
||||
if (TER const ter = homomorphicAdd(*auditorEc, (*sleMptoken)[sfAuditorEncryptedBalance], sum);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleMptoken)[sfAuditorEncryptedBalance] = sum;
|
||||
}
|
||||
}
|
||||
else if (
|
||||
!sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
|
||||
!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
|
||||
!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
|
||||
{
|
||||
(*sleMptoken)[sfConfidentialBalanceInbox] = holderEc;
|
||||
(*sleMptoken)[sfIssuerEncryptedBalance] = issuerEc;
|
||||
(*sleMptoken)[sfConfidentialBalanceVersion] = 0;
|
||||
|
||||
if (auditorEc)
|
||||
(*sleMptoken)[sfAuditorEncryptedBalance] = *auditorEc;
|
||||
|
||||
// encrypt sfConfidentialBalanceSpending with zero balance
|
||||
auto const zeroBalance =
|
||||
encryptCanonicalZeroAmount((*sleMptoken)[sfHolderElGamalPublicKey], account_, mptIssuanceID);
|
||||
|
||||
if (!zeroBalance)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
(*sleMptoken)[sfConfidentialBalanceSpending] = *zeroBalance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// both sfIssuerEncryptedBalance and sfConfidentialBalanceInbox should
|
||||
// exist together
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
view().update(sleIssuance);
|
||||
view().update(sleMptoken);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
29
src/xrpld/app/tx/detail/ConfidentialMPTConvert.h
Normal file
29
src/xrpld/app/tx/detail/ConfidentialMPTConvert.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef XRPL_TX_CONFIDENTIALCONVERT_H_INCLUDED
|
||||
#define XRPL_TX_CONFIDENTIALCONVERT_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
230
src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.cpp
Normal file
230
src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
#include <xrpld/app/tx/detail/ConfidentialMPTConvertBack.h>
|
||||
|
||||
#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 <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 (ctx.tx[sfBlindingFactor].size() != ecBlindingFactorLength)
|
||||
return temMALFORMED;
|
||||
|
||||
if (ctx.tx[sfBalanceCommitment].size() != ecPedersenCommitmentLength)
|
||||
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
|
||||
verifyProofs(STTx const& tx, std::shared_ptr<SLE const> const& issuance, std::shared_ptr<SLE const> const& mptoken)
|
||||
{
|
||||
if (!mptoken->isFieldPresent(sfHolderElGamalPublicKey))
|
||||
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)[sfHolderElGamalPublicKey];
|
||||
|
||||
auto const contextHash = getConvertBackContextHash(
|
||||
account, tx[sfSequence], mptIssuanceID, amount, (*mptoken)[~sfConfidentialBalanceVersion].value_or(0));
|
||||
|
||||
// Prepare Auditor Info
|
||||
std::optional<ConfidentialRecipient> auditor;
|
||||
bool const hasAuditor = issuance->isFieldPresent(sfAuditorElGamalPublicKey);
|
||||
if (hasAuditor)
|
||||
{
|
||||
auditor.emplace(ConfidentialRecipient{(*issuance)[sfAuditorElGamalPublicKey], tx[sfAuditorEncryptedAmount]});
|
||||
}
|
||||
|
||||
if (auto const ter = verifyRevealedAmount(
|
||||
amount,
|
||||
blindingFactor,
|
||||
{holderPubKey, tx[sfHolderEncryptedAmount]},
|
||||
{(*issuance)[sfIssuerElGamalPublicKey], tx[sfIssuerEncryptedAmount]},
|
||||
auditor);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Use a pointer to parse each proof component
|
||||
Buffer zkps = Buffer(tx[sfZKProof].data(), tx[sfZKProof].size());
|
||||
std::uint8_t* ptr = zkps.data();
|
||||
|
||||
// verify el gamal pedersen linkage
|
||||
{
|
||||
Buffer const pedersen{ptr, ecPedersenProofLength};
|
||||
if (auto const ter = verifyBalancePcmLinkage(
|
||||
pedersen,
|
||||
(*mptoken)[sfConfidentialBalanceSpending],
|
||||
holderPubKey,
|
||||
tx[sfBalanceCommitment],
|
||||
contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter;
|
||||
}
|
||||
|
||||
// increment pointer
|
||||
ptr += ecPedersenProofLength;
|
||||
}
|
||||
|
||||
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(lsfMPTCanPrivacy))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
|
||||
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey);
|
||||
|
||||
// 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(sfHolderElGamalPublicKey))
|
||||
{
|
||||
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;
|
||||
|
||||
auto sleIssuance = view().peek(keylet::mptIssuance(mptIssuanceID));
|
||||
if (!sleIssuance)
|
||||
return tecINTERNAL;
|
||||
|
||||
auto const amtToConvertBack = ctx_.tx[sfMPTAmount];
|
||||
auto const amt = (*sleMptoken)[~sfMPTAmount].value_or(0);
|
||||
|
||||
(*sleMptoken)[sfMPTAmount] = amt + amtToConvertBack;
|
||||
(*sleIssuance)[sfConfidentialOutstandingAmount] =
|
||||
(*sleIssuance)[sfConfidentialOutstandingAmount] - amtToConvertBack;
|
||||
|
||||
std::optional<Slice> const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
|
||||
|
||||
// homomorphically subtract holder's encrypted balance
|
||||
{
|
||||
Buffer res(ecGamalEncryptedTotalLength);
|
||||
if (TER const ter = homomorphicSubtract(
|
||||
(*sleMptoken)[sfConfidentialBalanceSpending], ctx_.tx[sfHolderEncryptedAmount], res);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleMptoken)[sfConfidentialBalanceSpending] = res;
|
||||
}
|
||||
|
||||
// homomorphically subtract issuer's encrypted balance
|
||||
{
|
||||
Buffer res(ecGamalEncryptedTotalLength);
|
||||
if (TER const ter =
|
||||
homomorphicSubtract((*sleMptoken)[sfIssuerEncryptedBalance], ctx_.tx[sfIssuerEncryptedAmount], res);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleMptoken)[sfIssuerEncryptedBalance] = res;
|
||||
}
|
||||
|
||||
if (auditorEc)
|
||||
{
|
||||
Buffer res(ecGamalEncryptedTotalLength);
|
||||
if (TER const ter =
|
||||
homomorphicSubtract((*sleMptoken)[sfAuditorEncryptedBalance], ctx_.tx[sfAuditorEncryptedAmount], res);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleMptoken)[sfAuditorEncryptedBalance] = res;
|
||||
}
|
||||
|
||||
// increment version
|
||||
incrementConfidentialVersion(*sleMptoken);
|
||||
|
||||
view().update(sleIssuance);
|
||||
view().update(sleMptoken);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
29
src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.h
Normal file
29
src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef XRPL_TX_CONFIDENTIALCONVERTBACK_H_INCLUDED
|
||||
#define XRPL_TX_CONFIDENTIALCONVERTBACK_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
92
src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.cpp
Normal file
92
src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include <xrpld/app/tx/detail/ConfidentialMPTMergeInbox.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>
|
||||
|
||||
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(lsfMPTCanPrivacy))
|
||||
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(sfHolderElGamalPublicKey))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
ConfidentialMPTMergeInbox::doApply()
|
||||
{
|
||||
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
|
||||
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, account_));
|
||||
if (!sleMptoken)
|
||||
return tecINTERNAL;
|
||||
|
||||
// sanity check
|
||||
if (!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
|
||||
!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
|
||||
!sleMptoken->isFieldPresent(sfHolderElGamalPublicKey))
|
||||
{
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
// homomorphically add holder's encrypted balance
|
||||
Buffer sum(ecGamalEncryptedTotalLength);
|
||||
if (TER const ter = homomorphicAdd(
|
||||
(*sleMptoken)[sfConfidentialBalanceSpending], (*sleMptoken)[sfConfidentialBalanceInbox], sum);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleMptoken)[sfConfidentialBalanceSpending] = sum;
|
||||
|
||||
auto const zeroEncryption =
|
||||
encryptCanonicalZeroAmount((*sleMptoken)[sfHolderElGamalPublicKey], account_, mptIssuanceID);
|
||||
|
||||
if (!zeroEncryption)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
(*sleMptoken)[sfConfidentialBalanceInbox] = *zeroEncryption;
|
||||
|
||||
// it's fine if it reaches max uint32, it just resets to 0
|
||||
(*sleMptoken)[sfConfidentialBalanceVersion] = (*sleMptoken)[~sfConfidentialBalanceVersion].value_or(0u) + 1u;
|
||||
|
||||
view().update(sleMptoken);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
29
src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.h
Normal file
29
src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef XRPL_TX_CONFIDENTIALMERGEINBOX_H_INCLUDED
|
||||
#define XRPL_TX_CONFIDENTIALMERGEINBOX_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
361
src/xrpld/app/tx/detail/ConfidentialMPTSend.cpp
Normal file
361
src/xrpld/app/tx/detail/ConfidentialMPTSend.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
#include <xrpld/app/tx/detail/ConfidentialMPTSend.h>
|
||||
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#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>
|
||||
|
||||
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 = getMultiCiphertextEqualityProofSize(recipientCount);
|
||||
auto const sizePedersenLinkage = 2 * ecPedersenProofLength;
|
||||
|
||||
if (ctx.tx[sfZKProof].length() != sizeEquality + sizePedersenLinkage)
|
||||
return temMALFORMED;
|
||||
|
||||
// Check the length of Pedersen commitments
|
||||
if (ctx.tx[sfBalanceCommitment].size() != ecPedersenCommitmentLength ||
|
||||
ctx.tx[sfAmountCommitment].size() != ecPedersenCommitmentLength)
|
||||
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;
|
||||
|
||||
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);
|
||||
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
|
||||
auto const proof = ctx.tx[sfZKProof];
|
||||
size_t remainingLength = proof.size();
|
||||
size_t currentOffset = 0;
|
||||
|
||||
// Extract equality proof
|
||||
auto const sizeEquality = getMultiCiphertextEqualityProofSize(recipientCount);
|
||||
if (remainingLength < sizeEquality)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const equalityProof = proof.substr(currentOffset, sizeEquality);
|
||||
currentOffset += sizeEquality;
|
||||
remainingLength -= sizeEquality;
|
||||
|
||||
// Extract Pedersen linkage proof for amount commitment
|
||||
if (remainingLength < ecPedersenProofLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const amountLinkageProof = proof.substr(currentOffset, ecPedersenProofLength);
|
||||
currentOffset += ecPedersenProofLength;
|
||||
remainingLength -= ecPedersenProofLength;
|
||||
|
||||
// Extract Pedersen linkage proof for balance commitment
|
||||
if (remainingLength < ecPedersenProofLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const balanceLinkageProof = proof.substr(currentOffset, ecPedersenProofLength);
|
||||
currentOffset += ecPedersenProofLength;
|
||||
remainingLength -= ecPedersenProofLength;
|
||||
|
||||
// todo: Extract range proof once the lib is ready
|
||||
if (remainingLength != 0)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Prepare receipient list
|
||||
std::vector<ConfidentialRecipient> recipients;
|
||||
recipients.reserve(recipientCount);
|
||||
|
||||
recipients.push_back({(*sleSenderMPToken)[sfHolderElGamalPublicKey], ctx.tx[sfSenderEncryptedAmount]});
|
||||
|
||||
recipients.push_back({(*sleDestinationMPToken)[sfHolderElGamalPublicKey], ctx.tx[sfDestinationEncryptedAmount]});
|
||||
|
||||
recipients.push_back({(*sleIssuance)[sfIssuerElGamalPublicKey], ctx.tx[sfIssuerEncryptedAmount]});
|
||||
|
||||
if (hasAuditor)
|
||||
{
|
||||
recipients.push_back({(*sleIssuance)[sfAuditorElGamalPublicKey], ctx.tx[sfAuditorEncryptedAmount]});
|
||||
}
|
||||
|
||||
// Prepare the context hash
|
||||
auto const contextHash = getSendContextHash(
|
||||
ctx.tx[sfAccount],
|
||||
ctx.tx[sfSequence],
|
||||
ctx.tx[sfMPTokenIssuanceID],
|
||||
ctx.tx[sfDestination],
|
||||
(*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));
|
||||
|
||||
// Verify the multi-ciphertext equality proof
|
||||
if (auto const ter = verifyMultiCiphertextEqualityProof(equalityProof, recipients, recipientCount, contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "ConfidentialMPTSend: Equality proof failed.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Verify amount linkage
|
||||
if (auto const ter = verifyAmountPcmLinkage(
|
||||
amountLinkageProof,
|
||||
ctx.tx[sfSenderEncryptedAmount],
|
||||
(*sleSenderMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfAmountCommitment],
|
||||
contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "ConfidentialMPTSend: Amount linkage proof failed.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Verify balance linkage
|
||||
if (auto const ter = verifyBalancePcmLinkage(
|
||||
balanceLinkageProof,
|
||||
(*sleSenderMPToken)[sfConfidentialBalanceSpending],
|
||||
(*sleSenderMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfBalanceCommitment],
|
||||
contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "ConfidentialMPTSend: Balance linkage proof failed.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
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(lsfMPTCanPrivacy))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// Check if issuance has issuer ElGamal public key
|
||||
if (!sleIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
|
||||
bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorElGamalPublicKey);
|
||||
|
||||
// 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(sfHolderElGamalPublicKey) ||
|
||||
!sleSenderMPToken->isFieldPresent(sfConfidentialBalanceSpending) ||
|
||||
!sleSenderMPToken->isFieldPresent(sfIssuerEncryptedBalance))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// Sanity check: MPToken's auditor field must be present if auditing is
|
||||
// enabled
|
||||
if (requiresAuditor && !sleSenderMPToken->isFieldPresent(sfAuditorEncryptedBalance))
|
||||
return tefINTERNAL;
|
||||
|
||||
// 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(sfHolderElGamalPublicKey) ||
|
||||
!sleDestinationMPToken->isFieldPresent(sfConfidentialBalanceInbox) ||
|
||||
!sleDestinationMPToken->isFieldPresent(sfIssuerEncryptedBalance))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
|
||||
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];
|
||||
Buffer newSpending(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter = homomorphicSubtract(curSpending, senderEc, newSpending); !isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleSenderMPToken)[sfConfidentialBalanceSpending] = newSpending;
|
||||
}
|
||||
|
||||
// Subtract from issuer's balance
|
||||
{
|
||||
Slice const curIssuerEnc = (*sleSenderMPToken)[sfIssuerEncryptedBalance];
|
||||
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter = homomorphicSubtract(curIssuerEnc, issuerEc, newIssuerEnc); !isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleSenderMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
|
||||
}
|
||||
|
||||
// Subtract from auditor's balance if present
|
||||
if (auditorEc)
|
||||
{
|
||||
Slice const curAuditorEnc = (*sleSenderMPToken)[sfAuditorEncryptedBalance];
|
||||
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter = homomorphicSubtract(curAuditorEnc, *auditorEc, newAuditorEnc); !isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleSenderMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
}
|
||||
|
||||
// Add to destination's inbox balance
|
||||
{
|
||||
Slice const curInbox = (*sleDestinationMPToken)[sfConfidentialBalanceInbox];
|
||||
Buffer newInbox(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter = homomorphicAdd(curInbox, destEc, newInbox); !isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleDestinationMPToken)[sfConfidentialBalanceInbox] = newInbox;
|
||||
}
|
||||
|
||||
// Add to issuer's balance
|
||||
{
|
||||
Slice const curIssuerEnc = (*sleDestinationMPToken)[sfIssuerEncryptedBalance];
|
||||
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter = homomorphicAdd(curIssuerEnc, issuerEc, newIssuerEnc); !isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleDestinationMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
|
||||
}
|
||||
|
||||
// Add to auditor's balance if present
|
||||
if (auditorEc)
|
||||
{
|
||||
Slice const curAuditorEnc = (*sleDestinationMPToken)[sfAuditorEncryptedBalance];
|
||||
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter = homomorphicAdd(curAuditorEnc, *auditorEc, newAuditorEnc); !isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleDestinationMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
}
|
||||
|
||||
// increment version
|
||||
incrementConfidentialVersion(*sleSenderMPToken);
|
||||
incrementConfidentialVersion(*sleDestinationMPToken);
|
||||
|
||||
view().update(sleSenderMPToken);
|
||||
view().update(sleDestinationMPToken);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
} // namespace xrpl
|
||||
29
src/xrpld/app/tx/detail/ConfidentialMPTSend.h
Normal file
29
src/xrpld/app/tx/detail/ConfidentialMPTSend.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef XRPL_TX_CONFIDENTIALSEND_H_INCLUDED
|
||||
#define XRPL_TX_CONFIDENTIALSEND_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ConfidentialMPTSend : public Transactor
|
||||
{
|
||||
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
|
||||
|
||||
#endif
|
||||
@@ -70,6 +70,23 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,14 @@ MPTokenIssuanceCreate::checkExtraFeatures(PreflightContext const& ctx)
|
||||
if (ctx.tx.isFieldPresent(sfMutableFlags) && !ctx.rules.enabled(featureDynamicMPT))
|
||||
return false;
|
||||
|
||||
if (ctx.tx.isFlag(tfMPTCanPrivacy) && !ctx.rules.enabled(featureConfidentialTransfer))
|
||||
return false;
|
||||
|
||||
// can not set tmfMPTCannotMutatePrivacy without featureConfidentialTransfer
|
||||
auto const mutableFlags = ctx.tx[~sfMutableFlags];
|
||||
if (mutableFlags && (*mutableFlags & tmfMPTCannotMutatePrivacy) && !ctx.rules.enabled(featureConfidentialTransfer))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,16 +27,19 @@ struct MPTMutabilityFlags
|
||||
{
|
||||
std::uint32_t setFlag;
|
||||
std::uint32_t clearFlag;
|
||||
std::uint32_t canMutateFlag;
|
||||
std::uint32_t mutabilityFlag;
|
||||
std::uint32_t targetFlag;
|
||||
bool isCannotMutate = false; // if true, cannot mutate by default.
|
||||
};
|
||||
|
||||
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}}};
|
||||
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},
|
||||
{tmfMPTSetPrivacy, tmfMPTClearPrivacy, lsmfMPTCannotMutatePrivacy, lsfMPTCanPrivacy, true}}};
|
||||
|
||||
NotTEC
|
||||
MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
|
||||
@@ -45,14 +48,27 @@ 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(sfIssuerElGamalPublicKey);
|
||||
auto const hasAuditorElGamalKey = ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey);
|
||||
auto const txFlags = ctx.tx.getFlags();
|
||||
|
||||
auto const mutatePrivacy = mutableFlags && ((*mutableFlags & (tmfMPTSetPrivacy | tmfMPTClearPrivacy)));
|
||||
|
||||
auto const hasDomain = ctx.tx.isFieldPresent(sfDomainID);
|
||||
auto const hasHolder = ctx.tx.isFieldPresent(sfHolder);
|
||||
|
||||
if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
|
||||
return temDISABLED;
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfDomainID) && ctx.tx.isFieldPresent(sfHolder))
|
||||
if ((hasIssuerElGamalKey || hasAuditorElGamalKey || mutatePrivacy) &&
|
||||
!ctx.rules.enabled(featureConfidentialTransfer))
|
||||
return temDISABLED;
|
||||
|
||||
if (hasDomain && hasHolder)
|
||||
return temMALFORMED;
|
||||
|
||||
auto const txFlags = ctx.tx.getFlags();
|
||||
if (mutatePrivacy && hasHolder)
|
||||
return temMALFORMED;
|
||||
|
||||
// fails if both flags are set
|
||||
if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock))
|
||||
@@ -63,10 +79,11 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
|
||||
if (holderID && accountID == holderID)
|
||||
return temMALFORMED;
|
||||
|
||||
if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT))
|
||||
if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT) ||
|
||||
ctx.rules.enabled(featureConfidentialTransfer))
|
||||
{
|
||||
// Is this transaction actually changing anything ?
|
||||
if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID) && !isMutate)
|
||||
if (txFlags == 0 && !hasDomain && !hasIssuerElGamalKey && !hasAuditorElGamalKey && !isMutate)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
@@ -104,6 +121,18 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasHolder && (hasIssuerElGamalKey || hasAuditorElGamalKey))
|
||||
return temMALFORMED;
|
||||
|
||||
if (hasAuditorElGamalKey && !hasIssuerElGamalKey)
|
||||
return temMALFORMED;
|
||||
|
||||
if (hasIssuerElGamalKey && ctx.tx[sfIssuerElGamalPublicKey].length() != ecPubKeyLength)
|
||||
return temMALFORMED;
|
||||
|
||||
if (hasAuditorElGamalKey && ctx.tx[sfAuditorElGamalPublicKey].length() != ecPubKeyLength)
|
||||
return temMALFORMED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -193,13 +222,26 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool { return currentMutableFlags & mutableFlag; };
|
||||
|
||||
if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
|
||||
auto const mutableFlags = ctx.tx[~sfMutableFlags];
|
||||
if (mutableFlags)
|
||||
{
|
||||
if (std::any_of(
|
||||
mptMutabilityFlags.begin(), mptMutabilityFlags.end(), [mutableFlags, &isMutableFlag](auto const& f) {
|
||||
return !isMutableFlag(f.canMutateFlag) && ((*mutableFlags & (f.setFlag | f.clearFlag)));
|
||||
bool const canMutate =
|
||||
f.isCannotMutate ? isMutableFlag(f.mutabilityFlag) : !isMutableFlag(f.mutabilityFlag);
|
||||
return canMutate && (*mutableFlags & (f.setFlag | f.clearFlag));
|
||||
}))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if ((*mutableFlags & tmfMPTSetPrivacy) || (*mutableFlags & tmfMPTClearPrivacy))
|
||||
{
|
||||
std::uint64_t const confidentialOA = (*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0);
|
||||
|
||||
// If there's any confidential outstanding amount, disallow toggling
|
||||
// the lsfMPTCanPrivacy flag
|
||||
if (confidentialOA > 0)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMutableFlag(lsmfMPTCanMutateMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata))
|
||||
@@ -218,6 +260,35 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
// cannot update issuer public key
|
||||
if (ctx.tx.isFieldPresent(sfIssuerElGamalPublicKey) && sleMptIssuance->isFieldPresent(sfIssuerElGamalPublicKey))
|
||||
{
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
// cannot update auditor public key
|
||||
if (ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey) && sleMptIssuance->isFieldPresent(sfAuditorElGamalPublicKey))
|
||||
{
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfIssuerElGamalPublicKey) && !sleMptIssuance->isFlag(lsfMPTCanPrivacy))
|
||||
{
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey) && !sleMptIssuance->isFlag(lsfMPTCanPrivacy))
|
||||
{
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
// cannot upload key if there's circulating supply of COA
|
||||
if ((ctx.tx.isFieldPresent(sfIssuerElGamalPublicKey) || ctx.tx.isFieldPresent(sfAuditorElGamalPublicKey)) &&
|
||||
sleMptIssuance->isFieldPresent(sfConfidentialOutstandingAmount))
|
||||
{
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -251,9 +322,9 @@ MPTokenIssuanceSet::doApply()
|
||||
for (auto const& f : mptMutabilityFlags)
|
||||
{
|
||||
if (mutableFlags & f.setFlag)
|
||||
flagsOut |= f.canMutateFlag;
|
||||
flagsOut |= f.targetFlag;
|
||||
else if (mutableFlags & f.clearFlag)
|
||||
flagsOut &= ~f.canMutateFlag;
|
||||
flagsOut &= ~f.targetFlag;
|
||||
}
|
||||
|
||||
if (mutableFlags & tmfMPTClearCanTransfer)
|
||||
@@ -303,6 +374,22 @@ MPTokenIssuanceSet::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const pubKey = ctx_.tx[~sfIssuerElGamalPublicKey])
|
||||
{
|
||||
// This is enforced in preflight.
|
||||
XRPL_ASSERT(sle->getType() == ltMPTOKEN_ISSUANCE, "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
|
||||
|
||||
sle->setFieldVL(sfIssuerElGamalPublicKey, *pubKey);
|
||||
}
|
||||
|
||||
if (auto const pubKey = ctx_.tx[~sfAuditorElGamalPublicKey])
|
||||
{
|
||||
// This is enforced in preflight.
|
||||
XRPL_ASSERT(sle->getType() == ltMPTOKEN_ISSUANCE, "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
|
||||
|
||||
sle->setFieldVL(sfAuditorElGamalPublicKey, *pubKey);
|
||||
}
|
||||
|
||||
view().update(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
Reference in New Issue
Block a user