Compare commits
199 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 861df8283c | |||
| c7a739d885 | |||
| 608962068a | |||
| e7615b4c08 | |||
| 5fa5ff8585 | |||
| 28a9338ab7 | |||
| 1d3747e9cd | |||
| c1c70ddace | |||
| 7ea690614e | |||
| 2909fd2505 | |||
| 7dd5450419 | |||
| e3a03e9d98 | |||
| dd39f94ca6 | |||
| 78232ff3a8 | |||
| aa02928620 | |||
| 52736ea0a3 | |||
| b7cc8e8100 | |||
| 520d4b4eab | |||
| 11a296acde | |||
| 8b1796147a | |||
| bf9d0e837e | |||
| a9301f5573 | |||
| e6e644e600 | |||
| 130975cebd | |||
| 5306d4b94a | |||
| 14a6c6b8ef | |||
| 2b31c8ad69 | |||
| ae02dcbe9a | |||
| e235199ff3 | |||
| 87ceee6c2d | |||
| 41e3e85159 | |||
| b570a4938c | |||
| cbde471d91 | |||
| 1e81092e59 | |||
| 65db5c3782 | |||
| a289de023a | |||
| cad1618bbd | |||
| d53a0669c9 | |||
| adcb66d268 | |||
| ec2015e5a4 | |||
| d163d6892c | |||
| 8eac4bce5f | |||
| ea19899914 | |||
| 7fe9ee0990 | |||
| c8314f7b56 | |||
| b07d2ce901 | |||
| 474bca538b | |||
| a958bdb0e0 | |||
| 1adc1b0f8d | |||
| f7be7b843b | |||
| 3878e389fe | |||
| a3a05a24e4 | |||
| 1bf4f40b79 | |||
| 4a909bda2d | |||
| 6f6a17d923 | |||
| d1b7abfc69 | |||
| 33535531ec | |||
| bd34433be2 | |||
| daffdd764b | |||
| 4942f848f8 | |||
| fa3ecd03df | |||
| 9c08143015 | |||
| 9c2fa466a0 | |||
| 4ee14df3c6 | |||
| c4ca673074 | |||
| 724e2739d4 | |||
| d483b9872b | |||
| f2f21a49b1 | |||
| 5f6122d045 | |||
| 53db771c35 | |||
| 59b167f0f3 | |||
| 60b3f22376 | |||
| e3e35f6dde | |||
| c145285dcf | |||
| 2550591cf1 | |||
| 18ec1c331b | |||
| 0520fc4fe6 | |||
| f5729a6109 | |||
| da99d88bce | |||
| 87be655738 | |||
| 7a05f26810 | |||
| 96fff8abc3 | |||
| f1e48c9094 | |||
| 02e8fea2fe | |||
| 266c41aeab | |||
| 410a9313dc | |||
| 870203e6fe | |||
| 8f699871d3 | |||
| 650b6b8bd4 | |||
| 19593db002 | |||
| d89d20ab0e | |||
| b982d4c5a6 | |||
| c4dbc4ea7b | |||
| 29f9a4d679 | |||
| 8ad1727ea7 | |||
| 79429b6870 | |||
| 1057317147 | |||
| c79e9d0e71 | |||
| d4a61dbb66 | |||
| 4989c4cece | |||
| 818aac3c73 | |||
| 04208fdcd3 | |||
| 673661bdb9 | |||
| b7ea8d0b22 | |||
| 70bde9353b | |||
| 0bfa68beda | |||
| 4a0242d905 | |||
| 1ee4d3d66e | |||
| c72543bb63 | |||
| ec46d14fe5 | |||
| fa0a05a01c | |||
| 3cc4bb2b9a | |||
| 89c60d57ec | |||
| 606cdd43f2 | |||
| 1f3255e1e8 | |||
| 8f4ceba0cc | |||
| 88e104423f | |||
| 09b886b324 | |||
| 4d891d61c1 | |||
| c4dc67cfc5 | |||
| b7488f7735 | |||
| 871a687b52 | |||
| 149a22bee1 | |||
| 9ca8361824 | |||
| 29f9451b27 | |||
| f7c2cc6550 | |||
| 3b671cb0bf | |||
| e1d3086a24 | |||
| b259bc45e8 | |||
| 638034c35f | |||
| 66552f14ca | |||
| 7d4122151e | |||
| 4659b6b6f1 | |||
| b529887e88 | |||
| c607f23a60 | |||
| 3a21213153 | |||
| 2194b853d5 | |||
| ce9c845d26 | |||
| fec249d598 | |||
| ff85622450 | |||
| a204844b8e | |||
| 154806402b | |||
| ee263314ef | |||
| cc83158ccc | |||
| ad1eb77d50 | |||
| 316aacbc62 | |||
| d9846bba74 | |||
| c69c3e35fd | |||
| 13793610af | |||
| 4eecccee04 | |||
| 939fd068c5 | |||
| 4f33613628 | |||
| 33aa5607a4 | |||
| 4eb13de957 | |||
| ada1fa0e5e | |||
| 164985ce16 | |||
| a6733fcf2b | |||
| 44e70b4765 | |||
| 22cc08c107 | |||
| ed030081b7 | |||
| 9292f88a57 | |||
| 4095e9f80f | |||
| 02fd790788 | |||
| 6d2960612f | |||
| 2d9b302137 | |||
| a7753897b2 | |||
| fe3297271f | |||
| 9d9471d314 | |||
| 5da798a7c5 | |||
| 0f97ec9ea7 | |||
| 0418bfee30 | |||
| 488d5f1e9b | |||
| 771b8ea606 | |||
| 28262a30fe | |||
| aa4276c39e | |||
| 1f49c178ee | |||
| f087b3807b | |||
| cb6cdac603 | |||
| 082976a3f0 | |||
| 420824005e | |||
| 98ded9b9c4 | |||
| 9baeb750ac | |||
| 1cf81058b6 | |||
| 9570c7a910 | |||
| 23756691b7 | |||
| d5f6cdc0cd | |||
| 378ec75a6e | |||
| 3eb986fc51 | |||
| b713a08a81 | |||
| 5bd079af4a | |||
| cd2c7c939c | |||
| 2f707e30c6 | |||
| f9726354b8 | |||
| fcd78eea7e | |||
| db740fa037 | |||
| 5971f39e7a | |||
| 02f2eb5ee9 | |||
| 02ef77bbcc | |||
| aa64124c28 |
+24
-8
@@ -372,6 +372,7 @@ if(NOT MANUAL_SUBMODULES)
|
||||
check_submodule(external/trezor-common)
|
||||
check_submodule(external/randomx)
|
||||
check_submodule(external/supercop)
|
||||
check_submodule(external/mx25519)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -458,7 +459,7 @@ elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
|
||||
set(BSDI TRUE)
|
||||
endif()
|
||||
|
||||
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include)
|
||||
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include)
|
||||
|
||||
if(APPLE)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
@@ -506,6 +507,7 @@ if(STATIC)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZMQ_STATIC")
|
||||
add_definitions("-DMX25519_STATIC")
|
||||
endif()
|
||||
|
||||
option(SANITIZE "Use ASAN memory sanitizer" OFF)
|
||||
@@ -531,14 +533,14 @@ add_definitions("-DBLOCKCHAIN_DB=${BLOCKCHAIN_DB}")
|
||||
# Can't install hook in static build on OSX, because OSX linker does not support --wrap
|
||||
# On ARM, having libunwind package (with .so's only) installed breaks static link.
|
||||
# When possible, avoid stack tracing using libunwind in favor of using easylogging++.
|
||||
if (APPLE OR NETBSD)
|
||||
if (APPLE)
|
||||
set(DEFAULT_STACK_TRACE OFF)
|
||||
set(LIBUNWIND_LIBRARIES "")
|
||||
elseif (DEPENDS AND NOT LINUX)
|
||||
set(DEFAULT_STACK_TRACE OFF)
|
||||
set(LIBUNWIND_LIBRARIES "")
|
||||
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT MINGW)
|
||||
set(DEFAULT_STACK_TRACE OFF)
|
||||
set(DEFAULT_STACK_TRACE ON)
|
||||
set(STACK_TRACE_LIB "easylogging++") # for diag output only
|
||||
set(LIBUNWIND_LIBRARIES "")
|
||||
elseif (ARM)
|
||||
@@ -547,7 +549,7 @@ elseif (ARM)
|
||||
else()
|
||||
find_package(Libunwind)
|
||||
if(LIBUNWIND_FOUND)
|
||||
set(DEFAULT_STACK_TRACE OFF)
|
||||
set(DEFAULT_STACK_TRACE ON)
|
||||
set(STACK_TRACE_LIB "libunwind") # for diag output only
|
||||
else()
|
||||
set(DEFAULT_STACK_TRACE OFF)
|
||||
@@ -1084,6 +1086,10 @@ endif()
|
||||
find_package(Boost 1.58 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
|
||||
add_definitions(-DBOOST_NO_AUTO_PTR)
|
||||
add_definitions(-DBOOST_UUID_DISABLE_ALIGNMENT) # This restores UUID's std::has_unique_object_representations property
|
||||
# Boost has two conflicting save/load impls for `std::variant`, one in serialization/variant.hpp,
|
||||
# and one in serialization/std_variant.hpp. This macro disables the one in variant.hpp.
|
||||
add_definitions(-DBOOST_NO_CXX17_HDR_VARIANT)
|
||||
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES})
|
||||
if(NOT Boost_FOUND)
|
||||
@@ -1107,7 +1113,9 @@ include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj")
|
||||
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt)
|
||||
if(NOT DEPENDS)
|
||||
if(DEPENDS)
|
||||
set(ICU_LIBRARIES icuio icui18n icuuc icudata icutu iconv)
|
||||
else()
|
||||
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
|
||||
endif()
|
||||
elseif(APPLE OR OPENBSD OR ANDROID)
|
||||
@@ -1182,7 +1190,6 @@ endif()
|
||||
if(STATIC)
|
||||
set(sodium_USE_STATIC_LIBS ON)
|
||||
endif()
|
||||
find_package(Sodium REQUIRED)
|
||||
|
||||
find_path(ZMQ_INCLUDE_PATH zmq.h)
|
||||
find_library(ZMQ_LIB zmq)
|
||||
@@ -1190,6 +1197,7 @@ find_library(PGM_LIBRARY pgm)
|
||||
find_library(NORM_LIBRARY norm)
|
||||
find_library(GSSAPI_LIBRARY gssapi_krb5)
|
||||
find_library(PROTOLIB_LIBRARY protolib)
|
||||
find_library(SODIUM_LIBRARY sodium)
|
||||
find_library(BSD_LIBRARY bsd)
|
||||
find_library(MD_LIBRARY md)
|
||||
find_library(PROTOKIT_LIBRARY protokit)
|
||||
@@ -1212,8 +1220,16 @@ endif()
|
||||
if(PROTOLIB_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${PROTOLIB_LIBRARY}")
|
||||
endif()
|
||||
if(Sodium_FOUND)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${sodium_LIBRARIES}")
|
||||
if(SODIUM_LIBRARY)
|
||||
message(STATUS "ZMQ_LIB: ${ZMQ_LIB};${SODIUM_LIBRARY}")
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}")
|
||||
find_path(SODIUM_INCLUDE_PATH sodium/crypto_verify_32.h)
|
||||
if (SODIUM_INCLUDE_PATH)
|
||||
message(STATUS "SODIUM_INCLUDE_PATH: ${SODIUM_INCLUDE_PATH}")
|
||||
include_directories(${SODIUM_INCLUDE_PATH})
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find required sodium/crypto_verify_32.h")
|
||||
endif()
|
||||
endif()
|
||||
if(BSD_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${BSD_LIBRARY}")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Salvium Zero v0.9.2
|
||||
# Salvium Zero v0.9.9
|
||||
|
||||
Copyright (c) 2023-2024, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
|
||||
|
||||
```bash
|
||||
cd salvium
|
||||
git checkout v0.9.2
|
||||
git checkout v0.9.9
|
||||
make
|
||||
```
|
||||
|
||||
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
|
||||
```bash
|
||||
git clone https://github.com/salvium/salvium
|
||||
cd salvium
|
||||
git checkout v0.9.2
|
||||
git checkout v0.9.9
|
||||
```
|
||||
|
||||
* Build:
|
||||
@@ -370,10 +370,10 @@ application.
|
||||
cd salvium
|
||||
```
|
||||
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.2'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.9'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v0.9.2
|
||||
git checkout v0.9.9
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
depends_prefix="`dirname ${ac_site_file}`/.."
|
||||
|
||||
cross_compiling=maybe
|
||||
host_alias=@HOST@
|
||||
ac_tool_prefix=${host_alias}-
|
||||
|
||||
if test -z $with_boost; then
|
||||
with_boost=$depends_prefix
|
||||
fi
|
||||
|
||||
if test x@host_os@ = xdarwin; then
|
||||
BREW=no
|
||||
PORT=no
|
||||
fi
|
||||
|
||||
PATH=$depends_prefix/native/bin:$PATH
|
||||
PKG_CONFIG="`which pkg-config` --static"
|
||||
|
||||
# These two need to remain exported because pkg-config does not see them
|
||||
# otherwise. That means they must be unexported at the end of configure.ac to
|
||||
# avoid ruining the cache. Sigh.
|
||||
export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig:$depends_prefix/lib/pkgconfig
|
||||
if test -z "@allow_host_packages@"; then
|
||||
export PKGCONFIG_LIBDIR=
|
||||
fi
|
||||
|
||||
CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS"
|
||||
LDFLAGS="-L$depends_prefix/lib $LDFLAGS"
|
||||
|
||||
CC="@CC@"
|
||||
CXX="@CXX@"
|
||||
OBJC="${CC}"
|
||||
CCACHE=$depends_prefix/native/bin/ccache
|
||||
PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH
|
||||
|
||||
if test -n "@AR@"; then
|
||||
AR=@AR@
|
||||
ac_cv_path_ac_pt_AR=${AR}
|
||||
fi
|
||||
|
||||
if test -n "@RANLIB@"; then
|
||||
RANLIB=@RANLIB@
|
||||
ac_cv_path_ac_pt_RANLIB=${RANLIB}
|
||||
fi
|
||||
|
||||
if test -n "@NM@"; then
|
||||
NM=@NM@
|
||||
ac_cv_path_ac_pt_NM=${NM}
|
||||
fi
|
||||
|
||||
if test -n "@debug@"; then
|
||||
enable_reduce_exports=no
|
||||
fi
|
||||
|
||||
if test -n "@CFLAGS@"; then
|
||||
CFLAGS="@CFLAGS@ $CFLAGS"
|
||||
fi
|
||||
if test -n "@CXXFLAGS@"; then
|
||||
CXXFLAGS="@CXXFLAGS@ $CXXFLAGS"
|
||||
fi
|
||||
if test -n "@CPPFLAGS@"; then
|
||||
CPPFLAGS="@CPPFLAGS@ $CPPFLAGS"
|
||||
fi
|
||||
if test -n "@LDFLAGS@"; then
|
||||
LDFLAGS="@LDFLAGS@ $LDFLAGS"
|
||||
fi
|
||||
@@ -1,4 +1,4 @@
|
||||
OSX_MIN_VERSION=10.13
|
||||
OSX_MIN_VERSION=11.0
|
||||
LD64_VERSION=609
|
||||
ifeq (aarch64, $(host_arch))
|
||||
CC_target=arm64-apple-$(host_os)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mingw32_CFLAGS=-pipe -pthread
|
||||
mingw32_CFLAGS=-pipe
|
||||
mingw32_CXXFLAGS=$(mingw32_CFLAGS)
|
||||
mingw32_ARFLAGS=cr
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package=boost
|
||||
$(package)_version=1_64_0
|
||||
$(package)_download_path=https://downloads.sourceforge.net/project/boost/boost/1.64.0/
|
||||
$(package)_file_name=$(package)_$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332
|
||||
$(package)_patches=fix_aroptions.patch fix_arm_arch.patch
|
||||
package=boost
|
||||
$(package)_version=1.84.0
|
||||
$(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/
|
||||
$(package)_file_name=$(package)_$(subst .,_,$($(package)_version)).tar.bz2
|
||||
$(package)_sha256_hash=cc4b893acf645c9d4b698e9a0f08ca8846aa5d6c68275c14c3e7949c24109454
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts_release=variant=release
|
||||
$(package)_config_opts_debug=variant=debug
|
||||
$(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam
|
||||
$(package)_config_opts+=--layout=system --user-config=user-config.jam variant=release
|
||||
$(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1
|
||||
$(package)_config_opts_linux=threadapi=pthread runtime-link=shared
|
||||
$(package)_config_opts_android=threadapi=pthread runtime-link=static target-os=android
|
||||
$(package)_config_opts_darwin=--toolset=darwin runtime-link=shared
|
||||
$(package)_config_opts_darwin=--toolset=darwin runtime-link=shared target-os=darwin
|
||||
$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static
|
||||
$(package)_config_opts_x86_64_mingw32=address-model=64
|
||||
$(package)_config_opts_i686_mingw32=address-model=32
|
||||
@@ -23,14 +22,13 @@ $(package)_toolset_darwin=darwin
|
||||
$(package)_archiver_darwin=$($(package)_libtool)
|
||||
$(package)_config_libraries_$(host_os)="chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization"
|
||||
$(package)_config_libraries_mingw32="chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale"
|
||||
$(package)_cxxflags=-std=c++11
|
||||
$(package)_cxxflags_linux=-fPIC
|
||||
$(package)_cxxflags_freebsd=-fPIC
|
||||
$(package)_cxxflags=-std=c++17
|
||||
$(package)_cxxflags_linux+=-fPIC
|
||||
$(package)_cxxflags_freebsd+=-fPIC
|
||||
$(package)_cxxflags_darwin+=-ffile-prefix-map=$($(package)_extract_dir)=/usr
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p1 < $($(package)_patch_dir)/fix_aroptions.patch &&\
|
||||
patch -p1 < $($(package)_patch_dir)/fix_arm_arch.patch &&\
|
||||
echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$(boost_archiver_$(host_os))\" <arflags>\"$($(package)_arflags)\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam
|
||||
endef
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package=icu4c
|
||||
$(package)_version=55.2
|
||||
$(package)_download_path=https://github.com/unicode-org/icu/releases/download/release-55-2/
|
||||
$(package)_file_name=$(package)-55_2-src.tgz
|
||||
$(package)_sha256_hash=eda2aa9f9c787748a2e2d310590720ca8bcc6252adf6b4cfb03b65bef9d66759
|
||||
$(package)_patches=icu-001-dont-build-static-dynamic-twice.patch
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC -DENABLE_STATIC=YES -DPGKDATA_MODE=static"
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
patch -p1 < $($(package)_patch_dir)/icu-001-dont-build-static-dynamic-twice.patch &&\
|
||||
mkdir builda &&\
|
||||
mkdir buildb &&\
|
||||
cd builda &&\
|
||||
sh ../source/runConfigureICU Linux &&\
|
||||
make &&\
|
||||
cd ../buildb &&\
|
||||
sh ../source/runConfigureICU MinGW --enable-static=yes --disable-shared --disable-layout --disable-layoutex --disable-tests --disable-samples --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\
|
||||
$(MAKE) $($(package)_build_opts)
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
cd buildb &&\
|
||||
$(MAKE) $($(package)_build_opts) DESTDIR=$($(package)_staging_dir) install lib/*
|
||||
endef
|
||||
@@ -0,0 +1,35 @@
|
||||
package=libiconv
|
||||
$(package)_version=1.15
|
||||
$(package)_download_path=https://ftp.gnu.org/gnu/libiconv
|
||||
$(package)_file_name=libiconv-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
|
||||
$(package)_patches=fix-whitespace.patch
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--disable-nls
|
||||
$(package)_config_opts=--enable-static
|
||||
$(package)_config_opts=--disable-shared
|
||||
$(package)_config_opts_linux=--with-pic
|
||||
$(package)_config_opts_freebsd=--with-pic
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux/ &&\
|
||||
patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
$($(package)_autoconf) AR_FLAGS=$($(package)_arflags)
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE)
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
rm lib/*.la
|
||||
endef
|
||||
@@ -0,0 +1,25 @@
|
||||
package=native_ccache
|
||||
$(package)_version=3.3.4
|
||||
$(package)_download_path=https://samba.org/ftp/ccache
|
||||
$(package)_file_name=ccache-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=fa9d7f38367431bc86b19ad107d709ca7ecf1574fdacca01698bdf0a47cd8567
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
$($(package)_autoconf)
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE)
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
rm -rf lib include
|
||||
endef
|
||||
@@ -5,17 +5,18 @@ $(package)_download_file=$($(package)_version).tar.gz
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=70a7189418c2086d20c299c5d59250cf5940782c778892ccc899c66516ed240e
|
||||
$(package)_build_subdir=cctools
|
||||
$(package)_dependencies=native_clang native_libtapi
|
||||
$(package)_patches=no-build-date.patch
|
||||
$(package)_dependencies=native_libtapi
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--target=$(host) --disable-lto-support --with-libtapi=$(host_prefix)
|
||||
$(package)_ldflags+=-Wl,-rpath=\\$$$$$$$$\$$$$$$$$ORIGIN/../lib
|
||||
$(package)_cc=$(host_prefix)/native/bin/clang
|
||||
$(package)_cxx=$(host_prefix)/native/bin/clang++
|
||||
$(package)_cc=$(clang_prog)
|
||||
$(package)_cxx=$(clangxx_prog)
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub cctools && \
|
||||
patch -p1 < $($(package)_patch_dir)/no-build-date.patch
|
||||
endef
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package=native_clang
|
||||
$(package)_version=9.0.0
|
||||
$(package)_download_path=https://releases.llvm.org/$($(package)_version)
|
||||
$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
|
||||
$(package)_file_name=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
|
||||
$(package)_sha256_hash=a23b082b30c128c9831dbdd96edad26b43f56624d0ad0ea9edec506f5385038d
|
||||
#$(package)_version=9.0.0
|
||||
#$(package)_download_path=https://releases.llvm.org/$($(package)_version)
|
||||
#$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
|
||||
#$(package)_file_name=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
|
||||
#$(package)_sha256_hash=a23b082b30c128c9831dbdd96edad26b43f56624d0ad0ea9edec506f5385038d
|
||||
|
||||
$(package)_version=12.0.0
|
||||
$(package)_download_path=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
|
||||
$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-20.04.tar.xz
|
||||
$(package)_file_name=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-20.04.tar.xz
|
||||
$(package)_sha256_hash=a9ff205eb0b73ca7c86afc6432eed1c2d49133bd0d49e47b15be59bbf0dd292e
|
||||
|
||||
define $(package)_extract_cmds
|
||||
echo $($(package)_sha256_hash) $($(package)_source) | sha256sum -c &&\
|
||||
|
||||
@@ -4,30 +4,16 @@ $(package)_download_path=https://github.com/tpoechtrager/apple-libtapi/archive
|
||||
$(package)_download_file=$($(package)_version).tar.gz
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=62e419c12d1c9fad67cc1cd523132bc00db050998337c734c15bc8d73cc02b61
|
||||
$(package)_build_subdir=build
|
||||
$(package)_dependencies=native_clang
|
||||
$(package)_patches=no_embed_git_rev.patch
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p1 -i $($(package)_patch_dir)/no_embed_git_rev.patch
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
echo -n $(build_prefix) > INSTALLPREFIX; \
|
||||
CC=$(host_prefix)/native/bin/clang CXX=$(host_prefix)/native/bin/clang++ \
|
||||
cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=RELEASE \
|
||||
-DTAPI_REPOSITORY_STRING="1100.0.11" \
|
||||
-DTAPI_FULL_VERSION="11.0.0" \
|
||||
-DCMAKE_CXX_FLAGS="-I $($(package)_extract_dir)/src/llvm/projects/clang/include -I $($(package)_build_dir)/projects/clang/include" \
|
||||
$($(package)_extract_dir)/src/llvm
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE) clangBasic && $(MAKE) libtapi
|
||||
CC=$(clang_prog) CXX=$(clangxx_prog) INSTALLPREFIX=$($(package)_staging_prefix_dir) ./build.sh
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install-libtapi install-tapi-headers
|
||||
./install.sh
|
||||
endef
|
||||
|
||||
@@ -46,6 +46,7 @@ define $(package)_set_vars
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . && \
|
||||
cp $($(package)_patch_dir)/fallback.c ncurses
|
||||
endef
|
||||
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
packages:=boost openssl zeromq expat unbound sodium
|
||||
native_packages := native_protobuf
|
||||
packages := boost openssl zeromq unbound sodium protobuf
|
||||
|
||||
hardware_packages := hidapi protobuf libusb
|
||||
hardware_native_packages := native_protobuf
|
||||
|
||||
android_native_packages = android_ndk $(hardware_native_packages)
|
||||
android_packages = ncurses readline protobuf
|
||||
|
||||
darwin_native_packages = $(hardware_native_packages)
|
||||
darwin_packages = ncurses readline $(hardware_packages)
|
||||
|
||||
# not really native...
|
||||
freebsd_native_packages = freebsd_base $(hardware_native_packages)
|
||||
freebsd_packages = ncurses readline protobuf libusb
|
||||
|
||||
linux_packages = eudev ncurses readline $(hardware_packages)
|
||||
linux_native_packages = $(hardware_native_packages)
|
||||
|
||||
ifeq ($(build_tests),ON)
|
||||
packages += gtest
|
||||
ifneq ($(host_os),android)
|
||||
packages += libusb
|
||||
endif
|
||||
|
||||
mingw32_packages = $(hardware_packages)
|
||||
mingw32_native_packages = $(hardware_native_packages)
|
||||
ifneq ($(host_os),freebsd)
|
||||
ifneq ($(host_os),android)
|
||||
packages += hidapi
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(host_os),mingw32)
|
||||
packages += ncurses readline
|
||||
endif
|
||||
|
||||
mingw32_native_packages :=
|
||||
mingw32_packages = icu4c libiconv
|
||||
|
||||
linux_native_packages :=
|
||||
linux_packages :=
|
||||
|
||||
freebsd_native_packages := freebsd_base
|
||||
freebsd_packages :=
|
||||
|
||||
ifneq ($(build_os),darwin)
|
||||
darwin_native_packages += darwin_sdk native_clang native_cctools native_libtapi
|
||||
darwin_native_packages := darwin_sdk native_clang native_cctools native_libtapi
|
||||
endif
|
||||
darwin_packages :=
|
||||
|
||||
android_native_packages := android_ndk
|
||||
android_packages :=
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package=unbound
|
||||
$(package)_version=1.19.1
|
||||
$(package)_version=1.22.0
|
||||
$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=bc1d576f3dd846a0739adc41ffaa702404c6767d2b6082deb9f2f97cbb24a3a9
|
||||
$(package)_dependencies=openssl expat
|
||||
$(package)_patches=disable-glibc-reallocarray.patch
|
||||
|
||||
$(package)_sha256_hash=c5dd1bdef5d5685b2cedb749158dd152c52d44f65529a34ac15cd88d4b1b3d43
|
||||
$(package)_dependencies=openssl
|
||||
$(package)_patches=no-expat.patch
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix)
|
||||
$(package)_config_opts+=--with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no
|
||||
$(package)_config_opts+=--with-libexpat=no --with-ssl=$(host_prefix) --with-libevent=no
|
||||
$(package)_config_opts+=--without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only
|
||||
$(package)_config_opts_linux=--with-pic
|
||||
$(package)_config_opts_w64=--enable-static-exe --sysconfdir=/etc --prefix=$(host_prefix) --target=$(host_prefix)
|
||||
$(package)_config_opts_x86_64_darwin=ac_cv_func_SHA384_Init=yes
|
||||
$(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread"
|
||||
$(package)_cflags_mingw32+="-D_WIN32_WINNT=0x600"
|
||||
endef
|
||||
|
||||
# Remove blobs
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p1 < $($(package)_patch_dir)/disable-glibc-reallocarray.patch &&\
|
||||
autoconf
|
||||
patch -p1 < $($(package)_patch_dir)/no-expat.patch &&\
|
||||
rm configure~ doc/*.odp doc/*.pdf contrib/*.tar.gz contrib/*.tar.bz2 &&\
|
||||
rm -rf testdata dnscrypt/testdata
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
@@ -34,3 +34,7 @@ endef
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
rm -rf share
|
||||
endef
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package=unwind
|
||||
$(package)_version=1.5.0
|
||||
$(package)_download_path=https://download.savannah.nongnu.org/releases/libunwind
|
||||
$(package)_file_name=lib$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=90337653d92d4a13de590781371c604f9031cdb50520366aa1e3a91e1efb1017
|
||||
$(package)_patches=fix_obj_order.patch
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p0 < $($(package)_patch_dir)/fix_obj_order.patch
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
cp -f $(BASEDIR)/config.guess config/config.guess &&\
|
||||
cp -f $(BASEDIR)/config.sub config/config.sub &&\
|
||||
$($(package)_autoconf) --disable-shared --enable-static --disable-tests --disable-documentation AR_FLAGS=$($(package)_arflags)
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE)
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
rm lib/*.la
|
||||
endef
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
--- boost_1_64_0/tools/build/src/tools/darwin.jam.O 2017-04-17 03:22:26.000000000 +0100
|
||||
+++ boost_1_64_0/tools/build/src/tools/darwin.jam 2022-05-04 17:26:29.984464447 +0000
|
||||
@@ -505,7 +505,7 @@
|
||||
if $(instruction-set) {
|
||||
options = -arch$(_)$(instruction-set) ;
|
||||
} else {
|
||||
- options = -arch arm ;
|
||||
+# options = -arch arm ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
--- boost_1_64_0/tools/build/src/tools/gcc.jam.O 2017-04-17 03:22:26.000000000 +0100
|
||||
+++ boost_1_64_0/tools/build/src/tools/gcc.jam 2019-11-15 15:46:16.957937137 +0000
|
||||
@@ -243,6 +243,8 @@
|
||||
{
|
||||
ECHO notice: using gcc archiver :: $(condition) :: $(archiver[1]) ;
|
||||
}
|
||||
+ local arflags = [ feature.get-values <arflags> : $(options) ] ;
|
||||
+ toolset.flags gcc.archive .ARFLAGS $(condition) : $(arflags) ;
|
||||
|
||||
# - Ranlib.
|
||||
local ranlib = [ common.get-invocation-command gcc
|
||||
@@ -970,6 +972,7 @@
|
||||
# logic in intel-linux, but that is hardly worth the trouble as on Linux, 'ar'
|
||||
# is always available.
|
||||
.AR = ar ;
|
||||
+.ARFLAGS = rc ;
|
||||
.RANLIB = ranlib ;
|
||||
|
||||
toolset.flags gcc.archive AROPTIONS <archiveflags> ;
|
||||
@@ -1011,7 +1014,7 @@
|
||||
#
|
||||
actions piecemeal archive
|
||||
{
|
||||
- "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)"
|
||||
+ "$(.AR)" $(AROPTIONS) $(.ARFLAGS) "$(<)" "$(>)"
|
||||
"$(.RANLIB)" "$(<)"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
Don't build object files twice
|
||||
|
||||
When passed --enable-static and --enable-shared, icu will generate
|
||||
both a shared and a static version of its libraries.
|
||||
|
||||
However, in order to do so, it builds each and every object file
|
||||
twice: once with -fPIC (for the shared library), and once without
|
||||
-fPIC (for the static library). While admittedly building -fPIC for a
|
||||
static library generates a slightly suboptimal code, this is what all
|
||||
the autotools-based project are doing. They build each object file
|
||||
once, and they use it for both the static and shared libraries.
|
||||
|
||||
icu builds the object files for the shared library as .o files, and
|
||||
the object files for static library as .ao files. By simply changing
|
||||
the suffix of object files used for static libraries to ".o", we tell
|
||||
icu to use the ones built for the shared library (i.e, with -fPIC),
|
||||
and avoid the double build of icu.
|
||||
|
||||
On a fast build server, this brings the target icu build from
|
||||
3m41.302s down to 1m43.926s (approximate numbers: some other builds
|
||||
are running on the system at the same time).
|
||||
|
||||
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
|
||||
Index: b/source/config/mh-linux
|
||||
===================================================================
|
||||
--- a/source/config/mh-linux
|
||||
+++ b/source/config/mh-linux
|
||||
@@ -38,7 +38,7 @@
|
||||
## Shared object suffix
|
||||
SO = so
|
||||
## Non-shared intermediate object suffix
|
||||
-STATIC_O = ao
|
||||
+STATIC_O = o
|
||||
|
||||
## Compilation rules
|
||||
%.$(STATIC_O): $(srcdir)/%.c
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/preload/configure b/preload/configure
|
||||
index aab5c77..e20b8f0 100755
|
||||
--- a/preload/configure
|
||||
+++ b/preload/configure
|
||||
@@ -588,7 +588,7 @@ MAKEFLAGS=
|
||||
PACKAGE_NAME='libiconv'
|
||||
PACKAGE_TARNAME='libiconv'
|
||||
PACKAGE_VERSION='0'
|
||||
-PACKAGE_STRING='libiconv 0'
|
||||
+PACKAGE_STRING='libiconv0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 5c7da197..e2b25288 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -1702,6 +1702,9 @@ AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT
|
||||
#ifndef _OPENBSD_SOURCE
|
||||
#define _OPENBSD_SOURCE 1
|
||||
#endif
|
||||
+#ifdef __linux__
|
||||
+# error reallocarray() is currently disabled on Linux to support glibc < 2.26
|
||||
+#endif
|
||||
#include <stdlib.h>
|
||||
int main(void) {
|
||||
void* p = reallocarray(NULL, 10, 100);
|
||||
@@ -0,0 +1,20 @@
|
||||
diff --git a/configure b/configure
|
||||
index a41e3e1e..7d6a58f0 100755
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -22053,6 +22053,7 @@ else $as_nop
|
||||
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
|
||||
fi
|
||||
|
||||
+if test x_$withval = x_yes -o x_$withval != x_no; then
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libexpat" >&5
|
||||
printf %s "checking for libexpat... " >&6; }
|
||||
found_libexpat="no"
|
||||
@@ -22090,6 +22091,7 @@ else $as_nop
|
||||
ac_have_decl=0
|
||||
fi
|
||||
printf "%s\n" "#define HAVE_DECL_XML_STOPPARSER $ac_have_decl" >>confdefs.h
|
||||
+fi
|
||||
|
||||
|
||||
# hiredis (redis C client for cachedb)
|
||||
@@ -0,0 +1,11 @@
|
||||
--- config/ltmain.sh.0 2020-11-10 17:25:26.000000000 +0100
|
||||
+++ config/ltmain.sh 2021-09-11 19:39:36.000000000 +0200
|
||||
@@ -10768,6 +10768,8 @@
|
||||
fi
|
||||
func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
|
||||
tool_oldlib=$func_to_tool_file_result
|
||||
+ oldobjs=`for obj in $oldobjs; do echo $obj; done | sort`
|
||||
+ oldobjs=" `echo $oldobjs`"
|
||||
eval cmds=\"$old_archive_cmds\"
|
||||
|
||||
func_len " $cmds"
|
||||
@@ -91,7 +91,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
SET(BREW OFF)
|
||||
SET(PORT OFF)
|
||||
SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/")
|
||||
SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
|
||||
SET(CMAKE_OSX_DEPLOYMENT_TARGET "11.0")
|
||||
SET(CMAKE_CXX_STANDARD 17)
|
||||
SET(LLVM_ENABLE_PIC OFF)
|
||||
SET(LLVM_ENABLE_PIE OFF)
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace epee
|
||||
public:
|
||||
using char_type = std::uint8_t;
|
||||
using Ch = char_type;
|
||||
using value_type = char_type;
|
||||
|
||||
//! Increase internal buffer by at least `byte_stream_increase` bytes.
|
||||
byte_stream() noexcept
|
||||
@@ -86,6 +87,7 @@ namespace epee
|
||||
~byte_stream() noexcept = default;
|
||||
byte_stream& operator=(byte_stream&& rhs) noexcept;
|
||||
|
||||
std::uint8_t* data() noexcept { return buffer_.get(); }
|
||||
const std::uint8_t* data() const noexcept { return buffer_.get(); }
|
||||
std::uint8_t* tellp() const noexcept { return next_write_; }
|
||||
std::size_t available() const noexcept { return end_ - next_write_; }
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <condition_variable>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
@@ -64,6 +65,7 @@
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "net"
|
||||
|
||||
#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 1000
|
||||
#define ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT 100 * 1024 * 1024
|
||||
|
||||
namespace epee
|
||||
{
|
||||
@@ -76,6 +78,13 @@ namespace net_utils
|
||||
protected:
|
||||
virtual ~i_connection_filter(){}
|
||||
};
|
||||
|
||||
struct i_connection_limit
|
||||
{
|
||||
virtual bool is_host_limit(const epee::net_utils::network_address &address)=0;
|
||||
protected:
|
||||
virtual ~i_connection_limit(){}
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
@@ -100,8 +109,8 @@ namespace net_utils
|
||||
using ec_t = boost::system::error_code;
|
||||
using handshake_t = boost::asio::ssl::stream_base::handshake_type;
|
||||
|
||||
using io_context_t = boost::asio::io_service;
|
||||
using strand_t = boost::asio::io_service::strand;
|
||||
using io_context_t = boost::asio::io_context;
|
||||
using strand_t = io_context_t::strand;
|
||||
using socket_t = boost::asio::ip::tcp::socket;
|
||||
|
||||
using network_throttle_t = epee::net_utils::network_throttle;
|
||||
@@ -162,6 +171,7 @@ namespace net_utils
|
||||
} read;
|
||||
struct {
|
||||
std::deque<epee::byte_slice> queue;
|
||||
std::size_t total_bytes;
|
||||
bool wait_consume;
|
||||
} write;
|
||||
};
|
||||
@@ -260,20 +270,28 @@ namespace net_utils
|
||||
struct shared_state : connection_basic_shared_state, t_protocol_handler::config_type
|
||||
{
|
||||
shared_state()
|
||||
: connection_basic_shared_state(), t_protocol_handler::config_type(), pfilter(nullptr), stop_signal_sent(false)
|
||||
: connection_basic_shared_state(),
|
||||
t_protocol_handler::config_type(),
|
||||
pfilter(nullptr),
|
||||
plimit(nullptr),
|
||||
response_soft_limit(ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT),
|
||||
stop_signal_sent(false)
|
||||
{}
|
||||
|
||||
i_connection_filter* pfilter;
|
||||
i_connection_limit* plimit;
|
||||
std::size_t response_soft_limit;
|
||||
bool stop_signal_sent;
|
||||
};
|
||||
|
||||
/// Construct a connection with the given io_service.
|
||||
explicit connection( boost::asio::io_service& io_service,
|
||||
/// Construct a connection with the given io_context.
|
||||
explicit connection( io_context_t& io_context,
|
||||
std::shared_ptr<shared_state> state,
|
||||
t_connection_type connection_type,
|
||||
epee::net_utils::ssl_support_t ssl_support);
|
||||
|
||||
explicit connection( boost::asio::ip::tcp::socket&& sock,
|
||||
explicit connection( io_context_t& io_context,
|
||||
boost::asio::ip::tcp::socket&& sock,
|
||||
std::shared_ptr<shared_state> state,
|
||||
t_connection_type connection_type,
|
||||
epee::net_utils::ssl_support_t ssl_support);
|
||||
@@ -306,7 +324,7 @@ namespace net_utils
|
||||
virtual bool close();
|
||||
virtual bool call_run_once_service_io();
|
||||
virtual bool request_callback();
|
||||
virtual boost::asio::io_service& get_io_service();
|
||||
virtual io_context_t& get_io_context();
|
||||
virtual bool add_ref();
|
||||
virtual bool release();
|
||||
//------------------------------------------------------
|
||||
@@ -336,7 +354,7 @@ namespace net_utils
|
||||
/// serve up files from the given directory.
|
||||
|
||||
boosted_tcp_server(t_connection_type connection_type);
|
||||
explicit boosted_tcp_server(boost::asio::io_service& external_io_service, t_connection_type connection_type);
|
||||
explicit boosted_tcp_server(boost::asio::io_context& external_io_context, t_connection_type connection_type);
|
||||
~boosted_tcp_server();
|
||||
|
||||
std::map<std::string, t_connection_type> server_type_map;
|
||||
@@ -349,7 +367,7 @@ namespace net_utils
|
||||
const std::string port_ipv6 = "", const std::string address_ipv6 = "::", bool use_ipv6 = false, bool require_ipv4 = true,
|
||||
ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
|
||||
|
||||
/// Run the server's io_service loop.
|
||||
/// Run the server's io_context loop.
|
||||
bool run_server(size_t threads_count, bool wait = true, const boost::thread::attributes& attrs = boost::thread::attributes());
|
||||
|
||||
/// wait for service workers stop
|
||||
@@ -369,6 +387,8 @@ namespace net_utils
|
||||
size_t get_threads_count(){return m_threads_count;}
|
||||
|
||||
void set_connection_filter(i_connection_filter* pfilter);
|
||||
void set_connection_limit(i_connection_limit* plimit);
|
||||
void set_response_soft_limit(std::size_t limit);
|
||||
|
||||
void set_default_remote(epee::net_utils::network_address remote)
|
||||
{
|
||||
@@ -409,7 +429,7 @@ namespace net_utils
|
||||
return connections_count;
|
||||
}
|
||||
|
||||
boost::asio::io_service& get_io_service(){return io_service_;}
|
||||
boost::asio::io_context& get_io_context(){return io_context_;}
|
||||
|
||||
struct idle_callback_conext_base
|
||||
{
|
||||
@@ -417,7 +437,7 @@ namespace net_utils
|
||||
|
||||
virtual bool call_handler(){return true;}
|
||||
|
||||
idle_callback_conext_base(boost::asio::io_service& io_serice):
|
||||
idle_callback_conext_base(boost::asio::io_context& io_serice):
|
||||
m_timer(io_serice)
|
||||
{}
|
||||
boost::asio::deadline_timer m_timer;
|
||||
@@ -426,7 +446,7 @@ namespace net_utils
|
||||
template <class t_handler>
|
||||
struct idle_callback_conext: public idle_callback_conext_base
|
||||
{
|
||||
idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, uint64_t period):
|
||||
idle_callback_conext(boost::asio::io_context& io_serice, t_handler& h, uint64_t period):
|
||||
idle_callback_conext_base(io_serice),
|
||||
m_handler(h)
|
||||
{this->m_period = period;}
|
||||
@@ -442,7 +462,7 @@ namespace net_utils
|
||||
template<class t_handler>
|
||||
bool add_idle_handler(t_handler t_callback, uint64_t timeout_ms)
|
||||
{
|
||||
boost::shared_ptr<idle_callback_conext<t_handler>> ptr(new idle_callback_conext<t_handler>(io_service_, t_callback, timeout_ms));
|
||||
boost::shared_ptr<idle_callback_conext<t_handler>> ptr(new idle_callback_conext<t_handler>(io_context_, t_callback, timeout_ms));
|
||||
//needed call handler here ?...
|
||||
ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
|
||||
ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler<t_handler>, this, ptr));
|
||||
@@ -461,14 +481,14 @@ namespace net_utils
|
||||
}
|
||||
|
||||
template<class t_handler>
|
||||
bool async_call(t_handler t_callback)
|
||||
bool async_call(t_handler&& t_callback)
|
||||
{
|
||||
io_service_.post(t_callback);
|
||||
boost::asio::post(io_context_, std::forward<t_handler>(t_callback));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Run the server's io_service loop.
|
||||
/// Run the server's io_context loop.
|
||||
bool worker_thread();
|
||||
/// Handle completion of an asynchronous accept operation.
|
||||
void handle_accept_ipv4(const boost::system::error_code& e);
|
||||
@@ -479,18 +499,18 @@ namespace net_utils
|
||||
|
||||
const std::shared_ptr<typename connection<t_protocol_handler>::shared_state> m_state;
|
||||
|
||||
/// The io_service used to perform asynchronous operations.
|
||||
/// The io_context used to perform asynchronous operations.
|
||||
struct worker
|
||||
{
|
||||
worker()
|
||||
: io_service(), work(io_service)
|
||||
: io_context(), work(io_context.get_executor())
|
||||
{}
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::io_service::work work;
|
||||
boost::asio::io_context io_context;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work;
|
||||
};
|
||||
std::unique_ptr<worker> m_io_service_local_instance;
|
||||
boost::asio::io_service& io_service_;
|
||||
std::unique_ptr<worker> m_io_context_local_instance;
|
||||
boost::asio::io_context& io_context_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
@@ -31,11 +31,12 @@
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/uuid/random_generator.hpp>
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/utility/value_init.hpp>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp> // TODO
|
||||
#include <boost/thread/condition_variable.hpp> // TODO
|
||||
@@ -145,23 +146,19 @@ namespace net_utils
|
||||
if (m_state.timers.general.wait_expire) {
|
||||
m_state.timers.general.cancel_expire = true;
|
||||
m_state.timers.general.reset_expire = true;
|
||||
ec_t ec;
|
||||
m_timers.general.expires_from_now(
|
||||
m_timers.general.expires_after(
|
||||
std::min(
|
||||
duration + (add ? m_timers.general.expires_from_now() : duration_t{}),
|
||||
duration + (add ? (m_timers.general.expiry() - std::chrono::steady_clock::now()) : duration_t{}),
|
||||
get_default_timeout()
|
||||
),
|
||||
ec
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
ec_t ec;
|
||||
m_timers.general.expires_from_now(
|
||||
m_timers.general.expires_after(
|
||||
std::min(
|
||||
duration + (add ? m_timers.general.expires_from_now() : duration_t{}),
|
||||
duration + (add ? (m_timers.general.expiry() - std::chrono::steady_clock::now()) : duration_t{}),
|
||||
get_default_timeout()
|
||||
),
|
||||
ec
|
||||
)
|
||||
);
|
||||
async_wait_timer();
|
||||
}
|
||||
@@ -202,8 +199,7 @@ namespace net_utils
|
||||
return;
|
||||
m_state.timers.general.cancel_expire = true;
|
||||
m_state.timers.general.reset_expire = false;
|
||||
ec_t ec;
|
||||
m_timers.general.cancel(ec);
|
||||
m_timers.general.cancel();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -225,7 +221,8 @@ namespace net_utils
|
||||
m_state.data.read.buffer.size()
|
||||
),
|
||||
boost::asio::transfer_exactly(epee::net_utils::get_ssl_magic_size()),
|
||||
m_strand.wrap(
|
||||
boost::asio::bind_executor(
|
||||
m_strand,
|
||||
[this, self](const ec_t &ec, size_t bytes_transferred){
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
m_state.socket.wait_read = false;
|
||||
@@ -246,7 +243,8 @@ namespace net_utils
|
||||
) {
|
||||
m_state.ssl.enabled = false;
|
||||
m_state.socket.handle_read = true;
|
||||
connection_basic::strand_.post(
|
||||
boost::asio::post(
|
||||
connection_basic::strand_,
|
||||
[this, self, bytes_transferred]{
|
||||
bool success = m_handler.handle_recv(
|
||||
reinterpret_cast<char *>(m_state.data.read.buffer.data()),
|
||||
@@ -304,7 +302,8 @@ namespace net_utils
|
||||
static_cast<shared_state&>(
|
||||
connection_basic::get_state()
|
||||
).ssl_options().configure(connection_basic::socket_, handshake);
|
||||
m_strand.post(
|
||||
boost::asio::post(
|
||||
m_strand,
|
||||
[this, self, on_handshake]{
|
||||
connection_basic::socket_.async_handshake(
|
||||
handshake,
|
||||
@@ -313,7 +312,7 @@ namespace net_utils
|
||||
m_state.ssl.forced ? 0 :
|
||||
epee::net_utils::get_ssl_magic_size()
|
||||
),
|
||||
m_strand.wrap(on_handshake)
|
||||
boost::asio::bind_executor(m_strand, on_handshake)
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -328,7 +327,7 @@ namespace net_utils
|
||||
return;
|
||||
}
|
||||
auto self = connection<T>::shared_from_this();
|
||||
if (m_connection_type != e_connection_type_RPC) {
|
||||
if (speed_limit_is_enabled()) {
|
||||
auto calc_duration = []{
|
||||
CRITICAL_REGION_LOCAL(
|
||||
network_throttle_manager_t::m_lock_get_global_throttle_in
|
||||
@@ -345,8 +344,7 @@ namespace net_utils
|
||||
};
|
||||
const auto duration = calc_duration();
|
||||
if (duration > duration_t{}) {
|
||||
ec_t ec;
|
||||
m_timers.throttle.in.expires_from_now(duration, ec);
|
||||
m_timers.throttle.in.expires_after(duration);
|
||||
m_state.timers.throttle.in.wait_expire = true;
|
||||
m_timers.throttle.in.async_wait([this, self](const ec_t &ec){
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
@@ -382,7 +380,7 @@ namespace net_utils
|
||||
m_conn_context.m_max_speed_down,
|
||||
speed
|
||||
);
|
||||
{
|
||||
if (speed_limit_is_enabled()) {
|
||||
CRITICAL_REGION_LOCAL(
|
||||
network_throttle_manager_t::m_lock_get_global_throttle_in
|
||||
);
|
||||
@@ -401,7 +399,8 @@ namespace net_utils
|
||||
// writes until the connection terminates without deadlocking waiting
|
||||
// for handle_recv.
|
||||
m_state.socket.handle_read = true;
|
||||
connection_basic::strand_.post(
|
||||
boost::asio::post(
|
||||
connection_basic::strand_,
|
||||
[this, self, bytes_transferred]{
|
||||
bool success = m_handler.handle_recv(
|
||||
reinterpret_cast<char *>(m_state.data.read.buffer.data()),
|
||||
@@ -428,17 +427,18 @@ namespace net_utils
|
||||
m_state.data.read.buffer.data(),
|
||||
m_state.data.read.buffer.size()
|
||||
),
|
||||
m_strand.wrap(on_read)
|
||||
boost::asio::bind_executor(m_strand, on_read)
|
||||
);
|
||||
else
|
||||
m_strand.post(
|
||||
boost::asio::post(
|
||||
m_strand,
|
||||
[this, self, on_read]{
|
||||
connection_basic::socket_.async_read_some(
|
||||
boost::asio::buffer(
|
||||
m_state.data.read.buffer.data(),
|
||||
m_state.data.read.buffer.size()
|
||||
),
|
||||
m_strand.wrap(on_read)
|
||||
boost::asio::bind_executor(m_strand, on_read)
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -454,7 +454,7 @@ namespace net_utils
|
||||
return;
|
||||
}
|
||||
auto self = connection<T>::shared_from_this();
|
||||
if (m_connection_type != e_connection_type_RPC) {
|
||||
if (speed_limit_is_enabled()) {
|
||||
auto calc_duration = [this]{
|
||||
CRITICAL_REGION_LOCAL(
|
||||
network_throttle_manager_t::m_lock_get_global_throttle_out
|
||||
@@ -473,8 +473,7 @@ namespace net_utils
|
||||
};
|
||||
const auto duration = calc_duration();
|
||||
if (duration > duration_t{}) {
|
||||
ec_t ec;
|
||||
m_timers.throttle.out.expires_from_now(duration, ec);
|
||||
m_timers.throttle.out.expires_after(duration);
|
||||
m_state.timers.throttle.out.wait_expire = true;
|
||||
m_timers.throttle.out.async_wait([this, self](const ec_t &ec){
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
@@ -498,10 +497,12 @@ namespace net_utils
|
||||
if (m_state.socket.cancel_write) {
|
||||
m_state.socket.cancel_write = false;
|
||||
m_state.data.write.queue.clear();
|
||||
m_state.data.write.total_bytes = 0;
|
||||
state_status_check();
|
||||
}
|
||||
else if (ec.value()) {
|
||||
m_state.data.write.queue.clear();
|
||||
m_state.data.write.total_bytes = 0;
|
||||
interrupt();
|
||||
}
|
||||
else {
|
||||
@@ -513,7 +514,7 @@ namespace net_utils
|
||||
m_conn_context.m_max_speed_down,
|
||||
speed
|
||||
);
|
||||
{
|
||||
if (speed_limit_is_enabled()) {
|
||||
CRITICAL_REGION_LOCAL(
|
||||
network_throttle_manager_t::m_lock_get_global_throttle_out
|
||||
);
|
||||
@@ -526,8 +527,11 @@ namespace net_utils
|
||||
|
||||
start_timer(get_default_timeout(), true);
|
||||
}
|
||||
assert(bytes_transferred == m_state.data.write.queue.back().size());
|
||||
const std::size_t byte_count = m_state.data.write.queue.back().size();
|
||||
assert(bytes_transferred == byte_count);
|
||||
m_state.data.write.queue.pop_back();
|
||||
m_state.data.write.total_bytes -=
|
||||
std::min(m_state.data.write.total_bytes, byte_count);
|
||||
m_state.condition.notify_all();
|
||||
start_write();
|
||||
}
|
||||
@@ -539,10 +543,11 @@ namespace net_utils
|
||||
m_state.data.write.queue.back().data(),
|
||||
m_state.data.write.queue.back().size()
|
||||
),
|
||||
m_strand.wrap(on_write)
|
||||
boost::asio::bind_executor(m_strand, on_write)
|
||||
);
|
||||
else
|
||||
m_strand.post(
|
||||
boost::asio::post(
|
||||
m_strand,
|
||||
[this, self, on_write]{
|
||||
boost::asio::async_write(
|
||||
connection_basic::socket_,
|
||||
@@ -550,7 +555,7 @@ namespace net_utils
|
||||
m_state.data.write.queue.back().data(),
|
||||
m_state.data.write.queue.back().size()
|
||||
),
|
||||
m_strand.wrap(on_write)
|
||||
boost::asio::bind_executor(m_strand, on_write)
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -587,10 +592,11 @@ namespace net_utils
|
||||
terminate();
|
||||
}
|
||||
};
|
||||
m_strand.post(
|
||||
boost::asio::post(
|
||||
m_strand,
|
||||
[this, self, on_shutdown]{
|
||||
connection_basic::socket_.async_shutdown(
|
||||
m_strand.wrap(on_shutdown)
|
||||
boost::asio::bind_executor(m_strand, on_shutdown)
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -605,15 +611,13 @@ namespace net_utils
|
||||
wait_socket = m_state.socket.cancel_handshake = true;
|
||||
if (m_state.timers.throttle.in.wait_expire) {
|
||||
m_state.timers.throttle.in.cancel_expire = true;
|
||||
ec_t ec;
|
||||
m_timers.throttle.in.cancel(ec);
|
||||
m_timers.throttle.in.cancel();
|
||||
}
|
||||
if (m_state.socket.wait_read)
|
||||
wait_socket = m_state.socket.cancel_read = true;
|
||||
if (m_state.timers.throttle.out.wait_expire) {
|
||||
m_state.timers.throttle.out.cancel_expire = true;
|
||||
ec_t ec;
|
||||
m_timers.throttle.out.cancel(ec);
|
||||
m_timers.throttle.out.cancel();
|
||||
}
|
||||
if (m_state.socket.wait_write)
|
||||
wait_socket = m_state.socket.cancel_write = true;
|
||||
@@ -671,8 +675,9 @@ namespace net_utils
|
||||
return;
|
||||
if (m_state.timers.throttle.out.wait_expire)
|
||||
return;
|
||||
if (m_state.socket.wait_write)
|
||||
return;
|
||||
// \NOTE See on_terminating() comments
|
||||
//if (m_state.socket.wait_write)
|
||||
// return;
|
||||
if (m_state.socket.wait_shutdown)
|
||||
return;
|
||||
if (m_state.protocol.wait_init)
|
||||
@@ -730,8 +735,13 @@ namespace net_utils
|
||||
return;
|
||||
if (m_state.timers.throttle.out.wait_expire)
|
||||
return;
|
||||
if (m_state.socket.wait_write)
|
||||
return;
|
||||
// Writes cannot be canceled due to `async_write` being a "composed"
|
||||
// handler. ASIO has new cancellation routines, not available in 1.66, to
|
||||
// handle this situation. The problem is that if cancel is called after an
|
||||
// intermediate handler is queued, the op will not check the cancel flag in
|
||||
// our code, and will instead queue up another write.
|
||||
//if (m_state.socket.wait_write)
|
||||
// return;
|
||||
if (m_state.socket.wait_shutdown)
|
||||
return;
|
||||
if (m_state.protocol.wait_init)
|
||||
@@ -758,6 +768,8 @@ namespace net_utils
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake)
|
||||
return false;
|
||||
if (std::numeric_limits<std::size_t>::max() - m_state.data.write.total_bytes < message.size())
|
||||
return false;
|
||||
|
||||
// Wait for the write queue to fall below the max. If it doesn't after a
|
||||
// randomized delay, drop the connection.
|
||||
@@ -775,7 +787,14 @@ namespace net_utils
|
||||
std::uniform_int_distribution<>(5000, 6000)(rng)
|
||||
);
|
||||
};
|
||||
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
|
||||
|
||||
// The bytes check intentionally does not include incoming message size.
|
||||
// This allows for a soft overflow; a single http response will never fail
|
||||
// this check, but multiple responses could. Clients can avoid this case
|
||||
// by reading the entire response before making another request. P2P
|
||||
// should never hit the MAX_BYTES check (when using default values).
|
||||
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
|
||||
m_state.data.write.total_bytes <= static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit)
|
||||
return true;
|
||||
m_state.data.write.wait_consume = true;
|
||||
bool success = m_state.condition.wait_for(
|
||||
@@ -784,14 +803,23 @@ namespace net_utils
|
||||
[this]{
|
||||
return (
|
||||
m_state.status != status_t::RUNNING ||
|
||||
m_state.data.write.queue.size() <=
|
||||
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT
|
||||
(
|
||||
m_state.data.write.queue.size() <=
|
||||
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
|
||||
m_state.data.write.total_bytes <=
|
||||
static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
m_state.data.write.wait_consume = false;
|
||||
if (!success) {
|
||||
terminate();
|
||||
// synchronize with intermediate writes on `m_strand`
|
||||
auto self = connection<T>::shared_from_this();
|
||||
boost::asio::post(m_strand, [this, self] {
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
terminate();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -817,7 +845,9 @@ namespace net_utils
|
||||
) {
|
||||
if (!wait_consume())
|
||||
return false;
|
||||
const std::size_t byte_count = message.size();
|
||||
m_state.data.write.queue.emplace_front(std::move(message));
|
||||
m_state.data.write.total_bytes += byte_count;
|
||||
start_write();
|
||||
}
|
||||
else {
|
||||
@@ -827,6 +857,7 @@ namespace net_utils
|
||||
m_state.data.write.queue.emplace_front(
|
||||
message.take_slice(CHUNK_SIZE)
|
||||
);
|
||||
m_state.data.write.total_bytes += m_state.data.write.queue.front().size();
|
||||
start_write();
|
||||
}
|
||||
}
|
||||
@@ -860,7 +891,7 @@ namespace net_utils
|
||||
ipv4_network_address{
|
||||
uint32_t{
|
||||
boost::asio::detail::socket_ops::host_to_network_long(
|
||||
endpoint.address().to_v4().to_ulong()
|
||||
endpoint.address().to_v4().to_uint()
|
||||
)
|
||||
},
|
||||
endpoint.port()
|
||||
@@ -873,6 +904,13 @@ namespace net_utils
|
||||
).pfilter;
|
||||
if (filter && !filter->is_remote_host_allowed(*real_remote))
|
||||
return false;
|
||||
|
||||
auto *limit = static_cast<shared_state&>(
|
||||
connection_basic::get_state()
|
||||
).plimit;
|
||||
if (limit && limit->is_host_limit(*real_remote))
|
||||
return false;
|
||||
|
||||
ec_t ec;
|
||||
#if !defined(_WIN32) || !defined(__i686)
|
||||
connection_basic::socket_.next_layer().set_option(
|
||||
@@ -938,7 +976,8 @@ namespace net_utils
|
||||
ssl_support_t ssl_support
|
||||
):
|
||||
connection(
|
||||
std::move(socket_t{io_context}),
|
||||
io_context,
|
||||
socket_t{io_context},
|
||||
std::move(shared_state),
|
||||
connection_type,
|
||||
ssl_support
|
||||
@@ -948,15 +987,16 @@ namespace net_utils
|
||||
|
||||
template<typename T>
|
||||
connection<T>::connection(
|
||||
io_context_t &io_context,
|
||||
socket_t &&socket,
|
||||
std::shared_ptr<shared_state> shared_state,
|
||||
t_connection_type connection_type,
|
||||
ssl_support_t ssl_support
|
||||
):
|
||||
connection_basic(std::move(socket), shared_state, ssl_support),
|
||||
connection_basic(io_context, std::move(socket), shared_state, ssl_support),
|
||||
m_handler(this, *shared_state, m_conn_context),
|
||||
m_connection_type(connection_type),
|
||||
m_io_context{GET_IO_SERVICE(connection_basic::socket_)},
|
||||
m_io_context{io_context},
|
||||
m_strand{m_io_context},
|
||||
m_timers{m_io_context}
|
||||
{
|
||||
@@ -1022,7 +1062,7 @@ namespace net_utils
|
||||
template<typename T>
|
||||
bool connection<T>::speed_limit_is_enabled() const
|
||||
{
|
||||
return m_connection_type != e_connection_type_RPC;
|
||||
return m_connection_type == e_connection_type_P2P;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -1075,7 +1115,7 @@ namespace net_utils
|
||||
return false;
|
||||
auto self = connection<T>::shared_from_this();
|
||||
++m_state.protocol.wait_callback;
|
||||
connection_basic::strand_.post([this, self]{
|
||||
boost::asio::post(connection_basic::strand_, [this, self]{
|
||||
m_handler.handle_qued_callback();
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
--m_state.protocol.wait_callback;
|
||||
@@ -1088,7 +1128,7 @@ namespace net_utils
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename connection<T>::io_context_t &connection<T>::get_io_service()
|
||||
typename connection<T>::io_context_t &connection<T>::get_io_context()
|
||||
{
|
||||
return m_io_context;
|
||||
}
|
||||
@@ -1128,10 +1168,10 @@ namespace net_utils
|
||||
template<class t_protocol_handler>
|
||||
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server( t_connection_type connection_type ) :
|
||||
m_state(std::make_shared<typename connection<t_protocol_handler>::shared_state>()),
|
||||
m_io_service_local_instance(new worker()),
|
||||
io_service_(m_io_service_local_instance->io_service),
|
||||
acceptor_(io_service_),
|
||||
acceptor_ipv6(io_service_),
|
||||
m_io_context_local_instance(new worker()),
|
||||
io_context_(m_io_context_local_instance->io_context),
|
||||
acceptor_(io_context_),
|
||||
acceptor_ipv6(io_context_),
|
||||
default_remote(),
|
||||
m_stop_signal_sent(false), m_port(0),
|
||||
m_threads_count(0),
|
||||
@@ -1145,11 +1185,11 @@ namespace net_utils
|
||||
}
|
||||
|
||||
template<class t_protocol_handler>
|
||||
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_service& extarnal_io_service, t_connection_type connection_type) :
|
||||
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_context& extarnal_io_context, t_connection_type connection_type) :
|
||||
m_state(std::make_shared<typename connection<t_protocol_handler>::shared_state>()),
|
||||
io_service_(extarnal_io_service),
|
||||
acceptor_(io_service_),
|
||||
acceptor_ipv6(io_service_),
|
||||
io_context_(extarnal_io_context),
|
||||
acceptor_(io_context_),
|
||||
acceptor_ipv6(io_context_),
|
||||
default_remote(),
|
||||
m_stop_signal_sent(false), m_port(0),
|
||||
m_threads_count(0),
|
||||
@@ -1196,24 +1236,27 @@ namespace net_utils
|
||||
|
||||
std::string ipv4_failed = "";
|
||||
std::string ipv6_failed = "";
|
||||
|
||||
boost::asio::ip::tcp::resolver resolver(io_context_);
|
||||
|
||||
try
|
||||
{
|
||||
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||
boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
|
||||
acceptor_.open(endpoint.protocol());
|
||||
const auto results = resolver.resolve(
|
||||
address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::canonical_name
|
||||
);
|
||||
acceptor_.open(results.begin()->endpoint().protocol());
|
||||
#if !defined(_WIN32)
|
||||
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
||||
#endif
|
||||
acceptor_.bind(endpoint);
|
||||
acceptor_.bind(*results.begin());
|
||||
acceptor_.listen();
|
||||
boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
|
||||
m_port = binded_endpoint.port();
|
||||
MDEBUG("start accept (IPv4)");
|
||||
new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
|
||||
new_connection_.reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, m_state->ssl_options().support));
|
||||
acceptor_.async_accept(new_connection_->socket(),
|
||||
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4, this,
|
||||
boost::asio::placeholders::error));
|
||||
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@@ -1234,23 +1277,25 @@ namespace net_utils
|
||||
try
|
||||
{
|
||||
if (port_ipv6 == 0) port_ipv6 = port; // default arg means bind to same port as ipv4
|
||||
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||
boost::asio::ip::tcp::resolver::query query(address_ipv6, boost::lexical_cast<std::string>(port_ipv6), boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
|
||||
acceptor_ipv6.open(endpoint.protocol());
|
||||
|
||||
const auto results = resolver.resolve(
|
||||
address_ipv6, boost::lexical_cast<std::string>(port_ipv6), boost::asio::ip::tcp::resolver::canonical_name
|
||||
);
|
||||
|
||||
acceptor_ipv6.open(results.begin()->endpoint().protocol());
|
||||
#if !defined(_WIN32)
|
||||
acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
||||
#endif
|
||||
acceptor_ipv6.set_option(boost::asio::ip::v6_only(true));
|
||||
acceptor_ipv6.bind(endpoint);
|
||||
acceptor_ipv6.bind(*results.begin());
|
||||
acceptor_ipv6.listen();
|
||||
boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_ipv6.local_endpoint();
|
||||
m_port_ipv6 = binded_endpoint.port();
|
||||
MDEBUG("start accept (IPv6)");
|
||||
new_connection_ipv6.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
|
||||
new_connection_ipv6.reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, m_state->ssl_options().support));
|
||||
acceptor_ipv6.async_accept(new_connection_ipv6->socket(),
|
||||
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6, this,
|
||||
boost::asio::placeholders::error));
|
||||
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@@ -1314,7 +1359,7 @@ namespace net_utils
|
||||
{
|
||||
try
|
||||
{
|
||||
io_service_.run();
|
||||
io_context_.run();
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception& ex)
|
||||
@@ -1349,6 +1394,20 @@ namespace net_utils
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
template<class t_protocol_handler>
|
||||
void boosted_tcp_server<t_protocol_handler>::set_connection_limit(i_connection_limit* plimit)
|
||||
{
|
||||
assert(m_state != nullptr); // always set in constructor
|
||||
m_state->plimit = plimit;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
template<class t_protocol_handler>
|
||||
void boosted_tcp_server<t_protocol_handler>::set_response_soft_limit(const std::size_t limit)
|
||||
{
|
||||
assert(m_state != nullptr); // always set in constructor
|
||||
m_state->response_soft_limit = limit;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
template<class t_protocol_handler>
|
||||
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs)
|
||||
{
|
||||
TRY_ENTRY();
|
||||
@@ -1358,7 +1417,7 @@ namespace net_utils
|
||||
while(!m_stop_signal_sent)
|
||||
{
|
||||
|
||||
// Create a pool of threads to run all of the io_services.
|
||||
// Create a pool of threads to run all of the io_contexts.
|
||||
CRITICAL_REGION_BEGIN(m_threads_lock);
|
||||
for (std::size_t i = 0; i < threads_count; ++i)
|
||||
{
|
||||
@@ -1450,7 +1509,7 @@ namespace net_utils
|
||||
}
|
||||
connections_.clear();
|
||||
connections_mutex.unlock();
|
||||
io_service_.stop();
|
||||
io_context_.stop();
|
||||
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void());
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
@@ -1497,7 +1556,7 @@ namespace net_utils
|
||||
(*current_new_connection)->setRpcStation(); // hopefully this is not needed actually
|
||||
}
|
||||
connection_ptr conn(std::move((*current_new_connection)));
|
||||
(*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support()));
|
||||
(*current_new_connection).reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, conn->get_ssl_support()));
|
||||
current_acceptor->async_accept((*current_new_connection)->socket(),
|
||||
boost::bind(accept_function_pointer, this,
|
||||
boost::asio::placeholders::error));
|
||||
@@ -1532,7 +1591,7 @@ namespace net_utils
|
||||
assert(m_state != nullptr); // always set in constructor
|
||||
_erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count);
|
||||
misc_utils::sleep_no_w(100);
|
||||
(*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support()));
|
||||
(*current_new_connection).reset(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support()));
|
||||
current_acceptor->async_accept((*current_new_connection)->socket(),
|
||||
boost::bind(accept_function_pointer, this,
|
||||
boost::asio::placeholders::error));
|
||||
@@ -1541,9 +1600,9 @@ namespace net_utils
|
||||
template<class t_protocol_handler>
|
||||
bool boosted_tcp_server<t_protocol_handler>::add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support)
|
||||
{
|
||||
if(std::addressof(get_io_service()) == std::addressof(GET_IO_SERVICE(sock)))
|
||||
if(std::addressof(get_io_context()) == std::addressof(sock.get_executor().context()))
|
||||
{
|
||||
connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support));
|
||||
connection_ptr conn(new connection<t_protocol_handler>(io_context_, std::move(sock), m_state, m_connection_type, ssl_support));
|
||||
if(conn->start(false, 1 < m_threads_count, std::move(real_remote)))
|
||||
{
|
||||
conn->get_context(out);
|
||||
@@ -1553,7 +1612,7 @@ namespace net_utils
|
||||
}
|
||||
else
|
||||
{
|
||||
MWARNING(out << " was not added, socket/io_service mismatch");
|
||||
MWARNING(out << " was not added, socket/io_context mismatch");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1566,7 +1625,7 @@ namespace net_utils
|
||||
sock_.open(remote_endpoint.protocol());
|
||||
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
|
||||
{
|
||||
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
|
||||
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address(bind_ip), 0);
|
||||
boost::system::error_code ec;
|
||||
sock_.bind(local_endpoint, ec);
|
||||
if (ec)
|
||||
@@ -1661,7 +1720,7 @@ namespace net_utils
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) );
|
||||
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
|
||||
connections_mutex.lock();
|
||||
connections_.insert(new_connection_l);
|
||||
MDEBUG("connections_ size now " << connections_.size());
|
||||
@@ -1671,14 +1730,16 @@ namespace net_utils
|
||||
|
||||
bool try_ipv6 = false;
|
||||
|
||||
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
boost::asio::ip::tcp::resolver resolver(io_context_);
|
||||
boost::asio::ip::tcp::resolver::results_type results{};
|
||||
boost::system::error_code resolve_error;
|
||||
boost::asio::ip::tcp::resolver::iterator iterator;
|
||||
|
||||
try
|
||||
{
|
||||
//resolving ipv4 address as ipv6 throws, catch here and move on
|
||||
iterator = resolver.resolve(query, resolve_error);
|
||||
results = resolver.resolve(
|
||||
boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
|
||||
);
|
||||
}
|
||||
catch (const boost::system::system_error& e)
|
||||
{
|
||||
@@ -1696,8 +1757,7 @@ namespace net_utils
|
||||
|
||||
std::string bind_ip_to_use;
|
||||
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
if(iterator == end)
|
||||
if(results.empty())
|
||||
{
|
||||
if (!m_use_ipv6)
|
||||
{
|
||||
@@ -1717,11 +1777,11 @@ namespace net_utils
|
||||
|
||||
if (try_ipv6)
|
||||
{
|
||||
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
results = resolver.resolve(
|
||||
boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
|
||||
);
|
||||
|
||||
iterator = resolver.resolve(query6, resolve_error);
|
||||
|
||||
if(iterator == end)
|
||||
if(results.empty())
|
||||
{
|
||||
_erro("Failed to resolve " << adr);
|
||||
return false;
|
||||
@@ -1741,6 +1801,8 @@ namespace net_utils
|
||||
|
||||
}
|
||||
|
||||
const auto iterator = results.begin();
|
||||
|
||||
MDEBUG("Trying to connect to " << adr << ":" << port << ", bind_ip = " << bind_ip_to_use);
|
||||
|
||||
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
|
||||
@@ -1767,7 +1829,6 @@ namespace net_utils
|
||||
if (r)
|
||||
{
|
||||
new_connection_l->get_context(conn_context);
|
||||
//new_connection_l.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_pfilter));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1786,7 +1847,7 @@ namespace net_utils
|
||||
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support)
|
||||
{
|
||||
TRY_ENTRY();
|
||||
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) );
|
||||
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_context_, m_state, m_connection_type, ssl_support) );
|
||||
connections_mutex.lock();
|
||||
connections_.insert(new_connection_l);
|
||||
MDEBUG("connections_ size now " << connections_.size());
|
||||
@@ -1796,14 +1857,16 @@ namespace net_utils
|
||||
|
||||
bool try_ipv6 = false;
|
||||
|
||||
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
boost::asio::ip::tcp::resolver resolver(io_context_);
|
||||
boost::asio::ip::tcp::resolver::results_type results{};
|
||||
boost::system::error_code resolve_error;
|
||||
boost::asio::ip::tcp::resolver::iterator iterator;
|
||||
|
||||
try
|
||||
{
|
||||
//resolving ipv4 address as ipv6 throws, catch here and move on
|
||||
iterator = resolver.resolve(query, resolve_error);
|
||||
results = resolver.resolve(
|
||||
boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
|
||||
);
|
||||
}
|
||||
catch (const boost::system::system_error& e)
|
||||
{
|
||||
@@ -1819,8 +1882,7 @@ namespace net_utils
|
||||
throw;
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
if(iterator == end)
|
||||
if(results.empty())
|
||||
{
|
||||
if (!try_ipv6)
|
||||
{
|
||||
@@ -1835,24 +1897,23 @@ namespace net_utils
|
||||
|
||||
if (try_ipv6)
|
||||
{
|
||||
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
results = resolver.resolve(
|
||||
boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
|
||||
);
|
||||
|
||||
iterator = resolver.resolve(query6, resolve_error);
|
||||
|
||||
if(iterator == end)
|
||||
if(results.empty())
|
||||
{
|
||||
_erro("Failed to resolve " << adr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
|
||||
boost::asio::ip::tcp::endpoint remote_endpoint(*results.begin());
|
||||
|
||||
sock_.open(remote_endpoint.protocol());
|
||||
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
|
||||
{
|
||||
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
|
||||
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address(bind_ip.c_str()), 0);
|
||||
boost::system::error_code ec;
|
||||
sock_.bind(local_endpoint, ec);
|
||||
if (ec)
|
||||
@@ -1864,7 +1925,7 @@ namespace net_utils
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_));
|
||||
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_context_));
|
||||
//start deadline
|
||||
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
|
||||
sh_deadline->async_wait([=](const boost::system::error_code& error)
|
||||
|
||||
@@ -112,21 +112,20 @@ class connection_basic { // not-templated base class for rapid developmet of som
|
||||
std::deque<byte_slice> m_send_que;
|
||||
volatile bool m_is_multithreaded;
|
||||
/// Strand to ensure the connection's handlers are not called concurrently.
|
||||
boost::asio::io_service::strand strand_;
|
||||
boost::asio::io_context::strand strand_;
|
||||
/// Socket for the connection.
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
|
||||
ssl_support_t m_ssl_support;
|
||||
|
||||
public:
|
||||
// first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator
|
||||
connection_basic(boost::asio::ip::tcp::socket&& socket, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
|
||||
connection_basic(boost::asio::io_service &io_service, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
|
||||
connection_basic(boost::asio::io_context &context, boost::asio::ip::tcp::socket&& sock, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
|
||||
connection_basic(boost::asio::io_context &context, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support);
|
||||
|
||||
virtual ~connection_basic() noexcept(false);
|
||||
|
||||
//! \return `shared_state` object passed in construction (ptr never changes).
|
||||
connection_basic_shared_state& get_state() noexcept { return *m_state; /* verified in constructor */ }
|
||||
connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number, ssl_support_t ssl);
|
||||
|
||||
boost::asio::ip::tcp::socket& socket() { return socket_.next_layer(); }
|
||||
ssl_support_t get_ssl_support() const { return m_ssl_support; }
|
||||
@@ -135,7 +134,7 @@ class connection_basic { // not-templated base class for rapid developmet of som
|
||||
bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {})
|
||||
{
|
||||
//m_state != nullptr verified in constructor
|
||||
return m_state->ssl_options().handshake(socket_, type, buffer);
|
||||
return m_state->ssl_options().handshake(strand_.context(), socket_, type, buffer);
|
||||
}
|
||||
|
||||
template<typename MutableBufferSequence, typename ReadHandler>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "net_utils_base.h"
|
||||
#include "http_auth.h"
|
||||
#include "http_base.h"
|
||||
@@ -54,8 +55,13 @@ namespace net_utils
|
||||
{
|
||||
std::string m_folder;
|
||||
std::vector<std::string> m_access_control_origins;
|
||||
std::unordered_map<std::string, std::size_t> m_connections;
|
||||
boost::optional<login> m_user;
|
||||
size_t m_max_content_length{std::numeric_limits<size_t>::max()};
|
||||
std::size_t m_connection_count{0};
|
||||
std::size_t m_max_public_ip_connections{3};
|
||||
std::size_t m_max_private_ip_connections{25};
|
||||
std::size_t m_max_connections{100};
|
||||
critical_section m_lock;
|
||||
};
|
||||
|
||||
@@ -70,7 +76,7 @@ namespace net_utils
|
||||
typedef http_server_config config_type;
|
||||
|
||||
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
|
||||
virtual ~simple_http_connection_handler(){}
|
||||
virtual ~simple_http_connection_handler();
|
||||
|
||||
bool release_protocol()
|
||||
{
|
||||
@@ -86,10 +92,7 @@ namespace net_utils
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool after_init_connection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool after_init_connection();
|
||||
virtual bool handle_recv(const void* ptr, size_t cb);
|
||||
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
|
||||
|
||||
@@ -146,6 +149,7 @@ namespace net_utils
|
||||
protected:
|
||||
i_service_endpoint* m_psnd_hndlr;
|
||||
t_connection_context& m_conn_context;
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
template<class t_connection_context>
|
||||
@@ -212,10 +216,6 @@ namespace net_utils
|
||||
}
|
||||
void handle_qued_callback()
|
||||
{}
|
||||
bool after_init_connection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
//simple_http_connection_handler::config_type m_stub_config;
|
||||
|
||||
@@ -208,11 +208,46 @@ namespace net_utils
|
||||
m_newlines(0),
|
||||
m_bytes_read(0),
|
||||
m_psnd_hndlr(psnd_hndlr),
|
||||
m_conn_context(conn_context)
|
||||
m_conn_context(conn_context),
|
||||
m_initialized(false)
|
||||
{
|
||||
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
simple_http_connection_handler<t_connection_context>::~simple_http_connection_handler()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_config.m_lock);
|
||||
if (m_config.m_connection_count)
|
||||
--m_config.m_connection_count;
|
||||
auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str());
|
||||
if (elem != m_config.m_connections.end())
|
||||
{
|
||||
if (elem->second == 1 || elem->second == 0)
|
||||
m_config.m_connections.erase(elem);
|
||||
else
|
||||
--(elem->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{}
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::after_init_connection()
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_config.m_lock);
|
||||
++m_config.m_connections[m_conn_context.m_remote_address.host_str()];
|
||||
++m_config.m_connection_count;
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::set_ready_state()
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
else if((query_info.m_URI == s_pattern) && (cond)) \
|
||||
{ \
|
||||
handled = true; \
|
||||
uint64_t ticks = misc_utils::get_tick_count(); \
|
||||
uint64_t ticks = epee::misc_utils::get_tick_count(); \
|
||||
boost::value_initialized<command_type::request> req; \
|
||||
bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
|
||||
if (!parse_res) \
|
||||
@@ -107,7 +107,7 @@
|
||||
else if(query_info.m_URI == s_pattern) \
|
||||
{ \
|
||||
handled = true; \
|
||||
uint64_t ticks = misc_utils::get_tick_count(); \
|
||||
uint64_t ticks = epee::misc_utils::get_tick_count(); \
|
||||
boost::value_initialized<command_type::request> req; \
|
||||
bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), epee::strspan<uint8_t>(query_info.m_body)); \
|
||||
if (!parse_res) \
|
||||
@@ -117,7 +117,7 @@
|
||||
response_info.m_response_comment = "Bad request"; \
|
||||
return true; \
|
||||
} \
|
||||
uint64_t ticks1 = misc_utils::get_tick_count(); \
|
||||
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
|
||||
boost::value_initialized<command_type::response> resp;\
|
||||
MINFO(m_conn_context << "calling " << s_pattern); \
|
||||
bool res = false; \
|
||||
@@ -129,7 +129,7 @@
|
||||
response_info.m_response_comment = "Internal Server Error"; \
|
||||
return true; \
|
||||
} \
|
||||
uint64_t ticks2 = misc_utils::get_tick_count(); \
|
||||
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
|
||||
epee::byte_slice buffer; \
|
||||
epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), buffer, 64 * 1024); \
|
||||
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||
@@ -171,6 +171,13 @@
|
||||
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
epee::serialization::storage_entry params_; \
|
||||
params_ = epee::serialization::storage_entry(epee::serialization::section()); \
|
||||
if(!ps.get_value("params", params_, nullptr)) \
|
||||
{ \
|
||||
epee::serialization::section params_section; \
|
||||
ps.set_value("params", std::move(params_section), nullptr); \
|
||||
} \
|
||||
if(false) return true; //just a stub to have "else if"
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "net/abstract_tcp_server2.h"
|
||||
#include "http_protocol_handler.h"
|
||||
#include "net/http_server_handlers_map2.h"
|
||||
@@ -44,7 +45,8 @@ namespace epee
|
||||
{
|
||||
|
||||
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
|
||||
class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>
|
||||
class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>,
|
||||
net_utils::i_connection_limit
|
||||
{
|
||||
|
||||
public:
|
||||
@@ -52,7 +54,7 @@ namespace epee
|
||||
: m_net_server(epee::net_utils::e_connection_type_RPC)
|
||||
{}
|
||||
|
||||
explicit http_server_impl_base(boost::asio::io_service& external_io_service)
|
||||
explicit http_server_impl_base(boost::asio::io_context& external_io_service)
|
||||
: m_net_server(external_io_service)
|
||||
{}
|
||||
|
||||
@@ -60,8 +62,16 @@ namespace epee
|
||||
const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
|
||||
std::vector<std::string> access_control_origins = std::vector<std::string>(),
|
||||
boost::optional<net_utils::http::login> user = boost::none,
|
||||
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect)
|
||||
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect,
|
||||
const std::size_t max_public_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP,
|
||||
const std::size_t max_private_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP,
|
||||
const std::size_t max_connections = DEFAULT_RPC_MAX_CONNECTIONS,
|
||||
const std::size_t response_soft_limit = DEFAULT_RPC_SOFT_LIMIT_SIZE)
|
||||
{
|
||||
if (max_connections < max_public_ip_connections)
|
||||
throw std::invalid_argument{"Max public IP connections cannot be more than max connections"};
|
||||
if (max_connections < max_private_ip_connections)
|
||||
throw std::invalid_argument{"Max private IP connections cannot be more than max connections"};
|
||||
|
||||
//set self as callback handler
|
||||
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
|
||||
@@ -75,6 +85,11 @@ namespace epee
|
||||
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
|
||||
|
||||
m_net_server.get_config_object().m_user = std::move(user);
|
||||
m_net_server.get_config_object().m_max_public_ip_connections = max_public_ip_connections;
|
||||
m_net_server.get_config_object().m_max_private_ip_connections = max_private_ip_connections;
|
||||
m_net_server.get_config_object().m_max_connections = max_connections;
|
||||
m_net_server.set_response_soft_limit(response_soft_limit);
|
||||
m_net_server.set_connection_limit(this);
|
||||
|
||||
MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
|
||||
if (use_ipv6)
|
||||
@@ -131,6 +146,26 @@ namespace epee
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool is_host_limit(const net_utils::network_address& na) override final
|
||||
{
|
||||
auto& config = m_net_server.get_config_object();
|
||||
CRITICAL_REGION_LOCAL(config.m_lock);
|
||||
if (config.m_max_connections <= config.m_connection_count)
|
||||
return true;
|
||||
|
||||
const bool is_private = na.is_loopback() || na.is_local();
|
||||
const auto elem = config.m_connections.find(na.host_str());
|
||||
if (elem != config.m_connections.end())
|
||||
{
|
||||
if (is_private)
|
||||
return config.m_max_private_ip_connections <= elem->second;
|
||||
else
|
||||
return config.m_max_public_ip_connections <= elem->second;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
struct anvoke_handler: invoke_response_handler_base
|
||||
{
|
||||
anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command)
|
||||
:m_cb(cb), m_timeout(timeout), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false),
|
||||
:m_cb(cb), m_timeout(timeout), m_con(con), m_timer(con.m_pservice_endpoint->get_io_context()), m_timer_started(false),
|
||||
m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command)
|
||||
{
|
||||
if(m_con.start_outer_call())
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
@@ -158,11 +158,11 @@ namespace net_utils
|
||||
inline
|
||||
try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
|
||||
{
|
||||
m_deadline.expires_from_now(timeout);
|
||||
m_deadline.expires_after(timeout);
|
||||
boost::unique_future<boost::asio::ip::tcp::socket> connection = m_connector(addr, port, m_deadline);
|
||||
for (;;)
|
||||
{
|
||||
m_io_service.reset();
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
|
||||
if (connection.is_ready())
|
||||
@@ -178,7 +178,7 @@ namespace net_utils
|
||||
// SSL Options
|
||||
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
||||
{
|
||||
if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout))
|
||||
if (!m_ssl_options.handshake(m_io_service, *m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout))
|
||||
{
|
||||
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
||||
{
|
||||
@@ -285,7 +285,7 @@ namespace net_utils
|
||||
|
||||
try
|
||||
{
|
||||
m_deadline.expires_from_now(timeout);
|
||||
m_deadline.expires_after(timeout);
|
||||
|
||||
// Set up the variable that receives the result of the asynchronous
|
||||
// operation. The error code is set to would_block to signal that the
|
||||
@@ -303,7 +303,7 @@ namespace net_utils
|
||||
// Block until the asynchronous operation has completed.
|
||||
while (ec == boost::asio::error::would_block)
|
||||
{
|
||||
m_io_service.reset();
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
|
||||
@@ -409,7 +409,7 @@ namespace net_utils
|
||||
// Set a deadline for the asynchronous operation. Since this function uses
|
||||
// a composed operation (async_read_until), the deadline applies to the
|
||||
// entire operation, rather than individual reads from the socket.
|
||||
m_deadline.expires_from_now(timeout);
|
||||
m_deadline.expires_after(timeout);
|
||||
|
||||
// Set up the variable that receives the result of the asynchronous
|
||||
// operation. The error code is set to would_block to signal that the
|
||||
@@ -436,7 +436,7 @@ namespace net_utils
|
||||
// Block until the asynchronous operation has completed.
|
||||
while (ec == boost::asio::error::would_block && !m_shutdowned)
|
||||
{
|
||||
m_io_service.reset();
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ namespace net_utils
|
||||
// Set a deadline for the asynchronous operation. Since this function uses
|
||||
// a composed operation (async_read_until), the deadline applies to the
|
||||
// entire operation, rather than individual reads from the socket.
|
||||
m_deadline.expires_from_now(timeout);
|
||||
m_deadline.expires_after(timeout);
|
||||
|
||||
// Set up the variable that receives the result of the asynchronous
|
||||
// operation. The error code is set to would_block to signal that the
|
||||
@@ -580,7 +580,7 @@ namespace net_utils
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::asio::io_service& get_io_service()
|
||||
boost::asio::io_context& get_io_service()
|
||||
{
|
||||
return m_io_service;
|
||||
}
|
||||
@@ -607,7 +607,7 @@ namespace net_utils
|
||||
// Check whether the deadline has passed. We compare the deadline against
|
||||
// the current time since a new asynchronous operation may have moved the
|
||||
// deadline before this actor had a chance to run.
|
||||
if (m_deadline.expires_at() <= std::chrono::steady_clock::now())
|
||||
if (m_deadline.expiry() <= std::chrono::steady_clock::now())
|
||||
{
|
||||
// The deadline has passed. The socket is closed so that any outstanding
|
||||
// asynchronous operations are cancelled. This allows the blocked
|
||||
@@ -628,11 +628,11 @@ namespace net_utils
|
||||
void shutdown_ssl() {
|
||||
// ssl socket shutdown blocks if server doesn't respond. We close after 2 secs
|
||||
boost::system::error_code ec = boost::asio::error::would_block;
|
||||
m_deadline.expires_from_now(std::chrono::milliseconds(2000));
|
||||
m_deadline.expires_after(std::chrono::milliseconds(2000));
|
||||
m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1);
|
||||
while (ec == boost::asio::error::would_block)
|
||||
{
|
||||
m_io_service.reset();
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
// Ignore "short read" error
|
||||
@@ -676,7 +676,7 @@ namespace net_utils
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::io_service m_io_service;
|
||||
boost::asio::io_context m_io_service;
|
||||
boost::asio::ssl::context m_ctx;
|
||||
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket;
|
||||
std::function<connect_func> m_connector;
|
||||
@@ -688,119 +688,6 @@ namespace net_utils
|
||||
std::atomic<uint64_t> m_bytes_sent;
|
||||
std::atomic<uint64_t> m_bytes_received;
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class async_blocked_mode_client: public blocked_mode_client
|
||||
{
|
||||
public:
|
||||
async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service)
|
||||
{
|
||||
|
||||
// No deadline is required until the first socket operation is started. We
|
||||
// set the deadline to positive infinity so that the actor takes no action
|
||||
// until a specific deadline is set.
|
||||
m_send_deadline.expires_at(boost::posix_time::pos_infin);
|
||||
|
||||
// Start the persistent actor that checks for deadline expiry.
|
||||
check_send_deadline();
|
||||
}
|
||||
~async_blocked_mode_client()
|
||||
{
|
||||
m_send_deadline.cancel();
|
||||
}
|
||||
|
||||
bool shutdown()
|
||||
{
|
||||
blocked_mode_client::shutdown();
|
||||
m_send_deadline.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
bool send(const void* data, size_t sz)
|
||||
{
|
||||
try
|
||||
{
|
||||
/*
|
||||
m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
|
||||
|
||||
// Set up the variable that receives the result of the asynchronous
|
||||
// operation. The error code is set to would_block to signal that the
|
||||
// operation is incomplete. Asio guarantees that its asynchronous
|
||||
// operations will never fail with would_block, so any other value in
|
||||
// ec indicates completion.
|
||||
boost::system::error_code ec = boost::asio::error::would_block;
|
||||
|
||||
// Start the asynchronous operation itself. The boost::lambda function
|
||||
// object is used as a callback and will update the ec variable when the
|
||||
// operation completes. The blocking_udp_client.cpp example shows how you
|
||||
// can use boost::bind rather than boost::lambda.
|
||||
boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
|
||||
|
||||
// Block until the asynchronous operation has completed.
|
||||
while(ec == boost::asio::error::would_block)
|
||||
{
|
||||
m_io_service.run_one();
|
||||
}*/
|
||||
|
||||
boost::system::error_code ec;
|
||||
|
||||
size_t writen = write(data, sz, ec);
|
||||
|
||||
if (!writen || ec)
|
||||
{
|
||||
LOG_PRINT_L3("Problems at write: " << ec.message());
|
||||
return false;
|
||||
}else
|
||||
{
|
||||
m_send_deadline.expires_at(boost::posix_time::pos_infin);
|
||||
}
|
||||
}
|
||||
|
||||
catch(const boost::system::system_error& er)
|
||||
{
|
||||
LOG_ERROR("Some problems at connect, message: " << er.what());
|
||||
return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LOG_ERROR("Some fatal problems.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::deadline_timer m_send_deadline;
|
||||
|
||||
void check_send_deadline()
|
||||
{
|
||||
// Check whether the deadline has passed. We compare the deadline against
|
||||
// the current time since a new asynchronous operation may have moved the
|
||||
// deadline before this actor had a chance to run.
|
||||
if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
|
||||
{
|
||||
// The deadline has passed. The socket is closed so that any outstanding
|
||||
// asynchronous operations are cancelled. This allows the blocked
|
||||
// connect(), read_line() or write_line() functions to return.
|
||||
LOG_PRINT_L3("Timed out socket");
|
||||
m_ssl_socket->next_layer().close();
|
||||
|
||||
// There is no longer an active deadline. The expiry is set to positive
|
||||
// infinity so that the actor takes no action until a new deadline is set.
|
||||
m_send_deadline.expires_at(boost::posix_time::pos_infin);
|
||||
}
|
||||
|
||||
// Put the actor back to sleep.
|
||||
m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
@@ -125,6 +126,7 @@ namespace net_utils
|
||||
\note It is strongly encouraged that clients using `system_ca`
|
||||
verification provide a non-empty `host` for rfc2818 verification.
|
||||
|
||||
\param io_context associated with `socket`.
|
||||
\param socket Used in SSL handshake and verification
|
||||
\param type Client or server
|
||||
\param host This parameter is only used when
|
||||
@@ -136,6 +138,7 @@ namespace net_utils
|
||||
\return True if the SSL handshake completes with peer verification
|
||||
settings. */
|
||||
bool handshake(
|
||||
boost::asio::io_context& io_context,
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
|
||||
boost::asio::ssl::stream_base::handshake_type type,
|
||||
boost::asio::const_buffer buffer = {},
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#define _NET_UTILS_BASE_H_
|
||||
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/address_v6.hpp>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
@@ -47,10 +47,12 @@
|
||||
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(((uint32_t)a4)<<24))
|
||||
#endif
|
||||
|
||||
/* Use the below function carefully. The executor and io_context are slightly
|
||||
different concepts. */
|
||||
#if BOOST_VERSION >= 107000
|
||||
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
|
||||
#define MONERO_GET_EXECUTOR(type) type . get_executor()
|
||||
#else
|
||||
#define GET_IO_SERVICE(s) ((s).get_io_service())
|
||||
#define MONERO_GET_EXECUTOR(type) type . get_io_context()
|
||||
#endif
|
||||
|
||||
namespace net
|
||||
@@ -443,7 +445,7 @@ namespace net_utils
|
||||
virtual bool send_done()=0;
|
||||
virtual bool call_run_once_service_io()=0;
|
||||
virtual bool request_callback()=0;
|
||||
virtual boost::asio::io_service& get_io_service()=0;
|
||||
virtual boost::asio::io_context& get_io_context()=0;
|
||||
//protect from deletion connection object(with protocol instance) during external call "invoke"
|
||||
virtual bool add_ref()=0;
|
||||
virtual bool release()=0;
|
||||
|
||||
@@ -46,13 +46,13 @@ namespace net_utils
|
||||
|
||||
|
||||
class network_throttle : public i_network_throttle {
|
||||
private:
|
||||
public:
|
||||
struct packet_info {
|
||||
size_t m_size; // octets sent. Summary for given small-window (e.g. for all packaged in 1 second)
|
||||
packet_info();
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
network_speed_bps m_target_speed;
|
||||
size_t m_network_add_cost; // estimated add cost of headers
|
||||
size_t m_network_minimal_segment; // estimated minimal cost of sending 1 byte to round up to
|
||||
|
||||
@@ -98,16 +98,18 @@ public: \
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \
|
||||
epee::serialization::selector<is_store>::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name);
|
||||
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \
|
||||
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(variable, val_name) \
|
||||
static_assert(std::is_trivially_copyable<decltype(this_ref.variable)>(), "t_type must be a trivially copyable type."); \
|
||||
static_assert(std::is_standard_layout<decltype(this_ref.variable)>(), "t_type must be a standard layout type."); \
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name)
|
||||
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(variable, val_name, default_value) \
|
||||
do { \
|
||||
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
|
||||
bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name); \
|
||||
static_assert(std::is_trivially_copyable<decltype(this_ref.variable)>(), "t_type must be a trivially copyable type."); \
|
||||
static_assert(std::is_standard_layout<decltype(this_ref.variable)>(), "t_type must be a standard layout type."); \
|
||||
bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name) \
|
||||
if (!ret) \
|
||||
epee::serialize_default(this_ref.varialble, default_value); \
|
||||
epee::serialize_default(this_ref.variable, default_value); \
|
||||
} while(0);
|
||||
|
||||
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \
|
||||
@@ -118,7 +120,7 @@ public: \
|
||||
#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_trivially_copyable and is_standard_layout compile time check
|
||||
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value)
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
+13
-10
@@ -133,17 +133,14 @@ namespace epee
|
||||
return {src.data(), src.size()};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool has_padding() noexcept
|
||||
{
|
||||
return !std::is_standard_layout<T>() || alignof(T) != 1;
|
||||
}
|
||||
|
||||
//! \return Cast data from `src` as `span<const std::uint8_t>`.
|
||||
template<typename T>
|
||||
span<const std::uint8_t> to_byte_span(const span<const T> src) noexcept
|
||||
{
|
||||
static_assert(!has_padding<T>(), "source type may have padding");
|
||||
static_assert(!std::is_empty<T>(), "empty value types will not work -> sizeof == 1");
|
||||
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
|
||||
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
|
||||
static_assert(alignof(T) == 1, "type may have padding");
|
||||
return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()};
|
||||
}
|
||||
|
||||
@@ -153,7 +150,9 @@ namespace epee
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(!std::is_empty<value_type>(), "empty value types will not work -> sizeof == 1");
|
||||
static_assert(!has_padding<value_type>(), "source value type may have padding");
|
||||
static_assert(std::is_standard_layout<value_type>(), "value type must have standard layout");
|
||||
static_assert(std::is_trivially_copyable<value_type>(), "value type must be trivially copyable");
|
||||
static_assert(alignof(value_type) == 1, "value type may have padding");
|
||||
return {reinterpret_cast<std::uint8_t*>(src.data()), src.size() * sizeof(value_type)};
|
||||
}
|
||||
|
||||
@@ -162,7 +161,9 @@ namespace epee
|
||||
span<const std::uint8_t> as_byte_span(const T& src) noexcept
|
||||
{
|
||||
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
|
||||
static_assert(!has_padding<T>(), "source type may have padding");
|
||||
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
|
||||
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
|
||||
static_assert(alignof(T) == 1, "type may have padding");
|
||||
return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)};
|
||||
}
|
||||
|
||||
@@ -171,7 +172,9 @@ namespace epee
|
||||
span<std::uint8_t> as_mut_byte_span(T& src) noexcept
|
||||
{
|
||||
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
|
||||
static_assert(!has_padding<T>(), "source type may have padding");
|
||||
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
|
||||
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
|
||||
static_assert(alignof(T) == 1, "type may have padding");
|
||||
return {reinterpret_cast<std::uint8_t*>(std::addressof(src)), sizeof(T)};
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include <boost/numeric/conversion/bounds.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/numeric/conversion/bounds.hpp>
|
||||
#include <typeinfo>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "mlocker.h"
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
@@ -69,23 +70,17 @@ namespace string_tools
|
||||
#ifdef _WIN32
|
||||
std::string get_current_module_path();
|
||||
#endif
|
||||
bool set_module_name_and_folder(const std::string& path_to_process_);
|
||||
bool trim_left(std::string& str);
|
||||
bool trim_right(std::string& str);
|
||||
void set_module_name_and_folder(const std::string& path_to_process_);
|
||||
//----------------------------------------------------------------------------
|
||||
inline std::string& trim(std::string& str)
|
||||
{
|
||||
trim_left(str);
|
||||
trim_right(str);
|
||||
boost::trim(str);
|
||||
return str;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline std::string trim(const std::string& str_)
|
||||
inline std::string trim(const std::string& str)
|
||||
{
|
||||
std::string str = str_;
|
||||
trim_left(str);
|
||||
trim_right(str);
|
||||
return str;
|
||||
return boost::trim_copy(str);
|
||||
}
|
||||
std::string pad_string(std::string s, size_t n, char c = ' ', bool prepend = false);
|
||||
|
||||
@@ -94,6 +89,7 @@ namespace string_tools
|
||||
std::string pod_to_hex(const t_pod_type& s)
|
||||
{
|
||||
static_assert(std::is_standard_layout<t_pod_type>(), "expected standard layout type");
|
||||
static_assert(alignof(t_pod_type) == 1, "type may have padding");
|
||||
return to_hex::string(as_byte_span(s));
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -101,6 +97,8 @@ namespace string_tools
|
||||
bool hex_to_pod(const boost::string_ref hex_str, t_pod_type& s)
|
||||
{
|
||||
static_assert(std::is_standard_layout<t_pod_type>(), "expected standard layout type");
|
||||
static_assert(alignof(t_pod_type) == 1, "type may have padding");
|
||||
static_assert(std::is_trivially_copyable<t_pod_type>(), "type must be trivially copyable");
|
||||
return from_hex::to_buffer(as_mut_byte_span(s), hex_str);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -135,6 +135,13 @@ namespace http
|
||||
http::url_content parsed{};
|
||||
const bool r = parse_url(address, parsed);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address);
|
||||
if (parsed.port == 0)
|
||||
{
|
||||
if (parsed.schema == "http")
|
||||
parsed.port = 80;
|
||||
else if (parsed.schema == "https")
|
||||
parsed.port = 443;
|
||||
}
|
||||
set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), std::move(ssl_options));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,11 @@ namespace epee
|
||||
{
|
||||
std::size_t space_needed = 0;
|
||||
for (const auto& source : sources)
|
||||
{
|
||||
if (std::numeric_limits<std::size_t>::max() - space_needed < source.size())
|
||||
throw std::bad_alloc{};
|
||||
space_needed += source.size();
|
||||
}
|
||||
|
||||
if (space_needed)
|
||||
{
|
||||
@@ -162,9 +166,9 @@ namespace epee
|
||||
|
||||
for (const auto& source : sources)
|
||||
{
|
||||
assert(source.size() <= out.size()); // see check above
|
||||
std::memcpy(out.data(), source.data(), source.size());
|
||||
if (out.remove_prefix(source.size()) < source.size())
|
||||
throw std::bad_alloc{}; // size_t overflow on space_needed
|
||||
out.remove_prefix(source.size());
|
||||
}
|
||||
storage_ = std::move(storage);
|
||||
}
|
||||
|
||||
@@ -46,12 +46,6 @@
|
||||
// TODO:
|
||||
#include "net/network_throttle-detail.hpp"
|
||||
|
||||
#if BOOST_VERSION >= 107000
|
||||
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context())
|
||||
#else
|
||||
#define GET_IO_SERVICE(s) ((s).get_io_service())
|
||||
#endif
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "net.conn"
|
||||
|
||||
@@ -127,12 +121,12 @@ connection_basic_pimpl::connection_basic_pimpl(const std::string &name) : m_thro
|
||||
int connection_basic_pimpl::m_default_tos;
|
||||
|
||||
// methods:
|
||||
connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
|
||||
connection_basic::connection_basic(boost::asio::io_context &io_context, boost::asio::ip::tcp::socket&& sock, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
|
||||
:
|
||||
m_state(std::move(state)),
|
||||
mI( new connection_basic_pimpl("peer") ),
|
||||
strand_(GET_IO_SERVICE(sock)),
|
||||
socket_(GET_IO_SERVICE(sock), get_context(m_state.get())),
|
||||
strand_(io_context),
|
||||
socket_(io_context, get_context(m_state.get())),
|
||||
m_want_close_connection(false),
|
||||
m_was_shutdown(false),
|
||||
m_is_multithreaded(false),
|
||||
@@ -152,12 +146,12 @@ connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, std::sha
|
||||
_note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_state->sock_count);
|
||||
}
|
||||
|
||||
connection_basic::connection_basic(boost::asio::io_service &io_service, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
|
||||
connection_basic::connection_basic(boost::asio::io_context &io_context, std::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support)
|
||||
:
|
||||
m_state(std::move(state)),
|
||||
mI( new connection_basic_pimpl("peer") ),
|
||||
strand_(io_service),
|
||||
socket_(io_service, get_context(m_state.get())),
|
||||
strand_(io_context),
|
||||
socket_(io_context, get_context(m_state.get())),
|
||||
m_want_close_connection(false),
|
||||
m_was_shutdown(false),
|
||||
m_is_multithreaded(false),
|
||||
|
||||
@@ -176,11 +176,12 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s
|
||||
std::vector<boost::filesystem::path> found_files;
|
||||
const boost::filesystem::directory_iterator end_itr;
|
||||
const boost::filesystem::path filename_base_path(filename_base);
|
||||
const std::string filename_base_name = filename_base_path.filename().string();
|
||||
const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : ".";
|
||||
for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter)
|
||||
{
|
||||
const std::string filename = iter->path().string();
|
||||
if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0)
|
||||
const std::string filename = iter->path().filename().string();
|
||||
if (filename.size() >= filename_base_name.size() && std::memcmp(filename.data(), filename_base_name.data(), filename_base_name.size()) == 0)
|
||||
{
|
||||
found_files.push_back(iter->path());
|
||||
}
|
||||
|
||||
@@ -4,22 +4,38 @@ namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct new_connection
|
||||
{
|
||||
boost::promise<boost::asio::ip::tcp::socket> result_;
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
template<typename T>
|
||||
explicit new_connection(T&& executor)
|
||||
: result_(), socket_(std::forward<T>(executor))
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||
direct_connect::operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer& timeout) const
|
||||
{
|
||||
// Get a list of endpoints corresponding to the server name.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
boost::asio::ip::tcp::resolver resolver(GET_IO_SERVICE(timeout));
|
||||
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
boost::asio::ip::tcp::resolver resolver(MONERO_GET_EXECUTOR(timeout));
|
||||
|
||||
bool try_ipv6 = false;
|
||||
boost::asio::ip::tcp::resolver::iterator iterator;
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
boost::asio::ip::tcp::resolver::results_type results{};
|
||||
boost::system::error_code resolve_error;
|
||||
|
||||
try
|
||||
{
|
||||
iterator = resolver.resolve(query, resolve_error);
|
||||
if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty
|
||||
results = resolver.resolve(
|
||||
boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error
|
||||
);
|
||||
|
||||
if (results.empty())
|
||||
{
|
||||
// if IPv4 resolution fails, try IPv6. Unintentional outgoing IPv6 connections should only
|
||||
// be possible if for some reason a hostname was given and that hostname fails IPv4 resolution,
|
||||
@@ -37,27 +53,20 @@ namespace net_utils
|
||||
}
|
||||
try_ipv6 = true;
|
||||
}
|
||||
|
||||
if (try_ipv6)
|
||||
{
|
||||
boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
iterator = resolver.resolve(query6);
|
||||
if (iterator == end)
|
||||
results = resolver.resolve(
|
||||
boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::canonical_name
|
||||
);
|
||||
if (results.empty())
|
||||
throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr};
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct new_connection
|
||||
{
|
||||
boost::promise<boost::asio::ip::tcp::socket> result_;
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
explicit new_connection(boost::asio::io_service& io_service)
|
||||
: result_(), socket_(io_service)
|
||||
{}
|
||||
};
|
||||
|
||||
const auto shared = std::make_shared<new_connection>(GET_IO_SERVICE(timeout));
|
||||
const auto shared = std::make_shared<new_connection>(MONERO_GET_EXECUTOR(timeout));
|
||||
timeout.async_wait([shared] (boost::system::error_code error)
|
||||
{
|
||||
if (error != boost::system::errc::operation_canceled && shared && shared->socket_.is_open())
|
||||
@@ -66,7 +75,7 @@ namespace net_utils
|
||||
shared->socket_.close();
|
||||
}
|
||||
});
|
||||
shared->socket_.async_connect(*iterator, [shared] (boost::system::error_code error)
|
||||
shared->socket_.async_connect(*results.begin(), [shared] (boost::system::error_code error)
|
||||
{
|
||||
if (shared)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/cerrno.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
@@ -45,6 +46,13 @@
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "net.ssl"
|
||||
|
||||
|
||||
#if BOOST_VERSION >= 107300
|
||||
#define MONERO_HOSTNAME_VERIFY boost::asio::ssl::host_name_verification
|
||||
#else
|
||||
#define MONERO_HOSTNAME_VERIFY boost::asio::ssl::rfc2818_verification
|
||||
#endif
|
||||
|
||||
// openssl genrsa -out /tmp/KEY 4096
|
||||
// openssl req -new -key /tmp/KEY -out /tmp/REQ
|
||||
// openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
|
||||
@@ -526,7 +534,7 @@ void ssl_options_t::configure(
|
||||
// preverified means it passed system or user CA check. System CA is never loaded
|
||||
// when fingerprints are whitelisted.
|
||||
const bool verified = preverified &&
|
||||
(verification != ssl_verification_t::system_ca || host.empty() || boost::asio::ssl::rfc2818_verification(host)(preverified, ctx));
|
||||
(verification != ssl_verification_t::system_ca || host.empty() || MONERO_HOSTNAME_VERIFY(host)(preverified, ctx));
|
||||
|
||||
if (!verified && !has_fingerprint(ctx))
|
||||
{
|
||||
@@ -544,6 +552,7 @@ void ssl_options_t::configure(
|
||||
}
|
||||
|
||||
bool ssl_options_t::handshake(
|
||||
boost::asio::io_context& io_context,
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
|
||||
boost::asio::ssl::stream_base::handshake_type type,
|
||||
boost::asio::const_buffer buffer,
|
||||
@@ -555,12 +564,11 @@ bool ssl_options_t::handshake(
|
||||
auto start_handshake = [&]{
|
||||
using ec_t = boost::system::error_code;
|
||||
using timer_t = boost::asio::steady_timer;
|
||||
using strand_t = boost::asio::io_service::strand;
|
||||
using strand_t = boost::asio::io_context::strand;
|
||||
using socket_t = boost::asio::ip::tcp::socket;
|
||||
|
||||
auto &io_context = GET_IO_SERVICE(socket);
|
||||
if (io_context.stopped())
|
||||
io_context.reset();
|
||||
io_context.restart();
|
||||
strand_t strand(io_context);
|
||||
timer_t deadline(io_context, timeout);
|
||||
|
||||
@@ -595,13 +603,13 @@ bool ssl_options_t::handshake(
|
||||
state.result = ec;
|
||||
if (!state.cancel_handshake) {
|
||||
state.cancel_timer = true;
|
||||
ec_t ec;
|
||||
deadline.cancel(ec);
|
||||
deadline.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
deadline.async_wait(on_timer);
|
||||
strand.post(
|
||||
boost::asio::post(
|
||||
strand,
|
||||
[&]{
|
||||
socket.async_handshake(
|
||||
type,
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#include "misc_log_ex.h"
|
||||
#include <boost/chrono.hpp>
|
||||
#include "misc_language.h"
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -186,6 +186,23 @@ void network_throttle::handle_trafic_exact(size_t packet_size)
|
||||
_handle_trafic_exact(packet_size, packet_size);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct output_history
|
||||
{
|
||||
const boost::circular_buffer< network_throttle::packet_info >& history;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const output_history& source)
|
||||
{
|
||||
out << '[';
|
||||
for (auto sample: source.history)
|
||||
out << sample.m_size << ' ';
|
||||
out << ']';
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_size)
|
||||
{
|
||||
tick();
|
||||
@@ -196,14 +213,11 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s
|
||||
m_total_packets++;
|
||||
m_total_bytes += packet_size;
|
||||
|
||||
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
|
||||
std::string history_str = oss.str();
|
||||
|
||||
MTRACE("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)"
|
||||
<< " Speed AVG=" << std::setw(4) << ((long int)(cts .average/1024)) <<"[w="<<cts .window<<"]"
|
||||
<< " " << std::setw(4) << ((long int)(cts2.average/1024)) <<"[w="<<cts2.window<<"]"
|
||||
<<" / " << " Limit="<< ((long int)(m_target_speed/1024)) <<" KiB/sec "
|
||||
<< " " << history_str
|
||||
<< " " << output_history{m_history}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -289,8 +303,6 @@ void network_throttle::calculate_times(size_t packet_size, calculate_times_struc
|
||||
}
|
||||
|
||||
if (dbg) {
|
||||
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
|
||||
std::string history_str = oss.str();
|
||||
MTRACE((cts.delay > 0 ? "SLEEP" : "")
|
||||
<< "dbg " << m_name << ": "
|
||||
<< "speed is A=" << std::setw(8) <<cts.average<<" vs "
|
||||
@@ -300,7 +312,7 @@ void network_throttle::calculate_times(size_t packet_size, calculate_times_struc
|
||||
<< "E="<< std::setw(8) << E << " (Enow="<<std::setw(8)<<Enow<<") "
|
||||
<< "M=" << std::setw(8) << M <<" W="<< std::setw(8) << cts.window << " "
|
||||
<< "R=" << std::setw(8) << cts.recomendetDataSize << " Wgood" << std::setw(8) << Wgood << " "
|
||||
<< "History: " << std::setw(8) << history_str << " "
|
||||
<< "History: " << std::setw(8) << output_history{m_history} << " "
|
||||
<< "m_last_sample_time=" << std::setw(8) << m_last_sample_time
|
||||
);
|
||||
|
||||
|
||||
@@ -38,9 +38,12 @@
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "misc_log_ex.h"
|
||||
#include "storages/parserse_base_utils.h"
|
||||
#include "hex.h"
|
||||
@@ -157,46 +160,20 @@ namespace string_tools
|
||||
return pname;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool set_module_name_and_folder(const std::string& path_to_process_)
|
||||
{
|
||||
std::string path_to_process = path_to_process_;
|
||||
|
||||
void set_module_name_and_folder(const std::string& path_to_process_)
|
||||
{
|
||||
boost::filesystem::path path_to_process = path_to_process_;
|
||||
|
||||
#ifdef _WIN32
|
||||
path_to_process = get_current_module_path();
|
||||
#endif
|
||||
std::string::size_type a = path_to_process.rfind( '\\' );
|
||||
if(a == std::string::npos )
|
||||
{
|
||||
a = path_to_process.rfind( '/' );
|
||||
}
|
||||
if ( a != std::string::npos )
|
||||
{
|
||||
get_current_module_name() = path_to_process.substr(a+1, path_to_process.size());
|
||||
get_current_module_folder() = path_to_process.substr(0, a);
|
||||
return true;
|
||||
}else
|
||||
return false;
|
||||
|
||||
}
|
||||
get_current_module_name() = path_to_process.filename().string();
|
||||
get_current_module_folder() = path_to_process.parent_path().string();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool trim_left(std::string& str)
|
||||
{
|
||||
for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast<unsigned char>(*it));)
|
||||
str.erase(str.begin());
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
bool trim_right(std::string& str)
|
||||
{
|
||||
|
||||
for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast<unsigned char>(*it));)
|
||||
str.erase( --((it++).base()));
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
std::string pad_string(std::string s, size_t n, char c, bool prepend)
|
||||
{
|
||||
if (s.size() < n)
|
||||
@@ -209,28 +186,22 @@ namespace string_tools
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string get_extension(const std::string& str)
|
||||
{
|
||||
std::string res;
|
||||
std::string::size_type pos = str.rfind('.');
|
||||
if(std::string::npos == pos)
|
||||
return res;
|
||||
|
||||
res = str.substr(pos+1, str.size()-pos);
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
std::string cut_off_extension(const std::string& str)
|
||||
{
|
||||
std::string res;
|
||||
std::string::size_type pos = str.rfind('.');
|
||||
if(std::string::npos == pos)
|
||||
return str;
|
||||
std::string get_extension(const std::string& str)
|
||||
{
|
||||
std::string ext_with_dot = boost::filesystem::path(str).extension().string();
|
||||
|
||||
if (ext_with_dot.empty())
|
||||
return {};
|
||||
|
||||
return ext_with_dot.erase(0, 1);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::string cut_off_extension(const std::string& str)
|
||||
{
|
||||
return boost::filesystem::path(str).replace_extension("").string();
|
||||
}
|
||||
|
||||
res = str.substr(0, pos);
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
#ifdef _WIN32
|
||||
std::wstring utf8_to_utf16(const std::string& str)
|
||||
{
|
||||
|
||||
Vendored
+1
@@ -70,3 +70,4 @@ add_subdirectory(db_drivers)
|
||||
add_subdirectory(easylogging++)
|
||||
add_subdirectory(qrcodegen)
|
||||
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(mx25519)
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
# Boost Serialization Vendoring
|
||||
|
||||
* optional_shim.hpp - Compare to file include/boost/serialization/optional.hpp in the Boost serialization repository, checked out at commit ff0a5f9f4a4040c6f992581245fed48d9634acfe. This is the newest version of the file at time of writing. Once minimum required Boost version is >= 1.84, then we can drop this header.
|
||||
* std_variant_shim.hpp - Compare to file include/boost/serialization/std_variant.hpp in the Boost serialization repository, checked out at commit 2805bc3faa8f8fb7ce80ec518158563c5fcf26f6. This is the newest version of the file at time of writing. Once minimum required Boost version includes commit 042c590a7ac84933413243d8e9c3b06a791803a7 in the multiprecision repository, then we can drop this header. Commit 042c590a7ac84933413243d8e9c3b06a791803a7 removes a namespace clash in Boost's multiprecision library with the std_variant.hpp header.
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#ifndef BOOST_VERSION
|
||||
#error "Missing Boost version"
|
||||
#endif
|
||||
|
||||
#if BOOST_VERSION >= 108400
|
||||
#include <boost/serialization/optional.hpp>
|
||||
#else
|
||||
|
||||
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
|
||||
// (C) Copyright 2002-4 Pavel Vozenilek .
|
||||
// Use, modification and distribution is subject to the Boost Software
|
||||
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// Provides non-intrusive serialization for boost::optional.
|
||||
|
||||
#ifndef BOOST_SERIALIZATION_OPTIONAL_HPP
|
||||
#define BOOST_SERIALIZATION_OPTIONAL_HPP
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
#include <optional>
|
||||
#endif
|
||||
|
||||
#include <boost/serialization/item_version_type.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
#include <boost/type_traits/is_pointer.hpp>
|
||||
#include <boost/serialization/detail/is_default_constructible.hpp>
|
||||
|
||||
// function specializations must be defined in the appropriate
|
||||
// namespace - boost::serialization
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
namespace detail {
|
||||
|
||||
// OT is of the form optional<T>
|
||||
template<class Archive, class OT>
|
||||
void save_impl(
|
||||
Archive & ar,
|
||||
const OT & ot
|
||||
){
|
||||
// It is an inherent limitation to the serialization of optional.hpp
|
||||
// that the underlying type must be either a pointer or must have a
|
||||
// default constructor. It's possible that this could change sometime
|
||||
// in the future, but for now, one will have to work around it. This can
|
||||
// be done by serialization the optional<T> as optional<T *>
|
||||
#ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
|
||||
BOOST_STATIC_ASSERT(
|
||||
boost::serialization::detail::is_default_constructible<typename OT::value_type>::value
|
||||
|| boost::is_pointer<typename OT::value_type>::value
|
||||
);
|
||||
#endif
|
||||
const bool tflag(ot);
|
||||
ar << boost::serialization::make_nvp("initialized", tflag);
|
||||
if (tflag){
|
||||
ar << boost::serialization::make_nvp("value", *ot);
|
||||
}
|
||||
}
|
||||
|
||||
// OT is of the form optional<T>
|
||||
template<class Archive, class OT>
|
||||
void load_impl(
|
||||
Archive & ar,
|
||||
OT & ot,
|
||||
const unsigned int version
|
||||
){
|
||||
bool tflag;
|
||||
ar >> boost::serialization::make_nvp("initialized", tflag);
|
||||
if(! tflag){
|
||||
ot.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if(0 == version){
|
||||
boost::serialization::item_version_type item_version(0);
|
||||
auto library_version(
|
||||
ar.get_library_version()
|
||||
);
|
||||
if(decltype(library_version)(3) < library_version){
|
||||
ar >> BOOST_SERIALIZATION_NVP(item_version);
|
||||
}
|
||||
}
|
||||
typename OT::value_type t;
|
||||
ar >> boost::serialization::make_nvp("value",t);
|
||||
ot = t;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Archive, class T>
|
||||
void save(
|
||||
Archive & ar,
|
||||
const boost::optional< T > & ot,
|
||||
const unsigned int /*version*/
|
||||
){
|
||||
detail::save_impl(ar, ot);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
template<class Archive, class T>
|
||||
void save(
|
||||
Archive & ar,
|
||||
const std::optional< T > & ot,
|
||||
const unsigned int /*version*/
|
||||
){
|
||||
detail::save_impl(ar, ot);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Archive, class T>
|
||||
void load(
|
||||
Archive & ar,
|
||||
boost::optional< T > & ot,
|
||||
const unsigned int version
|
||||
){
|
||||
detail::load_impl(ar, ot, version);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
template<class Archive, class T>
|
||||
void load(
|
||||
Archive & ar,
|
||||
std::optional< T > & ot,
|
||||
const unsigned int version
|
||||
){
|
||||
detail::load_impl(ar, ot, version);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Archive, class T>
|
||||
void serialize(
|
||||
Archive & ar,
|
||||
boost::optional< T > & ot,
|
||||
const unsigned int version
|
||||
){
|
||||
boost::serialization::split_free(ar, ot, version);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
template<class Archive, class T>
|
||||
void serialize(
|
||||
Archive & ar,
|
||||
std::optional< T > & ot,
|
||||
const unsigned int version
|
||||
){
|
||||
boost::serialization::split_free(ar, ot, version);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
struct version<boost::optional<T> >{
|
||||
BOOST_STATIC_CONSTANT(int, value = 1);
|
||||
};
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
template<class T>
|
||||
struct version<std::optional<T> >{
|
||||
BOOST_STATIC_CONSTANT(int, value = 1);
|
||||
};
|
||||
#endif
|
||||
|
||||
} // serialization
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_SERIALIZATION_OPTIONAL_HPP
|
||||
#endif //BOOST_VERSION < 108400
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
#ifndef BOOST_SERIALIZATION_STD_VARIANT_HPP
|
||||
#define BOOST_SERIALIZATION_STD_VARIANT_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// variant.hpp - non-intrusive serialization of variant types
|
||||
//
|
||||
// copyright (c) 2019 Samuel Debionne, ESRF
|
||||
//
|
||||
// Use, modification and distribution is subject to the Boost Software
|
||||
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// See http://www.boost.org for updates, documentation, and revision history.
|
||||
//
|
||||
// Widely inspired form boost::variant serialization
|
||||
//
|
||||
|
||||
#include <boost/serialization/throw_exception.hpp>
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include <boost/archive/archive_exception.hpp>
|
||||
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
#include <boost/serialization/serialization.hpp>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
|
||||
template<class Archive>
|
||||
struct std_variant_save_visitor
|
||||
{
|
||||
std_variant_save_visitor(Archive& ar) :
|
||||
m_ar(ar)
|
||||
{}
|
||||
template<class T>
|
||||
void operator()(T const & value) const
|
||||
{
|
||||
m_ar << BOOST_SERIALIZATION_NVP(value);
|
||||
}
|
||||
private:
|
||||
Archive & m_ar;
|
||||
};
|
||||
|
||||
|
||||
template<class Archive>
|
||||
struct std_variant_load_visitor
|
||||
{
|
||||
std_variant_load_visitor(Archive& ar) :
|
||||
m_ar(ar)
|
||||
{}
|
||||
template<class T>
|
||||
void operator()(T & value) const
|
||||
{
|
||||
m_ar >> BOOST_SERIALIZATION_NVP(value);
|
||||
}
|
||||
private:
|
||||
Archive & m_ar;
|
||||
};
|
||||
|
||||
template<class Archive, class ...Types>
|
||||
void save(
|
||||
Archive & ar,
|
||||
std::variant<Types...> const & v,
|
||||
unsigned int /*version*/
|
||||
){
|
||||
const std::size_t which = v.index();
|
||||
ar << BOOST_SERIALIZATION_NVP(which);
|
||||
std_variant_save_visitor<Archive> visitor(ar);
|
||||
std::visit(visitor, v);
|
||||
}
|
||||
|
||||
// Minimalist metaprogramming for handling parameter pack
|
||||
namespace mp_shim {
|
||||
namespace detail {
|
||||
template <typename Seq>
|
||||
struct front_impl;
|
||||
|
||||
template <template <typename...> class Seq, typename T, typename... Ts>
|
||||
struct front_impl<Seq<T, Ts...>> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename Seq>
|
||||
struct pop_front_impl;
|
||||
|
||||
template <template <typename...> class Seq, typename T, typename... Ts>
|
||||
struct pop_front_impl<Seq<T, Ts...>> {
|
||||
using type = Seq<Ts...>;
|
||||
};
|
||||
} //namespace detail
|
||||
|
||||
template <typename... Ts>
|
||||
struct typelist {};
|
||||
|
||||
template <typename Seq>
|
||||
using front = typename detail::front_impl<Seq>::type;
|
||||
|
||||
template <typename Seq>
|
||||
using pop_front = typename detail::pop_front_impl<Seq>::type;
|
||||
} // namespace mp_shim
|
||||
|
||||
template<std::size_t N, class Seq>
|
||||
struct variant_impl_shim
|
||||
{
|
||||
template<class Archive, class V>
|
||||
static void load (
|
||||
Archive & ar,
|
||||
std::size_t which,
|
||||
V & v,
|
||||
const unsigned int version
|
||||
){
|
||||
if(which == 0){
|
||||
// note: A non-intrusive implementation (such as this one)
|
||||
// necessary has to copy the value. This wouldn't be necessary
|
||||
// with an implementation that de-serialized to the address of the
|
||||
// aligned storage included in the variant.
|
||||
using type = mp_shim::front<Seq>;
|
||||
type value;
|
||||
ar >> BOOST_SERIALIZATION_NVP(value);
|
||||
v = std::move(value);
|
||||
type * new_address = & std::get<type>(v);
|
||||
ar.reset_object_address(new_address, & value);
|
||||
return;
|
||||
}
|
||||
//typedef typename mpl::pop_front<S>::type type;
|
||||
using types = mp_shim::pop_front<Seq>;
|
||||
variant_impl_shim<N - 1, types>::load(ar, which - 1, v, version);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Seq>
|
||||
struct variant_impl_shim<0, Seq>
|
||||
{
|
||||
template<class Archive, class V>
|
||||
static void load (
|
||||
Archive & /*ar*/,
|
||||
std::size_t /*which*/,
|
||||
V & /*v*/,
|
||||
const unsigned int /*version*/
|
||||
){}
|
||||
};
|
||||
|
||||
template<class Archive, class... Types>
|
||||
void load(
|
||||
Archive & ar,
|
||||
std::variant<Types...>& v,
|
||||
const unsigned int version
|
||||
){
|
||||
std::size_t which;
|
||||
ar >> BOOST_SERIALIZATION_NVP(which);
|
||||
if(which >= sizeof...(Types))
|
||||
// this might happen if a type was removed from the list of variant types
|
||||
boost::serialization::throw_exception(
|
||||
boost::archive::archive_exception(
|
||||
boost::archive::archive_exception::unsupported_version
|
||||
)
|
||||
);
|
||||
variant_impl_shim<sizeof...(Types), mp_shim::typelist<Types...>>::load(ar, which, v, version);
|
||||
}
|
||||
|
||||
template<class Archive,class... Types>
|
||||
inline void serialize(
|
||||
Archive & ar,
|
||||
std::variant<Types...> & v,
|
||||
const unsigned int file_version
|
||||
){
|
||||
split_free(ar,v,file_version);
|
||||
}
|
||||
|
||||
// Specialization for std::monostate
|
||||
template<class Archive>
|
||||
void serialize(Archive &, std::monostate &, const unsigned int /*version*/)
|
||||
{}
|
||||
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
|
||||
//template<typename T0_, BOOST_VARIANT_ENUM_SHIFTED_PARAMS(typename T)>
|
||||
|
||||
#include <boost/serialization/tracking.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
|
||||
template<class... Types>
|
||||
struct tracking_level<
|
||||
std::variant<Types...>
|
||||
>{
|
||||
typedef mpl::integral_c_tag tag;
|
||||
typedef mpl::int_< ::boost::serialization::track_always> type;
|
||||
BOOST_STATIC_CONSTANT(int, value = type::value);
|
||||
};
|
||||
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
|
||||
#endif //BOOST_SERIALIZATION_VARIANT_HPP
|
||||
Vendored
+1
-1
Submodule external/mx25519 updated: 84ca1290fa...42f36ede5f
@@ -83,6 +83,8 @@ endfunction ()
|
||||
include(Version)
|
||||
monero_add_library(version SOURCES ${CMAKE_BINARY_DIR}/version.cpp DEPENDS genversion)
|
||||
|
||||
add_subdirectory(carrot_core)
|
||||
add_subdirectory(carrot_impl)
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(crypto)
|
||||
add_subdirectory(ringct)
|
||||
@@ -97,6 +99,7 @@ add_subdirectory(hardforks)
|
||||
add_subdirectory(blockchain_db)
|
||||
add_subdirectory(mnemonics)
|
||||
add_subdirectory(rpc)
|
||||
add_subdirectory(seraphis_crypto)
|
||||
if(NOT IOS)
|
||||
add_subdirectory(serialization)
|
||||
endif()
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "profile_tools.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
|
||||
#include "lmdb/db_lmdb.h"
|
||||
|
||||
@@ -240,7 +241,10 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
|
||||
if (miner_tx && tx.version == 2)
|
||||
{
|
||||
cryptonote::tx_out vout = tx.vout[i];
|
||||
rct::key commitment = rct::zeroCommit(vout.amount);
|
||||
// TODO: avoid multiple expensive zeroCommitVartime call here + get_outs_by_last_locked_block + ver_non_input_consensus
|
||||
rct::key commitment;
|
||||
if (!rct::getCommitment(tx, i, commitment))
|
||||
throw std::runtime_error("Failed to get miner tx commitment, aborting");
|
||||
vout.amount = 0;
|
||||
amount_output_indices[i] = add_output(tx_hash, vout, i, unlock_time,
|
||||
&commitment);
|
||||
@@ -296,7 +300,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
blobdata protocol_bd = tx_to_blob(blk.protocol_tx);
|
||||
add_transaction(blk_hash, std::make_pair(blk.protocol_tx, blobdata_ref(protocol_bd)));
|
||||
|
||||
if (blk.miner_tx.version == 2)
|
||||
if (blk.miner_tx.version >= 2)
|
||||
{
|
||||
num_rct_outs += blk.miner_tx.vout.size();
|
||||
|
||||
@@ -311,7 +315,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
|
||||
std::map<std::string, int64_t> slippage_counts;
|
||||
uint64_t audit_total = 0, yield_total = 0;
|
||||
if (blk.protocol_tx.version == 2)
|
||||
if (blk.protocol_tx.version >= 2)
|
||||
{
|
||||
num_rct_outs += blk.protocol_tx.vout.size();
|
||||
|
||||
@@ -356,16 +360,16 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
slippage_counts[asset_type] = 0;
|
||||
slippage_counts[asset_type] += tx.first.amount_burnt;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this an AUDIT TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::AUDIT) {
|
||||
audit_total += tx.first.amount_burnt;
|
||||
}
|
||||
|
||||
// Is this a STAKE TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::STAKE) {
|
||||
yield_total += tx.first.amount_burnt;
|
||||
}
|
||||
// Is this an AUDIT TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::AUDIT) {
|
||||
audit_total += tx.first.amount_burnt;
|
||||
}
|
||||
|
||||
// Is this a STAKE TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::STAKE) {
|
||||
yield_total += tx.first.amount_burnt;
|
||||
}
|
||||
++tx_i;
|
||||
}
|
||||
|
||||
@@ -29,14 +29,18 @@
|
||||
#include "db_lmdb.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/circular_buffer.hpp>
|
||||
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <cstring> // memcpy
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winioctl.h>
|
||||
#endif
|
||||
|
||||
#include "string_tools.h"
|
||||
#include "file_io_utils.h"
|
||||
#include "common/util.h"
|
||||
#include "common/pruning.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
@@ -2083,6 +2087,54 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB()
|
||||
m_hardfork = nullptr;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
static bool disable_ntfs_compression(const boost::filesystem::path& filepath)
|
||||
{
|
||||
DWORD file_attributes = ::GetFileAttributesW(filepath.c_str());
|
||||
if (file_attributes == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
MERROR("Failed to get " << filepath.string() << " file attributes. Error: " << ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(file_attributes & FILE_ATTRIBUTE_COMPRESSED))
|
||||
return true; // not compressed
|
||||
|
||||
LOG_PRINT_L1("Disabling NTFS compression for " << filepath.string());
|
||||
HANDLE file_handle = ::CreateFileW(
|
||||
filepath.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
boost::filesystem::is_directory(filepath) ? FILE_FLAG_BACKUP_SEMANTICS : 0, // Needed to open handles to directories
|
||||
nullptr
|
||||
);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
MERROR("Failed to open handle: " << filepath.string() << ". Error: " << ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
USHORT compression_state = COMPRESSION_FORMAT_NONE;
|
||||
DWORD bytes_returned;
|
||||
BOOL ok = ::DeviceIoControl(
|
||||
file_handle,
|
||||
FSCTL_SET_COMPRESSION,
|
||||
&compression_state,
|
||||
sizeof(compression_state),
|
||||
nullptr,
|
||||
0,
|
||||
&bytes_returned,
|
||||
nullptr
|
||||
);
|
||||
|
||||
::CloseHandle(file_handle);
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||
{
|
||||
int result;
|
||||
@@ -2109,6 +2161,18 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||
throw DB_ERROR("Database could not be opened");
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// ensure NTFS compression is disabled on the directory and database file to avoid corruption of the blockchain
|
||||
if (!disable_ntfs_compression(filename))
|
||||
LOG_PRINT_L0("Failed to disable NTFS compression on folder: " << filename << ". Error: " << ::GetLastError());
|
||||
boost::filesystem::path datafile(filename);
|
||||
datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
|
||||
if (!boost::filesystem::exists(datafile))
|
||||
boost::filesystem::ofstream(datafile).close(); // create the file to see if NTFS compression is enabled beforehand
|
||||
if (!disable_ntfs_compression(datafile))
|
||||
throw DB_ERROR("Database file is NTFS compressed and compression could not be disabled");
|
||||
#endif
|
||||
|
||||
boost::optional<bool> is_hdd_result = tools::is_hdd(filename.c_str());
|
||||
if (is_hdd_result)
|
||||
{
|
||||
@@ -3778,6 +3842,11 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
|
||||
//amount += m_coinbase;
|
||||
}
|
||||
|
||||
if (amount < 0) {
|
||||
// Negative number can't be converted to a 64-bit UINT, so return 0, but retain -ve number privately
|
||||
LOG_PRINT_L2("BlockchainLMDB::" << __func__ << " - supply of " << currency_label << " is negative (" << amount << ") but outputting zero");
|
||||
amount = 0;
|
||||
}
|
||||
circulating_supply[currency_label] = amount.convert_to<uint64_t>();
|
||||
}
|
||||
|
||||
@@ -5467,12 +5536,11 @@ bool BlockchainLMDB::is_read_only() const
|
||||
|
||||
uint64_t BlockchainLMDB::get_database_size() const
|
||||
{
|
||||
uint64_t size = 0;
|
||||
boost::filesystem::path datafile(m_folder);
|
||||
datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
|
||||
if (!epee::file_io_utils::get_file_size(datafile.string(), size))
|
||||
size = 0;
|
||||
return size;
|
||||
boost::system::error_code ec{};
|
||||
const boost::uintmax_t size = boost::filesystem::file_size(datafile, ec);
|
||||
return (ec ? 0 : static_cast<uint64_t>(size));
|
||||
}
|
||||
|
||||
#define RENAME_DB(name) do { \
|
||||
|
||||
@@ -142,23 +142,20 @@ set(blockchain_scanner_private_headers)
|
||||
monero_private_headers(blockchain_scanner
|
||||
${blockchain_scanner_private_headers})
|
||||
|
||||
if (BUILD_TAG)
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
|
||||
set(blockchain_audit_sources
|
||||
blockchain_audit.cpp
|
||||
threadpool_boost.cpp
|
||||
)
|
||||
|
||||
set(blockchain_audit_private_headers
|
||||
threadpool_boost.h
|
||||
)
|
||||
|
||||
monero_private_headers(blockchain_audit
|
||||
${blockchain_audit_private_headers})
|
||||
else()
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
|
||||
set(blockchain_audit_sources
|
||||
blockchain_audit.cpp
|
||||
threadpool_boost.cpp
|
||||
)
|
||||
|
||||
set(blockchain_audit_private_headers
|
||||
threadpool_boost.h
|
||||
)
|
||||
|
||||
monero_private_headers(blockchain_audit
|
||||
${blockchain_audit_private_headers})
|
||||
else()
|
||||
message(STATUS "blockchain_audit.cpp not found - not building the audit tool")
|
||||
endif()
|
||||
message(STATUS "blockchain_audit.cpp not found - not building the audit tool")
|
||||
endif()
|
||||
|
||||
monero_add_executable(blockchain_import
|
||||
@@ -330,36 +327,33 @@ set_property(TARGET blockchain_scanner
|
||||
OUTPUT_NAME "salvium-blockchain-scanner")
|
||||
install(TARGETS blockchain_scanner DESTINATION bin)
|
||||
|
||||
if (BUILD_TAG)
|
||||
else()
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
|
||||
monero_add_executable(blockchain_audit
|
||||
${blockchain_audit_sources}
|
||||
${blockchain_audit_private_headers})
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
|
||||
monero_add_executable(blockchain_audit
|
||||
${blockchain_audit_sources}
|
||||
${blockchain_audit_private_headers})
|
||||
|
||||
target_include_directories(blockchain_audit PRIVATE /usr/include/mysql-cppconn/jdbc)
|
||||
target_include_directories(blockchain_audit PRIVATE /usr/include/mysql-cppconn/jdbc)
|
||||
|
||||
target_link_libraries(blockchain_audit
|
||||
PRIVATE
|
||||
wallet
|
||||
crypto
|
||||
cncrypto
|
||||
cryptonote_core
|
||||
blockchain_db
|
||||
version
|
||||
epee
|
||||
mysqlcppconn
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES})
|
||||
target_link_libraries(blockchain_audit
|
||||
PRIVATE
|
||||
wallet
|
||||
crypto
|
||||
cncrypto
|
||||
cryptonote_core
|
||||
blockchain_db
|
||||
version
|
||||
epee
|
||||
mysqlcppconn
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES})
|
||||
|
||||
set_property(TARGET blockchain_audit
|
||||
PROPERTY
|
||||
OUTPUT_NAME "salvium-blockchain-audit")
|
||||
install(TARGETS blockchain_audit DESTINATION bin)
|
||||
endif()
|
||||
set_property(TARGET blockchain_audit
|
||||
PROPERTY
|
||||
OUTPUT_NAME "salvium-blockchain-audit")
|
||||
install(TARGETS blockchain_audit DESTINATION bin)
|
||||
endif()
|
||||
|
||||
monero_add_executable(blockchain_stats
|
||||
|
||||
@@ -220,8 +220,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
|
||||
const std::map<uint8_t, std::pair<std::string, std::string>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
|
||||
const uint64_t audit_lock_period = get_config(net_type).AUDIT_LOCK_PERIOD;
|
||||
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
|
||||
|
||||
for (uint64_t h = block_start; h < block_stop; ++h)
|
||||
{
|
||||
@@ -300,7 +299,7 @@ skip:
|
||||
}
|
||||
protocol_tx_assets.insert(asset_type);
|
||||
if (protocol_tx_vout.amount > 25000000000000) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "large protocol TX amount detected from height " << (h-audit_lock_period) << delimiter << "amount:" << protocol_tx_vout.amount << std::endl;
|
||||
//std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "large protocol TX amount detected from height " << (h-audit_lock_period) << delimiter << "amount:" << protocol_tx_vout.amount << std::endl;
|
||||
}
|
||||
crypto::public_key key;
|
||||
cryptonote::get_output_public_key(protocol_tx_vout, key);
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
# Copyright (c) 2024, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(carrot_core_sources
|
||||
account_secrets.cpp
|
||||
address_utils.cpp
|
||||
carrot_enote_types.cpp
|
||||
core_types.cpp
|
||||
destination.cpp
|
||||
device_ram_borrowed.cpp
|
||||
enote_utils.cpp
|
||||
hash_functions.cpp
|
||||
lazy_amount_commitment.cpp
|
||||
output_set_finalization.cpp
|
||||
payment_proposal.cpp
|
||||
scan.cpp
|
||||
scan_unsafe.cpp
|
||||
sparc.cpp)
|
||||
|
||||
monero_find_all_headers(carrot_core_headers, "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
monero_add_library(carrot_core
|
||||
${carrot_core_sources}
|
||||
${carrot_core_headers})
|
||||
|
||||
target_link_libraries(carrot_core
|
||||
PUBLIC
|
||||
cncrypto
|
||||
epee
|
||||
mx25519_static
|
||||
ringct_basic
|
||||
seraphis_crypto
|
||||
PRIVATE
|
||||
${EXTRA_LIBRARIES})
|
||||
|
||||
target_include_directories(carrot_core
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
PRIVATE
|
||||
${Boost_INCLUDE_DIRS})
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "account_secrets.h"
|
||||
|
||||
//local headers
|
||||
#include "config.h"
|
||||
#include "crypto/generators.h"
|
||||
#include "hash_functions.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "transcript_fixed.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_provespend_key(const crypto::secret_key &s_master,
|
||||
crypto::secret_key &k_prove_spend_out)
|
||||
{
|
||||
// k_ps = H_n(s_m)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_PROVE_SPEND_KEY>();
|
||||
derive_scalar(transcript.data(), transcript.size(), &s_master, to_bytes(k_prove_spend_out));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
|
||||
crypto::secret_key &s_view_balance_out)
|
||||
{
|
||||
// s_vb = H_32(s_m)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_BALANCE_SECRET>();
|
||||
derive_bytes_32(transcript.data(), transcript.size(), &s_master, to_bytes(s_view_balance_out));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
|
||||
crypto::secret_key &k_generate_image_out)
|
||||
{
|
||||
// k_gi = H_n(s_vb)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_IMAGE_KEY>();
|
||||
derive_scalar(transcript.data(), transcript.size(), &s_view_balance, to_bytes(k_generate_image_out));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
|
||||
crypto::secret_key &k_view_out)
|
||||
{
|
||||
// k_v = H_n(s_vb)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_INCOMING_VIEW_KEY>();
|
||||
derive_scalar(transcript.data(), transcript.size(), &s_view_balance, to_bytes(k_view_out));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance,
|
||||
crypto::secret_key &s_generate_address_out)
|
||||
{
|
||||
// s_ga = H_32(s_vb)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET>();
|
||||
derive_bytes_32(transcript.data(), transcript.size(), &s_view_balance, to_bytes(s_generate_address_out));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image,
|
||||
const crypto::secret_key &k_prove_spend,
|
||||
crypto::public_key &spend_pubkey_out)
|
||||
{
|
||||
// k_ps T
|
||||
rct::key tmp;
|
||||
rct::scalarmultKey(tmp, rct::pk2rct(crypto::get_T()), rct::sk2rct(k_prove_spend));
|
||||
|
||||
// K_s = k_gi G + k_ps T
|
||||
rct::addKeys1(tmp, rct::sk2rct(k_generate_image), tmp);
|
||||
spend_pubkey_out = rct::rct2pk(tmp);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
////
|
||||
// Core implementation details for making Carrot privkeys, secrets, and pubkeys.
|
||||
// - Carrot is a specification for FCMP-RingCT compatible addressing
|
||||
//
|
||||
// references:
|
||||
// * https://github.com/jeffro256/carrot/blob/master/carrot.md
|
||||
///
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
/**
|
||||
* brief: make_carrot_provespend_key - prove-spend key, for signing input proofs to spend enotes
|
||||
* k_ps = H_n(s_m)
|
||||
* param: s_master - s_m
|
||||
* outparam: k_prove_spend_out - k_ps
|
||||
*/
|
||||
void make_carrot_provespend_key(const crypto::secret_key &s_master,
|
||||
crypto::secret_key &k_prove_spend_out);
|
||||
/**
|
||||
* brief: make_carrot_viewbalance_secret - view-balance secret, for viewing all balance information
|
||||
* s_vb = H_n(s_m)
|
||||
* param: s_master - s_m
|
||||
* outparam: s_view_balance_out - s_vb
|
||||
*/
|
||||
void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
|
||||
crypto::secret_key &s_view_balance_out);
|
||||
/**
|
||||
* brief: make_carrot_generateimage_key - generate-image key, for identifying enote spends
|
||||
* k_gi = H_n(s_vb)
|
||||
* param: s_view_balance - s_vb
|
||||
* outparam: k_generate_image_out - k_gi
|
||||
*/
|
||||
void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
|
||||
crypto::secret_key &k_generate_image_out);
|
||||
/**
|
||||
* brief: make_carrot_viewincoming_key - view-incoming key, for identifying received external enotes
|
||||
* k_v = H_n(s_vb)
|
||||
* param: s_view_balance - s_vb
|
||||
* outparam: k_view_out - k_v
|
||||
*/
|
||||
void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
|
||||
crypto::secret_key &k_view_out);
|
||||
/**
|
||||
* brief: make_carrot_generateaddress_secret - generate-address secret, for generating addresses
|
||||
* s_ga = H_32(s_vb)
|
||||
* param: s_view_balance - s_vb
|
||||
* outparam: s_generate_address_out - s_ga
|
||||
*/
|
||||
void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance,
|
||||
crypto::secret_key &s_generate_address_out);
|
||||
/**
|
||||
* brief: make_carrot_spend_pubkey - base public spendkey for rerandomizable RingCT
|
||||
* K_s = k_gi G + k_ps T
|
||||
* param: k_generate_image - k_gi
|
||||
* param: k_prove_spend - k_ps
|
||||
* outparam: spend_pubkey_out - K_s
|
||||
*/
|
||||
void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image,
|
||||
const crypto::secret_key &k_prove_spend,
|
||||
crypto::public_key &spend_pubkey_out);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "address_utils.h"
|
||||
|
||||
//local headers
|
||||
#include "config.h"
|
||||
#include "hash_functions.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "transcript_fixed.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_index_extension_generator(const crypto::secret_key &s_generate_address,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
crypto::secret_key &address_generator_out)
|
||||
{
|
||||
// s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN>(j_major, j_minor);
|
||||
derive_bytes_32(transcript.data(), transcript.size(), &s_generate_address, &address_generator_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_subaddress_scalar(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::secret_key &s_address_generator,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
crypto::secret_key &subaddress_scalar_out)
|
||||
{
|
||||
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR>(
|
||||
account_spend_pubkey, j_major, j_minor);
|
||||
derive_scalar(transcript.data(), transcript.size(), &s_address_generator, subaddress_scalar_out.data);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_address_spend_pubkey(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::secret_key &s_generate_address,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
crypto::public_key &address_spend_pubkey_out)
|
||||
{
|
||||
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||
crypto::secret_key subaddress_scalar;
|
||||
make_carrot_subaddress_scalar(account_spend_pubkey, s_generate_address, j_major, j_minor, subaddress_scalar);
|
||||
|
||||
// K^j_s = k^j_subscal * K_s
|
||||
address_spend_pubkey_out = rct::rct2pk(rct::scalarmultKey(
|
||||
rct::pk2rct(account_spend_pubkey), rct::sk2rct(subaddress_scalar)));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Utilities for building carrot addresses.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
/**
|
||||
* brief: is_main_address_index - determine whether j=(j_major, j_minor) represents the main address
|
||||
*/
|
||||
static constexpr bool is_main_address_index(const std::uint32_t j_major, const std::uint32_t j_minor)
|
||||
{
|
||||
return !(j_major || j_minor);
|
||||
}
|
||||
|
||||
/**
|
||||
* brief: make_carrot_index_extension_generator - s^j_gen
|
||||
* s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||
* param: s_generate_address - s_ga
|
||||
* param: j_major -
|
||||
* param: j_minor -
|
||||
* outparam: address_generator_out - s^j_gen
|
||||
*/
|
||||
void make_carrot_index_extension_generator(const crypto::secret_key &s_generate_address,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
crypto::secret_key &address_generator_out);
|
||||
/**
|
||||
* brief: make_carrot_address_privkey - d^j_a
|
||||
* k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||
* param: account_spend_pubkey - K_s = k_vb X + k_m U
|
||||
* param: s_address_generator - s^j_gen
|
||||
* param: j_major -
|
||||
* param: j_minor -
|
||||
* outparam: subaddress_scalar_out - k^j_subscal
|
||||
*/
|
||||
void make_carrot_subaddress_scalar(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::secret_key &s_address_generator,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
crypto::secret_key &subaddress_scalar_out);
|
||||
/**
|
||||
* brief: make_carrot_address_spend_pubkey - K^j_s
|
||||
* K^j_s = k^j_subscal * K_s
|
||||
* param: account_spend_pubkey - K_s = k_gi G + k_ps U
|
||||
* param: s_generate_address - s_ga
|
||||
* param: j_major -
|
||||
* param: j_minor -
|
||||
* outparam: address_spend_pubkey_out - K^j_s
|
||||
*/
|
||||
void make_carrot_address_spend_pubkey(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::secret_key &s_generate_address,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
crypto::public_key &address_spend_pubkey_out);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Utilities for scanning carrot enotes
|
||||
|
||||
//paired header
|
||||
#include "carrot_enote_types.h"
|
||||
|
||||
//local headers
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
/*
|
||||
onetime address
|
||||
// - amount commitment
|
||||
// - encrypted amount
|
||||
// - encrypted janus anchor
|
||||
// - view tag
|
||||
// - ephemeral pubkey
|
||||
// - tx first key image*/
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const CarrotEnoteV1 &a, const CarrotEnoteV1 &b)
|
||||
{
|
||||
return a.onetime_address == b.onetime_address &&
|
||||
a.amount_commitment == b.amount_commitment &&
|
||||
a.amount_enc == b.amount_enc &&
|
||||
a.anchor_enc == b.anchor_enc &&
|
||||
a.view_tag == b.view_tag &&
|
||||
a.tx_first_key_image == b.tx_first_key_image &&
|
||||
a.return_enc == b.return_enc &&
|
||||
a.asset_type == b.asset_type &&
|
||||
memcmp(a.enote_ephemeral_pubkey.data, b.enote_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b)
|
||||
{
|
||||
return a.onetime_address == b.onetime_address &&
|
||||
a.amount == b.amount &&
|
||||
a.anchor_enc == b.anchor_enc &&
|
||||
a.view_tag == b.view_tag &&
|
||||
a.block_index == b.block_index &&
|
||||
memcmp(a.enote_ephemeral_pubkey.data, b.enote_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Seraphis core types.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "core_types.h"
|
||||
#include "mx25519.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
////
|
||||
// CarrotEnoteV1
|
||||
// - onetime address
|
||||
// - amount commitment
|
||||
// - encrypted amount
|
||||
// - encrypted janus anchor
|
||||
// - view tag
|
||||
// - ephemeral pubkey
|
||||
// - tx first key image
|
||||
///
|
||||
struct CarrotEnoteV1 final
|
||||
{
|
||||
/// K_o
|
||||
crypto::public_key onetime_address;
|
||||
/// C_a
|
||||
rct::key amount_commitment;
|
||||
/// asset type
|
||||
std::string asset_type;
|
||||
/// a_enc
|
||||
encrypted_amount_t amount_enc;
|
||||
/// anchor_enc
|
||||
encrypted_janus_anchor_t anchor_enc;
|
||||
/// return_enc
|
||||
encrypted_return_pubkey_t return_enc;
|
||||
/// view_tag
|
||||
view_tag_t view_tag;
|
||||
/// D_e
|
||||
mx25519_pubkey enote_ephemeral_pubkey;
|
||||
/// L_0
|
||||
crypto::key_image tx_first_key_image;
|
||||
};
|
||||
|
||||
/// equality operators
|
||||
bool operator==(const CarrotEnoteV1 &a, const CarrotEnoteV1 &b);
|
||||
static inline bool operator!=(const CarrotEnoteV1 &a, const CarrotEnoteV1 &b) { return !(a == b); }
|
||||
|
||||
////
|
||||
// CarrotCoinbaseEnoteV1
|
||||
// - onetime address
|
||||
// - cleartext amount
|
||||
// - encrypted janus anchor
|
||||
// - view tag
|
||||
// - ephemeral pubkey
|
||||
// - block index
|
||||
///
|
||||
struct CarrotCoinbaseEnoteV1 final
|
||||
{
|
||||
/// K_o
|
||||
crypto::public_key onetime_address;
|
||||
/// a
|
||||
rct::xmr_amount amount;
|
||||
/// asset type
|
||||
std::string asset_type;
|
||||
/// anchor_enc
|
||||
encrypted_janus_anchor_t anchor_enc;
|
||||
/// view_tag
|
||||
view_tag_t view_tag;
|
||||
/// D_e
|
||||
mx25519_pubkey enote_ephemeral_pubkey;
|
||||
/// block_index
|
||||
std::uint64_t block_index;
|
||||
};
|
||||
|
||||
/// equality operators
|
||||
bool operator==(const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b);
|
||||
static inline bool operator!=(const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b) { return !(a == b); }
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Constants used in Carrot
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
// Carrot addressing protocol domain separators
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR[] = "Carrot commitment mask";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_ONETIME_EXTENSION_G[] = "Carrot key extension G";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_ONETIME_EXTENSION_T[] = "Carrot key extension T";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_ANCHOR[] = "Carrot encryption mask anchor";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_AMOUNT[] = "Carrot encryption mask a";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_PAYMENT_ID[] = "Carrot encryption mask pid";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL[] = "Carrot janus anchor special";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY[] = "Carrot sending key normal";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_VIEW_TAG[] = "Carrot view tag";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET[] = "Carrot sender-receiver secret";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_INPUT_CONTEXT_COINBASE = 'C';
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_INPUT_CONTEXT_RINGCT = 'R';
|
||||
|
||||
// Carrot account secret domain separators
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_PROVE_SPEND_KEY[] = "Carrot prove-spend key";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_VIEW_BALANCE_SECRET[] = "Carrot view-balance secret";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_GENERATE_IMAGE_KEY[] = "Carrot generate-image key";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_INCOMING_VIEW_KEY[] = "Carrot incoming view key";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET[] = "Carrot generate-address secret";
|
||||
|
||||
// Carrot address domain separators
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN[] = "Carrot address index generator";
|
||||
static constexpr const unsigned char CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR[] = "Carrot subaddress scalar";
|
||||
|
||||
// Carrot misc constants
|
||||
static constexpr const unsigned int CARROT_MIN_TX_OUTPUTS = 2;
|
||||
static constexpr const unsigned int CARROT_MAX_TX_OUTPUTS = 8;
|
||||
static constexpr const unsigned int CARROT_MIN_TX_INPUTS = 1;
|
||||
static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 128;
|
||||
|
||||
// SPARC addressing protocol domain separators
|
||||
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK[] = "SPARC return pubkey encryption mask";
|
||||
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR[] = "SPARC return address scalar";
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "core_types.h"
|
||||
|
||||
//local headers
|
||||
#include "crypto/crypto.h"
|
||||
extern "C"
|
||||
{
|
||||
#include "crypto/crypto-ops.h"
|
||||
}
|
||||
|
||||
//third party headers
|
||||
#include <cstring>
|
||||
|
||||
//standard headers
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
template <std::size_t Sz>
|
||||
static void xor_bytes(const unsigned char(&a)[Sz], const unsigned char(&b)[Sz], unsigned char(&c_out)[Sz])
|
||||
{
|
||||
for (std::size_t i{0}; i < Sz; ++i)
|
||||
c_out[i] = a[i] ^ b[i];
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
static T xor_bytes(const T &a, const T &b)
|
||||
{
|
||||
T temp;
|
||||
xor_bytes(a.bytes, b.bytes, temp.bytes);
|
||||
return temp;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(janus_anchor_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
janus_anchor_t operator^(const janus_anchor_t &a, const janus_anchor_t &b)
|
||||
{
|
||||
return xor_bytes(a, b);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const encrypted_amount_t &a, const encrypted_amount_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(encrypted_amount_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
encrypted_amount_t operator^(const encrypted_amount_t &a, const encrypted_amount_t &b)
|
||||
{
|
||||
return xor_bytes(a, b);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(encrypted_return_pubkey_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b)
|
||||
{
|
||||
return xor_bytes(a, b);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const payment_id_t &a, const payment_id_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(payment_id_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
payment_id_t operator^(const payment_id_t &a, const payment_id_t &b)
|
||||
{
|
||||
return xor_bytes(a, b);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(encrypted_payment_id_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
encrypted_payment_id_t operator^(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b)
|
||||
{
|
||||
return xor_bytes(a, b);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const input_context_t &a, const input_context_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(input_context_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const view_tag_t &a, const view_tag_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
janus_anchor_t gen_janus_anchor()
|
||||
{
|
||||
return crypto::rand<janus_anchor_t>();
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
payment_id_t gen_payment_id()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const payment_id_t res = crypto::rand<payment_id_t>();
|
||||
if (res != null_payment_id)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
encrypted_payment_id_t gen_encrypted_payment_id()
|
||||
{
|
||||
return crypto::rand<encrypted_payment_id_t>();
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
view_tag_t gen_view_tag()
|
||||
{
|
||||
return crypto::rand<view_tag_t>();
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
input_context_t gen_input_context()
|
||||
{
|
||||
return crypto::rand<input_context_t>();
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
mx25519_pubkey gen_x25519_pubkey()
|
||||
{
|
||||
unsigned char sc64[64];
|
||||
crypto::rand(sizeof(sc64), sc64);
|
||||
sc_reduce(sc64);
|
||||
ge_p3 P;
|
||||
ge_scalarmult_base(&P, sc64);
|
||||
mx25519_pubkey P_x25519;
|
||||
ge_p3_to_x25519(P_x25519.data, &P);
|
||||
return P_x25519;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Supporting types for Carrot (anchor, view tag, etc.).
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "mx25519.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
//forward declarations
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
constexpr std::size_t JANUS_ANCHOR_BYTES{16};
|
||||
|
||||
/// either encodes randomness the private key of, or an HMAC of, the ephemeral pubkey
|
||||
struct janus_anchor_t final
|
||||
{
|
||||
unsigned char bytes[JANUS_ANCHOR_BYTES];
|
||||
};
|
||||
|
||||
/// carrot janus anchor XORd with a user-defined secret
|
||||
using encrypted_janus_anchor_t = janus_anchor_t;
|
||||
|
||||
/// carrot enote types
|
||||
enum class CarrotEnoteType : unsigned char
|
||||
{
|
||||
PAYMENT = 0,
|
||||
CHANGE = 1
|
||||
};
|
||||
|
||||
/// carrot encrypted amount
|
||||
constexpr std::size_t ENCRYPTED_AMOUNT_BYTES{8};
|
||||
struct encrypted_amount_t final
|
||||
{
|
||||
unsigned char bytes[ENCRYPTED_AMOUNT_BYTES];
|
||||
};
|
||||
|
||||
/// legacy payment ID
|
||||
constexpr std::size_t PAYMENT_ID_BYTES{8};
|
||||
struct payment_id_t final
|
||||
{
|
||||
unsigned char bytes[PAYMENT_ID_BYTES];
|
||||
};
|
||||
static constexpr payment_id_t null_payment_id{{0}};
|
||||
|
||||
/// legacy encrypted payment ID
|
||||
struct encrypted_payment_id_t final
|
||||
{
|
||||
unsigned char bytes[PAYMENT_ID_BYTES];
|
||||
};
|
||||
|
||||
/// carrot view tags
|
||||
constexpr std::size_t VIEW_TAG_BYTES{3};
|
||||
struct view_tag_t final
|
||||
{
|
||||
unsigned char bytes[VIEW_TAG_BYTES];
|
||||
};
|
||||
|
||||
static_assert(sizeof(view_tag_t) < 32, "uint8_t cannot index all view tag bits");
|
||||
|
||||
/// carrot input context
|
||||
constexpr std::size_t INPUT_CONTEXT_BYTES{1 + 32};
|
||||
struct input_context_t final
|
||||
{
|
||||
unsigned char bytes[INPUT_CONTEXT_BYTES];
|
||||
};
|
||||
|
||||
// SPARC encrypted return public key
|
||||
constexpr std::size_t ENCRYPTED_RETURN_PUBKEY_BYTES{32};
|
||||
struct encrypted_return_pubkey_t final
|
||||
{
|
||||
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_BYTES];
|
||||
};
|
||||
|
||||
/// overloaded operators: address tag
|
||||
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b);
|
||||
static inline bool operator!=(const janus_anchor_t &a, const janus_anchor_t &b) { return !(a == b); }
|
||||
janus_anchor_t operator^(const janus_anchor_t &a, const janus_anchor_t &b);
|
||||
|
||||
/// overloaded operators: encrypted amount
|
||||
bool operator==(const encrypted_amount_t &a, const encrypted_amount_t &b);
|
||||
static inline bool operator!=(const encrypted_amount_t &a, const encrypted_amount_t &b) { return !(a == b); }
|
||||
encrypted_amount_t operator^(const encrypted_amount_t &a, const encrypted_amount_t &b);
|
||||
|
||||
/// overloaded operators: payment ID
|
||||
bool operator==(const payment_id_t &a, const payment_id_t &b);
|
||||
static inline bool operator!=(const payment_id_t &a, const payment_id_t &b) { return !(a == b); }
|
||||
payment_id_t operator^(const payment_id_t &a, const payment_id_t &b);
|
||||
|
||||
/// overloaded operators: encrypted payment ID
|
||||
bool operator==(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b);
|
||||
static inline bool operator!=(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b) { return !(a == b); }
|
||||
encrypted_payment_id_t operator^(const encrypted_payment_id_t &a, const encrypted_payment_id_t &b);
|
||||
|
||||
/// overloaded operators: input context
|
||||
bool operator==(const input_context_t &a, const input_context_t &b);
|
||||
static inline bool operator!=(const input_context_t &a, const input_context_t &b) { return !(a == b); }
|
||||
|
||||
/// overloaded operators: view tag
|
||||
bool operator==(const view_tag_t &a, const view_tag_t &b);
|
||||
static inline bool operator!=(const view_tag_t &a, const view_tag_t &b) { return !(a == b); }
|
||||
|
||||
/// overloaded operators: encrypted return pubkey
|
||||
bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
|
||||
static inline bool operator!=(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b) { return !(a == b); }
|
||||
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
|
||||
|
||||
/// generate a random janus anchor
|
||||
janus_anchor_t gen_janus_anchor();
|
||||
/// generate a random (non-null) payment ID
|
||||
payment_id_t gen_payment_id();
|
||||
/// generate a random encrypted payment ID
|
||||
encrypted_payment_id_t gen_encrypted_payment_id();
|
||||
/// generate a random view tag
|
||||
view_tag_t gen_view_tag();
|
||||
/// generate a random input context
|
||||
input_context_t gen_input_context();
|
||||
/// generate a random X25519 pubkey (unclamped)
|
||||
mx25519_pubkey gen_x25519_pubkey();
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "destination.h"
|
||||
|
||||
//local headers
|
||||
#include "address_utils.h"
|
||||
#include "exceptions.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot.dest"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b)
|
||||
{
|
||||
return a.address_spend_pubkey == b.address_spend_pubkey &&
|
||||
a.address_view_pubkey == b.address_view_pubkey &&
|
||||
a.is_subaddress == b.is_subaddress &&
|
||||
a.payment_id == b.payment_id;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_main_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::public_key &primary_address_view_pubkey,
|
||||
CarrotDestinationV1 &destination_out)
|
||||
{
|
||||
destination_out = CarrotDestinationV1{
|
||||
.address_spend_pubkey = account_spend_pubkey,
|
||||
.address_view_pubkey = primary_address_view_pubkey,
|
||||
.is_subaddress = false,
|
||||
.payment_id = null_payment_id
|
||||
};
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_subaddress_v1(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::public_key &account_view_pubkey,
|
||||
const generate_address_secret_device &s_generate_address_dev,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
CarrotDestinationV1 &destination_out)
|
||||
{
|
||||
CARROT_CHECK_AND_THROW(j_major || j_minor,
|
||||
bad_address_type, "j cannot be 0 for a subaddress, only for main addresses");
|
||||
|
||||
// s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||
crypto::secret_key address_index_generator;
|
||||
s_generate_address_dev.make_index_extension_generator(j_major, j_minor, address_index_generator);
|
||||
|
||||
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||
crypto::secret_key subaddress_scalar;
|
||||
make_carrot_subaddress_scalar(account_spend_pubkey, address_index_generator, j_major, j_minor, subaddress_scalar);
|
||||
|
||||
// K^j_s = k^j_subscal * K_s
|
||||
const rct::key address_spend_pubkey =
|
||||
rct::scalarmultKey(rct::pk2rct(account_spend_pubkey), rct::sk2rct(subaddress_scalar));
|
||||
|
||||
// K^j_v = k^j_subscal * K_v
|
||||
const rct::key address_view_pubkey =
|
||||
rct::scalarmultKey(rct::pk2rct(account_view_pubkey), rct::sk2rct(subaddress_scalar));
|
||||
|
||||
destination_out = CarrotDestinationV1{
|
||||
.address_spend_pubkey = rct::rct2pk(address_spend_pubkey),
|
||||
.address_view_pubkey = rct::rct2pk(address_view_pubkey),
|
||||
.is_subaddress = true,
|
||||
.payment_id = null_payment_id
|
||||
};
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_integrated_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::public_key &primary_address_view_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
CarrotDestinationV1 &destination_out)
|
||||
{
|
||||
destination_out = CarrotDestinationV1{
|
||||
.address_spend_pubkey = account_spend_pubkey,
|
||||
.address_view_pubkey = primary_address_view_pubkey,
|
||||
.is_subaddress = false,
|
||||
.payment_id = payment_id
|
||||
};
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
CarrotDestinationV1 gen_carrot_main_address_v1()
|
||||
{
|
||||
return CarrotDestinationV1{
|
||||
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
|
||||
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
|
||||
.is_subaddress = false,
|
||||
.payment_id = null_payment_id
|
||||
};
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
CarrotDestinationV1 gen_carrot_subaddress_v1()
|
||||
{
|
||||
return CarrotDestinationV1{
|
||||
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
|
||||
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
|
||||
.is_subaddress = true,
|
||||
.payment_id = null_payment_id
|
||||
};
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
CarrotDestinationV1 gen_carrot_integrated_address_v1()
|
||||
{
|
||||
// force generate non-zero payment id
|
||||
payment_id_t payment_id{gen_payment_id()};
|
||||
while (payment_id == null_payment_id)
|
||||
payment_id = gen_payment_id();
|
||||
|
||||
return CarrotDestinationV1{
|
||||
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
|
||||
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
|
||||
.is_subaddress = false,
|
||||
.payment_id = payment_id
|
||||
};
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// A 'payment proposal' is a proposal to make an enote sending funds to a Carrot address.
|
||||
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "core_types.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "device.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
////
|
||||
// CarrotDestinationV1
|
||||
// - for creating an output proposal to send an amount to someone
|
||||
///
|
||||
struct CarrotDestinationV1 final
|
||||
{
|
||||
/// K^j_s
|
||||
crypto::public_key address_spend_pubkey;
|
||||
/// K^j_v
|
||||
crypto::public_key address_view_pubkey;
|
||||
/// is a subaddress?
|
||||
bool is_subaddress;
|
||||
/// legacy payment id pid: null for main addresses and subaddresses
|
||||
payment_id_t payment_id;
|
||||
};
|
||||
|
||||
/// equality operators
|
||||
bool operator==(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b);
|
||||
static inline bool operator!=(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b) { return !(a == b); }
|
||||
|
||||
/**
|
||||
* brief: make_carrot_main_address_v1 - make a destination address
|
||||
* param: account_spend_pubkey - K_s
|
||||
* param: primary_address_view_pubkey - K^0_v = k_v G
|
||||
* outparam: destination_out - the full main address
|
||||
*/
|
||||
void make_carrot_main_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::public_key &primary_address_view_pubkey,
|
||||
CarrotDestinationV1 &destination_out);
|
||||
/**
|
||||
* brief: make_carrot_subaddress_v1 - make a destination address
|
||||
* param: account_spend_pubkey - K_s
|
||||
* param: account_view_pubkey - K_v = k_v K_s
|
||||
* param: s_generate_address_dev - device for s_ga
|
||||
* param: j_major -
|
||||
* param: j_minor -
|
||||
* outparam: destination_out - the full subaddress
|
||||
*/
|
||||
void make_carrot_subaddress_v1(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::public_key &account_view_pubkey,
|
||||
const generate_address_secret_device &s_generate_address_dev,
|
||||
const std::uint32_t j_major,
|
||||
const std::uint32_t j_minor,
|
||||
CarrotDestinationV1 &destination_out);
|
||||
/**
|
||||
* brief: make_carrot_integrated_address_v1 - make a destination address
|
||||
* param: account_spend_pubkey - K_s
|
||||
* param: primary_address_view_pubkey - K^0_v = k_v G
|
||||
* param: payment_id - pid
|
||||
* outparam: destination_out - the full main address
|
||||
*/
|
||||
void make_carrot_integrated_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||
const crypto::public_key &primary_address_view_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
CarrotDestinationV1 &destination_out);
|
||||
/**
|
||||
* brief: gen_carrot_main_address_v1 - generate a random main address
|
||||
*/
|
||||
CarrotDestinationV1 gen_carrot_main_address_v1();
|
||||
/**
|
||||
* brief: gen_carrot_main_address_v1 - generate a random subaddress
|
||||
*/
|
||||
CarrotDestinationV1 gen_carrot_subaddress_v1();
|
||||
/**
|
||||
* brief: gen_carrot_main_address_v1 - generate a random integrated address
|
||||
*/
|
||||
CarrotDestinationV1 gen_carrot_integrated_address_v1();
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Abstract interfaces for performing scanning without revealing account keys
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "core_types.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "mx25519.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
/**
|
||||
* These device interfaces were written primarily to be as lean as possible, for ease of the
|
||||
* implementor, so long as account keys don't leak. As such, the interfaces do not shield
|
||||
* sender-receiver secrets, and thus temporary access to this device interface can expose
|
||||
* transaction content permanently in a provable manner. The device interface currently used in
|
||||
* Monero (hw::device) also exposes transaction content, which can be saved permanently, but it
|
||||
* wouldn't necessarily be provable. Thus, in the case of a breach, the original user has some
|
||||
* plausible deniability with (hw::device), which cannot be said of the interfaces in this file.
|
||||
* It's not impossible to make carrot scanning happen completely on-device, but it is significantly
|
||||
* more involved.
|
||||
*/
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
/**
|
||||
* brief: base exception type for reporting carrot device errors.
|
||||
* note: devices should only throw this exception or derived classes
|
||||
*/
|
||||
struct device_error: public std::runtime_error
|
||||
{
|
||||
/**
|
||||
* param: dev_make - e.g. "Trezor", "Ledger"
|
||||
* param: dev_model - e.g. "Model T", "Nano X"
|
||||
* param: func_called - verbatim device interface method name, e.g. "view_key_scalar_mult_x25519"
|
||||
* param: msg - arbitrary error message
|
||||
* param: code - arbitrary error code
|
||||
*/
|
||||
device_error(std::string &&dev_make,
|
||||
std::string &&dev_model,
|
||||
std::string &&func_called,
|
||||
std::string &&msg,
|
||||
const int code)
|
||||
: std::runtime_error(make_formatted_message(dev_make, dev_model, func_called, msg, code)),
|
||||
dev_make(dev_make), dev_model(dev_model), func_called(func_called), msg(msg), code(code)
|
||||
{}
|
||||
|
||||
static std::string make_formatted_message(const std::string &dev_make,
|
||||
const std::string &dev_model,
|
||||
const std::string &func_called,
|
||||
const std::string &msg,
|
||||
const int code)
|
||||
{
|
||||
char buf[384];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"%s %s device error (%d), at %s(): %s",
|
||||
dev_make.c_str(), dev_model.c_str(), code, func_called.c_str(), msg.c_str());
|
||||
return {buf};
|
||||
}
|
||||
|
||||
const std::string dev_make;
|
||||
const std::string dev_model;
|
||||
const std::string func_called;
|
||||
const std::string msg;
|
||||
const int code;
|
||||
};
|
||||
|
||||
struct view_incoming_key_device
|
||||
{
|
||||
/**
|
||||
* brief: view_key_scalar_mult_ed25519 - do an Ed25519 scalar mult against the incoming view key
|
||||
* kvP = k_v * P
|
||||
* param: P - Ed25519 base point
|
||||
* outparam: kvP
|
||||
* return: true on success, false on failure (e.g. unable to decompress point)
|
||||
*/
|
||||
virtual bool view_key_scalar_mult_ed25519(const crypto::public_key &P, crypto::public_key &kvP) const = 0;
|
||||
|
||||
/**
|
||||
* brief: view_key_scalar_mult_x25519 - do an X25519 scalar mult and cofactor clear against the incoming view key
|
||||
* kvD = k_v * D
|
||||
* param: D - X25519 base point
|
||||
* outparam: kvD
|
||||
* return: true on success, false on failure (e.g. unable to decompress point)
|
||||
*/
|
||||
virtual bool view_key_scalar_mult_x25519(const mx25519_pubkey &D, mx25519_pubkey &kvD) const = 0;
|
||||
|
||||
/**
|
||||
* brief: make_janus_anchor_special - make a janus anchor for "special" enotes
|
||||
* anchor_sp = H_16(D_e, input_context, Ko, k_v)
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* param: input_context - input_context
|
||||
* param: account_spend_pubkey - K_s
|
||||
* outparam: anchor_special_out - anchor_sp
|
||||
*/
|
||||
virtual void make_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
janus_anchor_t &anchor_special_out) const = 0;
|
||||
|
||||
virtual ~view_incoming_key_device() = default;
|
||||
};
|
||||
|
||||
struct view_balance_secret_device
|
||||
{
|
||||
/**
|
||||
* brief: make_internal_view_tag - make an internal view tag, given non-secret data
|
||||
* vt = H_3(s_vb || input_context || Ko)
|
||||
* param: input_context - input_context
|
||||
* param: onetime_address - Ko
|
||||
* outparam: view_tag_out - vt
|
||||
*/
|
||||
virtual void make_internal_view_tag(const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
view_tag_t &view_tag_out) const = 0;
|
||||
|
||||
/**
|
||||
* brief: make_internal_sender_receiver_secret - make internal sender-receiver secret, given non-secret data
|
||||
* s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* param: input_context - input_context
|
||||
* outparam: s_sender_receiver_out - s^ctx_sr
|
||||
*/
|
||||
virtual void make_internal_sender_receiver_secret(const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
crypto::hash &s_sender_receiver_out) const = 0;
|
||||
|
||||
/**
|
||||
* brief: make_internal_return_privkey - make internal return private key, given non-secret data
|
||||
* k_return = H_32(s_vb || input_context || Ko)
|
||||
* param: input_context - input_context
|
||||
* param: onetime_address - Ko
|
||||
* outparam: return_privkey_out - k_return
|
||||
*/
|
||||
virtual void make_internal_return_privkey(const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
crypto::secret_key &return_privkey_out) const = 0;
|
||||
|
||||
virtual ~view_balance_secret_device() = default;
|
||||
};
|
||||
|
||||
struct generate_address_secret_device
|
||||
{
|
||||
/**
|
||||
* brief: make_index_extension_generator - make carrot index extension generator s^j_gen
|
||||
* s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||
* param: major_index - j_major
|
||||
* param: minor_index - j_minor
|
||||
* outparam: address_generator_out - s^j_gen
|
||||
*/
|
||||
virtual void make_index_extension_generator(const std::uint32_t major_index,
|
||||
const std::uint32_t minor_index,
|
||||
crypto::secret_key &address_generator_out) const = 0;
|
||||
|
||||
virtual ~generate_address_secret_device() = default;
|
||||
};
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//pair header
|
||||
#include "device_ram_borrowed.h"
|
||||
|
||||
//local headers
|
||||
#include "address_utils.h"
|
||||
#include "enote_utils.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_ed25519(const crypto::public_key &P,
|
||||
crypto::public_key &kvP) const
|
||||
{
|
||||
kvP = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(P), rct::sk2rct(m_k_view_incoming)));
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_x25519(const mx25519_pubkey &D,
|
||||
mx25519_pubkey &kvD) const
|
||||
{
|
||||
return make_carrot_uncontextualized_shared_key_receiver(m_k_view_incoming, D, kvD);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_incoming_key_ram_borrowed_device::make_janus_anchor_special(
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
janus_anchor_t &anchor_special_out) const
|
||||
{
|
||||
return make_carrot_janus_anchor_special(enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
onetime_address,
|
||||
m_k_view_incoming,
|
||||
anchor_special_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_balance_secret_ram_borrowed_device::make_internal_view_tag(const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
view_tag_t &view_tag_out) const
|
||||
{
|
||||
make_carrot_view_tag(to_bytes(m_s_view_balance), input_context, onetime_address, view_tag_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_balance_secret_ram_borrowed_device::make_internal_sender_receiver_secret(
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
crypto::hash &s_sender_receiver_out) const
|
||||
{
|
||||
make_carrot_sender_receiver_secret(to_bytes(m_s_view_balance),
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_balance_secret_ram_borrowed_device::make_internal_return_privkey(const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
crypto::secret_key &return_privkey_out) const
|
||||
{
|
||||
make_sparc_return_privkey(to_bytes(m_s_view_balance), input_context, onetime_address, return_privkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void generate_address_secret_ram_borrowed_device::make_index_extension_generator(
|
||||
const std::uint32_t major_index,
|
||||
const std::uint32_t minor_index,
|
||||
crypto::secret_key &address_generator_out) const
|
||||
{
|
||||
make_carrot_index_extension_generator(m_s_generate_address, major_index, minor_index, address_generator_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Carrot device implementations for in-memory keys & secrets
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "device.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
class view_incoming_key_ram_borrowed_device: virtual public view_incoming_key_device
|
||||
{
|
||||
public:
|
||||
view_incoming_key_ram_borrowed_device(const crypto::secret_key &k_view_incoming):
|
||||
m_k_view_incoming(k_view_incoming) {}
|
||||
|
||||
bool view_key_scalar_mult_ed25519(const crypto::public_key &P,
|
||||
crypto::public_key &kvP) const override;
|
||||
|
||||
bool view_key_scalar_mult_x25519(const mx25519_pubkey &D,
|
||||
mx25519_pubkey &kvD) const override;
|
||||
|
||||
void make_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
janus_anchor_t &anchor_special_out) const override;
|
||||
|
||||
protected:
|
||||
const crypto::secret_key &m_k_view_incoming;
|
||||
};
|
||||
|
||||
class view_balance_secret_ram_borrowed_device: public view_balance_secret_device
|
||||
{
|
||||
public:
|
||||
view_balance_secret_ram_borrowed_device(const crypto::secret_key &s_view_balance):
|
||||
m_s_view_balance(s_view_balance) {}
|
||||
|
||||
void make_internal_view_tag(const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
view_tag_t &view_tag_out) const override;
|
||||
|
||||
void make_internal_sender_receiver_secret(const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
crypto::hash &s_sender_receiver_out) const override;
|
||||
|
||||
void make_internal_return_privkey(const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
crypto::secret_key &return_privkey_out) const override;
|
||||
|
||||
protected:
|
||||
const crypto::secret_key &m_s_view_balance;
|
||||
};
|
||||
|
||||
class generate_address_secret_ram_borrowed_device: public generate_address_secret_device
|
||||
{
|
||||
public:
|
||||
generate_address_secret_ram_borrowed_device(const crypto::secret_key &s_generate_address):
|
||||
m_s_generate_address(s_generate_address) {}
|
||||
|
||||
void make_index_extension_generator(const std::uint32_t major_index,
|
||||
const std::uint32_t minor_index,
|
||||
crypto::secret_key &address_generator_out) const override;
|
||||
|
||||
protected:
|
||||
const crypto::secret_key &m_s_generate_address;
|
||||
};
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,565 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "enote_utils.h"
|
||||
|
||||
//local headers
|
||||
#include "config.h"
|
||||
extern "C"
|
||||
{
|
||||
#include "crypto/crypto-ops.h"
|
||||
}
|
||||
#include "crypto/generators.h"
|
||||
#include "crypto/wallet/crypto.h"
|
||||
#include "hash_functions.h"
|
||||
#include "int-util.h"
|
||||
#include "misc_language.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "transcript_fixed.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <mutex>
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static const mx25519_impl* get_mx25519_impl()
|
||||
{
|
||||
static std::once_flag of;
|
||||
static const mx25519_impl *impl;
|
||||
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
|
||||
if (impl == nullptr)
|
||||
throw std::runtime_error("failed to obtain a mx25519 implementation");
|
||||
return impl;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static encrypted_amount_t enc_amount(const rct::xmr_amount amount, const encrypted_amount_t &mask)
|
||||
{
|
||||
static_assert(sizeof(rct::xmr_amount) == sizeof(encrypted_amount_t), "");
|
||||
|
||||
// little_endian(amount) XOR H_8(q, Ko)
|
||||
encrypted_amount_t amount_LE;
|
||||
memcpy_swap64le(amount_LE.bytes, &amount, 1);
|
||||
return amount_LE ^ mask;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static rct::xmr_amount dec_amount(const encrypted_amount_t &encrypted_amount, const encrypted_amount_t &mask)
|
||||
{
|
||||
static_assert(sizeof(rct::xmr_amount) == sizeof(encrypted_amount_t), "");
|
||||
|
||||
// system_endian(encrypted_amount XOR H_8(q, Ko))
|
||||
const encrypted_amount_t decryptd_amount{encrypted_amount ^ mask};
|
||||
rct::xmr_amount amount;
|
||||
memcpy_swap64le(&amount, &decryptd_amount, 1);
|
||||
return amount;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
template <typename Pid,
|
||||
typename OtherPid = std::conditional_t<std::is_same_v<Pid, payment_id_t>, encrypted_payment_id_t, payment_id_t>>
|
||||
static OtherPid convert_payment_id(const Pid &v)
|
||||
{
|
||||
static_assert(sizeof(Pid) == PAYMENT_ID_BYTES);
|
||||
OtherPid conv;
|
||||
memcpy(&conv, &v, PAYMENT_ID_BYTES);
|
||||
return conv;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
crypto::secret_key &enote_ephemeral_privkey_out)
|
||||
{
|
||||
// k_e = (H_64(anchor_norm, input_context, K^j_s, pid)) mod l
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY>(
|
||||
anchor_norm, input_context, address_spend_pubkey, payment_id);
|
||||
derive_scalar(transcript.data(), transcript.size(), nullptr, &enote_ephemeral_privkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
mx25519_pubkey &enote_ephemeral_pubkey_out)
|
||||
{
|
||||
// D_e = d_e B
|
||||
mx25519_scmul_base(get_mx25519_impl(),
|
||||
&enote_ephemeral_pubkey_out,
|
||||
reinterpret_cast<const mx25519_privkey*>(&enote_ephemeral_privkey));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
mx25519_pubkey &enote_ephemeral_pubkey_out)
|
||||
{
|
||||
// deserialize K^j_s
|
||||
ge_p3 address_spend_pubkey_p3;
|
||||
ge_frombytes_vartime(&address_spend_pubkey_p3, to_bytes(address_spend_pubkey));
|
||||
|
||||
// K_e = d_e K^j_s
|
||||
ge_p3 D_e_in_ed25519;
|
||||
ge_scalarmult_p3(&D_e_in_ed25519, to_bytes(enote_ephemeral_privkey), &address_spend_pubkey_p3);
|
||||
|
||||
// D_e = ConvertPointE(K_e)
|
||||
ge_p3_to_x25519(enote_ephemeral_pubkey_out.data, &D_e_in_ed25519);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const bool is_subaddress,
|
||||
mx25519_pubkey &enote_ephemeral_pubkey_out)
|
||||
{
|
||||
if (is_subaddress)
|
||||
{
|
||||
// D_e = d_e ConvertPointE(K^j_s)
|
||||
make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey,
|
||||
address_spend_pubkey,
|
||||
enote_ephemeral_pubkey_out);
|
||||
}
|
||||
else // !is_subaddress
|
||||
{
|
||||
// D_e = d_e B
|
||||
make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey, enote_ephemeral_pubkey_out);
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &k_view,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
mx25519_pubkey &s_sender_receiver_unctx_out)
|
||||
{
|
||||
// s_sr = k_v D_e
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&s_sender_receiver_unctx_out,
|
||||
reinterpret_cast<const mx25519_privkey*>(&k_view),
|
||||
&enote_ephemeral_pubkey);
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
const crypto::public_key &address_view_pubkey,
|
||||
mx25519_pubkey &s_sender_receiver_unctx_out)
|
||||
{
|
||||
// if K^j_v not in prime order subgroup, then FAIL
|
||||
ge_p3 address_view_pubkey_p3;
|
||||
if (!rct::toPointCheckOrder(&address_view_pubkey_p3, to_bytes(address_view_pubkey)))
|
||||
return false;
|
||||
|
||||
// D^j_v = ConvertPointE(K^j_v)
|
||||
mx25519_pubkey address_view_pubkey_x25519;
|
||||
ge_p3_to_x25519(address_view_pubkey_x25519.data, &address_view_pubkey_p3);
|
||||
|
||||
// s_sr = d_e D^j_v
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&s_sender_receiver_unctx_out,
|
||||
reinterpret_cast<const mx25519_privkey*>(&enote_ephemeral_privkey),
|
||||
&address_view_pubkey_x25519);
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
view_tag_t &view_tag_out)
|
||||
{
|
||||
// vt = H_3(s_sr || input_context || Ko)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_TAG>(input_context, onetime_address);
|
||||
derive_bytes_3(transcript.data(), transcript.size(), s_sender_receiver_unctx, &view_tag_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
crypto::secret_key &return_privkey_out)
|
||||
{
|
||||
// k_return = H_32(s_sr || input_context || Ko)
|
||||
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR>(input_context, onetime_address);
|
||||
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_return_pubkey_t &return_pubkey_mask_out)
|
||||
{
|
||||
// m_return = H_32(s_sr || input_context || Ko)
|
||||
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK>(input_context, onetime_address);
|
||||
derive_bytes_32(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_pubkey_mask_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_return_pubkey_t &return_pubkey_out)
|
||||
{
|
||||
// K_return = k_return G ^ m_return
|
||||
crypto::secret_key k_return;
|
||||
crypto::public_key return_pub;
|
||||
encrypted_return_pubkey_t K_return;
|
||||
encrypted_return_pubkey_t m_return;
|
||||
s_view_balance_dev->make_internal_return_privkey(input_context, onetime_address, k_return);
|
||||
crypto::secret_key_to_public_key(k_return, return_pub);
|
||||
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.data), "Size mismatch");
|
||||
memcpy(K_return.bytes, return_pub.data, sizeof(encrypted_return_pubkey_t));
|
||||
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx, input_context, onetime_address, m_return);
|
||||
return_pubkey_out = K_return ^ m_return;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
input_context_t make_carrot_input_context_coinbase(const std::uint64_t block_index)
|
||||
{
|
||||
// input_context = "C" || IntToBytes256(block_index)
|
||||
input_context_t input_context{};
|
||||
input_context.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_COINBASE;
|
||||
memcpy_swap64le(input_context.bytes + 1, &block_index, 1);
|
||||
return input_context;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
input_context_t make_carrot_input_context(const crypto::key_image &first_rct_key_image)
|
||||
{
|
||||
// input_context = "R" || KI_1
|
||||
input_context_t input_context{};
|
||||
input_context.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_RINGCT;
|
||||
memcpy(input_context.bytes + 1, first_rct_key_image.data, sizeof(crypto::key_image));
|
||||
return input_context;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_unctx[32],
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
crypto::hash &s_sender_receiver_out)
|
||||
{
|
||||
// s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET>(
|
||||
enote_ephemeral_pubkey, input_context);
|
||||
derive_bytes_32(transcript.data(), transcript.size(), s_sender_receiver_unctx, &s_sender_receiver_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::secret_key &sender_extension_out)
|
||||
{
|
||||
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_G>(amount_commitment);
|
||||
derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &sender_extension_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::secret_key &sender_extension_out)
|
||||
{
|
||||
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_T>(amount_commitment);
|
||||
derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &sender_extension_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_onetime_address_extension_pubkey(const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::public_key &sender_extension_pubkey_out)
|
||||
{
|
||||
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
||||
crypto::secret_key sender_extension_g;
|
||||
make_carrot_onetime_address_extension_g(s_sender_receiver, amount_commitment, sender_extension_g);
|
||||
|
||||
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
||||
crypto::secret_key sender_extension_t;
|
||||
make_carrot_onetime_address_extension_t(s_sender_receiver, amount_commitment, sender_extension_t);
|
||||
|
||||
// K^o_ext = k^o_g G + k^o_t T
|
||||
rct::key sender_extension_pubkey_tmp;
|
||||
rct::addKeys2(sender_extension_pubkey_tmp,
|
||||
rct::sk2rct(sender_extension_g),
|
||||
rct::sk2rct(sender_extension_t),
|
||||
rct::pk2rct(crypto::get_T()));
|
||||
|
||||
sender_extension_pubkey_out = rct::rct2pk(sender_extension_pubkey_tmp);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::public_key &onetime_address_out)
|
||||
{
|
||||
// K^o_ext = k^o_g G + k^o_t T
|
||||
crypto::public_key sender_extension_pubkey;
|
||||
make_carrot_onetime_address_extension_pubkey(s_sender_receiver, amount_commitment, sender_extension_pubkey);
|
||||
|
||||
// Ko = K^j_s + K^o_ext
|
||||
onetime_address_out = rct::rct2pk(rct::addKeys(
|
||||
rct::pk2rct(address_spend_pubkey), rct::pk2rct(sender_extension_pubkey)));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver,
|
||||
const rct::xmr_amount amount,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const CarrotEnoteType enote_type,
|
||||
crypto::secret_key &amount_blinding_factor_out)
|
||||
{
|
||||
// k_a = H_n(s^ctx_sr, a, K^j_s, enote_type)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR>(
|
||||
amount, address_spend_pubkey, static_cast<unsigned char>(enote_type));
|
||||
derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &amount_blinding_factor_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_anchor_encryption_mask(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_janus_anchor_t &anchor_encryption_mask_out)
|
||||
{
|
||||
// m_anchor = H_16(s^ctx_sr, Ko)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_ANCHOR>(onetime_address);
|
||||
derive_bytes_16(transcript.data(), transcript.size(), &s_sender_receiver, &anchor_encryption_mask_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address)
|
||||
{
|
||||
// m_anchor = H_16(s^ctx_sr, Ko)
|
||||
encrypted_janus_anchor_t mask;
|
||||
make_carrot_anchor_encryption_mask(s_sender_receiver, onetime_address, mask);
|
||||
|
||||
// anchor_enc = anchor XOR m_anchor
|
||||
return anchor ^ mask;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
janus_anchor_t decrypt_carrot_anchor(const encrypted_janus_anchor_t &encrypted_anchor,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address)
|
||||
{
|
||||
// m_anchor = H_16(s^ctx_sr, Ko)
|
||||
encrypted_janus_anchor_t mask;
|
||||
make_carrot_anchor_encryption_mask(s_sender_receiver, onetime_address, mask);
|
||||
|
||||
// anchor = anchor_enc XOR m_anchor
|
||||
return encrypted_anchor ^ mask;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_amount_encryption_mask(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_amount_t &amount_encryption_mask_out)
|
||||
{
|
||||
// m_a = H_8(s^ctx_sr, Ko)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_AMOUNT>(onetime_address);
|
||||
derive_bytes_8(transcript.data(), transcript.size(), &s_sender_receiver, &amount_encryption_mask_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address)
|
||||
{
|
||||
// m_a = H_8(s^ctx_sr, Ko)
|
||||
encrypted_amount_t mask;
|
||||
make_carrot_amount_encryption_mask(s_sender_receiver, onetime_address, mask);
|
||||
|
||||
// a_enc = a XOR m_a [paying attention to system endianness]
|
||||
return enc_amount(amount, mask);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
rct::xmr_amount decrypt_carrot_amount(const encrypted_amount_t encrypted_amount,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address)
|
||||
{
|
||||
// m_a = H_8(s^ctx_sr, Ko)
|
||||
encrypted_amount_t mask;
|
||||
make_carrot_amount_encryption_mask(s_sender_receiver, onetime_address, mask);
|
||||
|
||||
// a = a_enc XOR m_a [paying attention to system endianness]
|
||||
return dec_amount(encrypted_amount, mask);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_payment_id_encryption_mask(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_payment_id_t &payment_id_encryption_mask_out)
|
||||
{
|
||||
// m_pid = H_8(s^ctx_sr, Ko)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_PAYMENT_ID>(onetime_address);
|
||||
derive_bytes_8(transcript.data(), transcript.size(), &s_sender_receiver, &payment_id_encryption_mask_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
encrypted_payment_id_t encrypt_legacy_payment_id(const payment_id_t payment_id,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address)
|
||||
{
|
||||
// m_pid = H_8(s^ctx_sr, Ko)
|
||||
encrypted_payment_id_t mask;
|
||||
make_carrot_payment_id_encryption_mask(s_sender_receiver, onetime_address, mask);
|
||||
|
||||
// pid_enc = pid XOR m_pid
|
||||
return convert_payment_id(payment_id) ^ mask;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
payment_id_t decrypt_legacy_payment_id(const encrypted_payment_id_t encrypted_payment_id,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address)
|
||||
{
|
||||
// m_pid = H_8(s^ctx_sr, Ko)
|
||||
encrypted_payment_id_t mask;
|
||||
make_carrot_payment_id_encryption_mask(s_sender_receiver, onetime_address, mask);
|
||||
|
||||
// pid = pid_enc XOR m_pid
|
||||
return convert_payment_id(encrypted_payment_id ^ mask);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const crypto::secret_key &k_view,
|
||||
janus_anchor_t &anchor_special_out)
|
||||
{
|
||||
// anchor_sp = H_16(D_e, input_context, Ko, k_v)
|
||||
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL>(
|
||||
enote_ephemeral_pubkey, input_context, onetime_address);
|
||||
derive_bytes_16(transcript.data(), transcript.size(), &k_view, &anchor_special_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void recover_address_spend_pubkey(const crypto::public_key &onetime_address,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::public_key &address_spend_key_out)
|
||||
{
|
||||
// K^o_ext = k^o_g G + k^o_t T
|
||||
crypto::public_key sender_extension_pubkey;
|
||||
make_carrot_onetime_address_extension_pubkey(s_sender_receiver, amount_commitment, sender_extension_pubkey);
|
||||
|
||||
// K^j_s = Ko - K^o_ext
|
||||
rct::key res_tmp;
|
||||
rct::subKeys(res_tmp, rct::pk2rct(onetime_address), rct::pk2rct(sender_extension_pubkey));
|
||||
address_spend_key_out = rct::rct2pk(res_tmp);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const view_tag_t view_tag)
|
||||
{
|
||||
// vt' = H_3(s_sr || input_context || Ko)
|
||||
view_tag_t nominal_view_tag;
|
||||
make_carrot_view_tag(s_sender_receiver_unctx, input_context, onetime_address, nominal_view_tag);
|
||||
|
||||
// vt' ?= vt
|
||||
return nominal_view_tag == view_tag;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_recompute_carrot_amount_commitment(const crypto::hash &s_sender_receiver,
|
||||
const rct::xmr_amount nominal_amount,
|
||||
const crypto::public_key &nominal_address_spend_pubkey,
|
||||
const CarrotEnoteType nominal_enote_type,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::secret_key &amount_blinding_factor_out)
|
||||
{
|
||||
// k_a' = H_n(s^ctx_sr, a', K^j_s', enote_type')
|
||||
make_carrot_amount_blinding_factor(s_sender_receiver,
|
||||
nominal_amount,
|
||||
nominal_address_spend_pubkey,
|
||||
nominal_enote_type,
|
||||
amount_blinding_factor_out);
|
||||
|
||||
// C_a' = k_a' G + a' H
|
||||
const rct::key nominal_amount_commitment = rct::commit(nominal_amount, rct::sk2rct(amount_blinding_factor_out));
|
||||
|
||||
// C_a' ?= C_a
|
||||
return nominal_amount_commitment == amount_commitment;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
|
||||
const encrypted_amount_t &encrypted_amount,
|
||||
const crypto::public_key &onetime_address,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const rct::key &amount_commitment,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out)
|
||||
{
|
||||
// a' = a_enc XOR m_a
|
||||
amount_out = decrypt_carrot_amount(encrypted_amount, s_sender_receiver, onetime_address);
|
||||
|
||||
// set enote_type <- "payment"
|
||||
enote_type_out = CarrotEnoteType::PAYMENT;
|
||||
|
||||
// if C_a ?= k_a' G + a' H, then PASS
|
||||
if (try_recompute_carrot_amount_commitment(s_sender_receiver,
|
||||
amount_out,
|
||||
address_spend_pubkey,
|
||||
enote_type_out,
|
||||
amount_commitment,
|
||||
amount_blinding_factor_out))
|
||||
return true;
|
||||
|
||||
// set enote_type <- "change"
|
||||
enote_type_out = CarrotEnoteType::CHANGE;
|
||||
|
||||
// if C_a ?= k_a' G + a' H, then PASS
|
||||
if (try_recompute_carrot_amount_commitment(s_sender_receiver,
|
||||
amount_out,
|
||||
address_spend_pubkey,
|
||||
enote_type_out,
|
||||
amount_commitment,
|
||||
amount_blinding_factor_out))
|
||||
return true;
|
||||
|
||||
// neither attempt at recomputing passed: so FAIL
|
||||
return false;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool verify_carrot_normal_janus_protection(const janus_anchor_t &nominal_anchor,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &nominal_address_spend_pubkey,
|
||||
const bool is_subaddress,
|
||||
const payment_id_t nominal_payment_id,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey)
|
||||
{
|
||||
// d_e' = H_n(anchor_norm, input_context, K^j_s, pid))
|
||||
crypto::secret_key nominal_enote_ephemeral_privkey;
|
||||
make_carrot_enote_ephemeral_privkey(nominal_anchor,
|
||||
input_context,
|
||||
nominal_address_spend_pubkey,
|
||||
nominal_payment_id,
|
||||
nominal_enote_ephemeral_privkey);
|
||||
|
||||
// recompute D_e' for d_e' and address type
|
||||
mx25519_pubkey nominal_enote_ephemeral_pubkey;
|
||||
make_carrot_enote_ephemeral_pubkey(nominal_enote_ephemeral_privkey,
|
||||
nominal_address_spend_pubkey,
|
||||
is_subaddress,
|
||||
nominal_enote_ephemeral_pubkey);
|
||||
|
||||
// D_e' ?= D_e
|
||||
return 0 == memcmp(&nominal_enote_ephemeral_pubkey, &enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,438 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// @file Utilities for making and handling enotes with carrot.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "crypto/crypto.h"
|
||||
#include "core_types.h"
|
||||
#include "mx25519.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "device.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
/**
|
||||
* brief: make_carrot_enote_ephemeral_privkey - enote ephemeral privkey k_e for Carrot enotes
|
||||
* d_e = H_n(anchor_norm, input_context, K^j_s, pid))
|
||||
* param: anchor_norm - anchor_norm
|
||||
* param: input_context - input_context
|
||||
* param: address_spend_pubkey - K^j_s
|
||||
* param: payment_id - pid
|
||||
* outparam: enote_ephemeral_privkey_out - k_e
|
||||
*/
|
||||
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
crypto::secret_key &enote_ephemeral_privkey_out);
|
||||
/**
|
||||
* brief: make_carrot_enote_ephemeral_pubkey_main - make enote ephemeral pubkey D_e for a main address
|
||||
* D_e = d_e B
|
||||
* param: enote_ephemeral_privkey - d_e
|
||||
* outparam: enote_ephemeral_pubkey_out - D_e
|
||||
*/
|
||||
void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
mx25519_pubkey &enote_ephemeral_pubkey_out);
|
||||
/**
|
||||
* brief: make_carrot_enote_ephemeral_pubkey_subaddress - make enote ephemeral pubkey D_e for a subaddress
|
||||
* D_e = d_e ConvertPointE(K^j_s)
|
||||
* param: enote_ephemeral_privkey - d_e
|
||||
* param: address_spend_pubkey - K^j_s
|
||||
* outparam: enote_ephemeral_pubkey_out - D_e
|
||||
*/
|
||||
void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
mx25519_pubkey &enote_ephemeral_pubkey_out);
|
||||
/**
|
||||
* brief: make_carrot_enote_ephemeral_pubkey - make enote ephemeral pubkey D_e for either either address type
|
||||
* [is_subaddress]: D_e = d_e ConvertPointE(K^j_s)
|
||||
* [!is_subaddress]: D_e = d_e B
|
||||
* param: enote_ephemeral_privkey - d_e
|
||||
* param: address_spend_pubkey - K^j_s
|
||||
* param: is_subaddress -
|
||||
* outparam: enote_ephemeral_pubkey_out - D_e
|
||||
*/
|
||||
void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const bool is_subaddress,
|
||||
mx25519_pubkey &enote_ephemeral_pubkey_out);
|
||||
/**
|
||||
* brief: make_carrot_uncontextualized_shared_key_receiver - perform the receiver-side ECDH exchange for Carrot enotes
|
||||
* s_sr = k_v D_e
|
||||
* param: k_view - k_v
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* outparam: s_sender_receiver_unctx_out - s_sr
|
||||
* return: true if successful, false if a failure occurred in point decompression
|
||||
*/
|
||||
bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &k_view,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
mx25519_pubkey &s_sender_receiver_unctx_out);
|
||||
/**
|
||||
* brief: make_carrot_uncontextualized_shared_key_sender - perform the sender-side ECDH exchange for Carrot enotes
|
||||
* s_sr = d_e ConvertPointE(K^j_v)
|
||||
* param: enote_ephemeral_privkey - d_e
|
||||
* param: address_view_pubkey - K^j_v
|
||||
* outparam: s_sender_receiver_unctx_out - s_sr
|
||||
* return: true if successful, false if a failure occurred in point decompression
|
||||
*/
|
||||
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
|
||||
const crypto::public_key &address_view_pubkey,
|
||||
mx25519_pubkey &s_sender_receiver_unctx_out);
|
||||
/**
|
||||
* brief: make_carrot_view_tag - used for optimized identification of enotes
|
||||
* vt = H_3(s_sr || input_context || Ko)
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: input_context - input_context
|
||||
* param: onetime_address - Ko
|
||||
* outparam: view_tag_out - vt
|
||||
*/
|
||||
void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
view_tag_t &view_tag_out);
|
||||
/**
|
||||
* brief: make_sparc_return_privkey - return private key, given non-secret data
|
||||
* k_return = H_32(s_sr || input_context || Ko)
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: input_context - input_context
|
||||
* param: onetime_address - Ko
|
||||
* outparam: return_privkey_out - k_return
|
||||
*/
|
||||
void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
crypto::secret_key &return_privkey_out);
|
||||
/**
|
||||
* brief: make_sparc_return_pubkey_encryption_mask - used for hiding return pubkey
|
||||
* m_return = H_32(s_sr || input_context || Ko)
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: input_context - input_context
|
||||
* param: onetime_address - Ko
|
||||
* outparam: return_pubkey_mask_out - m_return
|
||||
*/
|
||||
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_return_pubkey_t &return_pubkey_mask_out);
|
||||
|
||||
/**
|
||||
* brief: make_sparc_return_pubkey - construct the return pubkey
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: input_context - input_context
|
||||
* param: s_view_balance_dev - s_vb
|
||||
* param: onetime_address - Ko
|
||||
* outparam: return_pubkey_mask_out - K_return
|
||||
*/
|
||||
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_return_pubkey_t &return_pubkey_out);
|
||||
/**
|
||||
* brief: make_carrot_input_context_coinbase - input context for a sender-receiver secret (coinbase txs)
|
||||
* input_context = "C" || IntToBytes256(block_index)
|
||||
* param: block_index - block index of the coinbase tx
|
||||
* return: input_context
|
||||
*/
|
||||
input_context_t make_carrot_input_context_coinbase(const std::uint64_t block_index);
|
||||
/**
|
||||
* brief: make_carrot_input_context - input context for a sender-receiver secret (standard RingCT txs)
|
||||
* input_context = "R" || KI_1
|
||||
* param: first_rct_key_image - KI_1, the first spent RingCT key image in a tx
|
||||
* return: input_context
|
||||
*/
|
||||
input_context_t make_carrot_input_context(const crypto::key_image &first_rct_key_image);
|
||||
/**
|
||||
* brief: make_carrot_sender_receiver_secret - contextualized sender-receiver secret s^ctx_sr
|
||||
* s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* param: input_context - [standard: KI_1] [coinbase: block index]
|
||||
* outparam: s_sender_receiver_out - s^ctx_sr
|
||||
* - note: this is 'crypto::hash' instead of 'crypto::secret_key' for better performance in multithreaded environments
|
||||
*/
|
||||
void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_unctx[32],
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
crypto::hash &s_sender_receiver_out);
|
||||
/**
|
||||
* brief: make_carrot_onetime_address_extension_g - extension for transforming a receiver's spendkey into an
|
||||
* enote one-time address
|
||||
* k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: amount_commitment - C_a
|
||||
* outparam: sender_extension_out - k^o_g
|
||||
*/
|
||||
void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::secret_key &sender_extension_out);
|
||||
/**
|
||||
* brief: make_carrot_onetime_address_extension_t - extension for transforming a receiver's spendkey into an
|
||||
* enote one-time address
|
||||
* k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: amount_commitment - C_a
|
||||
* outparam: sender_extension_out - k^o_t
|
||||
*/
|
||||
void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::secret_key &sender_extension_out);
|
||||
/**
|
||||
* brief: make_carrot_onetime_address_extension_pubkey - create a FCMP++ onetime address extension pubkey
|
||||
* K^o_ext = k^o_g G + k^o_t T
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: amount_commitment - C_a
|
||||
* outparam: sender_extension_pubkey_out - K^o_ext
|
||||
*/
|
||||
void make_carrot_onetime_address_extension_pubkey(const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::public_key &sender_extension_pubkey_out);
|
||||
/**
|
||||
* brief: make_carrot_onetime_address - create a FCMP++ onetime address
|
||||
* Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
|
||||
* param: address_spend_pubkey - K^j_s
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: amount_commitment - C_a
|
||||
* outparam: onetime_address_out - Ko
|
||||
*/
|
||||
void make_carrot_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::public_key &onetime_address_out);
|
||||
/**
|
||||
* brief: make_carrot_amount_blinding_factor - create blinding factor for enote's amount commitment C_a
|
||||
* k_a = H_n(s^ctx_sr, a, K^j_s, enote_type)
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: amount - a
|
||||
* param: address_spend_pubkey - K^j_s
|
||||
* param: enote_type - enote_type
|
||||
* outparam: amount_blinding_factor_out - k_a
|
||||
*/
|
||||
void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver,
|
||||
const rct::xmr_amount amount,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const CarrotEnoteType enote_type,
|
||||
crypto::secret_key &amount_blinding_factor_out);
|
||||
/**
|
||||
* brief: make_carrot_anchor_encryption_mask - create XOR encryption mask for enote's anchor
|
||||
* m_anchor = H_16(s^ctx_sr, Ko)
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* outparam: anchor_encryption_mask_out - m_anchor
|
||||
*/
|
||||
void make_carrot_anchor_encryption_mask(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_janus_anchor_t &anchor_encryption_mask_out);
|
||||
/**
|
||||
* brief: encrypt_carrot_anchor - encrypt a Janus anchor for an enote
|
||||
* anchor_enc = anchor XOR m_anchor
|
||||
* param: anchor -
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* return: anchor_enc
|
||||
*/
|
||||
encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address);
|
||||
/**
|
||||
* brief: decrypt_carrot_address_tag - decrypt a Janus anchor from an enote
|
||||
* anchor = anchor_enc XOR m_anchor
|
||||
* param: encrypted_anchor - anchor_enc
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* return: anchor
|
||||
*/
|
||||
janus_anchor_t decrypt_carrot_anchor(const encrypted_janus_anchor_t &encrypted_anchor,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address);
|
||||
/**
|
||||
* brief: make_carrot_amount_encryption_mask - create XOR encryption mask for enote's amount
|
||||
* m_a = H_8(s^ctx_sr, Ko)
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* outparam: amount_encryption_mask_out - m_a
|
||||
*/
|
||||
void make_carrot_amount_encryption_mask(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_amount_t &amount_encryption_mask_out);
|
||||
/**
|
||||
* brief: encrypt_carrot_amount - encrypt an amount for an enote
|
||||
* a_enc = a XOR m_a
|
||||
* param: amount - a
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* return: a_enc
|
||||
*/
|
||||
encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address);
|
||||
/**
|
||||
* brief: decrypt_carrot_amount - decrypt an amount from an enote
|
||||
* a = a_enc XOR m_a
|
||||
* param: encrypted_amount - a_enc
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* return: a
|
||||
*/
|
||||
rct::xmr_amount decrypt_carrot_amount(const encrypted_amount_t encrypted_amount,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address);
|
||||
/**
|
||||
* brief: make_carrot_payment_id_encryption_mask - create XOR encryption mask for enote's payment ID
|
||||
* m_pid = H_8(s^ctx_sr, Ko)
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* outparam: payment_id_encryption_mask_out - m_pid
|
||||
*/
|
||||
void make_carrot_payment_id_encryption_mask(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address,
|
||||
encrypted_payment_id_t &payment_id_encryption_mask_out);
|
||||
/**
|
||||
* brief: encrypt_legacy_payment_id - encrypt a payment ID from an enote
|
||||
* pid_enc = pid XOR m_pid
|
||||
* param: payment_id - pid
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* return: pid_enc
|
||||
*/
|
||||
encrypted_payment_id_t encrypt_legacy_payment_id(const payment_id_t payment_id,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address);
|
||||
/**
|
||||
* brief: decrypt_legacy_payment_id - decrypt a payment ID from an enote
|
||||
* pid = pid_enc XOR m_pid
|
||||
* param: encrypted_payment_id - pid_enc
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: onetime_address - Ko
|
||||
* return: pid
|
||||
*/
|
||||
payment_id_t decrypt_legacy_payment_id(const encrypted_payment_id_t encrypted_payment_id,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &onetime_address);
|
||||
/**
|
||||
* brief: make_carrot_janus_anchor_special - make a janus anchor for "special" enotes
|
||||
* anchor_sp = H_16(D_e, input_context, Ko, k_v)
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* param: input_context -
|
||||
* param: onetime_address - Ko
|
||||
* param: k_view - k_v
|
||||
* outparam: anchor_special_out - anchor_sp
|
||||
*/
|
||||
void make_carrot_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const crypto::secret_key &k_view,
|
||||
janus_anchor_t &anchor_special_out);
|
||||
/**
|
||||
* brief: recover_address_spend_pubkey - get the receiver's spend key for which this RingCT onetime address
|
||||
* can be reconstructed as 'owned' by
|
||||
* K^j_s = Ko - K^o_ext = Ko - (k^o_g G + k^o_t U)
|
||||
* param: onetime_address - Ko
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: amount_commitment - C_a
|
||||
* outparam: address_spend_key_out: - K^j_s
|
||||
*/
|
||||
void recover_address_spend_pubkey(const crypto::public_key &onetime_address,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::public_key &address_spend_key_out);
|
||||
/**
|
||||
* brief: test_carrot_view_tag - test carrot view tag
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: input_context -
|
||||
* param: onetime_address - Ko
|
||||
* param: view_tag - vt
|
||||
* return: true if successfully recomputed the view tag
|
||||
*/
|
||||
bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const view_tag_t view_tag);
|
||||
/**
|
||||
* brief: try_recompute_carrot_amount_commitment - test recreating the amount commitment for given enote_type and amount
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: nominal_amount - a'
|
||||
* param: nominal_address_spend_pubkey - K^j_s'
|
||||
* param: nominal_enote_type - enote_type'
|
||||
* param: amount_commitment - C_a
|
||||
* outparam: amount_blinding_factor_out - k_a' = H_n(s^ctx_sr, enote_type')
|
||||
* return: true if successfully recomputed the amount commitment (C_a ?= k_a' G + a' H)
|
||||
*/
|
||||
bool try_recompute_carrot_amount_commitment(const crypto::hash &s_sender_receiver,
|
||||
const rct::xmr_amount nominal_amount,
|
||||
const crypto::public_key &nominal_address_spend_pubkey,
|
||||
const CarrotEnoteType nominal_enote_type,
|
||||
const rct::key &amount_commitment,
|
||||
crypto::secret_key &amount_blinding_factor_out);
|
||||
/**
|
||||
* brief: try_get_amount - test decrypting the amount and recomputing the amount commitment
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* param: encrypted_amount - a_enc
|
||||
* param: onetime_address - Ko
|
||||
* param: address_spend_pubkey - K^j_s
|
||||
* param: amount_commitment - C_a
|
||||
* outparam: enote_type_out - enote_type'
|
||||
* outparam: amount_out - a' = a_enc XOR m_a
|
||||
* outparam: amount_blinding_factor_out - k_a' = H_n(s^ctx_sr, enote_type')
|
||||
* return: true if successfully recomputed the amount commitment (C_a ?= k_a' G + a' H)
|
||||
*/
|
||||
bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
|
||||
const encrypted_amount_t &encrypted_amount,
|
||||
const crypto::public_key &onetime_address,
|
||||
const crypto::public_key &address_spend_pubkey,
|
||||
const rct::key &amount_commitment,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out);
|
||||
/**
|
||||
* brief: verify_carrot_normal_janus_protection - check normal external enote is Janus safe (i.e. can recompute D_e)
|
||||
* param: nominal_anchor - anchor'
|
||||
* param: input_context -
|
||||
* param: nominal_address_spend_pubkey - K^j_s'
|
||||
* param: is_subaddress -
|
||||
* param: nominal_payment_id - pid'
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* return: true if this normal external enote is safe from Janus attacks
|
||||
*/
|
||||
bool verify_carrot_normal_janus_protection(const janus_anchor_t &nominal_anchor,
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &nominal_address_spend_pubkey,
|
||||
const bool is_subaddress,
|
||||
const payment_id_t nominal_payment_id,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey);
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Exceptions thrown by Carrot
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <stdexcept>
|
||||
|
||||
//forward declarations
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
|
||||
|
||||
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_point, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(missing_components, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(missing_randomness, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_inputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error)
|
||||
|
||||
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_usable_money, carrot_runtime_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(unexpected_scan_failure, carrot_runtime_error)
|
||||
|
||||
/// one needs to include misc_log_ex.h to use the following macros
|
||||
#define CARROT_THROW(errtype, message) { \
|
||||
std::stringstream ss; \
|
||||
ss << message; \
|
||||
const std::string msg_str = ss.str(); \
|
||||
LOG_ERROR(msg_str); \
|
||||
throw errtype(msg_str); \
|
||||
}
|
||||
#define CARROT_CHECK_AND_THROW(expr, errtype, message) if (!(expr)) { CARROT_THROW(errtype, message) }
|
||||
|
||||
#undef CARROT_DEFINE_SIMPLE_ERROR_TYPE
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "hash_functions.h"
|
||||
|
||||
//local headers
|
||||
extern "C"
|
||||
{
|
||||
#include "crypto/crypto-ops.h"
|
||||
}
|
||||
#include "crypto/blake2b.h"
|
||||
#include "exceptions.h"
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
// H_x[k](data)
|
||||
// - if derivation_key == nullptr, then the hash is NOT keyed
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void hash_base(const void *derivation_key, //32 bytes
|
||||
const void *data,
|
||||
const std::size_t data_length,
|
||||
void *hash_out,
|
||||
const std::size_t out_length)
|
||||
{
|
||||
CARROT_CHECK_AND_THROW(blake2b(hash_out,
|
||||
out_length,
|
||||
data,
|
||||
data_length,
|
||||
derivation_key,
|
||||
derivation_key ? 32 : 0) == 0,
|
||||
crypto_function_failed, "carrot hash base: blake2b failed");
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void derive_bytes_3(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||
{
|
||||
// H_3(x): 2-byte output
|
||||
hash_base(key, data, data_length, hash_out, 3);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void derive_bytes_8(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||
{
|
||||
// H_8(x): 8-byte output
|
||||
hash_base(key, data, data_length, hash_out, 8);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void derive_bytes_16(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||
{
|
||||
// H_16(x): 16-byte output
|
||||
hash_base(key, data, data_length, hash_out, 16);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void derive_bytes_32(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||
{
|
||||
// H_32(x): 32-byte output
|
||||
hash_base(key, data, data_length, hash_out, 32);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void derive_bytes_64(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||
{
|
||||
// H_64(x): 64-byte output
|
||||
hash_base(key, data, data_length, hash_out, 64);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void derive_scalar(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||
{
|
||||
// H_n(x): Ed25519 group scalar output (32 bytes)
|
||||
// note: hash to 64 bytes then mod l
|
||||
unsigned char temp[64];
|
||||
hash_base(key, data, data_length, temp, 64);
|
||||
sc_reduce(temp); //mod l
|
||||
memcpy(hash_out, temp, 32);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Core hash functions for Seraphis (note: this implementation satisfies the Jamtis specification).
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <cstddef>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
/// H_3(x): 3-byte output
|
||||
void derive_bytes_3(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||
/// H_8(x): 8-byte output
|
||||
void derive_bytes_8(const void *data, const std::size_t data_length, const void* key, void *hash_out);
|
||||
/// H_16(x): 16-byte output
|
||||
void derive_bytes_16(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||
/// H_32(x): 32-byte output
|
||||
void derive_bytes_32(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||
/// H_64(x): 64-byte output
|
||||
void derive_bytes_64(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||
/// H_n(x): unclamped Curve25519/Ed25519 group scalar output (32 bytes)
|
||||
void derive_scalar(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "lazy_amount_commitment.h"
|
||||
|
||||
//local headers
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
rct::key calculate_amount_commitment(const lazy_amount_commitment_t &lazy_amount_commitment)
|
||||
{
|
||||
struct lazy_amount_commitment_visitor
|
||||
{
|
||||
rct::key operator()(const rct::key &C) const { return C; }
|
||||
rct::key operator()(const std::pair<rct::xmr_amount, rct::key> &op) const
|
||||
{ return rct::commit(op.first, op.second); }
|
||||
rct::key operator()(const rct::xmr_amount &a) const { return rct::zeroCommit(a); }
|
||||
};
|
||||
|
||||
return std::visit(lazy_amount_commitment_visitor{}, lazy_amount_commitment);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
using lazy_amount_commitment_t = std::variant<
|
||||
rct::key, // C
|
||||
std::pair<rct::xmr_amount, rct::key>, // (a, z) s.t. C = z G + a H
|
||||
rct::xmr_amount // a s.t. C = G + a H
|
||||
>;
|
||||
|
||||
rct::key calculate_amount_commitment(const lazy_amount_commitment_t &lazy_amount_commitment);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,447 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "output_set_finalization.h"
|
||||
|
||||
//local headers
|
||||
#include "common/container_helpers.h"
|
||||
#include "enote_utils.h"
|
||||
#include "exceptions.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <set>
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot.osf"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
struct compare_memcmp{ bool operator()(const T &a, const T &b) const { return memcmp(&a, &b, sizeof(T)) < 0; } };
|
||||
template <typename T>
|
||||
using memcmp_set = std::set<T, compare_memcmp<T>>;
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const bool need_change_output,
|
||||
const bool have_payment_type_selfsend)
|
||||
{
|
||||
const size_t num_outputs = num_outgoing + num_selfsend;
|
||||
const bool already_completed = num_outputs >= 2 && num_selfsend >= 1 && !need_change_output;
|
||||
if (num_outputs == 0)
|
||||
{
|
||||
CARROT_THROW(too_few_outputs, "set contains 0 outputs");
|
||||
}
|
||||
else if (already_completed)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
else if (num_outputs == 1)
|
||||
{
|
||||
if (num_selfsend == 0)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_SHARED;
|
||||
}
|
||||
else if (!need_change_output)
|
||||
{
|
||||
return AdditionalOutputType::DUMMY;
|
||||
}
|
||||
else // num_selfsend == 1 && need_change_output
|
||||
{
|
||||
if (have_payment_type_selfsend)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_SHARED;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AdditionalOutputType::PAYMENT_SHARED;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (num_outputs < CARROT_MAX_TX_OUTPUTS)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_UNIQUE;
|
||||
}
|
||||
else // num_outputs >= CARROT_MAX_TX_OUTPUTS
|
||||
{
|
||||
CARROT_THROW(too_many_outputs, "set needs finalization but already contains too many outputs");
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
tools::optional_variant<CarrotPaymentProposalV1, CarrotPaymentProposalSelfSendV1> get_additional_output_proposal(
|
||||
const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const rct::xmr_amount needed_change_amount,
|
||||
const bool have_payment_type_selfsend,
|
||||
const crypto::public_key &change_address_spend_pubkey)
|
||||
{
|
||||
const std::optional<AdditionalOutputType> additional_output_type = get_additional_output_type(
|
||||
num_outgoing,
|
||||
num_selfsend,
|
||||
needed_change_amount,
|
||||
have_payment_type_selfsend
|
||||
);
|
||||
|
||||
if (!additional_output_type)
|
||||
return {};
|
||||
|
||||
switch (*additional_output_type)
|
||||
{
|
||||
case AdditionalOutputType::PAYMENT_SHARED:
|
||||
return CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = needed_change_amount,
|
||||
.enote_type = CarrotEnoteType::PAYMENT,
|
||||
.enote_ephemeral_pubkey = std::nullopt
|
||||
};
|
||||
case AdditionalOutputType::CHANGE_SHARED:
|
||||
return CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = needed_change_amount,
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = std::nullopt
|
||||
};
|
||||
case AdditionalOutputType::CHANGE_UNIQUE:
|
||||
return CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = needed_change_amount,
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = std::nullopt
|
||||
};
|
||||
case AdditionalOutputType::DUMMY:
|
||||
return CarrotPaymentProposalV1{
|
||||
.destination = gen_carrot_main_address_v1(),
|
||||
.amount = 0,
|
||||
.randomness = gen_janus_anchor()
|
||||
};
|
||||
}
|
||||
|
||||
CARROT_THROW(std::invalid_argument, "unrecognized additional output type");
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const std::vector<CarrotPaymentProposalSelfSendV1> &selfsend_payment_proposals,
|
||||
const std::optional<encrypted_payment_id_t> &dummy_encrypted_payment_id,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const view_incoming_key_device *k_view_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||
cryptonote::transaction_type tx_type,
|
||||
size_t &change_index_out,
|
||||
std::unordered_map<crypto::public_key, size_t> &payments_indices_out,
|
||||
std::vector<std::pair<bool, std::size_t>> *payment_proposal_order_out)
|
||||
{
|
||||
output_enote_proposals_out.clear();
|
||||
encrypted_payment_id_out = {{0}};
|
||||
if (payment_proposal_order_out)
|
||||
payment_proposal_order_out->clear();
|
||||
|
||||
// assert payment proposals numbers
|
||||
const size_t num_selfsend_proposals = selfsend_payment_proposals.size();
|
||||
const size_t num_proposals = normal_payment_proposals.size() + num_selfsend_proposals;
|
||||
CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals");
|
||||
CARROT_CHECK_AND_THROW(num_proposals <= CARROT_MAX_TX_OUTPUTS, too_many_outputs, "too many payment proposals");
|
||||
CARROT_CHECK_AND_THROW(num_selfsend_proposals, too_few_outputs, "no selfsend payment proposal");
|
||||
|
||||
// assert there is a max of 1 integrated address payment proposals
|
||||
size_t num_integrated = 0;
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
if (normal_payment_proposal.destination.payment_id != null_payment_id)
|
||||
++num_integrated;
|
||||
CARROT_CHECK_AND_THROW(num_integrated <= 1,
|
||||
bad_address_type, "only one integrated address is allowed per tx output set");
|
||||
|
||||
// assert anchor_norm != 0 for payments
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
CARROT_CHECK_AND_THROW(normal_payment_proposal.randomness != janus_anchor_t{},
|
||||
missing_randomness, "normal payment proposal has unset anchor_norm AKA randomness");
|
||||
|
||||
// assert uniqueness of randomness for each payment
|
||||
memcmp_set<janus_anchor_t> randomnesses;
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
randomnesses.insert(normal_payment_proposal.randomness);
|
||||
const bool has_unique_randomness = randomnesses.size() == normal_payment_proposals.size();
|
||||
CARROT_CHECK_AND_THROW(has_unique_randomness,
|
||||
missing_randomness, "normal payment proposals contain duplicate anchor_norm AKA randomness");
|
||||
|
||||
// for each output: (enote proposal , is ss?, payment idx )
|
||||
std::vector<std::pair<RCTOutputEnoteProposal, std::pair<bool, size_t>>> sortable_data;
|
||||
sortable_data.reserve(num_proposals);
|
||||
|
||||
// D^other_e
|
||||
std::optional<mx25519_pubkey> other_enote_ephemeral_pubkey;
|
||||
|
||||
// map destinations to output keys in order to find the indices of these outputs in tx
|
||||
std::unordered_map<crypto::public_key, crypto::public_key> output_destinations_to_keys;
|
||||
|
||||
// construct normal enotes
|
||||
for (size_t i = 0; i < normal_payment_proposals.size(); ++i)
|
||||
{
|
||||
auto &output_entry = tools::add_element(sortable_data);
|
||||
output_entry.second = {false, i};
|
||||
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
if (tx_type == cryptonote::transaction_type::RETURN) {
|
||||
get_output_proposal_return_v1(normal_payment_proposals[i],
|
||||
tx_first_key_image,
|
||||
s_view_balance_dev,
|
||||
output_entry.first,
|
||||
encrypted_payment_id);
|
||||
} else {
|
||||
get_output_proposal_normal_v1(normal_payment_proposals[i],
|
||||
tx_first_key_image,
|
||||
s_view_balance_dev,
|
||||
output_entry.first,
|
||||
encrypted_payment_id);
|
||||
}
|
||||
|
||||
// if 1 normal, and 2 self-send, set D^other_e equal to this D_e
|
||||
if (num_proposals == 2)
|
||||
other_enote_ephemeral_pubkey = output_entry.first.enote.enote_ephemeral_pubkey;
|
||||
|
||||
// set pid_enc from integrated address proposal pic_enc
|
||||
const bool is_integrated = normal_payment_proposals[i].destination.payment_id != null_payment_id;
|
||||
if (is_integrated)
|
||||
encrypted_payment_id_out = encrypted_payment_id;
|
||||
|
||||
// save the one time key for this destination
|
||||
output_destinations_to_keys.insert(
|
||||
{output_entry.first.enote.onetime_address, normal_payment_proposals[i].destination.address_spend_pubkey}
|
||||
);
|
||||
}
|
||||
|
||||
// in the case that there is no required pid_enc, set it to the provided dummy
|
||||
if (0 == num_integrated)
|
||||
{
|
||||
CARROT_CHECK_AND_THROW(dummy_encrypted_payment_id,
|
||||
missing_components, "missing encrypted payment ID: no integrated address nor provided dummy");
|
||||
encrypted_payment_id_out = *dummy_encrypted_payment_id;
|
||||
}
|
||||
|
||||
// if 0 normal, and 2 self-send, set D^other_e equal to whichever *has* a D_e
|
||||
if (num_proposals == 2 && num_selfsend_proposals == 2)
|
||||
{
|
||||
const size_t present_ephem_pk_index = selfsend_payment_proposals.at(0).enote_ephemeral_pubkey ? 0 : 1;
|
||||
other_enote_ephemeral_pubkey = selfsend_payment_proposals.at(present_ephem_pk_index).enote_ephemeral_pubkey;
|
||||
}
|
||||
|
||||
// construct selfsend enotes, preferring internal enotes over special enotes when possible
|
||||
crypto::public_key change_address;
|
||||
for (size_t i = 0; i < num_selfsend_proposals; ++i)
|
||||
{
|
||||
const CarrotPaymentProposalSelfSendV1 &selfsend_payment_proposal = selfsend_payment_proposals.at(i);
|
||||
|
||||
auto &output_entry = tools::add_element(sortable_data);
|
||||
output_entry.second = {true, i};
|
||||
|
||||
if (s_view_balance_dev != nullptr)
|
||||
{
|
||||
get_output_proposal_internal_v1(selfsend_payment_proposal,
|
||||
*s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
other_enote_ephemeral_pubkey,
|
||||
output_entry.first);
|
||||
}
|
||||
else if (k_view_dev != nullptr)
|
||||
{
|
||||
get_output_proposal_special_v1(selfsend_payment_proposal,
|
||||
*k_view_dev,
|
||||
tx_first_key_image,
|
||||
other_enote_ephemeral_pubkey,
|
||||
output_entry.first);
|
||||
}
|
||||
else // neither k_v nor s_vb device passed
|
||||
{
|
||||
CARROT_THROW(std::invalid_argument, "neither a view-balance nor view-incoming device was provided");
|
||||
}
|
||||
|
||||
// save the change one time key
|
||||
if (selfsend_payment_proposal.enote_type == CarrotEnoteType::CHANGE)
|
||||
{
|
||||
change_address = output_entry.first.enote.onetime_address;
|
||||
}
|
||||
|
||||
// save the one time key for this destination
|
||||
output_destinations_to_keys.insert(
|
||||
{output_entry.first.enote.onetime_address, selfsend_payment_proposal.destination_address_spend_pubkey}
|
||||
);
|
||||
}
|
||||
|
||||
// sort enotes by K_o
|
||||
const auto sort_output_enote_proposal = [](const auto &a, const auto &b) -> bool
|
||||
{ return a.first.enote.onetime_address < b.first.enote.onetime_address; };
|
||||
std::sort(sortable_data.begin(), sortable_data.end(), sort_output_enote_proposal);
|
||||
|
||||
// get the change index
|
||||
for (size_t i = 0; i < sortable_data.size(); ++i)
|
||||
{
|
||||
if (sortable_data[i].first.enote.onetime_address == change_address)
|
||||
{
|
||||
change_index_out = i;
|
||||
}
|
||||
|
||||
// map destinations to output indices
|
||||
const auto spend_key = output_destinations_to_keys[sortable_data[i].first.enote.onetime_address];
|
||||
payments_indices_out.insert(
|
||||
{spend_key, i}
|
||||
);
|
||||
}
|
||||
|
||||
// collect output_enote_proposals_out and payment_proposal_order_out
|
||||
output_enote_proposals_out.reserve(num_proposals);
|
||||
if (payment_proposal_order_out)
|
||||
payment_proposal_order_out->reserve(num_proposals);
|
||||
for (const auto &output_entry : sortable_data)
|
||||
{
|
||||
output_enote_proposals_out.push_back(output_entry.first);
|
||||
if (payment_proposal_order_out)
|
||||
payment_proposal_order_out->push_back(output_entry.second);
|
||||
}
|
||||
|
||||
// assert uniqueness of D_e if >2-out, shared otherwise. also check D_e is not trivial
|
||||
memcmp_set<mx25519_pubkey> ephemeral_pubkeys;
|
||||
for (const RCTOutputEnoteProposal &p : output_enote_proposals_out)
|
||||
{
|
||||
const bool trivial_enote_ephemeral_pubkey = memcmp(p.enote.enote_ephemeral_pubkey.data,
|
||||
mx25519_pubkey{}.data,
|
||||
sizeof(mx25519_pubkey)) == 0;
|
||||
CARROT_CHECK_AND_THROW(!trivial_enote_ephemeral_pubkey, missing_randomness,
|
||||
"this set contains enote ephemeral pubkeys with x=0");
|
||||
ephemeral_pubkeys.insert(p.enote.enote_ephemeral_pubkey);
|
||||
}
|
||||
const bool has_unique_ephemeral_pubkeys = ephemeral_pubkeys.size() == output_enote_proposals_out.size();
|
||||
CARROT_CHECK_AND_THROW(!(num_proposals == 2 && has_unique_ephemeral_pubkeys),
|
||||
component_out_of_order, "this 2-out set needs to share their ephemeral pubkey");
|
||||
CARROT_CHECK_AND_THROW(!(num_proposals != 2 && !has_unique_ephemeral_pubkeys),
|
||||
missing_randomness, "this >2-out set contains duplicate enote ephemeral pubkeys");
|
||||
|
||||
// assert uniqueness of K_o
|
||||
CARROT_CHECK_AND_THROW(tools::is_sorted_and_unique(sortable_data, sort_output_enote_proposal),
|
||||
component_out_of_order, "this set contains duplicate onetime addresses");
|
||||
|
||||
// assert all K_o lie in prime order subgroup
|
||||
for (const RCTOutputEnoteProposal &output_enote_proposal : output_enote_proposals_out)
|
||||
{
|
||||
CARROT_CHECK_AND_THROW(rct::isInMainSubgroup(rct::pk2rct(output_enote_proposal.enote.onetime_address)),
|
||||
invalid_point, "this set contains an invalid onetime address");
|
||||
}
|
||||
|
||||
// assert unique and non-trivial k_a
|
||||
memcmp_set<crypto::secret_key> amount_blinding_factors;
|
||||
for (const RCTOutputEnoteProposal &output_enote_proposal : output_enote_proposals_out)
|
||||
{
|
||||
CARROT_CHECK_AND_THROW(output_enote_proposal.amount_blinding_factor != crypto::null_skey,
|
||||
missing_randomness, "this set contains a trivial amount blinding factor");
|
||||
|
||||
amount_blinding_factors.insert(output_enote_proposal.amount_blinding_factor);
|
||||
}
|
||||
CARROT_CHECK_AND_THROW(amount_blinding_factors.size() == num_proposals, missing_randomness,
|
||||
"this set contains duplicate amount blinding factors");
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_coinbase_output_enotes(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const uint64_t block_index,
|
||||
std::vector<CarrotCoinbaseEnoteV1> &output_coinbase_enotes_out)
|
||||
{
|
||||
output_coinbase_enotes_out.clear();
|
||||
|
||||
// assert payment proposals numbers
|
||||
const size_t num_proposals = normal_payment_proposals.size();
|
||||
|
||||
// assert there is a max of 1 integrated address payment proposals
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
{
|
||||
const CarrotDestinationV1 &destination = normal_payment_proposal.destination;
|
||||
CARROT_CHECK_AND_THROW(destination.payment_id == null_payment_id && !destination.is_subaddress,
|
||||
bad_address_type, "get coinbase output enotes: no integrated addresses or subaddresses allowed");
|
||||
}
|
||||
|
||||
// assert anchor_norm != 0 for payments
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
CARROT_CHECK_AND_THROW(normal_payment_proposal.randomness != janus_anchor_t{},
|
||||
missing_randomness, "normal payment proposal has unset anchor_norm AKA randomness");
|
||||
|
||||
// assert uniqueness of randomness for each payment
|
||||
memcmp_set<janus_anchor_t> randomnesses;
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
randomnesses.insert(normal_payment_proposal.randomness);
|
||||
const bool has_unique_randomness = randomnesses.size() == normal_payment_proposals.size();
|
||||
CARROT_CHECK_AND_THROW(has_unique_randomness,
|
||||
missing_randomness, "normal payment proposals contain duplicate anchor_norm AKA randomness");
|
||||
|
||||
// construct normal enotes
|
||||
output_coinbase_enotes_out.reserve(num_proposals);
|
||||
for (size_t i = 0; i < normal_payment_proposals.size(); ++i)
|
||||
{
|
||||
get_coinbase_output_proposal_v1(normal_payment_proposals[i],
|
||||
block_index,
|
||||
tools::add_element(output_coinbase_enotes_out));
|
||||
}
|
||||
|
||||
// assert uniqueness and non-trivial-ness of D_e
|
||||
memcmp_set<mx25519_pubkey> ephemeral_pubkeys;
|
||||
for (const CarrotCoinbaseEnoteV1 &enote : output_coinbase_enotes_out)
|
||||
{
|
||||
const bool trivial_enote_ephemeral_pubkey = memcmp(enote.enote_ephemeral_pubkey.data,
|
||||
mx25519_pubkey{}.data,
|
||||
sizeof(mx25519_pubkey)) == 0;
|
||||
CARROT_CHECK_AND_THROW(!trivial_enote_ephemeral_pubkey,
|
||||
missing_randomness, "this set contains enote ephemeral pubkeys with x=0");
|
||||
ephemeral_pubkeys.insert(enote.enote_ephemeral_pubkey);
|
||||
}
|
||||
const bool has_unique_ephemeral_pubkeys = ephemeral_pubkeys.size() == output_coinbase_enotes_out.size();
|
||||
CARROT_CHECK_AND_THROW(has_unique_ephemeral_pubkeys,
|
||||
missing_randomness, "a coinbase enote set needs unique ephemeral pubkeys, but this set doesn't");
|
||||
|
||||
// sort enotes by K_o
|
||||
const auto sort_output_enote_proposal = [](const CarrotCoinbaseEnoteV1 &a, const CarrotCoinbaseEnoteV1 &b)
|
||||
-> bool { return a.onetime_address < b.onetime_address; };
|
||||
std::sort(output_coinbase_enotes_out.begin(), output_coinbase_enotes_out.end(), sort_output_enote_proposal);
|
||||
|
||||
// assert uniqueness of K_o
|
||||
CARROT_CHECK_AND_THROW(tools::is_sorted_and_unique(output_coinbase_enotes_out, sort_output_enote_proposal),
|
||||
component_out_of_order, "this set contains duplicate onetime addresses");
|
||||
|
||||
// assert all K_o lie in prime order subgroup
|
||||
for (const CarrotCoinbaseEnoteV1 &output_enote : output_coinbase_enotes_out)
|
||||
{
|
||||
CARROT_CHECK_AND_THROW(rct::isInMainSubgroup(rct::pk2rct(output_enote.onetime_address)),
|
||||
invalid_point, "this set contains an invalid onetime address");
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Utilities for constructing output proposal sets that adhere to Carrot rules
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "carrot_enote_types.h"
|
||||
#include "common/variant.h"
|
||||
#include "config.h"
|
||||
#include "payment_proposal.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
enum class AdditionalOutputType
|
||||
{
|
||||
PAYMENT_SHARED, // selfsend proposal with enote_type="payment" with a shared D_e
|
||||
CHANGE_SHARED, // selfsend proposal with enote_type="change" with a shared D_e
|
||||
CHANGE_UNIQUE, // selfsend proposal with enote_type="change" with a unique D_e
|
||||
DUMMY // outgoing proposal to a random address
|
||||
};
|
||||
|
||||
/**
|
||||
* brief: get_additional_output_type - get the type of the additional enote needed to finalize an output set
|
||||
* param: num_outgoing - number of outgoing transfers
|
||||
* param: num_selfsend - number of selfsend transfers
|
||||
* param: need_change_output - whether an additional change output needs to be included for balance
|
||||
* param: have_payment_type_selfsend - true if the enote set has a selfsend enote with enote_type="payment"
|
||||
* return: AdditionalOutputType if need an additional enote, else std::nullopt
|
||||
* throw: std::runtime_error if the output set is in a state where it cannot be finalized
|
||||
*/
|
||||
std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const bool need_change_output,
|
||||
const bool have_payment_type_selfsend);
|
||||
/**
|
||||
* brief: get_additional_output_proposal - get an additional output proposal to complete an output set
|
||||
* param: num_outgoing - number of outgoing transfers
|
||||
* param: num_selfsend - number of selfsend transfers
|
||||
* param: needed_change_amount - the amount of leftover change needed to be included
|
||||
* param: have_payment_type_selfsend - true if the enote set has a selfsend enote with enote_type="payment"
|
||||
* param: change_address_spend_pubkey - K^j_s of our change address
|
||||
* return: an output proposal if need an additional enote, else none
|
||||
* throw: std::runtime_error if the output set is in a state where it cannot be finalized
|
||||
*/
|
||||
tools::optional_variant<CarrotPaymentProposalV1, CarrotPaymentProposalSelfSendV1> get_additional_output_proposal(
|
||||
const size_t num_outgoing,
|
||||
const size_t num_selfsend,
|
||||
const rct::xmr_amount needed_change_amount,
|
||||
const bool have_payment_type_selfsend,
|
||||
const crypto::public_key &change_address_spend_pubkey);
|
||||
/**
|
||||
* brief: get_output_enote_proposals - convert a *finalized* set of payment proposals into output enote proposals
|
||||
* param: normal_payment_proposals -
|
||||
* param: selfsend_payment_proposals -
|
||||
* param: dummy_encrypted_payment_id - random pid_enc, required if no integrated addresses in normal payment proposals
|
||||
* param: s_view_balance_dev - pointer to view-balance device (OPTIONAL)
|
||||
* param: k_view_dev - pointer to view-incoming device (OPTIONAL)
|
||||
* param: tx_first_key_image - KI_1
|
||||
* outparam: output_enote_proposals_out -
|
||||
* outparam: encrypted_payment_id_out - pid_enc
|
||||
* outparam: payment_proposal_order_out - (is self-send, payment idx) pairs which specify order of proposals
|
||||
* converted to output enotes (OPTIONAL)
|
||||
* throw: std::runtime_error if the payment proposals do not represent a valid tx output set, or if no devices
|
||||
*
|
||||
* If s_view_balance_dev is not NULL, then the selfsend payments are converted into *internal* enotes.
|
||||
* Otherwise, if k_view_dev is not NULL, then the selfsend payments are converted into *external* enotes.
|
||||
*/
|
||||
void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const std::vector<CarrotPaymentProposalSelfSendV1> &selfsend_payment_proposals,
|
||||
const std::optional<encrypted_payment_id_t> &dummy_encrypted_payment_id,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const view_incoming_key_device *k_view_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||
cryptonote::transaction_type tx_type,
|
||||
size_t &change_index_out,
|
||||
std::unordered_map<crypto::public_key, size_t> &payments_indices_out,
|
||||
std::vector<std::pair<bool, std::size_t>> *payment_proposal_order_out = nullptr);
|
||||
/**
|
||||
* brief: get_coinbase_output_enotes - convert a *finalized* set of payment proposals into coinbase output enotes
|
||||
* param: normal_payment_proposals -
|
||||
* param: block_index -
|
||||
* outparam: output_coinbase_enotes_out -
|
||||
* throw: std::runtime_error if the payment proposals do not represent a valid tx output set, or if no devices
|
||||
*/
|
||||
void get_coinbase_output_enotes(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
|
||||
const uint64_t block_index,
|
||||
std::vector<CarrotCoinbaseEnoteV1> &output_coinbase_enotes_out);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,585 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "payment_proposal.h"
|
||||
|
||||
//local headers
|
||||
#include "int-util.h"
|
||||
#include "enote_utils.h"
|
||||
#include "exceptions.h"
|
||||
#include "misc_language.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot.pp"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static const janus_anchor_t null_anchor{{0}};
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
static auto auto_wiper(T &obj)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable<T>());
|
||||
return epee::misc_utils::create_scope_leave_handler([&]{ memwipe(&obj, sizeof(T)); });
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static crypto::secret_key get_enote_ephemeral_privkey(const janus_anchor_t randomness,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const input_context_t &input_context)
|
||||
{
|
||||
// d_e = H_n(anchor_norm, input_context, K^j_s, pid))
|
||||
crypto::secret_key enote_ephemeral_privkey;
|
||||
make_carrot_enote_ephemeral_privkey(randomness,
|
||||
input_context,
|
||||
destination.address_spend_pubkey,
|
||||
destination.payment_id,
|
||||
enote_ephemeral_privkey);
|
||||
|
||||
return enote_ephemeral_privkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static mx25519_pubkey get_enote_ephemeral_pubkey(const janus_anchor_t randomness,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const input_context_t &input_context)
|
||||
{
|
||||
// d_e = H_n(anchor_norm, input_context, K^j_s, pid))
|
||||
const crypto::secret_key enote_ephemeral_privkey{get_enote_ephemeral_privkey(randomness,
|
||||
destination,
|
||||
input_context)};
|
||||
|
||||
mx25519_pubkey enote_ephemeral_pubkey;
|
||||
if (destination.is_subaddress)
|
||||
// D_e = d_e ConvertPointE(K^j_s)
|
||||
make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey,
|
||||
destination.address_spend_pubkey,
|
||||
enote_ephemeral_pubkey);
|
||||
else
|
||||
// D_e = d_e B
|
||||
make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey,
|
||||
enote_ephemeral_pubkey);
|
||||
|
||||
return enote_ephemeral_pubkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void get_normal_proposal_ecdh_parts(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context,
|
||||
mx25519_pubkey &enote_ephemeral_pubkey_out,
|
||||
mx25519_pubkey &s_sender_receiver_unctx_out)
|
||||
{
|
||||
// 1. d_e = H_n(anchor_norm, input_context, K^j_s, pid))
|
||||
const crypto::secret_key enote_ephemeral_privkey = get_enote_ephemeral_privkey(proposal.randomness,
|
||||
proposal.destination,
|
||||
input_context);
|
||||
|
||||
// 2. make D_e
|
||||
enote_ephemeral_pubkey_out = get_enote_ephemeral_pubkey(proposal, input_context);
|
||||
|
||||
// 3. s_sr = d_e ConvertPointE(K^j_v)
|
||||
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
|
||||
proposal.destination.address_view_pubkey,
|
||||
s_sender_receiver_unctx_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void get_output_proposal_parts(const crypto::hash &s_sender_receiver,
|
||||
const crypto::public_key &destination_spend_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
const rct::xmr_amount amount,
|
||||
const CarrotEnoteType enote_type,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const bool coinbase_amount_commitment,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
rct::key &amount_commitment_out,
|
||||
crypto::public_key &onetime_address_out,
|
||||
encrypted_amount_t &encrypted_amount_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
{
|
||||
// 1. k_a = H_n(s^ctx_sr, a, K^j_s, enote_type) if !coinbase, else 1
|
||||
if (coinbase_amount_commitment)
|
||||
sc_1(to_bytes(amount_blinding_factor_out));
|
||||
else
|
||||
make_carrot_amount_blinding_factor(s_sender_receiver,
|
||||
amount,
|
||||
destination_spend_pubkey,
|
||||
enote_type,
|
||||
amount_blinding_factor_out);
|
||||
|
||||
// 2. C_a = k_a G + a H
|
||||
amount_commitment_out = rct::commit(amount, rct::sk2rct(amount_blinding_factor_out));
|
||||
|
||||
// 3. Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
|
||||
make_carrot_onetime_address(destination_spend_pubkey,
|
||||
s_sender_receiver,
|
||||
amount_commitment_out,
|
||||
onetime_address_out);
|
||||
|
||||
// 4. a_enc = a XOR m_a
|
||||
encrypted_amount_out = encrypt_carrot_amount(amount,
|
||||
s_sender_receiver,
|
||||
onetime_address_out);
|
||||
|
||||
// 5. pid_enc = pid XOR m_pid
|
||||
encrypted_payment_id_out = encrypt_legacy_payment_id(payment_id, s_sender_receiver, onetime_address_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const crypto::public_key &destination_spend_pubkey,
|
||||
const payment_id_t payment_id,
|
||||
const rct::xmr_amount amount,
|
||||
const CarrotEnoteType enote_type,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const bool coinbase_amount_commitment,
|
||||
crypto::hash &s_sender_receiver_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
rct::key &amount_commitment_out,
|
||||
crypto::public_key &onetime_address_out,
|
||||
encrypted_amount_t &encrypted_amount_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||
view_tag_t &view_tag_out,
|
||||
encrypted_return_pubkey_t return_pubkey_out)
|
||||
{
|
||||
// 1. s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver_out);
|
||||
|
||||
// 2. get other parts: k_a, C_a, Ko, a_enc, pid_enc
|
||||
get_output_proposal_parts(s_sender_receiver_out,
|
||||
destination_spend_pubkey,
|
||||
payment_id,
|
||||
amount,
|
||||
enote_type,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
coinbase_amount_commitment,
|
||||
amount_blinding_factor_out,
|
||||
amount_commitment_out,
|
||||
onetime_address_out,
|
||||
encrypted_amount_out,
|
||||
encrypted_payment_id_out);
|
||||
|
||||
// 3. vt = H_3(s_sr || input_context || Ko)
|
||||
make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, onetime_address_out, view_tag_out);
|
||||
|
||||
// 4. construct the return pubkey
|
||||
if (s_view_balance_dev != nullptr)
|
||||
make_sparc_return_pubkey(s_sender_receiver_unctx.data,
|
||||
input_context,
|
||||
s_view_balance_dev,
|
||||
onetime_address_out,
|
||||
return_pubkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b)
|
||||
{
|
||||
return a.destination == b.destination &&
|
||||
a.amount == b.amount &&
|
||||
a.randomness == b.randomness;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentProposalSelfSendV1 &b)
|
||||
{
|
||||
return a.destination_address_spend_pubkey == b.destination_address_spend_pubkey &&
|
||||
a.amount == b.amount &&
|
||||
a.enote_type == b.enote_type &&
|
||||
a.internal_message == b.internal_message &&
|
||||
0 == memcmp(&a.enote_ephemeral_pubkey, &b.enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
crypto::secret_key get_enote_ephemeral_privkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context)
|
||||
{
|
||||
return get_enote_ephemeral_privkey(proposal.randomness, proposal.destination, input_context);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
mx25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context)
|
||||
{
|
||||
return get_enote_ephemeral_pubkey(proposal.randomness, proposal.destination, input_context);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const std::uint64_t block_index,
|
||||
CarrotCoinbaseEnoteV1 &output_enote_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor,
|
||||
missing_randomness, "invalid randomness for janus anchor (zero).");
|
||||
CARROT_CHECK_AND_THROW(!proposal.destination.is_subaddress,
|
||||
bad_address_type, "subaddresses aren't allowed as destinations of coinbase outputs");
|
||||
CARROT_CHECK_AND_THROW(proposal.destination.payment_id == null_payment_id,
|
||||
bad_address_type, "integrated addresses aren't allowed as destinations of coinbase outputs");
|
||||
|
||||
// 2. coinbase input context
|
||||
const input_context_t input_context= make_carrot_input_context_coinbase(block_index);
|
||||
|
||||
// 3. make D_e and do external ECDH
|
||||
mx25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||
get_normal_proposal_ecdh_parts(proposal,
|
||||
input_context,
|
||||
output_enote_out.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
encrypted_return_pubkey_t return_pubkey_out;
|
||||
crypto::secret_key dummy_amount_blinding_factor;
|
||||
rct::key dummy_amount_commitment;
|
||||
encrypted_amount_t dummy_encrypted_amount;
|
||||
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||
get_external_output_proposal_parts(s_sender_receiver_unctx,
|
||||
proposal.destination.address_spend_pubkey,
|
||||
null_payment_id,
|
||||
proposal.amount,
|
||||
CarrotEnoteType::PAYMENT,
|
||||
output_enote_out.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
nullptr, // s_view_balance_dev
|
||||
true, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
dummy_amount_blinding_factor,
|
||||
dummy_amount_commitment,
|
||||
output_enote_out.onetime_address,
|
||||
dummy_encrypted_amount,
|
||||
dummy_encrypted_payment_id,
|
||||
output_enote_out.view_tag,
|
||||
return_pubkey_out);
|
||||
|
||||
// 5. anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.anchor_enc = encrypt_carrot_anchor(proposal.randomness,
|
||||
s_sender_receiver,
|
||||
output_enote_out.onetime_address);
|
||||
|
||||
// 6. save the amount and block index and asset type
|
||||
output_enote_out.amount = proposal.amount;
|
||||
output_enote_out.asset_type = proposal.asset_type;
|
||||
output_enote_out.block_index = block_index;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor,
|
||||
missing_randomness, "invalid randomness for janus anchor (zero).");
|
||||
|
||||
// 2. input context: input_context = "R" || KI_1
|
||||
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
|
||||
|
||||
// 3. make D_e and do external ECDH
|
||||
mx25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||
get_normal_proposal_ecdh_parts(proposal,
|
||||
input_context,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
encrypted_return_pubkey_t return_pubkey;
|
||||
get_external_output_proposal_parts(s_sender_receiver_unctx,
|
||||
proposal.destination.address_spend_pubkey,
|
||||
proposal.destination.payment_id,
|
||||
proposal.amount,
|
||||
CarrotEnoteType::PAYMENT,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_view_balance_dev,
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
encrypted_payment_id_out,
|
||||
output_enote_out.enote.view_tag,
|
||||
return_pubkey);
|
||||
|
||||
// 5. anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness,
|
||||
s_sender_receiver,
|
||||
output_enote_out.enote.onetime_address);
|
||||
|
||||
// 6. save the amount and first key image
|
||||
output_enote_out.amount = proposal.amount;
|
||||
output_enote_out.enote.asset_type = proposal.asset_type;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.enote.return_enc = return_pubkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
|
||||
RCTOutputEnoteProposal &output_enote_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
CARROT_CHECK_AND_THROW(!proposal.internal_message,
|
||||
component_out_of_order, "internal messages are only for internal selfsends, not special selfsends");
|
||||
|
||||
// 2. input context: input_context = "R" || KI_1
|
||||
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
|
||||
|
||||
// 3. D_e
|
||||
const bool missing_enote_ephemeral_pubkeys = !proposal.enote_ephemeral_pubkey && !other_enote_ephemeral_pubkey;
|
||||
const bool mismatched_enote_ephemeral_pubkeys = proposal.enote_ephemeral_pubkey &&
|
||||
other_enote_ephemeral_pubkey &&
|
||||
memcmp(&*proposal.enote_ephemeral_pubkey, &*other_enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
|
||||
CARROT_CHECK_AND_THROW(!missing_enote_ephemeral_pubkeys,
|
||||
missing_components, "no enote ephemeral pubkey provided");
|
||||
CARROT_CHECK_AND_THROW(!mismatched_enote_ephemeral_pubkeys,
|
||||
component_out_of_order, "mismatched enote ephemeral pubkeys provided");
|
||||
const mx25519_pubkey enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey.value_or(
|
||||
other_enote_ephemeral_pubkey.value_or(mx25519_pubkey{}));
|
||||
|
||||
// 4. s_sr = k_v D_e
|
||||
mx25519_pubkey s_sender_receiver_unctx; auto ecdh_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||
CARROT_CHECK_AND_THROW(k_view_dev.view_key_scalar_mult_x25519(enote_ephemeral_pubkey, s_sender_receiver_unctx),
|
||||
crypto_function_failed, "HW device failed to perform ECDH with ephemeral pubkey");
|
||||
|
||||
// 5. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
encrypted_return_pubkey_t return_pubkey_out;
|
||||
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||
get_external_output_proposal_parts(s_sender_receiver_unctx,
|
||||
proposal.destination_address_spend_pubkey,
|
||||
null_payment_id,
|
||||
proposal.amount,
|
||||
proposal.enote_type,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
nullptr,
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
dummy_encrypted_payment_id,
|
||||
output_enote_out.enote.view_tag,
|
||||
return_pubkey_out);
|
||||
|
||||
// 6. make special janus anchor: anchor_sp = H_16(D_e, input_context, Ko, k_v)
|
||||
janus_anchor_t janus_anchor_special;
|
||||
k_view_dev.make_janus_anchor_special(enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
output_enote_out.enote.onetime_address,
|
||||
janus_anchor_special);
|
||||
|
||||
// 7. encrypt special anchor: anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(janus_anchor_special,
|
||||
s_sender_receiver,
|
||||
output_enote_out.enote.onetime_address);
|
||||
|
||||
// 8. save the enote ephemeral pubkey, first tx key image, and amount
|
||||
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.enote.asset_type = "SAL1";
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
output_enote_out.amount = proposal.amount;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor,
|
||||
missing_randomness, "invalid randomness for janus anchor (zero).");
|
||||
|
||||
// 2. input context: input_context = "R" || KI_1
|
||||
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
|
||||
|
||||
// 3. make D_e and do external ECDH
|
||||
mx25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||
get_normal_proposal_ecdh_parts(proposal,
|
||||
input_context,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
encrypted_return_pubkey_t return_pubkey;
|
||||
get_external_output_proposal_parts(
|
||||
s_sender_receiver_unctx,
|
||||
proposal.destination.address_spend_pubkey,
|
||||
proposal.destination.payment_id,
|
||||
proposal.amount,
|
||||
CarrotEnoteType::PAYMENT,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_view_balance_dev,
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
encrypted_payment_id_out,
|
||||
output_enote_out.enote.view_tag,
|
||||
return_pubkey
|
||||
);
|
||||
|
||||
// 5. Override the values that change because of the enote onetime address (K_o) changing
|
||||
// i.e. {K_o, vt, m_a, a_enc, m_anchor, anchor_enc, m_pid, pid_enc}
|
||||
|
||||
// Override the onetime address
|
||||
output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey), rct::pk2rct(proposal.destination.address_view_pubkey)));
|
||||
|
||||
// Recalculate the view tag : vt = H_3(s_sr || input_context || Ksra)
|
||||
make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, output_enote_out.enote.onetime_address, output_enote_out.enote.view_tag);
|
||||
|
||||
// Recalculate a_enc = BytesToInt64(a) XOR m_a
|
||||
output_enote_out.enote.amount_enc = encrypt_carrot_amount(proposal.amount, s_sender_receiver, output_enote_out.enote.onetime_address);
|
||||
|
||||
// Recalculate anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness, s_sender_receiver, output_enote_out.enote.onetime_address);
|
||||
|
||||
// Recalculate the pid_enc = pid XOR m_pid
|
||||
encrypted_payment_id_out = encrypt_legacy_payment_id(proposal.destination.payment_id, s_sender_receiver, output_enote_out.enote.onetime_address);
|
||||
|
||||
// 6. save the amount & first key image & asset type
|
||||
output_enote_out.amount = proposal.amount;
|
||||
output_enote_out.enote.asset_type = proposal.asset_type;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
// disable returning an already returned tx.
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
|
||||
RCTOutputEnoteProposal &output_enote_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
// @TODO
|
||||
|
||||
// 2. input_context = "R" || KI_1
|
||||
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
|
||||
|
||||
// 3. D_e
|
||||
const bool missing_enote_ephemeral_pubkeys = !proposal.enote_ephemeral_pubkey && !other_enote_ephemeral_pubkey;
|
||||
const bool mismatched_enote_ephemeral_pubkeys = proposal.enote_ephemeral_pubkey &&
|
||||
other_enote_ephemeral_pubkey &&
|
||||
memcmp(&*proposal.enote_ephemeral_pubkey, &*other_enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
|
||||
CARROT_CHECK_AND_THROW(!missing_enote_ephemeral_pubkeys,
|
||||
missing_components, "no enote ephemeral pubkey provided");
|
||||
CARROT_CHECK_AND_THROW(!mismatched_enote_ephemeral_pubkeys,
|
||||
component_out_of_order, "mismatched enote ephemeral pubkeys provided");
|
||||
const mx25519_pubkey enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey.value_or(
|
||||
other_enote_ephemeral_pubkey.value_or(mx25519_pubkey{}));
|
||||
|
||||
// 4. s^ctx_sr = H_32(s_vb, D_e, input_context)
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
s_view_balance_dev.make_internal_sender_receiver_secret(enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver);
|
||||
|
||||
// 5. build the output enote address pieces
|
||||
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||
get_output_proposal_parts(s_sender_receiver,
|
||||
proposal.destination_address_spend_pubkey,
|
||||
null_payment_id,
|
||||
proposal.amount,
|
||||
proposal.enote_type,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
false, // coinbase_amount_commitment
|
||||
output_enote_out.amount_blinding_factor,
|
||||
output_enote_out.enote.amount_commitment,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
dummy_encrypted_payment_id);
|
||||
|
||||
// 6. vt = H_3(s_vb || input_context || Ko)
|
||||
s_view_balance_dev.make_internal_view_tag(input_context,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.view_tag);
|
||||
|
||||
// 7. anchor = given message OR 0s, if not available
|
||||
const janus_anchor_t anchor = proposal.internal_message.value_or(janus_anchor_t{});
|
||||
|
||||
// 8. encrypt anchor: anchor_enc = anchor XOR m_anchor
|
||||
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(anchor,
|
||||
s_sender_receiver,
|
||||
output_enote_out.enote.onetime_address);
|
||||
|
||||
// 9. save the enote ephemeral pubkey, first tx key image, and amount
|
||||
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
|
||||
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
|
||||
output_enote_out.enote.asset_type = "SAL1";
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
output_enote_out.amount = proposal.amount;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
|
||||
const bool has_payment_id,
|
||||
const rct::xmr_amount amount,
|
||||
const std::size_t num_random_memo_elements)
|
||||
{
|
||||
CarrotPaymentProposalV1 temp;
|
||||
|
||||
if (is_subaddress)
|
||||
temp.destination = gen_carrot_subaddress_v1();
|
||||
else if (has_payment_id)
|
||||
temp.destination = gen_carrot_integrated_address_v1();
|
||||
else
|
||||
temp.destination = gen_carrot_main_address_v1();
|
||||
|
||||
temp.amount = amount;
|
||||
temp.randomness = gen_janus_anchor();
|
||||
|
||||
return temp;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// A 'payment proposal' is a proposal to make an enote sending funds to a Carrot address.
|
||||
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "carrot_enote_types.h"
|
||||
#include "destination.h"
|
||||
#include "device.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "common/variant.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <optional>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
////
|
||||
// CarrotPaymentProposalV1
|
||||
// - for creating an output proposal to send an amount to someone
|
||||
///
|
||||
struct CarrotPaymentProposalV1 final
|
||||
{
|
||||
/// user address
|
||||
CarrotDestinationV1 destination;
|
||||
/// b
|
||||
rct::xmr_amount amount;
|
||||
/// asset type
|
||||
std::string asset_type;
|
||||
/// anchor_norm: secret 16-byte randomness for Janus anchor
|
||||
janus_anchor_t randomness;
|
||||
};
|
||||
|
||||
////
|
||||
// CarrotPaymentProposalSelfSendV1
|
||||
// - for creating an output proposal to send an change to yourself
|
||||
///
|
||||
struct CarrotPaymentProposalSelfSendV1 final
|
||||
{
|
||||
/// one of our own address spend pubkeys: K^j_s
|
||||
crypto::public_key destination_address_spend_pubkey;
|
||||
/// a
|
||||
rct::xmr_amount amount;
|
||||
|
||||
/// enote_type
|
||||
CarrotEnoteType enote_type;
|
||||
/// enote ephemeral pubkey: D_e
|
||||
std::optional<mx25519_pubkey> enote_ephemeral_pubkey;
|
||||
/// anchor: arbitrary, pre-encrypted message for _internal_ selfsends
|
||||
std::optional<janus_anchor_t> internal_message;
|
||||
};
|
||||
|
||||
struct RCTOutputEnoteProposal
|
||||
{
|
||||
CarrotEnoteV1 enote;
|
||||
|
||||
// we need this opening information to make amount range proofs
|
||||
rct::xmr_amount amount;
|
||||
crypto::secret_key amount_blinding_factor;
|
||||
};
|
||||
|
||||
/// equality operators
|
||||
bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b);
|
||||
/// equality operators
|
||||
bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentProposalSelfSendV1 &b);
|
||||
|
||||
/**
|
||||
* brief: get_enote_ephemeral_privkey - get the proposal's enote ephemeral privkey d_e
|
||||
* param: proposal -
|
||||
* param: input_context -
|
||||
* return: d_e
|
||||
*/
|
||||
crypto::secret_key get_enote_ephemeral_privkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context);
|
||||
/**
|
||||
* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey D_e
|
||||
* param: proposal -
|
||||
* param: input_context -
|
||||
* return: D_e
|
||||
*/
|
||||
mx25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||
const input_context_t &input_context);
|
||||
/**
|
||||
* brief: get_coinbase_output_proposal_v1 - convert the carrot proposal to a coinbase output proposal
|
||||
* param: proposal -
|
||||
* param: block_index - index of the coinbase tx's block
|
||||
* outparam: output_enote_out -
|
||||
* outparam: partial_memo_out -
|
||||
*/
|
||||
void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const std::uint64_t block_index,
|
||||
CarrotCoinbaseEnoteV1 &output_enote_out);
|
||||
/**
|
||||
* brief: get_output_proposal_normal_v1 - convert the carrot proposal to an output proposal
|
||||
* param: proposal -
|
||||
* param: tx_first_key_image -
|
||||
* outparam: output_enote_out -
|
||||
* outparam: encrypted_payment_id_out - pid_enc
|
||||
*/
|
||||
void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
/**
|
||||
* brief: get_output_proposal_return_v1 - convert the carrot proposal to an output proposal
|
||||
* param: proposal -
|
||||
* param: tx_first_key_image -
|
||||
* param: k_view_dev -
|
||||
* outparam: output_enote_out -
|
||||
* outparam: encrypted_payment_id_out - pid_enc
|
||||
*/
|
||||
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out);
|
||||
/**
|
||||
* brief: get_output_proposal_special_v1 - convert the carrot proposal to an output proposal (external selfsend)
|
||||
* param: proposal -
|
||||
* param: k_view_dev -
|
||||
* param: tx_first_key_image -
|
||||
* param: other_enote_ephemeral_pubkey -
|
||||
* outparam: output_enote_out -
|
||||
*/
|
||||
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
|
||||
RCTOutputEnoteProposal &output_enote_out);
|
||||
/**
|
||||
* brief: get_output_proposal_internal_v1 - convert the carrot proposal to an output proposal (internal)
|
||||
* param: proposal -
|
||||
* param: s_view_balance_dev -
|
||||
* param: account_spend_pubkey -
|
||||
* param: tx_first_key_image -
|
||||
* param: other_enote_ephemeral_pubkey -
|
||||
* outparam: output_enote_out -
|
||||
*/
|
||||
void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
|
||||
RCTOutputEnoteProposal &output_enote_out);
|
||||
/**
|
||||
* brief: gen_jamtis_payment_proposal_v1 - generate a random proposal
|
||||
* param: is_subaddress - whether to generate a proposal to subaddress
|
||||
* param: has_payment_id - true to generate non-zero payment ID, false for null payment ID
|
||||
* param: amount -
|
||||
* return: a random proposal
|
||||
*/
|
||||
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
|
||||
const bool has_payment_id,
|
||||
const rct::xmr_amount amount);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,403 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Utilities for scanning carrot enotes
|
||||
|
||||
//paired header
|
||||
#include "scan.h"
|
||||
|
||||
//local headers
|
||||
#include "destination.h"
|
||||
#include "enote_utils.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "scan_unsafe.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool is_main_address_spend_pubkey(const crypto::public_key &address_spend_pubkey,
|
||||
const epee::span<const crypto::public_key> main_address_spend_pubkeys)
|
||||
{
|
||||
for (const crypto::public_key &main_address_spend_pubkey : main_address_spend_pubkeys)
|
||||
if (address_spend_pubkey == main_address_spend_pubkey)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static crypto::secret_key make_enote_ephemeral_privkey_sender(const janus_anchor_t &anchor_norm,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const input_context_t &input_context)
|
||||
{
|
||||
// d_e = H_n(anchor_norm, input_context, K^j_s, pid))
|
||||
crypto::secret_key enote_ephemeral_privkey;
|
||||
make_carrot_enote_ephemeral_privkey(anchor_norm,
|
||||
input_context,
|
||||
destination.address_spend_pubkey,
|
||||
destination.payment_id,
|
||||
enote_ephemeral_privkey);
|
||||
return enote_ephemeral_privkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool try_scan_carrot_coinbase_enote_checked(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const epee::span<const crypto::public_key> main_addresss_spend_pubkeys,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out)
|
||||
{
|
||||
// s^ctx_sr, k^g_o, k^g_t, K^j_s, pid, anchor
|
||||
janus_anchor_t nominal_janus_anchor;
|
||||
if (!try_scan_carrot_coinbase_enote_no_janus(enote,
|
||||
s_sender_receiver_unctx,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
address_spend_pubkey_out,
|
||||
nominal_janus_anchor))
|
||||
return false;
|
||||
|
||||
if (!is_main_address_spend_pubkey(address_spend_pubkey_out, main_addresss_spend_pubkeys))
|
||||
return false;
|
||||
|
||||
return verify_carrot_normal_janus_protection(nominal_janus_anchor,
|
||||
make_carrot_input_context_coinbase(enote.block_index),
|
||||
address_spend_pubkey_out,
|
||||
/*is_subaddress=*/false,
|
||||
null_payment_id,
|
||||
enote.enote_ephemeral_pubkey);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool try_scan_carrot_enote_external_normal_checked(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
payment_id_t &payment_id_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
janus_anchor_t &nominal_janus_anchor_out,
|
||||
bool &verified_normal_janus)
|
||||
{
|
||||
if (!try_scan_carrot_enote_external_no_janus(enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
address_spend_pubkey_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out,
|
||||
payment_id_out,
|
||||
enote_type_out,
|
||||
nominal_janus_anchor_out))
|
||||
return false;
|
||||
|
||||
verified_normal_janus = verify_carrot_normal_janus_protection(
|
||||
make_carrot_input_context(enote.tx_first_key_image),
|
||||
address_spend_pubkey_out,
|
||||
!is_main_address_spend_pubkey(address_spend_pubkey_out, main_address_spend_pubkeys),
|
||||
enote.enote_ephemeral_pubkey,
|
||||
nominal_janus_anchor_out,
|
||||
payment_id_out);
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool make_carrot_uncontextualized_shared_key_receiver(
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
mx25519_pubkey &s_sender_receiver_unctx_out)
|
||||
{
|
||||
return k_view_dev.view_key_scalar_mult_x25519(enote_ephemeral_pubkey, s_sender_receiver_unctx_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_coinbase_enote_sender(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const janus_anchor_t &anchor_norm,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out)
|
||||
{
|
||||
const crypto::secret_key enote_ephemeral_privkey = make_enote_ephemeral_privkey_sender(anchor_norm,
|
||||
destination,
|
||||
make_carrot_input_context_coinbase(enote.block_index));
|
||||
|
||||
return try_scan_carrot_coinbase_enote_sender(enote,
|
||||
destination,
|
||||
enote_ephemeral_privkey,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_coinbase_enote_sender(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const crypto::secret_key &enote_ephemeral_privkey,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out)
|
||||
{
|
||||
// s_sr = d_e ConvertPointE(K^j_v)
|
||||
mx25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
|
||||
destination.address_view_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
crypto::public_key dummy_main_address_spend_pubkey;
|
||||
if (!try_scan_carrot_coinbase_enote_checked(enote,
|
||||
s_sender_receiver_unctx,
|
||||
{&destination.address_spend_pubkey, 1},
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
dummy_main_address_spend_pubkey))
|
||||
return false;
|
||||
|
||||
// this should've already been checked, but just for good measure...
|
||||
return dummy_main_address_spend_pubkey == destination.address_spend_pubkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_coinbase_enote_receiver(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &main_address_spend_pubkey_out)
|
||||
{
|
||||
return try_scan_carrot_coinbase_enote_checked(enote,
|
||||
s_sender_receiver_unctx,
|
||||
main_address_spend_pubkeys,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
main_address_spend_pubkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_coinbase_enote_receiver(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const crypto::public_key &main_address_spend_pubkey,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out)
|
||||
{
|
||||
crypto::public_key dummy_main_address_spend_pubkey;
|
||||
return try_scan_carrot_coinbase_enote_receiver(
|
||||
enote,
|
||||
s_sender_receiver_unctx,
|
||||
{&main_address_spend_pubkey, 1},
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
dummy_main_address_spend_pubkey);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const janus_anchor_t &anchor_norm,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid)
|
||||
{
|
||||
const crypto::secret_key enote_ephemeral_privkey = make_enote_ephemeral_privkey_sender(anchor_norm,
|
||||
destination,
|
||||
make_carrot_input_context(enote.tx_first_key_image));
|
||||
|
||||
return try_scan_carrot_enote_external_sender(enote,
|
||||
encrypted_payment_id,
|
||||
destination,
|
||||
enote_ephemeral_privkey,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out,
|
||||
enote_type_out,
|
||||
check_pid);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const crypto::secret_key &enote_ephemeral_privkey,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid)
|
||||
{
|
||||
// s_sr = d_e ConvertPointE(K^j_v)
|
||||
mx25519_pubkey s_sender_receiver_unctx;
|
||||
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
|
||||
destination.address_view_pubkey,
|
||||
s_sender_receiver_unctx);
|
||||
|
||||
return try_scan_carrot_enote_external_sender(enote,
|
||||
encrypted_payment_id,
|
||||
destination,
|
||||
s_sender_receiver_unctx,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out,
|
||||
enote_type_out,
|
||||
check_pid);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid)
|
||||
{
|
||||
crypto::public_key recovered_address_spend_pubkey;
|
||||
payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
janus_anchor_t dummy_janus_anchor;
|
||||
bool verified_normal_janus = false;
|
||||
if (!try_scan_carrot_enote_external_normal_checked(enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
{&destination.address_spend_pubkey, 1},
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
recovered_address_spend_pubkey,
|
||||
amount_out,
|
||||
amount_blinding_factor_out,
|
||||
recovered_payment_id,
|
||||
recovered_enote_type,
|
||||
dummy_janus_anchor,
|
||||
verified_normal_janus))
|
||||
return false;
|
||||
else if (!verified_normal_janus)
|
||||
return false;
|
||||
else if (recovered_address_spend_pubkey != destination.address_spend_pubkey)
|
||||
return false;
|
||||
else if (check_pid && recovered_payment_id != destination.payment_id)
|
||||
return false;
|
||||
else if (recovered_enote_type != CarrotEnoteType::PAYMENT)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
payment_id_t &payment_id_out,
|
||||
CarrotEnoteType &enote_type_out)
|
||||
{
|
||||
janus_anchor_t nominal_janus_anchor;
|
||||
bool verified_normal_janus = false;
|
||||
if (!try_scan_carrot_enote_external_normal_checked(enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
main_address_spend_pubkeys,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
address_spend_pubkey_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out,
|
||||
payment_id_out,
|
||||
enote_type_out,
|
||||
nominal_janus_anchor,
|
||||
verified_normal_janus))
|
||||
return false;
|
||||
|
||||
if (!verified_normal_janus && !verify_carrot_special_janus_protection(enote.tx_first_key_image,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
enote.onetime_address,
|
||||
k_view_dev,
|
||||
nominal_janus_anchor))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
janus_anchor_t &internal_message_out)
|
||||
{
|
||||
// input_context
|
||||
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
|
||||
|
||||
// vt = H_3(s_sr || input_context || Ko)
|
||||
view_tag_t nominal_view_tag;
|
||||
s_view_balance_dev.make_internal_view_tag(input_context, enote.onetime_address, nominal_view_tag);
|
||||
|
||||
// test view tag
|
||||
if (nominal_view_tag != enote.view_tag)
|
||||
return false;
|
||||
|
||||
// s^ctx_sr = H_32(s_vb, D_e, input_context)
|
||||
crypto::hash s_sender_receiver;
|
||||
s_view_balance_dev.make_internal_sender_receiver_secret(enote.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver);
|
||||
|
||||
return try_scan_carrot_enote_internal_burnt(enote,
|
||||
s_sender_receiver,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
address_spend_pubkey_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out,
|
||||
enote_type_out,
|
||||
internal_message_out);
|
||||
|
||||
// janus protection checks are not needed for internal scans
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Utilities for scanning carrot enotes
|
||||
|
||||
/**
|
||||
* The "scan process" mentioned in this file is described in Section 8.1 "Enote Scan" of the Carrot specification:
|
||||
* https://github.com/jeffro256/carrot/blob/master/carrot.md#81-enote-scan
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "carrot_enote_types.h"
|
||||
#include "device.h"
|
||||
#include "span.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <optional>
|
||||
|
||||
//forward declarations
|
||||
namespace carrot { struct CarrotDestinationV1; }
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
/**
|
||||
* brief: make_carrot_uncontextualized_shared_key_receiver - perform the receiver-side ECDH exchange for Carrot enotes
|
||||
* s_sr = k_v D_e
|
||||
* param: k_view_dev -
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* outparam: s_sender_receiver_unctx_out - s_sr
|
||||
* return: true if successful, false if a failure occurred in point decompression
|
||||
*/
|
||||
bool make_carrot_uncontextualized_shared_key_receiver(
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
mx25519_pubkey &s_sender_receiver_unctx_out);
|
||||
/**
|
||||
* brief: try_scan_carrot_coinbase_enote_[sender/receiver] - attempt scan process on coinbase enote
|
||||
* param: enote -
|
||||
* param: destination - (is_subaddress, K^j_s, K^j_v, pid)
|
||||
* param: anchor_norm - anchor_norm
|
||||
* param: enote_ephemeral_privkey - d_e
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: main_address_spend_pubkeys - {K^0_s, ...}
|
||||
* outparam: sender_extension_g_out - k^g_o
|
||||
* outparam: sender_extension_g_out - k^t_o
|
||||
* outparam: main_address_spend_pubkey_out - K^0_s, which will be one of main_address_spend_pubkeys
|
||||
* return: true iff the scan process succeeded
|
||||
*/
|
||||
bool try_scan_carrot_coinbase_enote_sender(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const janus_anchor_t &anchor_norm,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out);
|
||||
bool try_scan_carrot_coinbase_enote_sender(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const crypto::secret_key &enote_ephemeral_privkey,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out);
|
||||
bool try_scan_carrot_coinbase_enote_receiver(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &main_address_spend_pubkey_out);
|
||||
bool try_scan_carrot_coinbase_enote_receiver(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const crypto::public_key &main_address_spend_pubkey,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out);
|
||||
/**
|
||||
* brief: try_scan_carrot_enote_external_[sender/receiver] - attempt scan process on external enote
|
||||
* param: enote -
|
||||
* param: encrypted_payment_id - pid_enc
|
||||
* param: destination - (is_subaddress, K^j_s, K^j_v, pid)
|
||||
* param: anchor_norm - anchor_norm
|
||||
* param: enote_ephemeral_privkey - d_e
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: main_address_spend_pubkeys - {K^0_s, ...}
|
||||
* param: k_view_dev -
|
||||
* outparam: sender_extension_g_out - k^g_o
|
||||
* outparam: sender_extension_g_out - k^t_o
|
||||
* outparam: address_spend_pubkey_out - K^j_s
|
||||
* outparam: amount_out - a
|
||||
* outparam: amount_blinding_factor_out - k_a
|
||||
* outparam: payment_id_out - pid
|
||||
* outparam: enote_type_out - enote_type
|
||||
* return: true iff the scan process succeeded
|
||||
*
|
||||
* warning: the _sender overloads cannot check for Janus protection on special enotes
|
||||
*/
|
||||
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const janus_anchor_t &anchor_norm,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid = true);
|
||||
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const crypto::secret_key &enote_ephemeral_privkey,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid = true);
|
||||
bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const CarrotDestinationV1 &destination,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid = true);
|
||||
bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
payment_id_t &payment_id_out,
|
||||
CarrotEnoteType &enote_type_out);
|
||||
/**
|
||||
* brief: try_scan_carrot_enote_internal_receiver - attempt scan process on internal enote
|
||||
* param: enote -
|
||||
* param: s_view_balance_dev -
|
||||
* outparam: sender_extension_g_out - k^g_o
|
||||
* outparam: sender_extension_g_out - k^t_o
|
||||
* outparam: address_spend_pubkey_out - K^j_s
|
||||
* outparam: amount_out - a
|
||||
* outparam: amount_blinding_factor_out - k_a
|
||||
* outparam: enote_type_out - enote_type
|
||||
* outparam: internal_message_out - anchor'
|
||||
* return: true iff the scan process succeeded
|
||||
*/
|
||||
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
janus_anchor_t &internal_message_out);
|
||||
//! @TODO: try_scan_carrot_enote_internal_sender(): can't validate burning w/o passing s_sr = s_vb
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Utilities for scanning carrot enotes
|
||||
|
||||
//paired header
|
||||
#include "scan_unsafe.h"
|
||||
|
||||
//local headers
|
||||
#include "enote_utils.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static void scan_carrot_dest_info(const crypto::public_key &onetime_address,
|
||||
const rct::key &amount_commitment,
|
||||
const encrypted_janus_anchor_t &encrypted_janus_anchor,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
payment_id_t &nominal_payment_id_out,
|
||||
janus_anchor_t &nominal_janus_anchor_out)
|
||||
{
|
||||
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
||||
make_carrot_onetime_address_extension_g(s_sender_receiver,
|
||||
amount_commitment,
|
||||
sender_extension_g_out);
|
||||
|
||||
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
||||
make_carrot_onetime_address_extension_t(s_sender_receiver,
|
||||
amount_commitment,
|
||||
sender_extension_t_out);
|
||||
|
||||
// K^j_s = Ko - K^o_ext = Ko - (k^o_g G + k^o_t T)
|
||||
recover_address_spend_pubkey(onetime_address,
|
||||
s_sender_receiver,
|
||||
amount_commitment,
|
||||
address_spend_pubkey_out);
|
||||
|
||||
// pid = pid_enc XOR m_pid, if applicable
|
||||
if (encrypted_payment_id)
|
||||
nominal_payment_id_out = decrypt_legacy_payment_id(*encrypted_payment_id, s_sender_receiver, onetime_address);
|
||||
else
|
||||
nominal_payment_id_out = null_payment_id;
|
||||
|
||||
// anchor = anchor_enc XOR m_anchor
|
||||
nominal_janus_anchor_out = decrypt_carrot_anchor(encrypted_janus_anchor,
|
||||
s_sender_receiver,
|
||||
onetime_address);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool try_scan_carrot_external_noamount(const crypto::public_key &onetime_address,
|
||||
const lazy_amount_commitment_t &lazy_amount_commitment,
|
||||
const encrypted_janus_anchor_t &encrypted_janus_anchor,
|
||||
const view_tag_t view_tag,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const input_context_t &input_context,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
crypto::hash &s_sender_receiver_out,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
payment_id_t &nominal_payment_id_out,
|
||||
janus_anchor_t &janus_anchor_out)
|
||||
{
|
||||
// if vt' != vt, then FAIL
|
||||
if (!test_carrot_view_tag(s_sender_receiver_unctx.data, input_context, onetime_address, view_tag))
|
||||
return false;
|
||||
|
||||
// s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver_out);
|
||||
|
||||
// get C_a
|
||||
const rct::key amount_commitment = calculate_amount_commitment(lazy_amount_commitment);
|
||||
|
||||
// k^g_o, k^t_o, K^j_s', pid', anchor'
|
||||
scan_carrot_dest_info(onetime_address,
|
||||
amount_commitment,
|
||||
encrypted_janus_anchor,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_out,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
address_spend_pubkey_out,
|
||||
nominal_payment_id_out,
|
||||
janus_anchor_out);
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_coinbase_enote_no_janus(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &nominal_address_spend_pubkey_out,
|
||||
janus_anchor_t &nominal_janus_anchor_out)
|
||||
{
|
||||
// input_context
|
||||
const input_context_t input_context = make_carrot_input_context_coinbase(enote.block_index);
|
||||
|
||||
// s^ctx_sr, k^g_o, k^g_t, K^j_s, pid, anchor
|
||||
crypto::hash dummy_s_sender_receiver;
|
||||
payment_id_t dummy_payment_id;
|
||||
if (!try_scan_carrot_external_noamount(enote.onetime_address,
|
||||
enote.amount,
|
||||
enote.anchor_enc,
|
||||
enote.view_tag,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
std::nullopt,
|
||||
input_context,
|
||||
s_sender_receiver_unctx,
|
||||
dummy_s_sender_receiver,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
nominal_address_spend_pubkey_out,
|
||||
dummy_payment_id,
|
||||
nominal_janus_anchor_out))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_external_no_janus(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
payment_id_t &nominal_payment_id_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
janus_anchor_t &nominal_janus_anchor_out)
|
||||
{
|
||||
// input_context
|
||||
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
|
||||
|
||||
// s^ctx_sr, k^g_o, k^g_t, K^j_s, pid, and Janus verification
|
||||
crypto::hash s_sender_receiver;
|
||||
if (!try_scan_carrot_external_noamount(enote.onetime_address,
|
||||
enote.amount_commitment,
|
||||
enote.anchor_enc,
|
||||
enote.view_tag,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
encrypted_payment_id,
|
||||
input_context,
|
||||
s_sender_receiver_unctx,
|
||||
s_sender_receiver,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
address_spend_pubkey_out,
|
||||
nominal_payment_id_out,
|
||||
nominal_janus_anchor_out))
|
||||
return false;
|
||||
|
||||
// enote_type, a, z
|
||||
return try_get_carrot_amount(s_sender_receiver,
|
||||
enote.amount_enc,
|
||||
enote.onetime_address,
|
||||
address_spend_pubkey_out,
|
||||
enote.amount_commitment,
|
||||
enote_type_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_internal_burnt(const CarrotEnoteV1 &enote,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
janus_anchor_t &internal_message_out)
|
||||
{
|
||||
// k^g_o, k^t_o, K^j_s', pid', anchor'
|
||||
payment_id_t dummy_payment_id;
|
||||
scan_carrot_dest_info(enote.onetime_address,
|
||||
enote.amount_commitment,
|
||||
enote.anchor_enc,
|
||||
std::nullopt,
|
||||
s_sender_receiver,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
address_spend_pubkey_out,
|
||||
dummy_payment_id,
|
||||
internal_message_out);
|
||||
|
||||
// enote_type, a, z
|
||||
return try_get_carrot_amount(s_sender_receiver,
|
||||
enote.amount_enc,
|
||||
enote.onetime_address,
|
||||
address_spend_pubkey_out,
|
||||
enote.amount_commitment,
|
||||
enote_type_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool verify_carrot_normal_janus_protection(const input_context_t &input_context,
|
||||
const crypto::public_key &nominal_address_spend_pubkey,
|
||||
const bool is_subaddress,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const janus_anchor_t &nominal_janus_anchor,
|
||||
payment_id_t &nominal_payment_id_inout)
|
||||
{
|
||||
// if can recompute D_e with pid', then PASS
|
||||
if (verify_carrot_normal_janus_protection(nominal_janus_anchor,
|
||||
input_context,
|
||||
nominal_address_spend_pubkey,
|
||||
is_subaddress,
|
||||
nominal_payment_id_inout,
|
||||
enote_ephemeral_pubkey))
|
||||
return true;
|
||||
|
||||
// if can recompute D_e with null pid, then PASS
|
||||
nominal_payment_id_inout = null_payment_id;
|
||||
return verify_carrot_normal_janus_protection(nominal_janus_anchor,
|
||||
input_context,
|
||||
nominal_address_spend_pubkey,
|
||||
is_subaddress,
|
||||
nominal_payment_id_inout,
|
||||
enote_ephemeral_pubkey);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool verify_carrot_special_janus_protection(const crypto::key_image &tx_first_key_image,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const crypto::public_key &onetime_address,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const janus_anchor_t &nominal_janus_anchor)
|
||||
{
|
||||
// input_context = "R" || KI_1
|
||||
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
|
||||
|
||||
// anchor_sp = H_16(D_e, input_context, Ko, k_v)
|
||||
janus_anchor_t expected_special_anchor;
|
||||
k_view_dev.make_janus_anchor_special(enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
onetime_address,
|
||||
expected_special_anchor);
|
||||
|
||||
// attempt special janus check: anchor_sp ?= anchor'
|
||||
return expected_special_anchor == nominal_janus_anchor;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//! @file Low-level helper utilities for scanning carrot enotes
|
||||
|
||||
/**
|
||||
* These functions should not be used directly unless you know what you're doing. Using these
|
||||
* functions incorrectly can result in Janus attacks, incorrect PIDs, and view tag skips. For
|
||||
* safer high-level scanning functions, see the scan.h header.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "carrot_enote_types.h"
|
||||
#include "device.h"
|
||||
#include "lazy_amount_commitment.h"
|
||||
#include "span.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <optional>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
/**
|
||||
* brief: try_scan_carrot_coinbase_enote_no_janus - attempt scan process on coinbase enote w/o Janus protection
|
||||
* param: enote -
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* outparam: sender_extension_g_out - k^g_o
|
||||
* outparam: sender_extension_g_out - k^t_o
|
||||
* outparam: nominal_address_spend_pubkey - K^j_s'
|
||||
* outparam: nominal_janus_anchor_out - anchor'
|
||||
* return: true iff the scan process (w/o Janus checks) succeeded
|
||||
*/
|
||||
bool try_scan_carrot_coinbase_enote_no_janus(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &nominal_address_spend_pubkey_out,
|
||||
janus_anchor_t &nominal_janus_anchor_out);
|
||||
/**
|
||||
* brief: try_scan_carrot_enote_external_no_janus - attempt scan process on external enote,
|
||||
* w/o Janus protection nor correct PID
|
||||
* param: enote -
|
||||
* param: encrypted_payment_id - pid_enc
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: k_view_dev -
|
||||
* outparam: sender_extension_g_out - k^g_o
|
||||
* outparam: sender_extension_g_out - k^t_o
|
||||
* outparam: address_spend_pubkey_out - K^j_s
|
||||
* outparam: amount_out - a
|
||||
* outparam: amount_blinding_factor_out - k_a
|
||||
* outparam: payment_id_out - pid
|
||||
* outparam: enote_type_out - enote_type
|
||||
* outparam: nominal_janus_anchor_out - anchor'
|
||||
* return: true iff the scan process (w/o Janus or PID checks) succeeded
|
||||
*/
|
||||
bool try_scan_carrot_enote_external_no_janus(const CarrotEnoteV1 &enote,
|
||||
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
payment_id_t &nominal_payment_id_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
janus_anchor_t &nominal_janus_anchor_out);
|
||||
/**
|
||||
* brief: try_scan_carrot_enote_internal_burnt - attempt scan process on internal enote w/o
|
||||
* regular burning bug check or view tag check
|
||||
* param: enote -
|
||||
* param: s_sender_receiver - s^ctx_sr
|
||||
* outparam: sender_extension_g_out - k^g_o
|
||||
* outparam: sender_extension_g_out - k^t_o
|
||||
* outparam: address_spend_pubkey_out - K^j_s
|
||||
* outparam: amount_out - a
|
||||
* outparam: amount_blinding_factor_out - k_a
|
||||
* outparam: enote_type_out - enote_type
|
||||
* outparam: internal_message_out - anchor'
|
||||
* return: true iff the scan process (w/o view tag check) succeeded
|
||||
*/
|
||||
bool try_scan_carrot_enote_internal_burnt(const CarrotEnoteV1 &enote,
|
||||
const crypto::hash &s_sender_receiver,
|
||||
crypto::secret_key &sender_extension_g_out,
|
||||
crypto::secret_key &sender_extension_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
janus_anchor_t &internal_message_out);
|
||||
/**
|
||||
* brief: verify_carrot_normal_janus_protection - verify that scanned normal enote is not attempting a
|
||||
* Janus attack, and check pid
|
||||
* param: input_context - input_context
|
||||
* param: nominal_address_spend_pubkey - K^j_s'
|
||||
* param: is_subaddress - true iff K^j_s' corresponds to a subaddress
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* param: nominal_janus_anchor - anchor'
|
||||
* outparam: nominal_payment_id_inout - takes pid', sets to nullpid if not associated to this enote
|
||||
* return: true iff it is computationally intractable for a sender to succeed at a Janus attack
|
||||
*/
|
||||
bool verify_carrot_normal_janus_protection(const input_context_t &input_context,
|
||||
const crypto::public_key &nominal_address_spend_pubkey,
|
||||
const bool is_subaddress,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const janus_anchor_t &nominal_janus_anchor,
|
||||
payment_id_t &nominal_payment_id_inout);
|
||||
/**
|
||||
* brief: verify_carrot_special_janus_protection - verify that scanned special enote is not attempting a Janus attack
|
||||
* param: tx_first_key_image - KI_1
|
||||
* param: enote_ephemeral_pubkey - D_e
|
||||
* param: onetime_address - K_o
|
||||
* param: k_view_dev -
|
||||
* param: nominal_janus_anchor - anchor'
|
||||
* return: true iff it is computationally intractable for a sender to succeed at a Janus attack
|
||||
*/
|
||||
bool verify_carrot_special_janus_protection(const crypto::key_image &tx_first_key_image,
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const crypto::public_key &onetime_address,
|
||||
const view_incoming_key_device &k_view_dev,
|
||||
const janus_anchor_t &nominal_janus_anchor);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2024, Salvium (author: SRCG)
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// A 'return address' is a proposal to facilitate pseudonymous transfers of received funds
|
||||
// back to the originating wallet, as per the previously-published "Return Address Scheme"
|
||||
// published by knaccc at https://github.com/monero-project/research-lab/issues/53
|
||||
// This code is designed to implement the F point management and zero-knowledge proofs
|
||||
// required to support the "Return Address Scheme" in Carrot.
|
||||
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
|
||||
|
||||
//paired header
|
||||
#include "sparc.h"
|
||||
|
||||
//local headers
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/generators.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "address_utils.h"
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace carrot {
|
||||
|
||||
// Optimized function to hash a vector of keys into a scalar
|
||||
rct::key hash_to_scalar(std::vector<rct::key>& keys) {
|
||||
|
||||
// Create a fixed-size buffer large enough to hold all keys and a domain separator
|
||||
const char* domain_separator = "sparc_spend_authority_proof";
|
||||
const size_t domain_separator_length = strlen(domain_separator);
|
||||
size_t total_size = keys.size() * sizeof(rct::key) + domain_separator_length;
|
||||
std::vector<uint8_t> data(total_size);
|
||||
|
||||
// Copy the keys into the buffer
|
||||
size_t offset = 0;
|
||||
for (const auto& key : keys) {
|
||||
std::memcpy(data.data() + offset, key.bytes, sizeof(rct::key));
|
||||
offset += sizeof(rct::key);
|
||||
}
|
||||
|
||||
// Add the domain separator "sparc_spend_authority_proof" at the _end_ of the buffer
|
||||
std::memcpy(data.data() + offset, domain_separator, domain_separator_length);
|
||||
|
||||
// Hash the concatenated data into a fixed-size hash
|
||||
rct::key hash_output;
|
||||
keccak((const uint8_t *)data.data(), total_size, hash_output.bytes, sizeof(rct::key));
|
||||
sc_reduce32(hash_output.bytes); // Reduce to valid scalar
|
||||
|
||||
return hash_output;
|
||||
}
|
||||
|
||||
// Function to generate the zero-knowledge proof
|
||||
void make_sparc_spend_authority_proof(const rct::key &x, const rct::key &y, const rct::key &K_o, rct::zk_proof &proof_out) {
|
||||
|
||||
// Step 1: Generate random scalars r_x and r_y
|
||||
rct::key r_x = rct::skGen(); // Random scalar for G commitment
|
||||
rct::key r_y = rct::skGen(); // Random scalar for T commitment
|
||||
|
||||
// Step 2: Calculate commitment by summing terms for G and T
|
||||
rct::key commitment;
|
||||
rct::key commitment_G = rct::scalarmultBase(r_x); // r_x * G
|
||||
rct::key commitment_T = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), r_y); // r_y * T (using T generator)
|
||||
commitment = rct::addKeys(commitment_G, commitment_T); // R = r_xG + r_yT
|
||||
|
||||
// Step 3: Calculate the challenge scalar
|
||||
std::vector<rct::key> keys{commitment, K_o};
|
||||
rct::key challenge = rct::hash_to_scalar(keys); // c = H(R || K_o)
|
||||
|
||||
// Step 4: Calculate responses
|
||||
rct::key response_x;
|
||||
sc_muladd(response_x.bytes, challenge.bytes, x.bytes, r_x.bytes); // z_x = r_x + c * x
|
||||
sc_reduce32(response_x.bytes);
|
||||
rct::key response_y;
|
||||
sc_muladd(response_y.bytes, challenge.bytes, y.bytes, r_y.bytes); // z_y = r_y + c * y
|
||||
sc_reduce32(response_y.bytes);
|
||||
|
||||
// Step 5: Construct and return the proof
|
||||
proof_out.R = commitment;
|
||||
proof_out.z1 = response_x;
|
||||
proof_out.z2 = response_y;
|
||||
|
||||
// Step 6: Sanity checks
|
||||
rct::key resC = rct::addKeys(commitment, rct::scalarmultKey(K_o, challenge));
|
||||
rct::key resZ = rct::addKeys(rct::scalarmultBase(response_x), rct::scalarmultKey(rct::pk2rct(crypto::get_T()), response_y));
|
||||
if (!rct::equalKeys(resZ, resC)) assert(false);
|
||||
}
|
||||
|
||||
// Function to verify the zero-knowledge proof
|
||||
bool verify_sparc_spend_authority_proof(const rct::zk_proof &proof, const rct::key &K_o) {
|
||||
|
||||
// Step 1: calculate the challenge
|
||||
std::vector<rct::key> keys{proof.R, K_o};
|
||||
rct::key recomputed_challenge = rct::hash_to_scalar(keys);
|
||||
|
||||
// Step 2: Calculate z_xG + x_yT
|
||||
rct::key z_xG = rct::scalarmultBase(proof.z1); // z1 * G
|
||||
rct::key x_yT = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), proof.z2); // z2 * T
|
||||
rct::key resZ = rct::addKeys(z_xG, x_yT); // z_xG + x_yT
|
||||
|
||||
// Step 3: Calculate R + cK_o
|
||||
rct::key resC = rct::addKeys(proof.R, rct::scalarmultKey(K_o, recomputed_challenge)); // R + cK_o
|
||||
|
||||
// Step 4: verify z_xG + x_yT ?= R + cK_o
|
||||
return rct::equalKeys(resZ, resC);
|
||||
}
|
||||
|
||||
} // namespace carrot
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2024, Salvium (author: SRCG)
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// A 'return address' is a proposal to facilitate pseudonymous transfers of received funds
|
||||
// back to the originating wallet, as per the previously-published "Return Address Scheme"
|
||||
// published by knaccc at https://github.com/monero-project/research-lab/issues/53
|
||||
// This code is designed to implement the F point management and zero-knowledge proofs
|
||||
// required to support the "Return Address Scheme" in Carrot.
|
||||
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
/**
|
||||
* brief: make_sparc_spend_authority_proof - generate the zero-knowledge proof for spend authority for SPARC output pubkey
|
||||
* param: x - G-term for the given output key
|
||||
* param: y - T-term for the given output key
|
||||
* param: K_o - output key to generate spend authority proof for
|
||||
* outparam: proof_out - the complete spend authority proof
|
||||
*/
|
||||
void make_sparc_spend_authority_proof(const rct::key &x, const rct::key &y, const rct::key &K_o, rct::zk_proof &proof_out);
|
||||
|
||||
/**
|
||||
* brief: verify_sparc_spend_authority_proof - verify the zero-knowledge proof for spend authority for SPARC output pubkey
|
||||
* param: proof - the zero-knowledge proof to verify
|
||||
* param: K_o - output key to verify the spend authority proof for
|
||||
* return: true if proof is valid, false otherwise
|
||||
*/
|
||||
bool verify_sparc_spend_authority_proof(const rct::zk_proof &proof, const rct::key &K_o);
|
||||
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Transcript class for assembling data that needs to be hashed.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "int-util.h"
|
||||
#include "memwipe.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
//forward declarations
|
||||
|
||||
|
||||
namespace sp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename... Ts>
|
||||
constexpr size_t sizeof_sum()
|
||||
{
|
||||
return (sizeof(Ts) + ...);
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr size_t sizeof_sum<>()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
} //namespace detail
|
||||
|
||||
////
|
||||
// SpFixedTranscript
|
||||
// - build a transcript of a fixed bytesize and input types, enforced at compile time
|
||||
// - written to be the simplest correct transcript of data possible
|
||||
// - requires domain separators at compile-time as well
|
||||
// - ensures that no two transcripts with different domain separators will ever be equal
|
||||
// - does not use dynamic allocation
|
||||
// - unsigned integers are added to the transcript in little-endian form
|
||||
// - signed integers are not allowed
|
||||
// - domain separator is length-prefixed with a single unsigned byte at the beginning
|
||||
// - passed domain separator can be null terminated or not, null bytes and after will be dropped
|
||||
///
|
||||
template <std::size_t N, const unsigned char domain_sep[N], typename... Ts>
|
||||
class SpFixedTranscript final
|
||||
{
|
||||
public:
|
||||
//constructors
|
||||
/// normal constructor
|
||||
SpFixedTranscript(const Ts&... args)
|
||||
{
|
||||
// copy domain separator length prefix
|
||||
m_transcript[0] = static_cast<unsigned char>(domain_sep_size());
|
||||
|
||||
// copy domain separator
|
||||
memcpy(m_transcript + 1, domain_sep, domain_sep_size());
|
||||
|
||||
// copy types into buffer
|
||||
append<1 + domain_sep_size()>(args...);
|
||||
}
|
||||
|
||||
//overloaded operators
|
||||
/// disable copy/move
|
||||
SpFixedTranscript& operator=(const SpFixedTranscript&) = delete;
|
||||
SpFixedTranscript& operator=(SpFixedTranscript&&) = delete;
|
||||
|
||||
//member functions
|
||||
constexpr const void* data() const noexcept { return m_transcript; }
|
||||
|
||||
static constexpr std::size_t size()
|
||||
{
|
||||
return 1 + domain_sep_size() + detail::sizeof_sum<Ts...>();
|
||||
}
|
||||
|
||||
//destructors
|
||||
~SpFixedTranscript()
|
||||
{
|
||||
// wipe the buffer on leave in case it contains sensitive data
|
||||
memwipe(m_transcript, sizeof(m_transcript));
|
||||
}
|
||||
|
||||
private:
|
||||
//member functions
|
||||
template <std::size_t>
|
||||
void append() {}
|
||||
|
||||
template <std::size_t offset, typename U0, typename... Us>
|
||||
void append(const U0 &arg0, const Us&... args)
|
||||
{
|
||||
// write current argument to buffer
|
||||
write<offset>(arg0);
|
||||
|
||||
// call append for next argument
|
||||
static constexpr size_t new_offset = offset + sizeof(arg0);
|
||||
append<new_offset>(args...);
|
||||
}
|
||||
|
||||
template <std::size_t offset, typename U>
|
||||
void write(const U &val)
|
||||
{
|
||||
static_assert(std::has_unique_object_representations_v<U>);
|
||||
static_assert(std::is_standard_layout_v<U>);
|
||||
static_assert(!std::is_signed_v<U> || std::is_same_v<U, char>);
|
||||
static_assert(alignof(U) == 1);
|
||||
static_assert(!std::is_pointer_v<U>);
|
||||
|
||||
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||
}
|
||||
|
||||
template <std::size_t offset>
|
||||
void write(std::uint16_t val)
|
||||
{
|
||||
val = SWAP16LE(val);
|
||||
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||
}
|
||||
|
||||
template <std::size_t offset>
|
||||
void write(std::uint32_t val)
|
||||
{
|
||||
val = SWAP32LE(val);
|
||||
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||
}
|
||||
|
||||
template <std::size_t offset>
|
||||
void write(std::uint64_t val)
|
||||
{
|
||||
val = SWAP64LE(val);
|
||||
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static constexpr std::size_t domain_sep_size()
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
if (domain_sep[i] == '\0')
|
||||
return i;
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
static_assert(domain_sep_size() <= 255, "domain separator must be less than 256 characters long");
|
||||
|
||||
//member variables
|
||||
/// the transcript buffer
|
||||
unsigned char m_transcript[size()];
|
||||
};
|
||||
|
||||
template <const auto & domain_sep, typename... Ts>
|
||||
auto make_fixed_transcript(const Ts&... args)
|
||||
{
|
||||
return SpFixedTranscript<std::size(domain_sep), domain_sep, Ts...>(args...);
|
||||
}
|
||||
|
||||
} //namespace sp
|
||||
@@ -0,0 +1,56 @@
|
||||
# Copyright (c) 2024, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(carrot_impl_sources
|
||||
account.cpp
|
||||
address_device_ram_borrowed.cpp
|
||||
address_utils_compat.cpp
|
||||
format_utils.cpp
|
||||
input_selection.cpp
|
||||
tx_builder_outputs.cpp
|
||||
tx_proposal_utils.cpp
|
||||
)
|
||||
|
||||
monero_find_all_headers(carrot_impl_headers, "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
monero_add_library(carrot_impl
|
||||
${carrot_impl_sources}
|
||||
${carrot_impl_headers})
|
||||
|
||||
target_link_libraries(carrot_impl
|
||||
PUBLIC
|
||||
carrot_core
|
||||
cryptonote_format_utils_basic
|
||||
PRIVATE
|
||||
${EXTRA_LIBRARIES})
|
||||
|
||||
target_include_directories(carrot_impl
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
PRIVATE
|
||||
${Boost_INCLUDE_DIRS})
|
||||
@@ -0,0 +1,355 @@
|
||||
// Copyright (c) 2014-2022, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "account.h"
|
||||
#include "warnings.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/generators.h"
|
||||
extern "C"
|
||||
{
|
||||
#include "crypto/keccak.h"
|
||||
}
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot_impl_account"
|
||||
|
||||
using namespace std;
|
||||
|
||||
DISABLE_VS_WARNINGS(4244 4345)
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
CarrotDestinationV1 carrot_and_legacy_account::cryptonote_address(const payment_id_t payment_id,
|
||||
const AddressDeriveType derive_type) const
|
||||
{
|
||||
CarrotDestinationV1 addr;
|
||||
switch (resolve_derive_type(derive_type))
|
||||
{
|
||||
case AddressDeriveType::Carrot:
|
||||
make_carrot_integrated_address_v1(get_keys().m_carrot_main_address.m_spend_public_key,
|
||||
get_keys().m_carrot_main_address.m_view_public_key,
|
||||
payment_id,
|
||||
addr);
|
||||
break;
|
||||
case AddressDeriveType::PreCarrot:
|
||||
make_carrot_integrated_address_v1(get_keys().m_account_address.m_spend_public_key,
|
||||
get_keys().m_account_address.m_view_public_key,
|
||||
payment_id,
|
||||
addr);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("address derive type not recognized");
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
CarrotDestinationV1 carrot_and_legacy_account::subaddress(const subaddress_index_extended &subaddress_index) const
|
||||
{
|
||||
if (!subaddress_index.index.is_subaddress())
|
||||
return cryptonote_address(null_payment_id, subaddress_index.derive_type);
|
||||
|
||||
const cryptonote::account_keys &keys = get_keys();
|
||||
|
||||
CarrotDestinationV1 addr;
|
||||
cryptonote::account_public_address cnaddr;
|
||||
switch (resolve_derive_type(subaddress_index.derive_type))
|
||||
{
|
||||
case AddressDeriveType::Carrot:
|
||||
make_carrot_subaddress_v1(keys.m_carrot_account_address.m_spend_public_key,
|
||||
keys.m_carrot_account_address.m_view_public_key,
|
||||
s_generate_address_dev,
|
||||
subaddress_index.index.major,
|
||||
subaddress_index.index.minor,
|
||||
addr);
|
||||
break;
|
||||
case AddressDeriveType::PreCarrot:
|
||||
cnaddr =
|
||||
keys.m_device->get_subaddress(keys, {subaddress_index.index.major, subaddress_index.index.minor});
|
||||
addr = CarrotDestinationV1{
|
||||
.address_spend_pubkey = cnaddr.m_spend_public_key,
|
||||
.address_view_pubkey = cnaddr.m_view_public_key,
|
||||
.is_subaddress = true,
|
||||
.payment_id = null_payment_id
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("address derive type not recognized");
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::get_subaddress_map_cn() const
|
||||
{
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> res;
|
||||
for (const auto &p : subaddress_map)
|
||||
res.emplace(p.first, cryptonote::subaddress_index{p.second.index.major, p.second.index.minor});
|
||||
CHECK_AND_ASSERT_THROW_MES(!res.empty(),
|
||||
"carrot_and_legacy_account::get_subaddress_map: subaddress map does not contain subaddresses");
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
std::unordered_map<crypto::public_key, subaddress_index_extended>& carrot_and_legacy_account::get_subaddress_map_ref() {
|
||||
return subaddress_map;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_extended &subaddress_index,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out) const
|
||||
{
|
||||
const bool is_subaddress = subaddress_index.index.is_subaddress();
|
||||
const uint32_t major_index = subaddress_index.index.major;
|
||||
const uint32_t minor_index = subaddress_index.index.minor;
|
||||
|
||||
const cryptonote::account_keys &keys = get_keys();
|
||||
|
||||
crypto::secret_key address_index_generator;
|
||||
crypto::secret_key subaddress_scalar;
|
||||
crypto::secret_key subaddress_extension;
|
||||
|
||||
switch (resolve_derive_type(subaddress_index.derive_type))
|
||||
{
|
||||
case AddressDeriveType::Carrot:
|
||||
// s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||
make_carrot_index_extension_generator(keys.s_generate_address, major_index, minor_index, address_index_generator);
|
||||
|
||||
if (is_subaddress)
|
||||
{
|
||||
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||
make_carrot_subaddress_scalar(keys.m_carrot_account_address.m_spend_public_key, address_index_generator, major_index, minor_index, subaddress_scalar);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k^j_subscal = 1
|
||||
sc_1(to_bytes(subaddress_scalar));
|
||||
}
|
||||
|
||||
// k^g_a = k_gi * k^j_subscal
|
||||
sc_mul(to_bytes(address_privkey_g_out), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
|
||||
|
||||
// k^t_a = k_ps * k^j_subscal
|
||||
sc_mul(to_bytes(address_privkey_t_out), to_bytes(keys.k_prove_spend), to_bytes(subaddress_scalar));
|
||||
break;
|
||||
case AddressDeriveType::PreCarrot:
|
||||
// m = Hn(k_v || j_major || j_minor) if subaddress else 0
|
||||
subaddress_extension = is_subaddress
|
||||
? keys.get_device().get_subaddress_secret_key(keys.m_view_secret_key, {major_index, minor_index})
|
||||
: crypto::null_skey;
|
||||
|
||||
// k^g_a = k_s + m
|
||||
sc_add(to_bytes(address_privkey_g_out), to_bytes(keys.m_spend_secret_key), to_bytes(subaddress_extension));
|
||||
|
||||
// k^t_a = 0
|
||||
memset(address_privkey_t_out.data, 0, sizeof(address_privkey_t_out));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("address derive type not recognized");
|
||||
}
|
||||
|
||||
// perform sanity check
|
||||
const CarrotDestinationV1 addr = subaddress(subaddress_index);
|
||||
rct::key recomputed_address_spend_pubkey;
|
||||
rct::addKeys2(recomputed_address_spend_pubkey,
|
||||
rct::sk2rct(address_privkey_g_out),
|
||||
rct::sk2rct(address_privkey_t_out),
|
||||
rct::pk2rct(crypto::get_T()));
|
||||
CHECK_AND_ASSERT_THROW_MES(rct::rct2pk(recomputed_address_spend_pubkey) == addr.address_spend_pubkey,
|
||||
"carrot and legacy account: opening for subaddress: failed sanity check");
|
||||
address_spend_pubkey_out = addr.address_spend_pubkey;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool carrot_and_legacy_account::try_searching_for_opening_for_subaddress(const crypto::public_key &address_spend_pubkey,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out) const
|
||||
{
|
||||
const auto it = subaddress_map.find(address_spend_pubkey);
|
||||
if (it == subaddress_map.cend())
|
||||
return false;
|
||||
|
||||
crypto::public_key recomputed_address_spend_pubkey;
|
||||
opening_for_subaddress(it->second,
|
||||
address_privkey_g_out,
|
||||
address_privkey_t_out,
|
||||
recomputed_address_spend_pubkey);
|
||||
|
||||
return address_spend_pubkey == recomputed_address_spend_pubkey;
|
||||
}
|
||||
bool carrot_and_legacy_account::try_searching_for_opening_for_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
crypto::secret_key &x_out,
|
||||
crypto::secret_key &y_out) const
|
||||
{
|
||||
// k^{j,g}_addr, k^{j,t}_addr
|
||||
crypto::secret_key address_privkey_g;
|
||||
crypto::secret_key address_privkey_t;
|
||||
if (!try_searching_for_opening_for_subaddress(address_spend_pubkey,
|
||||
address_privkey_g,
|
||||
address_privkey_t))
|
||||
return false;
|
||||
|
||||
// x = k^{j,g}_addr + k^g_o
|
||||
sc_add(to_bytes(x_out), to_bytes(address_privkey_g), to_bytes(sender_extension_g));
|
||||
|
||||
// y = k^{j,t}_addr + k^t_o
|
||||
sc_add(to_bytes(y_out), to_bytes(address_privkey_t), to_bytes(sender_extension_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool carrot_and_legacy_account::can_open_fcmp_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const
|
||||
{
|
||||
crypto::secret_key x, y;
|
||||
if (!try_searching_for_opening_for_onetime_address(address_spend_pubkey,
|
||||
sender_extension_g,
|
||||
sender_extension_t,
|
||||
x,
|
||||
y))
|
||||
return false;
|
||||
|
||||
// O' = x G + y T
|
||||
rct::key recomputed_onetime_address;
|
||||
rct::addKeys2(recomputed_onetime_address,
|
||||
rct::sk2rct(x),
|
||||
rct::sk2rct(y),
|
||||
rct::pk2rct(crypto::get_T()));
|
||||
|
||||
// O' ?= O
|
||||
return 0 == memcmp(&recomputed_onetime_address, &onetime_address, sizeof(rct::key));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(can_open_fcmp_onetime_address(
|
||||
address_spend_pubkey,
|
||||
sender_extension_g,
|
||||
sender_extension_t,
|
||||
onetime_address),
|
||||
"carrot and legacy account: derive key image: cannot open onetime address");
|
||||
|
||||
crypto::secret_key x, y;
|
||||
try_searching_for_opening_for_onetime_address(address_spend_pubkey,
|
||||
sender_extension_g,
|
||||
sender_extension_t,
|
||||
x,
|
||||
y);
|
||||
|
||||
crypto::key_image L;
|
||||
crypto::generate_key_image(onetime_address, x, L);
|
||||
return L;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::generate_subaddress_map()
|
||||
{
|
||||
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
|
||||
|
||||
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
|
||||
{
|
||||
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
|
||||
{
|
||||
for (const AddressDeriveType derive_type : derive_types)
|
||||
{
|
||||
const subaddress_index_extended subaddr_index{{major_index, minor_index}, derive_type, false};
|
||||
const CarrotDestinationV1 addr = subaddress(subaddr_index);
|
||||
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
crypto::secret_key carrot_and_legacy_account::generate(
|
||||
const crypto::secret_key& recovery_key,
|
||||
bool recover,
|
||||
bool two_random,
|
||||
const AddressDeriveType default_derive_type)
|
||||
{
|
||||
// generate the legacy keys
|
||||
crypto::secret_key retval = cryptonote::account_base::generate(recovery_key, recover, two_random);
|
||||
|
||||
// generate carrot keys
|
||||
set_carrot_keys(default_derive_type);
|
||||
|
||||
return retval;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
|
||||
{
|
||||
// top level keys
|
||||
m_keys.s_master = m_keys.m_spend_secret_key;
|
||||
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
|
||||
make_carrot_viewbalance_secret(m_keys.s_master, m_keys.s_view_balance);
|
||||
|
||||
// view balance keys
|
||||
make_carrot_viewincoming_key(m_keys.s_view_balance, m_keys.k_view_incoming);
|
||||
make_carrot_generateimage_key(m_keys.s_view_balance, m_keys.k_generate_image);
|
||||
make_carrot_generateaddress_secret(m_keys.s_view_balance, m_keys.s_generate_address);
|
||||
|
||||
// carrot account address
|
||||
make_carrot_spend_pubkey(m_keys.k_generate_image, m_keys.k_prove_spend, m_keys.m_carrot_account_address.m_spend_public_key);
|
||||
k_view_incoming_dev.view_key_scalar_mult_ed25519(
|
||||
m_keys.m_carrot_account_address.m_spend_public_key,
|
||||
m_keys.m_carrot_account_address.m_view_public_key
|
||||
);
|
||||
|
||||
// carrot main wallet address
|
||||
m_keys.m_carrot_main_address.m_spend_public_key = m_keys.m_carrot_account_address.m_spend_public_key;
|
||||
k_view_incoming_dev.view_key_scalar_mult_ed25519(
|
||||
crypto::get_G(),
|
||||
m_keys.m_carrot_main_address.m_view_public_key
|
||||
);
|
||||
|
||||
this->default_derive_type = default_derive_type;
|
||||
generate_subaddress_map();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map_cn)
|
||||
{
|
||||
for (const auto &p : subaddress_map_cn)
|
||||
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
AddressDeriveType carrot_and_legacy_account::resolve_derive_type(const AddressDeriveType derive_type) const
|
||||
{
|
||||
return derive_type == AddressDeriveType::Auto ? default_derive_type : derive_type;
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "carrot_core/account_secrets.h"
|
||||
#include "carrot_core/address_utils.h"
|
||||
#include "carrot_core/destination.h"
|
||||
#include "carrot_core/device_ram_borrowed.h"
|
||||
#include "carrot_core/enote_utils.h"
|
||||
#include "carrot_impl/subaddress_index.h"
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/subaddress_index.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static constexpr std::uint32_t MAX_SUBADDRESS_MAJOR_INDEX = 5;
|
||||
static constexpr std::uint32_t MAX_SUBADDRESS_MINOR_INDEX = 20;
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
class carrot_and_legacy_account : public cryptonote::account_base
|
||||
{
|
||||
public:
|
||||
view_incoming_key_ram_borrowed_device k_view_incoming_dev;
|
||||
view_balance_secret_ram_borrowed_device s_view_balance_dev;
|
||||
generate_address_secret_ram_borrowed_device s_generate_address_dev;
|
||||
|
||||
AddressDeriveType default_derive_type;
|
||||
|
||||
carrot_and_legacy_account(): k_view_incoming_dev(get_keys().k_view_incoming),
|
||||
s_view_balance_dev(get_keys().s_view_balance),
|
||||
s_generate_address_dev(get_keys().s_generate_address)
|
||||
{}
|
||||
|
||||
carrot_and_legacy_account(const carrot_and_legacy_account &k) = delete;
|
||||
carrot_and_legacy_account(carrot_and_legacy_account&&) = delete;
|
||||
|
||||
carrot_and_legacy_account& operator=(const carrot_and_legacy_account&) = delete;
|
||||
carrot_and_legacy_account& operator=(carrot_and_legacy_account&&) = delete;
|
||||
|
||||
CarrotDestinationV1 cryptonote_address(const payment_id_t payment_id = null_payment_id,
|
||||
const AddressDeriveType derive_type = AddressDeriveType::Auto) const;
|
||||
|
||||
CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const;
|
||||
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> get_subaddress_map_cn() const;
|
||||
std::unordered_map<crypto::public_key, subaddress_index_extended>& get_subaddress_map_ref();
|
||||
|
||||
// brief: opening_for_subaddress - return (k^g_a, k^t_a) for j s.t. K^j_s = (k^g_a * G + k^t_a * T)
|
||||
void opening_for_subaddress(const subaddress_index_extended &subaddress_index,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out) const;
|
||||
|
||||
bool try_searching_for_opening_for_subaddress(const crypto::public_key &address_spend_pubkey,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out) const;
|
||||
|
||||
bool try_searching_for_opening_for_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
crypto::secret_key &x_out,
|
||||
crypto::secret_key &y_out) const;
|
||||
|
||||
bool can_open_fcmp_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
crypto::key_image derive_key_image(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
void generate_subaddress_map();
|
||||
|
||||
crypto::secret_key generate(
|
||||
const crypto::secret_key& recovery_key = crypto::secret_key(),
|
||||
bool recover = false,
|
||||
bool two_random = false,
|
||||
const AddressDeriveType default_derive_type = AddressDeriveType::Carrot
|
||||
);
|
||||
|
||||
void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
|
||||
void insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map);
|
||||
|
||||
AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<crypto::public_key, subaddress_index_extended> subaddress_map;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "carrot_core/device.h"
|
||||
#include "subaddress_index.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
static constexpr const int E_UNSUPPORTED_ADDRESS_TYPE = 1;
|
||||
|
||||
struct cryptonote_hierarchy_address_device: virtual public view_incoming_key_device
|
||||
{
|
||||
/**
|
||||
* brief: get_cryptonote_account_spend_pubkey - K_s
|
||||
* return: K_s
|
||||
*/
|
||||
virtual crypto::public_key get_cryptonote_account_spend_pubkey() const = 0;
|
||||
|
||||
/**
|
||||
* brief: make_legacy_subaddress_extension - k^j_subext
|
||||
* k^j_subext = ScalarDeriveLegacy("SubAddr" || IntToBytes8(0) || k_v || IntToBytes32(j_major) || IntToBytes32(j_minor))
|
||||
* param: major_index - j_major
|
||||
* param: minor_index - j_minor
|
||||
* outparam: legacy_subaddress_extension_out - k^j_subext
|
||||
* throw: std::invalid_argument if major_index == minor_index == 0
|
||||
*/
|
||||
virtual void make_legacy_subaddress_extension(const std::uint32_t major_index,
|
||||
const std::uint32_t minor_index,
|
||||
crypto::secret_key &legacy_subaddress_extension_out) const = 0;
|
||||
};
|
||||
|
||||
struct carrot_hierarchy_address_device: public generate_address_secret_device
|
||||
{
|
||||
/**
|
||||
* brief: get_carrot_account_spend_pubkey - K_s = K^0_s
|
||||
* return: K_s = K^0_s
|
||||
*/
|
||||
virtual crypto::public_key get_carrot_account_spend_pubkey() const = 0;
|
||||
|
||||
/**
|
||||
* brief: get_carrot_account_view_pubkey - K_v
|
||||
* return: K_v
|
||||
*/
|
||||
virtual crypto::public_key get_carrot_account_view_pubkey() const = 0;
|
||||
|
||||
/**
|
||||
* brief: get_carrot_main_address_view_pubkey - K^0_v
|
||||
* return: K^0_v
|
||||
*/
|
||||
virtual crypto::public_key get_carrot_main_address_view_pubkey() const = 0;
|
||||
};
|
||||
|
||||
struct hybrid_hierarchy_address_device
|
||||
{
|
||||
virtual bool supports_address_derivation_type(AddressDeriveType derive_type) const = 0;
|
||||
|
||||
virtual cryptonote_hierarchy_address_device &access_cryptonote_hierarchy_device() const = 0;
|
||||
|
||||
virtual carrot_hierarchy_address_device &access_carrot_hierarchy_device() const = 0;
|
||||
|
||||
virtual ~hybrid_hierarchy_address_device() = default;
|
||||
};
|
||||
|
||||
struct interactive_address_device
|
||||
{
|
||||
virtual void request_explicit_on_device_address_confirmation(
|
||||
const subaddress_index_extended &index) = 0;
|
||||
|
||||
virtual ~interactive_address_device() = default;
|
||||
};
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2024, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//paired header
|
||||
#include "address_device_ram_borrowed.h"
|
||||
|
||||
//local headers
|
||||
#include "address_utils_compat.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot_impl"
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void cryptonote_hierarchy_address_device_ram_borrowed::make_legacy_subaddress_extension(
|
||||
const std::uint32_t major_index,
|
||||
const std::uint32_t minor_index,
|
||||
crypto::secret_key &legacy_subaddress_extension_out) const
|
||||
{
|
||||
return carrot::make_legacy_subaddress_extension(m_k_view_incoming,
|
||||
major_index,
|
||||
minor_index,
|
||||
legacy_subaddress_extension_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
//local headers
|
||||
#include "address_device.h"
|
||||
#include "carrot_core/device_ram_borrowed.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
//standard headers
|
||||
|
||||
//forward declarations
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
struct cryptonote_hierarchy_address_device_ram_borrowed:
|
||||
public cryptonote_hierarchy_address_device,
|
||||
public view_incoming_key_ram_borrowed_device
|
||||
{
|
||||
cryptonote_hierarchy_address_device_ram_borrowed(
|
||||
const crypto::public_key &cryptonote_account_spend_pubkey,
|
||||
const crypto::secret_key &k_view_incoming):
|
||||
view_incoming_key_ram_borrowed_device(k_view_incoming),
|
||||
m_cryptonote_account_spend_pubkey(cryptonote_account_spend_pubkey)
|
||||
{}
|
||||
|
||||
crypto::public_key get_cryptonote_account_spend_pubkey() const override
|
||||
{
|
||||
return m_cryptonote_account_spend_pubkey;
|
||||
}
|
||||
|
||||
void make_legacy_subaddress_extension(const std::uint32_t major_index,
|
||||
const std::uint32_t minor_index,
|
||||
crypto::secret_key &legacy_subaddress_extension_out) const override;
|
||||
|
||||
protected:
|
||||
const crypto::public_key &m_cryptonote_account_spend_pubkey;
|
||||
};
|
||||
} //namespace carrot
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user