Compare commits
315 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 | |||
| bc7db51f03 | |||
| 6889321361 | |||
| 3cb473132e | |||
| b298f542a6 | |||
| eb9f799b8b | |||
| 8bf35db67e | |||
| aeef1a6677 | |||
| ee586a3fca | |||
| 19be3a6146 | |||
| 64a69268fe | |||
| 7312652540 | |||
| 8cd587ec54 | |||
| 62c43a4ed2 | |||
| 0180051a8c | |||
| a7c1ba652b | |||
| 2a4d08b67f | |||
| 3fc5ea3543 | |||
| 3b72dc0555 | |||
| b48c86afe0 | |||
| 0f88d91fa0 | |||
| f4612357b2 | |||
| e9a2b6fbb7 | |||
| ca2069facc | |||
| 114297d784 | |||
| 6368aee05f | |||
| 8599cdf95b | |||
| d39f2f180e | |||
| c763febe98 | |||
| cef01372b1 | |||
| e15dbb5db2 | |||
| 82d706aacb | |||
| f3522764a1 | |||
| 0448a6bf9a | |||
| 2ce22c2508 | |||
| 1334bac45a | |||
| e909e3eef1 | |||
| 4b594142ca | |||
| 29e435bd39 | |||
| 4abde92c1a | |||
| dd23331df9 | |||
| 7d2025bc19 | |||
| a01422a5e0 | |||
| e68f7f46ed | |||
| b87c243da1 | |||
| 49fd907073 | |||
| 8c999520d1 | |||
| 945bdc72e7 | |||
| c6d843b6f5 | |||
| 8f5111aeda | |||
| b285ec550d | |||
| afc2518ef7 | |||
| a9406b158d | |||
| e45643e157 | |||
| 1df18ca6a4 | |||
| 91b2ec275a | |||
| e45fdb863c | |||
| cb2f9d3f75 | |||
| 59025bb27b | |||
| 204c6fc778 | |||
| 78c2b4b1fb | |||
| fcac456902 | |||
| 1824a34a68 | |||
| 1786c628bf | |||
| 6b8df3cee5 | |||
| 884db2b499 | |||
| ce7a1bdd96 | |||
| 57cbb146db | |||
| 85c856411e | |||
| 6fefb49da0 | |||
| c5c828516b | |||
| 218911d9fc | |||
| da3ef2511d | |||
| 8b2b039036 | |||
| 4d1c84fcaf | |||
| bb91b01cf7 | |||
| 7f0eda828a | |||
| d3f15211d7 | |||
| b828703bbd | |||
| 2fa8ef97ef | |||
| acb3af43f0 | |||
| d1eed6e9ff | |||
| 30a2931067 | |||
| 1c73dd0c9f | |||
| 1c84c00fe6 | |||
| 7abf28d87c | |||
| ea919eb6ea | |||
| 42aee311cd | |||
| d51ca28d7b | |||
| 77683cafa1 | |||
| e193d141bf | |||
| c248b78831 | |||
| 6b75cb1fb5 | |||
| a926092d9a | |||
| 6d8e925fdb | |||
| 0ee3c669d6 | |||
| addd26cd47 | |||
| beffdd7ed9 | |||
| 9e96fcfe3a | |||
| 91d52ba063 | |||
| 71d50f1b3f | |||
| 1b0a1b6047 | |||
| 5a10a54563 | |||
| 2dfbcce6a4 | |||
| 9f34b46961 | |||
| 8a82655fa4 | |||
| 301f2973d7 | |||
| 5f48afaf31 | |||
| b4379b005a | |||
| 9c83633120 | |||
| c424c5d544 | |||
| 8d5018ea0a | |||
| c6a6ae724a | |||
| a743288940 | |||
| d91729268f | |||
| 1c3d026972 | |||
| 847db19867 |
@@ -0,0 +1,127 @@
|
||||
name: ci/gh-actions/depends
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**/README.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**/README.md'
|
||||
|
||||
env:
|
||||
APT_SET_CONF: |
|
||||
echo "Acquire::Retries \"3\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||
echo "Acquire::http::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||
echo "Acquire::ftp::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||
CCACHE_SETTINGS: |
|
||||
ccache --max-size=150M
|
||||
ccache --set-config=compression=true
|
||||
USE_DEVICE_TREZOR_MANDATORY: ON
|
||||
|
||||
jobs:
|
||||
build-cross:
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- name: "RISCV 64bit"
|
||||
host: "riscv64-linux-gnu"
|
||||
packages: "python3 gperf g++-riscv64-linux-gnu"
|
||||
- name: "ARM v7"
|
||||
host: "arm-linux-gnueabihf"
|
||||
packages: "python3 gperf g++-arm-linux-gnueabihf"
|
||||
- name: "ARM v8"
|
||||
host: "aarch64-linux-gnu"
|
||||
packages: "python3 gperf g++-aarch64-linux-gnu"
|
||||
- name: "i686 Win"
|
||||
host: "i686-w64-mingw32"
|
||||
packages: "python3 g++-mingw-w64-i686"
|
||||
- name: "i686 Linux"
|
||||
host: "i686-pc-linux-gnu"
|
||||
packages: "gperf cmake g++-multilib python3-zmq"
|
||||
- name: "Win64"
|
||||
host: "x86_64-w64-mingw32"
|
||||
packages: "cmake python3 g++-mingw-w64-x86-64"
|
||||
- name: "x86_64 Linux"
|
||||
host: "x86_64-unknown-linux-gnu"
|
||||
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
- name: "Cross-Mac x86_64"
|
||||
host: "x86_64-apple-darwin"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
- name: "Cross-Mac aarch64"
|
||||
host: "aarch64-apple-darwin"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
- name: "x86_64 Freebsd"
|
||||
host: "x86_64-unknown-freebsd"
|
||||
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
# - name: "ARMv8 Android"
|
||||
# host: "aarch64-linux-android"
|
||||
# packages: "gperf cmake python3"
|
||||
name: ${{ matrix.toolchain.name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
# Most volatile cache
|
||||
- name: ccache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-${{ matrix.toolchain.host }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.toolchain.host }}-
|
||||
# Less volatile cache
|
||||
- name: depends cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: contrib/depends/built
|
||||
key: depends-${{ matrix.toolchain.host }}-${{ hashFiles('contrib/depends/packages/*') }}
|
||||
restore-keys: |
|
||||
depends-${{ matrix.toolchain.host }}-${{ hashFiles('contrib/depends/packages/*') }}
|
||||
depends-${{ matrix.toolchain.host }}-
|
||||
# Static cache
|
||||
- name: OSX SDK cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: contrib/depends/sdk-sources
|
||||
key: sdk-${{ matrix.toolchain.host }}-${{ matrix.toolchain.osx_sdk }}
|
||||
restore-keys: sdk-${{ matrix.toolchain.host }}-${{ matrix.toolchain.osx_sdk }}
|
||||
- name: set apt conf
|
||||
run: ${{env.APT_SET_CONF}}
|
||||
- name: install dependencies
|
||||
run: sudo apt update; sudo apt -y install build-essential libtool libssl-dev cmake autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache ${{ matrix.toolchain.packages }}
|
||||
- name: prepare w64-mingw32
|
||||
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'i686-w64-mingw32' }}
|
||||
run: |
|
||||
sudo update-alternatives --set ${{ matrix.toolchain.host }}-g++ $(which ${{ matrix.toolchain.host }}-g++-posix)
|
||||
sudo update-alternatives --set ${{ matrix.toolchain.host }}-gcc $(which ${{ matrix.toolchain.host }}-gcc-posix)
|
||||
- name: build
|
||||
run: |
|
||||
${{env.CCACHE_SETTINGS}}
|
||||
make depends target=${{ matrix.toolchain.host }} -j2
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
|
||||
with:
|
||||
name: ${{ matrix.toolchain.name }}
|
||||
path: |
|
||||
/home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/build/${{ matrix.toolchain.host }}/release/bin/salvium-wallet-*
|
||||
/home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/build/${{ matrix.toolchain.host }}/release/bin/salviumd*
|
||||
- name: zip daemon & cli
|
||||
run: |
|
||||
zip salvium-${GITHUB_REF_NAME}-${{ matrix.toolchain.host }}.zip /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/build/${{ matrix.toolchain.host }}/release/bin/salvium-wallet-rpc* /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/build/${{ matrix.toolchain.host }}/release/bin/salvium-wallet-cli* /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/build/${{ matrix.toolchain.host }}/release/bin/salviumd*
|
||||
ls -l
|
||||
- name: "Deploy"
|
||||
uses: keithweaver/aws-s3-github-action@v1.0.0
|
||||
with:
|
||||
command: cp
|
||||
source: ./salvium-${{ github.ref_name }}-${{ matrix.toolchain.host }}.zip
|
||||
destination: s3://${{ vars.S3_BUCKET }}/salvium-${{ github.ref_name }}-${{ matrix.toolchain.host }}.zip
|
||||
aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws_region: eu-west-1
|
||||
flags: --acl public-read
|
||||
@@ -30,6 +30,9 @@ contrib/gitian/builder/
|
||||
contrib/gitian/docker/
|
||||
contrib/gitian/sigs/
|
||||
|
||||
# Audit tool
|
||||
src/blockchain_utilities/blockchain_audit.cpp
|
||||
|
||||
# Created by https://www.gitignore.io
|
||||
|
||||
### C++ ###
|
||||
|
||||
@@ -14,3 +14,6 @@
|
||||
[submodule "external/miniupnp"]
|
||||
path = external/miniupnp
|
||||
url = https://github.com/miniupnp/miniupnp
|
||||
[submodule "external/mx25519"]
|
||||
path = external/mx25519
|
||||
url = https://github.com/tevador/mx25519
|
||||
|
||||
+22
-6
@@ -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,7 +533,7 @@ 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)
|
||||
@@ -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}")
|
||||
|
||||
+13
-13
@@ -41,26 +41,26 @@ RUN set -ex && \
|
||||
rm -rf /var/lib/apt
|
||||
COPY --from=builder /src/build/x86_64-linux-gnu/release/bin /usr/local/bin/
|
||||
|
||||
# Create monero user
|
||||
RUN adduser --system --group --disabled-password monero && \
|
||||
mkdir -p /wallet /home/monero/.bitmonero && \
|
||||
chown -R monero:monero /home/monero/.bitmonero && \
|
||||
chown -R monero:monero /wallet
|
||||
# Create salvium user
|
||||
RUN adduser --system --group --disabled-password salvium && \
|
||||
mkdir -p /wallet /home/salvium/.salvium && \
|
||||
chown -R salvium:salvium /home/salvium/.salvium && \
|
||||
chown -R salvium:salvium /wallet
|
||||
|
||||
# Contains the blockchain
|
||||
VOLUME /home/monero/.bitmonero
|
||||
VOLUME /home/salvium/.salvium
|
||||
|
||||
# Generate your wallet via accessing the container and run:
|
||||
# cd /wallet
|
||||
# monero-wallet-cli
|
||||
# salvium-wallet-cli
|
||||
VOLUME /wallet
|
||||
|
||||
EXPOSE 18080
|
||||
EXPOSE 18081
|
||||
EXPOSE 19080
|
||||
EXPOSE 19081
|
||||
|
||||
# switch to user monero
|
||||
USER monero
|
||||
# switch to user salvium
|
||||
USER salvium
|
||||
|
||||
ENTRYPOINT ["monerod"]
|
||||
CMD ["--p2p-bind-ip=0.0.0.0", "--p2p-bind-port=18080", "--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=18081", "--non-interactive", "--confirm-external-bind"]
|
||||
ENTRYPOINT ["salviumd"]
|
||||
CMD ["--p2p-bind-ip=0.0.0.0", "--p2p-bind-port=19080", "--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=19081", "--non-interactive", "--confirm-external-bind"]
|
||||
|
||||
|
||||
@@ -1 +1,647 @@
|
||||
# Welcome to Salvium
|
||||
# Salvium Zero v0.9.9
|
||||
|
||||
Copyright (c) 2023-2024, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
Portions Copyright (c) 2012-2013 The Cryptonote developers.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Development resources](#development-resources)
|
||||
- [Introduction](#introduction)
|
||||
- [About this project](#about-this-project)
|
||||
- [Supporting the project](#supporting-the-project)
|
||||
- [License](#license)
|
||||
- [Contributing](#contributing)
|
||||
- [Compiling Salvium from source](#compiling-salvium-from-source)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Gitian builds](#gitian-builds)
|
||||
- [Internationalization](#Internationalization)
|
||||
- [Debugging](#Debugging)
|
||||
- [Known issues](#known-issues)
|
||||
|
||||
## Development resources
|
||||
|
||||
- Web: [salvium.io](https://salvium.io)
|
||||
- Mail: [dev@salvium.io](mailto:dev@salvium.io)
|
||||
- GitHub: [https://github.com/salvium/salvium](https://github.com/salvium/salvium)
|
||||
|
||||
## Introduction
|
||||
|
||||
Salvium is a private, secure, untraceable, decentralised digital currency. You are your bank, you control your funds, and nobody can trace your transfers unless you allow them to do so.
|
||||
|
||||
**Privacy:** Salvium uses a cryptographically sound system to allow you to send and receive funds without your transactions being easily revealed on the blockchain (the ledger of transactions that everyone has). This ensures that your purchases, receipts, and all transfers remain private by default.
|
||||
|
||||
**Security:** Using the power of a distributed peer-to-peer consensus network, every transaction on the network is cryptographically secured. Individual wallets have a 25-word mnemonic seed that is only displayed once and can be written down to backup the wallet. Wallet files should be encrypted with a strong passphrase to ensure they are useless if ever stolen.
|
||||
|
||||
**Untraceability:** By taking advantage of ring signatures, a special property of a certain type of cryptography, Salvium is able to ensure that transactions are not only untraceable but have an optional measure of ambiguity that ensures that transactions cannot easily be tied back to an individual user or computer.
|
||||
|
||||
**Decentralization:** The utility of Salvium depends on its decentralised peer-to-peer consensus network - anyone should be able to run the salvium software, validate the integrity of the blockchain, and participate in all aspects of the salvium network using consumer-grade commodity hardware. Decentralization of the salvium network is maintained by software development that minimizes the costs of running the salvium software and inhibits the proliferation of specialized, non-commodity hardware.
|
||||
|
||||
## About this project
|
||||
|
||||
This is the core implementation of Salvium. It is open source and completely free to use without restrictions, except for those specified in the license agreement below. There are no restrictions on anyone creating an alternative implementation of Salvium that uses the protocol and network in a compatible manner.
|
||||
|
||||
As with many development projects, the repository on GitHub is considered to be the "staging" area for the latest changes. Before changes are merged into that branch on the main repository, they are tested by individual developers in their own branches, submitted as a pull request, and then subsequently tested by contributors who focus on testing and code reviews. That having been said, the repository should be carefully considered before using it in a production environment, unless there is a patch in the repository for a particular show-stopping issue you are experiencing. It is generally a better idea to use a tagged release for stability.
|
||||
|
||||
**Anyone is welcome to contribute to Salvium's codebase!** If you have a fix or code change, feel free to submit it as a pull request directly to the "master" branch. In cases where the change is relatively small or does not affect other parts of the codebase, it may be merged in immediately by any one of the collaborators. On the other hand, if the change is particularly large or complex, it is expected that it will be discussed at length either well in advance of the pull request being submitted, or even directly on the pull request.
|
||||
|
||||
## Supporting the project
|
||||
|
||||
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate/](https://salvium.io/donate/) for more information.
|
||||
|
||||
## License
|
||||
|
||||
See [LICENSE](LICENSE).
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to help out, see [CONTRIBUTING](docs/CONTRIBUTING.md) for a set of guidelines.
|
||||
|
||||
## Compiling Salvium from source
|
||||
|
||||
### Dependencies
|
||||
|
||||
The following table summarizes the tools and libraries required to build. A
|
||||
few of the libraries are also included in this repository (marked as
|
||||
"Vendored"). By default, the build uses the library installed on the system
|
||||
and ignores the vendored sources. However, if no library is found installed on
|
||||
the system, then the vendored source will be built and used. The vendored
|
||||
sources are also used for statically-linked builds because distribution
|
||||
packages often include only shared library binaries (`.so`) but not static
|
||||
library archives (`.a`).
|
||||
|
||||
| Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Void pkg | Fedora pkg | Optional | Purpose |
|
||||
| ------------ | ------------- | -------- | -------------------- | ------------ | ------------------ | ------------------- | -------- | --------------- |
|
||||
| GCC | 7 | NO | `build-essential` | `base-devel` | `base-devel` | `gcc` | NO | |
|
||||
| CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | `cmake` | NO | |
|
||||
| pkg-config | any | NO | `pkg-config` | `base-devel` | `base-devel` | `pkgconf` | NO | |
|
||||
| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | `boost-devel` | NO | C++ libraries |
|
||||
| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | `openssl-devel` | NO | sha256 sum |
|
||||
| libzmq | 4.2.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | `zeromq-devel` | NO | ZeroMQ library |
|
||||
| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | | `openpgm-devel` | NO | For ZeroMQ |
|
||||
| libnorm[2] | ? | NO | `libnorm-dev` | | | | YES | For ZeroMQ |
|
||||
| libunbound | 1.4.16 | NO | `libunbound-dev` | `unbound` | `unbound-devel` | `unbound-devel` | NO | DNS resolver |
|
||||
| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | `libsodium-devel` | NO | cryptography |
|
||||
| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | `libunwind-devel` | YES | Stack traces |
|
||||
| liblzma | any | NO | `liblzma-dev` | `xz` | `liblzma-devel` | `xz-devel` | YES | For libunwind |
|
||||
| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | `readline-devel` | YES | Input editing |
|
||||
| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | `expat-devel` | YES | XML parsing |
|
||||
| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | `gtest-devel` | YES | Test suite |
|
||||
| ccache | any | NO | `ccache` | `ccache` | `ccache` | `ccache` | YES | Compil. cache |
|
||||
| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | `doxygen` | YES | Documentation |
|
||||
| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | `graphviz` | YES | Documentation |
|
||||
| lrelease | ? | NO | `qttools5-dev-tools` | `qt5-tools` | `qt5-tools` | `qt5-linguist` | YES | Translations |
|
||||
| libhidapi | ? | NO | `libhidapi-dev` | `hidapi` | `hidapi-devel` | `hidapi-devel` | YES | Hardware wallet |
|
||||
| libusb | ? | NO | `libusb-1.0-0-dev` | `libusb` | `libusb-devel` | `libusbx-devel` | YES | Hardware wallet |
|
||||
| libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | `protobuf-devel` | YES | Hardware wallet |
|
||||
| protoc | ? | NO | `protobuf-compiler` | `protobuf` | `protobuf` | `protobuf-compiler` | YES | Hardware wallet |
|
||||
| libudev | ? | NO | `libudev-dev` | `systemd` | `eudev-libudev-devel` | `systemd-devel` | YES | Hardware wallet |
|
||||
|
||||
[1] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must
|
||||
build the library binary manually. This can be done with the following command `sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make`
|
||||
then:
|
||||
|
||||
* on Debian:
|
||||
`sudo mv libg* /usr/lib/`
|
||||
* on Ubuntu:
|
||||
`sudo mv lib/libg* /usr/lib/`
|
||||
|
||||
[2] libnorm-dev is needed if your zmq library was built with libnorm, and not needed otherwise
|
||||
|
||||
Install all dependencies at once on Debian/Ubuntu:
|
||||
|
||||
```
|
||||
sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev python3 ccache doxygen graphviz
|
||||
```
|
||||
|
||||
Install all dependencies at once on Arch:
|
||||
```
|
||||
sudo pacman -Syu --needed base-devel cmake boost openssl zeromq libpgm unbound libsodium libunwind xz readline expat gtest python3 ccache doxygen graphviz qt5-tools hidapi libusb protobuf systemd
|
||||
```
|
||||
|
||||
Install all dependencies at once on Fedora:
|
||||
```
|
||||
sudo dnf install gcc gcc-c++ cmake pkgconf boost-devel openssl-devel zeromq-devel openpgm-devel unbound-devel libsodium-devel libunwind-devel xz-devel readline-devel expat-devel gtest-devel ccache doxygen graphviz qt5-linguist hidapi-devel libusbx-devel protobuf-devel protobuf-compiler systemd-devel
|
||||
```
|
||||
|
||||
Install all dependencies at once on openSUSE:
|
||||
|
||||
```
|
||||
sudo zypper ref && sudo zypper in cppzmq-devel libboost_chrono-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_program_options-devel libboost_regex-devel libboost_serialization-devel libboost_system-devel libboost_thread-devel libexpat-devel libminiupnpc-devel libsodium-devel libunwind-devel unbound-devel cmake doxygen ccache fdupes gcc-c++ libevent-devel libopenssl-devel pkgconf-pkg-config readline-devel xz-devel libqt5-qttools-devel patterns-devel-C-C++-devel_C_C++
|
||||
```
|
||||
|
||||
Install all dependencies at once on macOS with the provided Brewfile:
|
||||
|
||||
```
|
||||
brew update && brew bundle --file=contrib/brew/Brewfile
|
||||
```
|
||||
|
||||
FreeBSD 12.1 one-liner required to build dependencies:
|
||||
|
||||
```
|
||||
pkg install git gmake cmake pkgconf boost-libs libzmq4 libsodium unbound
|
||||
```
|
||||
|
||||
### Cloning the repository
|
||||
|
||||
Clone recursively to pull-in needed submodule(s):
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/salvium/salvium
|
||||
```
|
||||
|
||||
If you already have a repo cloned, initialize and update:
|
||||
|
||||
```
|
||||
cd salvium && git submodule init && git submodule update
|
||||
```
|
||||
|
||||
*Note*: If there are submodule differences between branches, you may need
|
||||
to use `git submodule sync && git submodule update` after changing branches
|
||||
to build successfully.
|
||||
|
||||
### Build instructions
|
||||
|
||||
Salvium uses the CMake build system and a top-level [Makefile](Makefile) that
|
||||
invokes cmake commands as needed.
|
||||
|
||||
#### On Linux and macOS
|
||||
|
||||
* Install the dependencies
|
||||
* Change to the root of the source code directory, change to the most recent release branch, and build:
|
||||
|
||||
```bash
|
||||
cd salvium
|
||||
git checkout v0.9.9
|
||||
make
|
||||
```
|
||||
|
||||
*Optional*: If your machine has several cores and enough memory, enable
|
||||
parallel build by running `make -j<number of threads>` instead of `make`. For
|
||||
this to be worthwhile, the machine should have one core and about 2GB of RAM
|
||||
available per thread.
|
||||
|
||||
*Note*: The instructions above will compile the most stable release of the
|
||||
Salvium software. If you would like to use and test the most recent software,
|
||||
use `git checkout master`. The master branch may contain updates that are
|
||||
both unstable and incompatible with release software, though testing is always
|
||||
encouraged.
|
||||
|
||||
* The resulting executables can be found in `build/release/bin`
|
||||
|
||||
* Add `PATH="$PATH:$HOME/salvium/build/release/bin"` to `.profile`
|
||||
|
||||
* Run Salvium with `salviumd --detach`
|
||||
|
||||
* **Optional**: build and run the test suite to verify the binaries:
|
||||
|
||||
```bash
|
||||
make release-test
|
||||
```
|
||||
|
||||
*NOTE*: `core_tests` test may take a few hours to complete.
|
||||
|
||||
* **Optional**: to build binaries suitable for debugging:
|
||||
|
||||
```bash
|
||||
make debug
|
||||
```
|
||||
|
||||
* **Optional**: to build statically-linked binaries:
|
||||
|
||||
```bash
|
||||
make release-static
|
||||
```
|
||||
|
||||
Dependencies need to be built with -fPIC. Static libraries usually aren't, so you may have to build them yourself with -fPIC. Refer to their documentation for how to build them.
|
||||
|
||||
* **Optional**: build documentation in `doc/html` (omit `HAVE_DOT=YES` if `graphviz` is not installed):
|
||||
|
||||
```bash
|
||||
HAVE_DOT=YES doxygen Doxyfile
|
||||
```
|
||||
|
||||
* **Optional**: use ccache not to rebuild translation units, that haven't really changed. Salvium's CMakeLists.txt file automatically handles it
|
||||
|
||||
```bash
|
||||
sudo apt install ccache
|
||||
```
|
||||
|
||||
#### On the Raspberry Pi
|
||||
|
||||
Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (2017-09-07 or later) from https://www.raspberrypi.org/downloads/raspbian/. If you are using Raspian Jessie, [please see note in the following section](#note-for-raspbian-jessie-users).
|
||||
|
||||
* `apt-get update && apt-get upgrade` to install all of the latest software
|
||||
|
||||
* Install the dependencies for Salvium from the 'Debian' column in the table above.
|
||||
|
||||
* Increase the system swap size:
|
||||
|
||||
```bash
|
||||
sudo /etc/init.d/dphys-swapfile stop
|
||||
sudo nano /etc/dphys-swapfile
|
||||
CONF_SWAPSIZE=2048
|
||||
sudo /etc/init.d/dphys-swapfile start
|
||||
```
|
||||
|
||||
* If using an external hard disk without an external power supply, ensure it gets enough power to avoid hardware issues when syncing, by adding the line "max_usb_current=1" to /boot/config.txt
|
||||
|
||||
* Clone Salvium and checkout the most recent release version:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/salvium/salvium
|
||||
cd salvium
|
||||
git checkout v0.9.9
|
||||
```
|
||||
|
||||
* Build:
|
||||
|
||||
```bash
|
||||
USE_SINGLE_BUILDDIR=1 make release
|
||||
```
|
||||
|
||||
* Wait 4-6 hours
|
||||
|
||||
* The resulting executables can be found in `build/release/bin`
|
||||
|
||||
* Add `export PATH="$PATH:$HOME/salvium/build/release/bin"` to `$HOME/.profile`
|
||||
|
||||
* Run `source $HOME/.profile`
|
||||
|
||||
* Run Salvium with `salviumd --detach`
|
||||
|
||||
* You may wish to reduce the size of the swap file after the build has finished, and delete the boost directory from your home directory
|
||||
|
||||
#### *Note for Raspbian Jessie users:*
|
||||
|
||||
If you are using the older Raspbian Jessie image, compiling Salvium is a bit more complicated. The version of Boost available in the Debian Jessie repositories is too old to use with Salvium, and thus you must compile a newer version yourself. The following explains the extra steps and has been tested on a Raspberry Pi 2 with a clean install of minimal Raspbian Jessie.
|
||||
|
||||
* As before, `apt-get update && apt-get upgrade` to install all of the latest software, and increase the system swap size
|
||||
|
||||
```bash
|
||||
sudo /etc/init.d/dphys-swapfile stop
|
||||
sudo nano /etc/dphys-swapfile
|
||||
CONF_SWAPSIZE=2048
|
||||
sudo /etc/init.d/dphys-swapfile start
|
||||
```
|
||||
|
||||
|
||||
* Then, install the dependencies for Salvium except for `libunwind` and `libboost-all-dev`
|
||||
|
||||
* Install the latest version of boost (this may first require invoking `apt-get remove --purge libboost*-dev` to remove a previous version if you're not using a clean install):
|
||||
|
||||
```bash
|
||||
cd
|
||||
wget https://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.bz2
|
||||
tar xvfo boost_1_72_0.tar.bz2
|
||||
cd boost_1_72_0
|
||||
./bootstrap.sh
|
||||
sudo ./b2
|
||||
```
|
||||
|
||||
* Wait ~8 hours
|
||||
|
||||
```bash
|
||||
sudo ./bjam cxxflags=-fPIC cflags=-fPIC -a install
|
||||
```
|
||||
|
||||
* Wait ~4 hours
|
||||
|
||||
* From here, follow the [general Raspberry Pi instructions](#on-the-raspberry-pi) from the "Clone Salvium and checkout most recent release version" step.
|
||||
|
||||
#### On Windows:
|
||||
|
||||
Binaries for Windows are built on Windows using the MinGW toolchain within
|
||||
[MSYS2 environment](https://www.msys2.org). The MSYS2 environment emulates a
|
||||
POSIX system. The toolchain runs within the environment and *cross-compiles*
|
||||
binaries that can run outside of the environment as a regular Windows
|
||||
application.
|
||||
|
||||
**Preparing the build environment**
|
||||
|
||||
* Download and install the [MSYS2 installer](https://www.msys2.org), either the 64-bit or the 32-bit package, depending on your system.
|
||||
* Open the MSYS shell via the `MSYS2 Shell` shortcut
|
||||
* Update packages using pacman:
|
||||
|
||||
```bash
|
||||
pacman -Syu
|
||||
```
|
||||
|
||||
* Exit the MSYS shell using Alt+F4
|
||||
* Edit the properties for the `MSYS2 Shell` shortcut changing "msys2_shell.bat" to "msys2_shell.cmd -mingw64" for 64-bit builds or "msys2_shell.cmd -mingw32" for 32-bit builds
|
||||
* Restart MSYS shell via modified shortcut and update packages again using pacman:
|
||||
|
||||
```bash
|
||||
pacman -Syu
|
||||
```
|
||||
|
||||
|
||||
* Install dependencies:
|
||||
|
||||
To build for 64-bit Windows:
|
||||
|
||||
```bash
|
||||
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-unbound
|
||||
```
|
||||
|
||||
To build for 32-bit Windows:
|
||||
|
||||
```bash
|
||||
pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-zeromq mingw-w64-i686-libsodium mingw-w64-i686-hidapi mingw-w64-i686-unbound
|
||||
```
|
||||
|
||||
* Open the MingW shell via `MinGW-w64-Win64 Shell` shortcut on 64-bit Windows
|
||||
or `MinGW-w64-Win64 Shell` shortcut on 32-bit Windows. Note that if you are
|
||||
running 64-bit Windows, you will have both 64-bit and 32-bit MinGW shells.
|
||||
|
||||
**Cloning**
|
||||
|
||||
* To git clone, run:
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/salvium/salvium
|
||||
```
|
||||
|
||||
**Building**
|
||||
|
||||
* Change to the cloned directory, run:
|
||||
|
||||
```bash
|
||||
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.9'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v0.9.9
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
```bash
|
||||
make release-static-win64
|
||||
```
|
||||
|
||||
* If you are on a 32-bit system, run:
|
||||
|
||||
```bash
|
||||
make release-static-win32
|
||||
```
|
||||
|
||||
* The resulting executables can be found in `build/release/bin`
|
||||
|
||||
* **Optional**: to build Windows binaries suitable for debugging on a 64-bit system, run:
|
||||
|
||||
```bash
|
||||
make debug-static-win64
|
||||
```
|
||||
|
||||
* **Optional**: to build Windows binaries suitable for debugging on a 32-bit system, run:
|
||||
|
||||
```bash
|
||||
make debug-static-win32
|
||||
```
|
||||
|
||||
* The resulting executables can be found in `build/debug/bin`
|
||||
|
||||
### On FreeBSD:
|
||||
|
||||
The project can be built from scratch by following instructions for Linux above(but use `gmake` instead of `make`).
|
||||
If you are running Salvium in a jail, you need to add `sysvsem="new"` to your jail configuration, otherwise lmdb will throw the error message: `Failed to open lmdb environment: Function not implemented`.
|
||||
|
||||
### On OpenBSD:
|
||||
|
||||
You will need to add a few packages to your system. `pkg_add cmake gmake zeromq libiconv boost libunbound`.
|
||||
|
||||
The `doxygen` and `graphviz` packages are optional and require the xbase set.
|
||||
Running the test suite also requires `py3-requests` package.
|
||||
|
||||
Build salvium: `gmake`
|
||||
|
||||
Note: you may encounter the following error when compiling the latest version of Salvium as a normal user:
|
||||
|
||||
```
|
||||
LLVM ERROR: out of memory
|
||||
c++: error: unable to execute command: Abort trap (core dumped)
|
||||
```
|
||||
|
||||
Then you need to increase the data ulimit size to 2GB and try again: `ulimit -d 2000000`
|
||||
|
||||
### On NetBSD:
|
||||
|
||||
Check that the dependencies are present: `pkg_info -c libexecinfo boost-headers boost-libs protobuf readline libusb1 zeromq git-base pkgconf gmake cmake | more`, and install any that are reported missing, using `pkg_add` or from your pkgsrc tree. Readline is optional but worth having.
|
||||
|
||||
Third-party dependencies are usually under `/usr/pkg/`, but if you have a custom setup, adjust the "/usr/pkg" (below) accordingly.
|
||||
|
||||
Clone the salvium repository recursively and checkout the most recent release as described above. Then build salvium: `gmake BOOST_ROOT=/usr/pkg LDFLAGS="-Wl,-R/usr/pkg/lib" release`. The resulting executables can be found in `build/NetBSD/[Release version]/Release/bin/`.
|
||||
|
||||
### On Solaris:
|
||||
|
||||
The default Solaris linker can't be used, you have to install GNU ld, then run cmake manually with the path to your copy of GNU ld:
|
||||
|
||||
```bash
|
||||
mkdir -p build/release
|
||||
cd build/release
|
||||
cmake -DCMAKE_LINKER=/path/to/ld -D CMAKE_BUILD_TYPE=Release ../..
|
||||
cd ../..
|
||||
```
|
||||
|
||||
Then you can run make as usual.
|
||||
|
||||
### Building portable statically linked binaries
|
||||
|
||||
By default, in either dynamically or statically linked builds, binaries target the specific host processor on which the build happens and are not portable to other processors. Portable binaries can be built using the following targets:
|
||||
|
||||
* ```make release-static-linux-x86_64``` builds binaries on Linux on x86_64 portable across POSIX systems on x86_64 processors
|
||||
* ```make release-static-linux-i686``` builds binaries on Linux on x86_64 or i686 portable across POSIX systems on i686 processors
|
||||
* ```make release-static-linux-armv8``` builds binaries on Linux portable across POSIX systems on armv8 processors
|
||||
* ```make release-static-linux-armv7``` builds binaries on Linux portable across POSIX systems on armv7 processors
|
||||
* ```make release-static-linux-armv6``` builds binaries on Linux portable across POSIX systems on armv6 processors
|
||||
* ```make release-static-win64``` builds binaries on 64-bit Windows portable across 64-bit Windows systems
|
||||
* ```make release-static-win32``` builds binaries on 64-bit or 32-bit Windows portable across 32-bit Windows systems
|
||||
|
||||
### Cross Compiling
|
||||
|
||||
You can also cross-compile static binaries on Linux for Windows and macOS with the `depends` system.
|
||||
|
||||
* ```make depends target=x86_64-linux-gnu``` for 64-bit linux binaries.
|
||||
* ```make depends target=x86_64-w64-mingw32``` for 64-bit windows binaries.
|
||||
* Requires: `python3 g++-mingw-w64-x86-64 wine1.6 bc`
|
||||
* You also need to run:
|
||||
```update-alternatives --set x86_64-w64-mingw32-g++ x86_64-w64-mingw32-g++-posix && update-alternatives --set x86_64-w64-mingw32-gcc x86_64-w64-mingw32-gcc-posix```
|
||||
* ```make depends target=x86_64-apple-darwin``` for macOS binaries.
|
||||
* Requires: `cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev`
|
||||
* ```make depends target=i686-linux-gnu``` for 32-bit linux binaries.
|
||||
* Requires: `g++-multilib bc`
|
||||
* ```make depends target=i686-w64-mingw32``` for 32-bit windows binaries.
|
||||
* Requires: `python3 g++-mingw-w64-i686`
|
||||
* ```make depends target=arm-linux-gnueabihf``` for armv7 binaries.
|
||||
* Requires: `g++-arm-linux-gnueabihf`
|
||||
* ```make depends target=aarch64-linux-gnu``` for armv8 binaries.
|
||||
* Requires: `g++-aarch64-linux-gnu`
|
||||
* ```make depends target=riscv64-linux-gnu``` for RISC V 64 bit binaries.
|
||||
* Requires: `g++-riscv64-linux-gnu`
|
||||
* ```make depends target=x86_64-unknown-freebsd``` for freebsd binaries.
|
||||
* Requires: `clang-8`
|
||||
* ```make depends target=arm-linux-android``` for 32bit android binaries
|
||||
* ```make depends target=aarch64-linux-android``` for 64bit android binaries
|
||||
|
||||
|
||||
The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. The `depends` system has been tested on Ubuntu 18.04 and 20.04.
|
||||
|
||||
Using `depends` might also be easier to compile Salvium on Windows than using MSYS. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above.
|
||||
|
||||
The produced binaries still link libc dynamically. If the binary is compiled on a current distribution, it might not run on an older distribution with an older installation of libc. Passing `-DBACKCOMPAT=ON` to cmake will make sure that the binary will run on systems having at least libc version 2.17.
|
||||
|
||||
### Trezor hardware wallet support
|
||||
|
||||
If you have an issue with building Salvium with Trezor support, you can disable it by setting `USE_DEVICE_TREZOR=OFF`, e.g.,
|
||||
|
||||
```bash
|
||||
USE_DEVICE_TREZOR=OFF make release
|
||||
```
|
||||
|
||||
For more information, please check out Trezor [src/device_trezor/README.md](src/device_trezor/README.md).
|
||||
|
||||
### Gitian builds
|
||||
|
||||
See [contrib/gitian/README.md](contrib/gitian/README.md).
|
||||
|
||||
## Running salviumd
|
||||
|
||||
The build places the binary in `bin/` sub-directory within the build directory
|
||||
from which cmake was invoked (repository root by default). To run in the
|
||||
foreground:
|
||||
|
||||
```bash
|
||||
./bin/salviumd
|
||||
```
|
||||
|
||||
To list all available options, run `./bin/salviumd --help`. Options can be
|
||||
specified either on the command line or in a configuration file passed by the
|
||||
`--config-file` argument. To specify an option in the configuration file, add
|
||||
a line with the syntax `argumentname=value`, where `argumentname` is the name
|
||||
of the argument without the leading dashes, for example, `log-level=1`.
|
||||
|
||||
To run in background:
|
||||
|
||||
```bash
|
||||
./bin/salviumd --log-file salviumd.log --detach
|
||||
```
|
||||
|
||||
To run as a systemd service, copy
|
||||
[salviumd.service](utils/systemd/salviumd.service) to `/etc/systemd/system/` and
|
||||
[salviumd.conf](utils/conf/salviumd.conf) to `/etc/`. The [example
|
||||
service](utils/systemd/salviumd.service) assumes that the user `salvium` exists
|
||||
and its home is the data directory specified in the [example
|
||||
config](utils/conf/salviumd.conf).
|
||||
|
||||
If you're on Mac, you may need to add the `--max-concurrency 1` option to
|
||||
salvium-wallet-cli, and possibly salviumd, if you get crashes refreshing.
|
||||
|
||||
## Internationalization
|
||||
|
||||
See [README.i18n.md](docs/README.i18n.md).
|
||||
|
||||
## Debugging
|
||||
|
||||
This section contains general instructions for debugging failed installs or problems encountered with Salvium. First, ensure you are running the latest version built from the GitHub repo.
|
||||
|
||||
### Obtaining stack traces and core dumps on Unix systems
|
||||
|
||||
We generally use the tool `gdb` (GNU debugger) to provide stack trace functionality, and `ulimit` to provide core dumps in builds which crash or segfault.
|
||||
|
||||
* To use `gdb` in order to obtain a stack trace for a build that has stalled:
|
||||
|
||||
Run the build.
|
||||
|
||||
Once it stalls, enter the following command:
|
||||
|
||||
```bash
|
||||
gdb /path/to/salviumd `pidof salviumd`
|
||||
```
|
||||
|
||||
Type `thread apply all bt` within gdb in order to obtain the stack trace
|
||||
|
||||
* If however the core dumps or segfaults:
|
||||
|
||||
Enter `ulimit -c unlimited` on the command line to enable unlimited filesizes for core dumps
|
||||
|
||||
Enter `echo core | sudo tee /proc/sys/kernel/core_pattern` to stop cores from being hijacked by other tools
|
||||
|
||||
Run the build.
|
||||
|
||||
When it terminates with an output along the lines of "Segmentation fault (core dumped)", there should be a core dump file in the same directory as salviumd. It may be named just `core`, or `core.xxxx` with numbers appended.
|
||||
|
||||
You can now analyse this core dump with `gdb` as follows:
|
||||
|
||||
```bash
|
||||
gdb /path/to/salviumd /path/to/dumpfile`
|
||||
```
|
||||
|
||||
Print the stack trace with `bt`
|
||||
|
||||
* If a program crashed and cores are managed by systemd, the following can also get a stack trace for that crash:
|
||||
|
||||
```bash
|
||||
coredumpctl -1 gdb
|
||||
```
|
||||
|
||||
#### To run Salvium within gdb:
|
||||
|
||||
Type `gdb /path/to/salviumd`
|
||||
|
||||
Pass command-line options with `--args` followed by the relevant arguments
|
||||
|
||||
Type `run` to run salviumd
|
||||
|
||||
### Analysing memory corruption
|
||||
|
||||
There are two tools available:
|
||||
|
||||
#### ASAN
|
||||
|
||||
Configure Salvium with the -D SANITIZE=ON cmake flag, eg:
|
||||
|
||||
```bash
|
||||
cd build/debug && cmake -D SANITIZE=ON -D CMAKE_BUILD_TYPE=Debug ../..
|
||||
```
|
||||
|
||||
You can then run the salvium tools normally. Performance will typically halve.
|
||||
|
||||
#### valgrind
|
||||
|
||||
Install valgrind and run as `valgrind /path/to/salviumd`. It will be very slow.
|
||||
|
||||
### LMDB
|
||||
|
||||
Instructions for debugging suspected blockchain corruption as per @HYC
|
||||
|
||||
There is an `mdb_stat` command in the LMDB source that can print statistics about the database but it's not routinely built. This can be built with the following command:
|
||||
|
||||
```bash
|
||||
cd ~/salvium/external/db_drivers/liblmdb && make
|
||||
```
|
||||
|
||||
The output of `mdb_stat -ea <path to blockchain dir>` will indicate inconsistencies in the blocks, block_heights and block_info table.
|
||||
|
||||
The output of `mdb_dump -s blocks <path to blockchain dir>` and `mdb_dump -s block_info <path to blockchain dir>` is useful for indicating whether blocks and block_info contain the same keys.
|
||||
|
||||
These records are dumped as hex data, where the first line is the key and the second line is the data.
|
||||
|
||||
# Known Issues
|
||||
|
||||
## Protocols
|
||||
|
||||
### Socket-based
|
||||
|
||||
Because of the nature of the socket-based protocols that drive salvium, certain protocol weaknesses are somewhat unavoidable at this time. While these weaknesses can theoretically be fully mitigated, the effort required (the means) may not justify the ends. As such, please consider taking the following precautions if you are a salvium node operator:
|
||||
|
||||
- Run `salviumd` on a "secured" machine. If operational security is not your forte, at a very minimum, have a dedicated a computer running `salviumd` and **do not** browse the web, use email clients, or use any other potentially harmful apps on your `salviumd` machine. **Do not click links or load URL/MUA content on the same machine**. Doing so may potentially exploit weaknesses in commands which accept "localhost" and "127.0.0.1".
|
||||
- If you plan on hosting a public "remote" node, start `salviumd` with `--restricted-rpc`. This is a must.
|
||||
|
||||
### Blockchain-based
|
||||
|
||||
Certain blockchain "features" can be considered "bugs" if misused correctly. Consequently, please consider the following:
|
||||
|
||||
- When receiving salvium, be aware that it may be locked for an arbitrary time if the sender elected to, preventing you from spending that salvium until the lock time expires. You may want to hold off acting upon such a transaction until the unlock time lapses. To get a sense of that time, you can consider the remaining blocktime until unlock as seen in the `show_transfers` command.
|
||||
|
||||
@@ -6,10 +6,10 @@ function(_trezor_default_val val_name val_default)
|
||||
endfunction()
|
||||
|
||||
# Define default options via env vars
|
||||
_trezor_default_val(USE_DEVICE_TREZOR ON)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR OFF)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR_MANDATORY OFF)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR_PROTOBUF_TEST ON)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR_LIBUSB ON)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR_PROTOBUF_TEST OFF)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR_LIBUSB OFF)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR_UDP_RELEASE OFF)
|
||||
_trezor_default_val(USE_DEVICE_TREZOR_DEBUG OFF)
|
||||
_trezor_default_val(TREZOR_DEBUG OFF)
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -5,7 +5,7 @@ $(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=6653ef5910f17954861fe72332e68b03ca6e4d9c7160eb3a8de5a5a913bfab43
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve
|
||||
$(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve --with-cv-impl=pthread
|
||||
$(package)_config_opts_linux=--with-pic
|
||||
$(package)_config_opts_freebsd=--with-pic
|
||||
$(package)_cxxflags=-std=c++11
|
||||
|
||||
@@ -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
|
||||
+1
Submodule external/mx25519 added at 42f36ede5f
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the git information
|
||||
TAG=`git tag -l --points-at HEAD`
|
||||
COMMIT=`git rev-parse --short=9 HEAD`
|
||||
|
||||
# Build the 64-bit Windows release
|
||||
USE_DEVICE_TREZOR=OFF make depends target=x86_64-w64-mingw32 -j12
|
||||
pushd ./build/x86_64-w64-mingw32/release/bin > /dev/null
|
||||
zip -ur ~/releases/salvium-${TAG}-win64.zip salviumd.exe salvium-wallet-cli.exe salvium-wallet-rpc.exe
|
||||
popd > /dev/null
|
||||
|
||||
# Build the 64-bit Apple Silicon release
|
||||
USE_DEVICE_TREZOR=OFF make depends target=aarch64-apple-darwin -j12
|
||||
pushd ./build/aarch64-apple-darwin/release/bin > /dev/null
|
||||
zip -ur ~/releases/salvium-${TAG}-macos-arm64.zip salviumd salvium-wallet-cli salvium-wallet-rpc
|
||||
popd > /dev/null
|
||||
|
||||
# Build the 64-bit MacOS Intel Silicon release
|
||||
USE_DEVICE_TREZOR=OFF make depends target=x86_64-apple-darwin -j12
|
||||
pushd ./build/x86_64-apple-darwin/release/bin > /dev/null
|
||||
zip -ur ~/releases/salvium-${TAG}-macos-x86_64.zip salviumd salvium-wallet-cli salvium-wallet-rpc
|
||||
popd > /dev/null
|
||||
|
||||
# Build the 64-bit Linux release
|
||||
USE_DEVICE_TREZOR=OFF make depends target=x86_64-linux-gnu -j12
|
||||
pushd ./build/x86_64-linux-gnu/release/bin > /dev/null
|
||||
zip -ur ~/releases/salvium-${TAG}-linux-x86_64.zip salviumd salvium-wallet-cli salvium-wallet-rpc
|
||||
popd > /dev/null
|
||||
|
||||
# Finish
|
||||
@@ -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);
|
||||
@@ -267,8 +271,9 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, blobdata>>& txs
|
||||
, const cryptonote::network_type& nettype
|
||||
, const cryptonote::network_type nettype
|
||||
, cryptonote::yield_block_info& ybi
|
||||
, cryptonote::audit_block_info& abi
|
||||
)
|
||||
{
|
||||
const block &blk = blck.first;
|
||||
@@ -295,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();
|
||||
|
||||
@@ -309,8 +314,8 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
}
|
||||
|
||||
std::map<std::string, int64_t> slippage_counts;
|
||||
uint64_t yield_total = 0;
|
||||
if (blk.protocol_tx.version == 2)
|
||||
uint64_t audit_total = 0, yield_total = 0;
|
||||
if (blk.protocol_tx.version >= 2)
|
||||
{
|
||||
num_rct_outs += blk.protocol_tx.vout.size();
|
||||
|
||||
@@ -355,11 +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 a YIELD 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;
|
||||
}
|
||||
@@ -410,7 +420,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
|
||||
// call out to subclass implementation to add the block & metadata
|
||||
time1 = epee::misc_utils::get_tick_count();
|
||||
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, nettype, ybi);
|
||||
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, nettype, ybi, abi);
|
||||
TIME_MEASURE_FINISH(time1);
|
||||
time_add_block1 += time1;
|
||||
|
||||
|
||||
@@ -430,8 +430,10 @@ private:
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
const cryptonote::network_type& nettype,
|
||||
cryptonote::yield_block_info& ybi
|
||||
uint64_t audit_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
@@ -884,8 +886,9 @@ public:
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, blobdata>>& txs
|
||||
, const cryptonote::network_type& nettype
|
||||
, const cryptonote::network_type nettype
|
||||
, cryptonote::yield_block_info& ybi
|
||||
, cryptonote::audit_block_info& abi
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -1915,8 +1918,11 @@ public:
|
||||
*/
|
||||
virtual uint64_t get_database_size() const = 0;
|
||||
|
||||
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) = 0;
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) = 0;
|
||||
virtual int get_audit_block_info(const uint64_t height, audit_block_info& abi) const = 0;
|
||||
virtual int get_audit_tx_info(const uint64_t height, std::vector<yield_tx_info>& ati_container) const = 0;
|
||||
|
||||
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const = 0;
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
@@ -215,6 +219,9 @@ namespace
|
||||
* yield_block_data block height {slippage_coins, locked_coins, lc_total, network_health}
|
||||
* yield_tx_data block height {txn hash, locked_coins, return_address}
|
||||
*
|
||||
* audit_block_data block height {locked_coins, lc_total}
|
||||
* audit_tx_data block height {txn hash, locked_coins, return_address}
|
||||
*
|
||||
* Note: where the data items are of uniform size, DUPFIXED tables have
|
||||
* been used to save space. In most of these cases, a dummy "zerokval"
|
||||
* key is used when accessing the table; the Key listed above will be
|
||||
@@ -289,6 +296,8 @@ const char* const LMDB_CIRC_SUPPLY_TALLY = "circ_supply_tally";
|
||||
*/
|
||||
const char* const LMDB_YIELD_TXS = "yield_txs";
|
||||
const char* const LMDB_YIELD_BLOCKS = "yield_blocks";
|
||||
const char* const LMDB_AUDIT_TXS = "audit_txs";
|
||||
const char* const LMDB_AUDIT_BLOCKS = "audit_blocks";
|
||||
|
||||
const char zerokey[8] = {0};
|
||||
const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey };
|
||||
@@ -800,7 +809,76 @@ estim:
|
||||
return threshold_size;
|
||||
}
|
||||
|
||||
int BlockchainLMDB::get_yield_block_info(const uint64_t height, yield_block_info& ybi)
|
||||
int BlockchainLMDB::get_audit_block_info(const uint64_t height, audit_block_info& abi) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
|
||||
// Clear the ABI, just in case
|
||||
std::memset(&abi, 0, sizeof(struct audit_block_info));
|
||||
|
||||
// Query for the matured AUDIT_BLOCK_INFO information
|
||||
TXN_PREFIX_RDONLY();
|
||||
RCURSOR(audit_blocks);
|
||||
|
||||
MDB_val v;
|
||||
MDB_val_set(k, height);
|
||||
int ret = mdb_cursor_get(m_cur_audit_blocks, &k, &v, MDB_SET);
|
||||
if (ret == MDB_NOTFOUND) {
|
||||
LOG_ERROR("Failed to locate ABI for block height " << height);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to enumerate audit block info: ", ret).c_str()));
|
||||
|
||||
audit_block_info *p = (audit_block_info*)v.mv_data;
|
||||
abi = *p;
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
|
||||
// Return success to caller
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BlockchainLMDB::get_audit_tx_info(const uint64_t height, std::vector<yield_tx_info>& ati_container) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
|
||||
// Clear the container
|
||||
ati_container.clear();
|
||||
|
||||
// Query for the (presumably matured) AUDIT_TX_INFO information (we actually reuse YIELD_TX_INFO for this)
|
||||
TXN_PREFIX_RDONLY();
|
||||
RCURSOR(audit_txs);
|
||||
|
||||
MDB_val v;
|
||||
MDB_val_set(k, height);
|
||||
MDB_cursor_op op = MDB_SET;
|
||||
while (1)
|
||||
{
|
||||
int ret = mdb_cursor_get(m_cur_audit_txs, &k, &v, op);
|
||||
op = MDB_NEXT_DUP;
|
||||
if (ret == MDB_NOTFOUND)
|
||||
break;
|
||||
if (ret)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to enumerate audit TX info: ", ret).c_str()));
|
||||
|
||||
// Get the data
|
||||
yield_tx_info *p = (yield_tx_info*)v.mv_data;
|
||||
// Push result back into the container
|
||||
ati_container.emplace_back(*p);
|
||||
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
|
||||
ati_container.back().block_height = height;
|
||||
}
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
|
||||
// Return success to caller
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BlockchainLMDB::get_yield_block_info(const uint64_t height, yield_block_info& ybi) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
@@ -825,11 +903,13 @@ int BlockchainLMDB::get_yield_block_info(const uint64_t height, yield_block_info
|
||||
yield_block_info *p = (yield_block_info*)v.mv_data;
|
||||
ybi = *p;
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
|
||||
// Return success to caller
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container)
|
||||
int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
@@ -853,16 +933,21 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_t
|
||||
if (ret)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to enumerate yield TX info: ", ret).c_str()));
|
||||
|
||||
// Push result back into the container
|
||||
// Get the data
|
||||
yield_tx_info *p = (yield_tx_info*)v.mv_data;
|
||||
// Push result back into the container
|
||||
yti_container.emplace_back(*p);
|
||||
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
|
||||
yti_container.back().block_height = height;
|
||||
}
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
|
||||
// Return success to caller
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, const cryptonote::network_type& nettype, cryptonote::yield_block_info& ybi)
|
||||
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, uint64_t audit_total, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
@@ -892,6 +977,18 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
|
||||
int result = 0;
|
||||
|
||||
// Create the AUDIT_BLOCK_INFO instance for this block
|
||||
CURSOR(audit_blocks)
|
||||
abi.block_height = m_height;
|
||||
abi.locked_coins_this_block = audit_total;
|
||||
|
||||
// Put the YBI into the table
|
||||
MDB_val_set(key, m_height);
|
||||
MDB_val_set(abi_val, abi);
|
||||
result = mdb_cursor_put(m_cur_audit_blocks, &key, &abi_val, MDB_APPEND);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add ABI to db: ", result).c_str()));
|
||||
|
||||
CURSOR(yield_blocks)
|
||||
yield_block_info ybi_matured, ybi_prev;
|
||||
uint64_t yield_lock_period = cryptonote::get_config(nettype).STAKE_LOCK_PERIOD;
|
||||
@@ -928,7 +1025,6 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
ybi.network_health_percentage = 100;
|
||||
|
||||
// Put the YBI into the table
|
||||
MDB_val_set(key, m_height);
|
||||
MDB_val_set(ybi_val, ybi);
|
||||
result = mdb_cursor_put(m_cur_yield_blocks, &key, &ybi_val, MDB_APPEND);
|
||||
if (result)
|
||||
@@ -1000,6 +1096,7 @@ void BlockchainLMDB::remove_block()
|
||||
CURSOR(blocks)
|
||||
CURSOR(circ_supply_tally)
|
||||
CURSOR(yield_blocks)
|
||||
CURSOR(audit_blocks)
|
||||
MDB_val_copy<uint64_t> k(m_height - 1);
|
||||
MDB_val h = k;
|
||||
if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH)))
|
||||
@@ -1027,6 +1124,11 @@ void BlockchainLMDB::remove_block()
|
||||
throw1(BLOCK_DNE(lmdb_error("Attempting to remove yield block info that's not in the db: ", result).c_str()));
|
||||
if ((result = mdb_cursor_del(m_cur_yield_blocks, 0)))
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of yield block info to db transaction: ", result).c_str()));
|
||||
|
||||
// Is the block within an audit window?
|
||||
if ((result = mdb_cursor_get(m_cur_audit_blocks, &k2, NULL, MDB_SET)) == 0)
|
||||
if ((result = mdb_cursor_del(m_cur_audit_blocks, 0)))
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of audit block info to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
boost::multiprecision::int128_t
|
||||
@@ -1112,6 +1214,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
CURSOR(circ_supply)
|
||||
CURSOR(circ_supply_tally)
|
||||
CURSOR(yield_txs)
|
||||
CURSOR(audit_txs)
|
||||
|
||||
MDB_val_set(val_tx_id, tx_id);
|
||||
MDB_val_set(val_h, tx_hash);
|
||||
@@ -1179,13 +1282,19 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add prunable tx prunable hash to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
const uint8_t hf_version = m_hardfork->get_ideal_version(m_height);
|
||||
if (tx.type == cryptonote::transaction_type::MINER) {
|
||||
|
||||
// Update the circulating supply tally because of potentially burnt block_reward proportion
|
||||
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type("SAL"));
|
||||
std::string miner_asset_type = "SAL";
|
||||
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
miner_asset_type = "SAL1";
|
||||
}
|
||||
|
||||
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(miner_asset_type));
|
||||
boost::multiprecision::int128_t source_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
|
||||
if (result && (m_height>0 || result != MDB_NOTFOUND))
|
||||
if (result && result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
boost::multiprecision::int128_t final_source_tally = source_tally;
|
||||
for (const auto& out: tx.vout) {
|
||||
@@ -1199,20 +1308,44 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
}
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
|
||||
LOG_PRINT_L1("tx ID " << tx_id << "\n\tTally before burn = " << source_tally.str() << "\n\tTally after burn = " << final_source_tally.str());
|
||||
|
||||
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
|
||||
boost::multiprecision::int128_t burn_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
|
||||
if (result && result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
// Sanity check - prevent overflow
|
||||
if (burn_tally > burn_tally + tx.amount_burnt)
|
||||
throw0(DB_ERROR("burn overflow detected when adding miner_tx for db transaction"));
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally + tx.amount_burnt;
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE) {
|
||||
if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::TRANSFER) {
|
||||
|
||||
// Get the current tally value for the source currency type
|
||||
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(tx.source_asset_type));
|
||||
boost::multiprecision::int128_t source_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
|
||||
boost::multiprecision::int128_t final_source_tally = source_tally - tx.amount_burnt - tx.rct_signatures.txnFee;
|
||||
boost::multiprecision::int128_t coinbase = get_block_already_generated_coins(m_height-1);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
// Sanity check - prevent underflow
|
||||
if (source_tally < final_source_tally)
|
||||
throw0(DB_ERROR("numeric underflow detected when processing C/B/S/A/T for db transaction"));
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
|
||||
LOG_PRINT_L1("tx ID " << tx_id << "\n\tTally before burn = " << source_tally.str() << "\n\tTally after burn = " << final_source_tally.str());
|
||||
|
||||
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
|
||||
boost::multiprecision::int128_t burn_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
|
||||
if (result && /*(m_height>0 ||*/ result != MDB_NOTFOUND/*)*/)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally + tx.amount_burnt + tx.rct_signatures.txnFee;
|
||||
// Sanity check - prevent underflow
|
||||
if (burn_tally > final_burn_tally)
|
||||
throw0(DB_ERROR("burn overflow detected when adding tx for db transaction"));
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
@@ -1236,17 +1369,25 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
MDB_val_copy<uint64_t> source_idx(asset.first);
|
||||
boost::multiprecision::int128_t source_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
|
||||
if (result)
|
||||
if (result == MDB_NOTFOUND)
|
||||
throw0(DB_ERROR("minted asset not found"));
|
||||
else if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
if (source_tally > source_tally + asset.second)
|
||||
throw0(DB_ERROR("add_transaction_data() - mint overflow"));
|
||||
boost::multiprecision::int128_t final_source_tally = source_tally + asset.second;
|
||||
boost::multiprecision::int128_t coinbase = get_block_already_generated_coins(m_height-1);
|
||||
if (source_tally == 0 && result == MDB_NOTFOUND) {
|
||||
if (tx.source_asset_type == "SAL") {
|
||||
final_source_tally += coinbase;
|
||||
}
|
||||
}
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
|
||||
LOG_PRINT_L1("tx ID " << tx_id << "\n\tAsset Type = " << cryptonote::asset_type_from_id(asset.first) << "\n\tTally before burn =" << source_tally.str() << "\n\tTally after burn =" << final_source_tally.str());
|
||||
|
||||
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
|
||||
boost::multiprecision::int128_t burn_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
|
||||
if (result && result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
if (burn_tally < asset.second)
|
||||
throw0(DB_ERROR("add_transaction_data() - burn underflow"));
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally - asset.second;
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,7 +1398,17 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
yield_tx_info yield_data;
|
||||
yield_data.block_height = m_height;
|
||||
yield_data.tx_hash = tx_hash;
|
||||
yield_data.return_address = tx.return_address;
|
||||
if (tx.version == TRANSACTION_VERSION_2_OUTS) {
|
||||
if (tx.return_address == crypto::null_pkey)
|
||||
throw0(DB_ERROR("missing return_address entry (needed to create yield data for PROTOCOL_TX) - v2 STAKE"));
|
||||
yield_data.return_address = tx.return_address;
|
||||
} else if (tx.version >= TRANSACTION_VERSION_N_OUTS) {
|
||||
if (tx.return_address_list.empty())
|
||||
throw0(DB_ERROR("no return_address_list entry (needed to create yield data for the PROTOCOL_TX)"));
|
||||
else if (tx.return_address_list.size() > 1)
|
||||
throw0(DB_ERROR("too many return_address_list entries provided (only one needed to create yield data for the PROTOCOL_TX)"));
|
||||
yield_data.return_address = tx.return_address_list[0];
|
||||
}
|
||||
yield_data.locked_coins = tx.amount_burnt;
|
||||
if (tx.vin.empty())
|
||||
throw0(DB_ERROR("tx.vin is empty (needed to create yield data for the PROTOCOL_TX)"));
|
||||
@@ -1294,6 +1445,60 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
throw0(DB_ERROR( lmdb_error("Failed to add tx yield data to db transaction: ", result).c_str() ));
|
||||
}
|
||||
|
||||
// Is there audit_tx data to add?
|
||||
if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
|
||||
// Create the object we are going to write to the database
|
||||
yield_tx_info audit_data;
|
||||
audit_data.block_height = m_height;
|
||||
audit_data.tx_hash = tx_hash;
|
||||
if (tx.version == TRANSACTION_VERSION_2_OUTS) {
|
||||
if (tx.return_address == crypto::null_pkey)
|
||||
throw0(DB_ERROR("missing return_address entry (needed to create audit data for PROTOCOL_TX) - v2 STAKE"));
|
||||
audit_data.return_address = tx.return_address;
|
||||
} else if (tx.version >= TRANSACTION_VERSION_N_OUTS) {
|
||||
if (tx.return_address_list.empty())
|
||||
throw0(DB_ERROR("no return_address_list entry (needed to create audit data for the PROTOCOL_TX)"));
|
||||
else if (tx.return_address_list.size() > 1)
|
||||
throw0(DB_ERROR("too many return_address_list entries provided (only one needed to create audit data for the PROTOCOL_TX)"));
|
||||
audit_data.return_address = tx.return_address_list[0];
|
||||
}
|
||||
audit_data.locked_coins = tx.amount_burnt;
|
||||
if (tx.vin.empty())
|
||||
throw0(DB_ERROR("tx.vin is empty (needed to create audit data for the PROTOCOL_TX)"));
|
||||
if (tx.vin[0].type() != typeid(cryptonote::txin_to_key))
|
||||
throw0(DB_ERROR("tx.vin[0] is wrong type (needed to create audit data for the PROTOCOL_TX)"));
|
||||
audit_data.return_pubkey = tx.return_pubkey;
|
||||
if (tx.vout.size() != 1)
|
||||
throw0(DB_ERROR("tx.vout is wrong size (needed to create audit data for the PROTOCOL_TX)"));
|
||||
if (!cryptonote::get_output_public_key(tx.vout[0], audit_data.P_change))
|
||||
throw0(DB_ERROR("failed to get P_change from tx.vout[0] (needed to create audit data for the PROTOCOL_TX)"));
|
||||
|
||||
// Because LMDB is shockingly bad at handling duplicates, we have resorted to using a counter of elements
|
||||
// in the first element of the struct.
|
||||
MDB_val data;
|
||||
MDB_val_set(val_height, m_height);
|
||||
result = mdb_cursor_get(m_cur_audit_txs, &val_height, &data, MDB_SET);
|
||||
if (!result)
|
||||
{
|
||||
mdb_size_t num_elems = 0;
|
||||
result = mdb_cursor_count(m_cur_audit_txs, &num_elems);
|
||||
if (result)
|
||||
throw0(DB_ERROR(std::string("Failed to get number of audit TXs for height: ").append(mdb_strerror(result)).c_str()));
|
||||
audit_data.block_height = num_elems;
|
||||
}
|
||||
else if (result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get output amount in db transaction: ", result).c_str()));
|
||||
else
|
||||
audit_data.block_height = 0;
|
||||
|
||||
// Now we know how many there are, write out the data to the DB
|
||||
MDB_val_set(val_audit_tx_data, audit_data);
|
||||
result = mdb_cursor_put(m_cur_audit_txs, &val_height, &val_audit_tx_data, MDB_APPENDDUP);
|
||||
if (result)
|
||||
throw0(DB_ERROR( lmdb_error("Failed to add tx audit data to db transaction: ", result).c_str() ));
|
||||
}
|
||||
|
||||
return tx_id;
|
||||
}
|
||||
|
||||
@@ -1316,6 +1521,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
CURSOR(tx_outputs)
|
||||
CURSOR(circ_supply_tally)
|
||||
CURSOR(yield_txs)
|
||||
CURSOR(audit_txs)
|
||||
|
||||
MDB_val_set(val_h, tx_hash);
|
||||
|
||||
@@ -1359,42 +1565,70 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of prunable hash tx to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
const uint8_t hf_version = m_hardfork->get_ideal_version(m_height);
|
||||
if (tx.type == cryptonote::transaction_type::MINER) {
|
||||
|
||||
// Update the circulating supply tally because of potentially burnt block_reward proportion
|
||||
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type("SAL"));
|
||||
std::string miner_asset_type = "SAL";
|
||||
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
miner_asset_type = "SAL1";
|
||||
}
|
||||
|
||||
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(miner_asset_type));
|
||||
boost::multiprecision::int128_t source_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
|
||||
if (result && (m_height>0 || result != MDB_NOTFOUND))
|
||||
if (result && result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("remove_transaction_data() - Failed to get circulating supply tally when removing db transaction: ", result).c_str()));
|
||||
boost::multiprecision::int128_t final_source_tally = source_tally;
|
||||
for (const auto& out: tx.vout) {
|
||||
|
||||
// Sanity check - prevent underflow
|
||||
if (final_source_tally < final_source_tally - out.amount)
|
||||
throw0(DB_ERROR("remove_transaction_data() - numeric underflow detected when removing miner_tx for db transaction"));
|
||||
throw0(DB_ERROR("numeric underflow detected when removing miner_tx for db transaction"));
|
||||
|
||||
// Fetch the amount for this output
|
||||
final_source_tally -= out.amount;
|
||||
}
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
|
||||
LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\n\tTally before burn = " << source_tally.str() << "\n\tTally after burn = " << final_source_tally.str());
|
||||
|
||||
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
|
||||
boost::multiprecision::int128_t burn_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
|
||||
if (result && result != MDB_NOTFOUND)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
// Sanity check - prevent underflow
|
||||
if (burn_tally < tx.amount_burnt)
|
||||
throw0(DB_ERROR("burn underflow detected when removing miner_tx for db transaction"));
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally - tx.amount_burnt;
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE) {
|
||||
if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::TRANSFER) {
|
||||
|
||||
// Get the current tally value for the source currency type
|
||||
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(tx.source_asset_type));
|
||||
boost::multiprecision::int128_t source_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
|
||||
if (result == MDB_NOTFOUND)
|
||||
throw0(DB_ERROR("remove_transaction_data() - minted asset not found"));
|
||||
// Sanity check - prevent overflow
|
||||
if (source_tally > source_tally + tx.amount_burnt + tx.rct_signatures.txnFee)
|
||||
throw0(DB_ERROR("remove_transaction_data() - numeric overflow detected when processing C/B/S for db transaction"));
|
||||
boost::multiprecision::int128_t final_source_tally = source_tally + tx.amount_burnt + tx.rct_signatures.txnFee;
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when removing db transaction: ", result).c_str()));
|
||||
// Sanity check - prevent overflow
|
||||
if (source_tally > final_source_tally)
|
||||
throw0(DB_ERROR("numeric overflow detected when processing C/B/S/A/T for db transaction"));
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
|
||||
LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\n\tTally before remint =" << source_tally.str() << "\n\tTally after remint =" << final_source_tally.str());
|
||||
|
||||
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
|
||||
boost::multiprecision::int128_t burn_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
|
||||
if (result && /*(m_height>0 ||*/ result != MDB_NOTFOUND/*)*/)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally - tx.amount_burnt - tx.rct_signatures.txnFee;
|
||||
// Sanity check - prevent underflow
|
||||
if (burn_tally < (tx.amount_burnt + tx.rct_signatures.txnFee))
|
||||
throw0(DB_ERROR("burn underflow detected when removing tx for db transaction"));
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
@@ -1408,7 +1642,6 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
bool ok = cryptonote::get_output_asset_type(out, asset_type);
|
||||
if (!ok)
|
||||
throw0(DB_ERROR("failed to get output asset type (needed to update the circulating supply data for the PROTOCOL_TX)"));
|
||||
|
||||
minted_amounts[cryptonote::asset_id_from_type(asset_type)] += out.amount;
|
||||
}
|
||||
|
||||
@@ -1420,12 +1653,24 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
boost::multiprecision::int128_t source_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
|
||||
if (result == MDB_NOTFOUND)
|
||||
throw0(DB_ERROR("remove_transaction_data() - minted asset not found"));
|
||||
throw0(DB_ERROR("minted asset not found"));
|
||||
else if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when removing db transaction: ", result).c_str()));
|
||||
if (source_tally < asset.second)
|
||||
throw0(DB_ERROR("remove_transaction_data() - mint underflow"));
|
||||
boost::multiprecision::int128_t final_source_tally = source_tally - asset.second;
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
|
||||
LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\n\tAsset Type = " << cryptonote::asset_type_from_id(asset.first) << "\n\tTally before undoing mint =" << source_tally.str() << "\n\tTally after undoing mint =" << final_source_tally.str());
|
||||
|
||||
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
|
||||
boost::multiprecision::int128_t burn_tally = 0;
|
||||
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when removing db transaction: ", result).c_str()));
|
||||
if (burn_tally > burn_tally + asset.second)
|
||||
throw0(DB_ERROR("remove_transaction_data() - burn overflow"));
|
||||
boost::multiprecision::int128_t final_burn_tally = burn_tally + asset.second;
|
||||
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
|
||||
}
|
||||
}
|
||||
remove_tx_outputs(tip->data.tx_id, tx);
|
||||
@@ -1442,7 +1687,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of tx outputs to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
// SRCG: The following code is designed to clean up the STAKE transactions, but it is very poorly written
|
||||
// SRCG: The following code is designed to clean up AUDIT+STAKE transactions, but it is very poorly written
|
||||
// Since transactions are ALWAYS supposed to be created in order, it stands that they should ALWAYS be
|
||||
// removed in REVERSE ORDER. Yet the following loop starts from the beginning - this is the worst possible
|
||||
// implementation in performance terms, since it will ALWAYS take the longest possible time to remove the
|
||||
@@ -1450,6 +1695,30 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
|
||||
// RECODE TO START FROM THE END OF THE DATABASE TABLE, AND THROW AN EXCEPTION IF YOU DO NOT MATCH FIRST TIME!
|
||||
|
||||
// Is there audit_tx data to remove?
|
||||
if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
// Remove any yield_tx data for this transaction
|
||||
MDB_val_set(val_height, m_height);
|
||||
MDB_val v;
|
||||
MDB_cursor_op op = MDB_SET;
|
||||
while (1) {
|
||||
result = mdb_cursor_get(m_cur_audit_txs, &val_height, &v, op);
|
||||
if (result == MDB_NOTFOUND) {
|
||||
throw1(DB_ERROR("Failed to locate audit tx for removal from db transaction"));
|
||||
} else if (result) {
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate audit_tx data for removal: ", result).c_str()));
|
||||
}
|
||||
op = MDB_NEXT_DUP;
|
||||
const yield_tx_info ati = *(const yield_tx_info*)v.mv_data;
|
||||
if (ati.tx_hash == tx_hash) {
|
||||
result = mdb_cursor_del(m_cur_audit_txs, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of audit_tx data to db transaction: ", result).c_str()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is there yield_tx data to remove?
|
||||
if (tx.type == cryptonote::transaction_type::STAKE) {
|
||||
// Remove any yield_tx data for this transaction
|
||||
@@ -1818,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;
|
||||
@@ -1844,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)
|
||||
{
|
||||
@@ -1956,6 +2285,9 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||
lmdb_db_open(txn, LMDB_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_yield_txs, "Failed to open db handle for m_yield_txs");
|
||||
lmdb_db_open(txn, LMDB_YIELD_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_yield_blocks, "Failed to open db handle for m_yield_blocks");
|
||||
|
||||
lmdb_db_open(txn, LMDB_AUDIT_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_audit_txs, "Failed to open db handle for m_audit_txs");
|
||||
lmdb_db_open(txn, LMDB_AUDIT_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_audit_blocks, "Failed to open db handle for m_audit_blocks");
|
||||
|
||||
mdb_set_dupsort(txn, m_spent_keys, compare_hash32);
|
||||
mdb_set_dupsort(txn, m_block_heights, compare_hash32);
|
||||
mdb_set_dupsort(txn, m_tx_indices, compare_hash32);
|
||||
@@ -1979,6 +2311,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||
mdb_set_compare(txn, m_circ_supply_tally, compare_uint64);
|
||||
|
||||
mdb_set_dupsort(txn, m_yield_txs, compare_uint64);
|
||||
mdb_set_dupsort(txn, m_audit_txs, compare_uint64);
|
||||
|
||||
if (!(mdb_flags & MDB_RDONLY))
|
||||
{
|
||||
@@ -2160,6 +2493,10 @@ void BlockchainLMDB::reset()
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_yield_txs: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_yield_blocks, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_yield_blocks: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_audit_txs, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_txs: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_audit_blocks, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_blocks: ", result).c_str()));
|
||||
|
||||
// init with current version
|
||||
MDB_val_str(k, "version");
|
||||
@@ -3469,9 +3806,12 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
|
||||
uint64_t m_coinbase = get_block_already_generated_coins(m_height-1);
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " - mined supply for SAL = " << m_coinbase);
|
||||
|
||||
// SRCG: For V1, we can simply return this number, because there is no other source of coins
|
||||
//circulating_supply["SAL"] = m_coinbase;
|
||||
//return circulating_supply;
|
||||
uint64_t staked_coins = 0;
|
||||
yield_block_info ybi;
|
||||
int result = get_yield_block_info(m_height-1, ybi);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get YBI when querying supply: ", result).c_str()));
|
||||
staked_coins = ybi.locked_coins_tally;
|
||||
|
||||
check_open();
|
||||
|
||||
@@ -3480,8 +3820,6 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
|
||||
|
||||
MDB_val k;
|
||||
MDB_val v;
|
||||
int result = 0;
|
||||
|
||||
MDB_cursor_op op = MDB_FIRST;
|
||||
while (1)
|
||||
{
|
||||
@@ -3501,9 +3839,14 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
|
||||
// Check for SAL - we need to adjust the total for them
|
||||
if (currency_type == 0) {
|
||||
// Get the current circulating supply for SAL
|
||||
amount += m_coinbase;
|
||||
//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>();
|
||||
}
|
||||
|
||||
@@ -3512,8 +3855,13 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
|
||||
// NEAC: check for empty supply tally - only happens prior to first conversion on chain
|
||||
if (circulating_supply.empty()) {
|
||||
circulating_supply["SAL"] = m_coinbase;
|
||||
circulating_supply["SAL1"] = 0;
|
||||
circulating_supply["BURN"] = 0;
|
||||
}
|
||||
circulating_supply["BURN"] = m_coinbase - circulating_supply["SAL"];
|
||||
|
||||
// Adjust the supply to account for the staked coins
|
||||
circulating_supply["STAKE"] = staked_coins;
|
||||
|
||||
return circulating_supply;
|
||||
}
|
||||
|
||||
@@ -4705,7 +5053,7 @@ void BlockchainLMDB::block_rtxn_abort() const
|
||||
}
|
||||
|
||||
uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
|
||||
const std::vector<std::pair<transaction, blobdata>>& txs, const cryptonote::network_type& nettype, cryptonote::yield_block_info& ybi)
|
||||
const std::vector<std::pair<transaction, blobdata>>& txs, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
@@ -4723,7 +5071,7 @@ uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t
|
||||
|
||||
try
|
||||
{
|
||||
BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, nettype, ybi);
|
||||
BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, nettype, ybi, abi);
|
||||
}
|
||||
catch (const DB_ERROR_TXN_START& e)
|
||||
{
|
||||
@@ -5188,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 { \
|
||||
@@ -5220,8 +5567,38 @@ uint64_t BlockchainLMDB::get_database_size() const
|
||||
|
||||
#define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global"))
|
||||
|
||||
void BlockchainLMDB::migrate_2_3()
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
uint64_t i;
|
||||
int result;
|
||||
mdb_txn_safe txn(false);
|
||||
MDB_val k, v;
|
||||
char *ptr;
|
||||
|
||||
MGINFO_YELLOW("Migrating blockchain from DB version 2 to 3 - this may take a while:");
|
||||
|
||||
// Create the missing (and empty) "audit_block_info" records for all blocks
|
||||
do {
|
||||
} while(0);
|
||||
|
||||
uint32_t version = VERSION;
|
||||
v.mv_data = (void *)&version;
|
||||
v.mv_size = sizeof(version);
|
||||
MDB_val_str(vk, "version");
|
||||
result = mdb_txn_begin(m_env, NULL, 0, txn);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
|
||||
result = mdb_put(txn, m_properties, &vk, &v, 0);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str()));
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
void BlockchainLMDB::migrate(const uint32_t oldversion)
|
||||
{
|
||||
//if (oldversion < 3)
|
||||
// migrate_2_3();
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
||||
|
||||
@@ -78,6 +78,8 @@ typedef struct mdb_txn_cursors
|
||||
MDB_cursor *m_txc_circ_supply_tally;
|
||||
MDB_cursor *m_txc_yield_txs;
|
||||
MDB_cursor *m_txc_yield_blocks;
|
||||
MDB_cursor *m_txc_audit_txs;
|
||||
MDB_cursor *m_txc_audit_blocks;
|
||||
|
||||
} mdb_txn_cursors;
|
||||
|
||||
@@ -104,6 +106,8 @@ typedef struct mdb_txn_cursors
|
||||
#define m_cur_circ_supply_tally m_cursors->m_txc_circ_supply_tally
|
||||
#define m_cur_yield_txs m_cursors->m_txc_yield_txs
|
||||
#define m_cur_yield_blocks m_cursors->m_txc_yield_blocks
|
||||
#define m_cur_audit_txs m_cursors->m_txc_audit_txs
|
||||
#define m_cur_audit_blocks m_cursors->m_txc_audit_blocks
|
||||
|
||||
typedef struct mdb_rflags
|
||||
{
|
||||
@@ -131,6 +135,8 @@ typedef struct mdb_rflags
|
||||
bool m_rf_circ_supply_tally;
|
||||
bool m_rf_yield_txs;
|
||||
bool m_rf_yield_blocks;
|
||||
bool m_rf_audit_txs;
|
||||
bool m_rf_audit_blocks;
|
||||
} mdb_rflags;
|
||||
|
||||
typedef struct mdb_threadinfo
|
||||
@@ -341,8 +347,9 @@ public:
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, blobdata>>& txs
|
||||
, const cryptonote::network_type& nettype
|
||||
, const cryptonote::network_type nettype
|
||||
, cryptonote::yield_block_info& ybi
|
||||
, cryptonote::audit_block_info& abi
|
||||
);
|
||||
|
||||
virtual void set_batch_transactions(bool batch_transactions);
|
||||
@@ -400,8 +407,10 @@ private:
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
const cryptonote::network_type& nettype,
|
||||
cryptonote::yield_block_info& ybi
|
||||
uint64_t audit_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
);
|
||||
|
||||
virtual void remove_block();
|
||||
@@ -455,12 +464,16 @@ private:
|
||||
// migrate from older DB version to current
|
||||
void migrate(const uint32_t oldversion);
|
||||
|
||||
// migrate from DB version 0 to 1
|
||||
//void migrate_0_1();
|
||||
// migrate from DB version 2 to 3
|
||||
void migrate_2_3();
|
||||
|
||||
void cleanup_batch();
|
||||
|
||||
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi);
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container);
|
||||
virtual int get_audit_block_info(const uint64_t height, audit_block_info& abi) const;
|
||||
virtual int get_audit_tx_info(const uint64_t height, std::vector<yield_tx_info>& ati_container) const;
|
||||
|
||||
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const;
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const;
|
||||
|
||||
private:
|
||||
MDB_env* m_env;
|
||||
@@ -499,6 +512,9 @@ private:
|
||||
MDB_dbi m_yield_txs;
|
||||
MDB_dbi m_yield_blocks;
|
||||
|
||||
MDB_dbi m_audit_txs;
|
||||
MDB_dbi m_audit_blocks;
|
||||
|
||||
mutable uint64_t m_cum_size; // used in batch size estimation
|
||||
mutable unsigned int m_cum_count;
|
||||
std::string m_folder;
|
||||
|
||||
+32
-14
@@ -76,7 +76,7 @@ public:
|
||||
virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }
|
||||
virtual cryptonote::block_header get_block_header(const crypto::hash& h) const override { return cryptonote::block_header(); }
|
||||
virtual uint64_t get_block_timestamp(const uint64_t& height) const override { return 0; }
|
||||
virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const override { return {}; }
|
||||
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const override { return {}; }
|
||||
virtual uint64_t get_top_block_timestamp() const override { return 0; }
|
||||
virtual size_t get_block_weight(const uint64_t& height) const override { return 128; }
|
||||
virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override { return {}; }
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { if (block_height) *block_height = 0; return crypto::hash(); }
|
||||
virtual cryptonote::block get_top_block() const override { return cryptonote::block(); }
|
||||
virtual uint64_t height() const override { return 1; }
|
||||
virtual std::map<std::string, uint64_t> get_circulating_supply() const override { return std::map<std::string, uint64_t>{}; }
|
||||
virtual bool tx_exists(const crypto::hash& h) const override { return false; }
|
||||
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const override { return false; }
|
||||
virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const override { return 0; }
|
||||
@@ -101,20 +102,23 @@ public:
|
||||
virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const override { return std::vector<cryptonote::transaction>(); }
|
||||
virtual uint64_t get_tx_block_height(const crypto::hash& h) const override { return 0; }
|
||||
virtual uint64_t get_num_outputs(const uint64_t& amount) const override { return 1; }
|
||||
virtual uint64_t get_num_outputs_of_asset_type(const std::string asset_type) const override { return 1; }
|
||||
virtual uint64_t get_indexing_base() const override { return 0; }
|
||||
virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const override { return cryptonote::output_data_t(); }
|
||||
virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const override { return cryptonote::tx_out_index(); }
|
||||
virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const override { return cryptonote::tx_out_index(); }
|
||||
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const override {}
|
||||
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const override {}
|
||||
virtual void get_output_id_from_asset_type_output_index(const std::string asset_type, const std::vector<uint64_t> &asset_type_output_indices, std::vector<uint64_t> &output_indices) const override {}
|
||||
virtual uint64_t get_output_id_from_asset_type_output_index(const std::string asset_type, const uint64_t &asset_type_output_index) const override { return 1; }
|
||||
virtual bool can_thread_bulk_indices() const override { return false; }
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const override { return std::vector<std::vector<uint64_t>>(); }
|
||||
virtual std::vector<std::vector<std::pair<uint64_t, uint64_t>>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const override { return std::vector<std::vector<std::pair<uint64_t, uint64_t>>>(); }
|
||||
virtual bool has_key_image(const crypto::key_image& img) const override { return false; }
|
||||
virtual void remove_block() override { }
|
||||
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<cryptonote::transaction, cryptonote::blobdata_ref>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) override {return 0;}
|
||||
virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) override {}
|
||||
virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) override {return 0;}
|
||||
virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) override {}
|
||||
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<cryptonote::transaction, cryptonote::blobdata_ref>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash, const bool miner_tx) override {return 0;}
|
||||
virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx, const bool miner_tx) override {}
|
||||
virtual std::pair<uint64_t, uint64_t> add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) override {return std::make_pair(0,0);}
|
||||
virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<std::pair<uint64_t, uint64_t>>& amount_output_indices) override {}
|
||||
virtual void add_spent_key(const crypto::key_image& k_image) override {}
|
||||
virtual void remove_spent_key(const crypto::key_image& k_image) override {}
|
||||
|
||||
@@ -135,17 +139,31 @@ public:
|
||||
virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const override { return false; }
|
||||
virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const override { return false; }
|
||||
virtual uint64_t get_database_size() const override { return 0; }
|
||||
|
||||
virtual int get_audit_block_info(const uint64_t height, audit_block_info& abi) const override { return 0; }
|
||||
virtual int get_audit_tx_info(const uint64_t height, std::vector<yield_tx_info>& ati_container) const override { return 0; }
|
||||
|
||||
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const override { return 0; }
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const override { return 0; }
|
||||
|
||||
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const override { return ""; }
|
||||
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata_ref*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const override { return false; }
|
||||
|
||||
virtual void add_block( const cryptonote::block& blk
|
||||
, size_t block_weight
|
||||
, uint64_t long_term_block_weight
|
||||
, const cryptonote::difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, uint64_t num_rct_outs
|
||||
, const crypto::hash& blk_hash
|
||||
) override { }
|
||||
virtual void add_block( const block& blk,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
) override { }
|
||||
virtual cryptonote::block get_block_from_height(const uint64_t& height) const override { return cryptonote::block(); }
|
||||
virtual void set_hard_fork_version(uint64_t height, uint8_t version) override {}
|
||||
virtual uint8_t get_hard_fork_version(uint64_t height) const override { return 0; }
|
||||
|
||||
@@ -133,6 +133,31 @@ monero_private_headers(blockchain_stats
|
||||
${blockchain_stats_private_headers})
|
||||
|
||||
|
||||
set(blockchain_scanner_sources
|
||||
blockchain_scanner.cpp
|
||||
)
|
||||
|
||||
set(blockchain_scanner_private_headers)
|
||||
|
||||
monero_private_headers(blockchain_scanner
|
||||
${blockchain_scanner_private_headers})
|
||||
|
||||
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()
|
||||
|
||||
monero_add_executable(blockchain_import
|
||||
${blockchain_import_sources}
|
||||
${blockchain_import_private_headers})
|
||||
@@ -281,6 +306,56 @@ set_property(TARGET blockchain_depth
|
||||
OUTPUT_NAME "salvium-blockchain-depth")
|
||||
install(TARGETS blockchain_depth DESTINATION bin)
|
||||
|
||||
monero_add_executable(blockchain_scanner
|
||||
${blockchain_scanner_sources}
|
||||
${blockchain_scanner_private_headers})
|
||||
|
||||
target_link_libraries(blockchain_scanner
|
||||
PRIVATE
|
||||
cryptonote_core
|
||||
blockchain_db
|
||||
version
|
||||
epee
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES})
|
||||
|
||||
set_property(TARGET blockchain_scanner
|
||||
PROPERTY
|
||||
OUTPUT_NAME "salvium-blockchain-scanner")
|
||||
install(TARGETS blockchain_scanner DESTINATION bin)
|
||||
|
||||
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_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()
|
||||
|
||||
monero_add_executable(blockchain_stats
|
||||
${blockchain_stats_sources}
|
||||
${blockchain_stats_private_headers})
|
||||
|
||||
@@ -488,8 +488,9 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
|
||||
try
|
||||
{
|
||||
cryptonote::yield_block_info ybi; // This just gets discarded because we aren't looking to maintain a cache of YBI data in the import utility
|
||||
cryptonote::audit_block_info abi; // This just gets discarded because we aren't looking to maintain a cache of ABI data in the import utility
|
||||
uint64_t long_term_block_weight = core.get_blockchain_storage().get_next_long_term_block_weight(block_weight);
|
||||
core.get_blockchain_storage().get_db().add_block(std::make_pair(b, block_to_blob(b)), block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, ybi);
|
||||
core.get_blockchain_storage().get_db().add_block(std::make_pair(b, block_to_blob(b)), block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, ybi, abi);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
// Copyright (c) 2014-2019, 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 <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "common/command_line.h"
|
||||
#include "common/i18n.h"
|
||||
#include "common/password.h"
|
||||
#include "common/varint.h"
|
||||
#include "cryptonote_basic/cryptonote_boost_serialization.h"
|
||||
#include "cryptonote_core/tx_pool.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "oracle/pricing_record.h"
|
||||
#include "version.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "bcutil"
|
||||
|
||||
#define DELIM "|"
|
||||
#define DEF_STAKE_MODE "all"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
|
||||
namespace scanner
|
||||
{
|
||||
const char* tr(const char* str)
|
||||
{
|
||||
return i18n_translate(str, "tools::scanner");
|
||||
}
|
||||
}
|
||||
|
||||
static bool stop_requested = false;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
||||
uint32_t log_level = 0;
|
||||
uint64_t block_start = 0;
|
||||
uint64_t block_stop = 0;
|
||||
|
||||
tools::on_startup();
|
||||
|
||||
boost::filesystem::path output_file_path;
|
||||
|
||||
po::options_description desc_cmd_only("Command line options");
|
||||
po::options_description desc_cmd_sett("Command line options and settings options");
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_start = {"block-start", "start at block number", block_start};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
|
||||
const command_line::arg_descriptor<std::string> arg_delimiter = {"delimiter", "\"<string>\"", DELIM};
|
||||
const command_line::arg_descriptor<std::string> arg_stake_mode = {"stake", "\"<string>\"", DEF_STAKE_MODE};
|
||||
const command_line::arg_descriptor<bool> arg_check_asset_types = {"check-asset-types", "Scan for asset-type issues", false};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_start);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_stop);
|
||||
command_line::add_arg(desc_cmd_sett, arg_delimiter);
|
||||
command_line::add_arg(desc_cmd_sett, arg_stake_mode);
|
||||
command_line::add_arg(desc_cmd_sett, arg_check_asset_types);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (! r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << "Salvium '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL;
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
mlog_configure(mlog_get_default_log_path("monero-blockchain-stats.log"), true);
|
||||
if (!command_line::is_arg_defaulted(vm, arg_log_level))
|
||||
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
|
||||
else
|
||||
mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
|
||||
|
||||
LOG_PRINT_L0("Starting...");
|
||||
|
||||
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
|
||||
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
|
||||
block_start = command_line::get_arg(vm, arg_block_start);
|
||||
block_stop = command_line::get_arg(vm, arg_block_stop);
|
||||
std::string delimiter = command_line::get_arg(vm, arg_delimiter);
|
||||
std::string stake_mode = command_line::get_arg(vm, arg_stake_mode);
|
||||
bool opt_check_asset_types = command_line::get_arg(vm, arg_check_asset_types);
|
||||
|
||||
// If we wanted to use the memory pool, we would set up a fake_core.
|
||||
|
||||
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
|
||||
// 1. Blockchain has the init() method for easy setup
|
||||
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
|
||||
//
|
||||
// cannot match blockchain_storage setup above with just one line,
|
||||
// e.g.
|
||||
// Blockchain* core_storage = new Blockchain(NULL);
|
||||
// because unlike blockchain_storage constructor, which takes a pointer to
|
||||
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
|
||||
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
|
||||
std::unique_ptr<BlockchainAndPool> core_storage = std::make_unique<BlockchainAndPool>();
|
||||
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
LOG_ERROR("Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
LOG_PRINT_L0("database: LMDB");
|
||||
|
||||
boost::filesystem::path folder(opt_data_dir);
|
||||
if (opt_stagenet) {
|
||||
folder /= std::to_string(STAGENET_VERSION);
|
||||
} else if (opt_testnet) {
|
||||
folder /= std::to_string(TESTNET_VERSION);
|
||||
}
|
||||
folder /= db->get_db_name();
|
||||
LOG_PRINT_L0("Loading blockchain from folder " << folder << " ...");
|
||||
const std::string filename = folder.string();
|
||||
|
||||
try
|
||||
{
|
||||
db->open(filename, DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_PRINT_L0("Error opening database: " << e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->blockchain.init(db, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
LOG_PRINT_L0("Source blockchain storage initialized OK");
|
||||
|
||||
tools::signal_handler::install([](int type) {
|
||||
stop_requested = true;
|
||||
});
|
||||
|
||||
const uint64_t db_height = db->height();
|
||||
if (!block_stop)
|
||||
block_stop = db_height;
|
||||
MINFO("Starting from height " << block_start << ", stopping at height " << block_stop);
|
||||
|
||||
/*
|
||||
* The default output can be plotted with GnuPlot using these commands:
|
||||
set key autotitle columnhead
|
||||
set title "Salvium Blockchain Growth"
|
||||
set timefmt "%Y-%m-%d"
|
||||
set xdata time
|
||||
set xrange ["2014-04-17":*]
|
||||
set format x "%Y-%m-%d"
|
||||
set yrange [0:*]
|
||||
set y2range [0:*]
|
||||
set ylabel "Txs/Day"
|
||||
set y2label "Bytes"
|
||||
set y2tics nomirror
|
||||
plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' using (timecolumn(1,"%Y-%m-%d")):7 axes x1y2 with lines
|
||||
*/
|
||||
|
||||
// spit out a comment that GnuPlot can use as an index
|
||||
std::cout << ENDL << "# DATA" << ENDL;
|
||||
std::cout << "Date" << delimiter << "Height" << delimiter << "Transaction ID" << delimiter << "Reason" << delimiter << "Extra Information";
|
||||
std::cout << ENDL;
|
||||
|
||||
#define MAX_INOUT 0xffffffff
|
||||
#define MAX_RINGS 0xffffffff
|
||||
|
||||
struct tm prevtm = {0}, currtm;
|
||||
uint64_t prevsz = 0, currsz = 0;
|
||||
uint64_t prevtxs = 0, currtxs = 0;
|
||||
uint64_t currblks = 0;
|
||||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
|
||||
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)
|
||||
{
|
||||
cryptonote::blobdata bd = db->get_block_blob_from_height(h);
|
||||
cryptonote::block blk;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, blk))
|
||||
{
|
||||
LOG_PRINT_L0("Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
time_t tt = blk.timestamp;
|
||||
char timebuf[64];
|
||||
epee::misc_utils::get_gmt_time(tt, currtm);
|
||||
if (!prevtm.tm_year)
|
||||
prevtm = currtm;
|
||||
// catch change of day
|
||||
if (currtm.tm_mday > prevtm.tm_mday || (currtm.tm_mday == 1 && prevtm.tm_mday > 27))
|
||||
{
|
||||
// check for timestamp fudging around month ends
|
||||
if (prevtm.tm_mday == 1 && currtm.tm_mday > 27)
|
||||
goto skip;
|
||||
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d", &currtm);
|
||||
prevtm = currtm;
|
||||
}
|
||||
skip:
|
||||
currsz += bd.size();
|
||||
uint64_t coinbase_amount;
|
||||
uint64_t tx_fee_amount = 0;
|
||||
std::set<std::string> used_assets, miner_tx_assets, protocol_tx_assets;
|
||||
std::map<size_t, std::vector<std::string>> used_tx_versions;
|
||||
used_assets.insert("SAL");
|
||||
|
||||
uint8_t hf_version = blk.major_version;
|
||||
|
||||
// Check TX versions
|
||||
if (blk.miner_tx.version != 2) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.miner_tx.hash << "" << delimiter << "invalid miner TX version detected" << delimiter << "version:" << blk.miner_tx.version << std::endl;
|
||||
}
|
||||
if (blk.protocol_tx.version != 2 && h>0) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "invalid protocol TX version detected" << delimiter << "version:" << blk.protocol_tx.version << std::endl;
|
||||
}
|
||||
|
||||
// Get the miner_tx assets
|
||||
std::set<crypto::public_key> used_keys;
|
||||
for (const auto& miner_tx_vout : blk.miner_tx.vout) {
|
||||
std::string asset_type;
|
||||
if (!cryptonote::get_output_asset_type(miner_tx_vout, asset_type)) {
|
||||
throw std::runtime_error("Aborting: failed to get output asset type from miner_tx");
|
||||
} else if (blk.major_version >= HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL1") {
|
||||
throw std::runtime_error("Aborting: invalid output asset type from miner_tx: " + asset_type);
|
||||
} else if (blk.major_version < HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL") {
|
||||
throw std::runtime_error("Aborting: invalid output asset type from miner_tx: " + asset_type + ", HF:" + std::to_string(blk.major_version));
|
||||
}
|
||||
miner_tx_assets.insert(asset_type);
|
||||
if (miner_tx_vout.amount > 13500000000 && h>0) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.miner_tx.hash << "" << delimiter << "invalid miner TX amount detected" << delimiter << "amount:" << miner_tx_vout.amount << std::endl;
|
||||
}
|
||||
crypto::public_key key;
|
||||
cryptonote::get_output_public_key(miner_tx_vout, key);
|
||||
if (used_keys.count(key)) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.miner_tx.hash << "" << delimiter << "invalid miner TX - duplicate output detected" << delimiter << "pubkey:" << key << std::endl;
|
||||
}
|
||||
used_keys.insert(key);
|
||||
}
|
||||
|
||||
// Get the protocol_tx assets
|
||||
used_keys.clear();
|
||||
for (const auto& protocol_tx_vout : blk.protocol_tx.vout) {
|
||||
std::string asset_type;
|
||||
if (!cryptonote::get_output_asset_type(protocol_tx_vout, asset_type)) {
|
||||
throw std::runtime_error("Aborting: failed to get output asset type from protocol_tx");
|
||||
} else if (blk.major_version >= HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL1") {
|
||||
throw std::runtime_error("Aborting: invalid output asset type from protocol_tx: " + asset_type);
|
||||
} else if (blk.major_version < HF_VERSION_SALVIUM_ONE_PROOFS && asset_type != "SAL") {
|
||||
throw std::runtime_error("Aborting: invalid output asset type from protocol_tx: " + asset_type + ", HF:" + std::to_string(blk.major_version));
|
||||
}
|
||||
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;
|
||||
}
|
||||
crypto::public_key key;
|
||||
cryptonote::get_output_public_key(protocol_tx_vout, key);
|
||||
if (used_keys.count(key)) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << blk.protocol_tx.hash << "" << delimiter << "invalid protocol TX - duplicate output detected" << delimiter << "pubkey:" << key << std::endl;
|
||||
}
|
||||
used_keys.insert(key);
|
||||
}
|
||||
|
||||
for (const auto& tx_id : blk.tx_hashes)
|
||||
{
|
||||
if (tx_id == crypto::null_hash)
|
||||
{
|
||||
throw std::runtime_error("Aborting: tx == null_hash");
|
||||
}
|
||||
if (!db->get_pruned_tx_blob(tx_id, bd))
|
||||
{
|
||||
throw std::runtime_error("Aborting: tx not found");
|
||||
}
|
||||
transaction tx;
|
||||
if (!parse_and_validate_tx_base_from_blob(bd, tx))
|
||||
{
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX detected" << delimiter << std::endl;
|
||||
continue;
|
||||
}
|
||||
currsz += bd.size();
|
||||
if (db->get_prunable_tx_blob(tx_id, bd))
|
||||
currsz += bd.size();
|
||||
currtxs++;
|
||||
|
||||
if (tx.type != cryptonote::transaction_type::TRANSFER &&
|
||||
tx.type != cryptonote::transaction_type::BURN &&
|
||||
tx.type != cryptonote::transaction_type::STAKE &&
|
||||
tx.type != cryptonote::transaction_type::AUDIT) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX type detected" << delimiter << "type:" << (uint8_t)tx.type << std::endl;
|
||||
}
|
||||
|
||||
if ((tx.version != 2 && hf_version < HF_VERSION_ENABLE_N_OUTS) || (tx.version != 3 && hf_version < HF_VERSION_ENABLE_N_OUTS && tx.type == cryptonote::transaction_type::TRANSFER)) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX version detected" << delimiter << "version:" << tx.version << std::endl;
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::STAKE && stake_mode.compare("off")) {
|
||||
if (stake_mode.compare("all") == 0) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "STAKE TX detected" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << std::endl;
|
||||
} else if (stake_mode.compare("large") == 0 && tx.amount_burnt > 25000000000000llu) {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "large STAKE TX detected" << delimiter << "amount:" << (tx.amount_burnt / 100000000) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_check_asset_types) {
|
||||
if (tx.source_asset_type != "SAL") {
|
||||
throw std::runtime_error("Aborting: invalid source asset type found in tx");
|
||||
} else if (tx.destination_asset_type != "SAL") {
|
||||
if (tx.destination_asset_type == "BURN") {
|
||||
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "BURN TX detected" << delimiter << "amount:" << tx.amount_burnt << std::endl;
|
||||
} else {
|
||||
throw std::runtime_error("Aborting: invalid destination asset type found in tx");
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& tx_vout : tx.vout) {
|
||||
std::string asset_type;
|
||||
if (!cryptonote::get_output_asset_type(tx_vout, asset_type)) {
|
||||
throw std::runtime_error("Aborting: failed to get output asset type from tx");
|
||||
} else if (asset_type != "SAL") {
|
||||
throw std::runtime_error("Aborting: invalid output asset type from tx");
|
||||
}
|
||||
}
|
||||
|
||||
// Add the source currency to the list of expected ones
|
||||
used_assets.insert(tx.source_asset_type);
|
||||
}
|
||||
}
|
||||
|
||||
currblks++;
|
||||
|
||||
if (stop_requested)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Stats reporting error", 1);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <iostream>
|
||||
#include "threadpool_boost.h"
|
||||
|
||||
ThreadPool::ThreadPool(size_t numThreads)
|
||||
: workGuard(boost::asio::make_work_guard(ioService)) {
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
workers.emplace_back([this]() {
|
||||
std::cerr << "Thread started" << std::endl;
|
||||
ioService.run();
|
||||
std::cerr << "Thread finished" << std::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::enqueue(std::function<void()> task) {
|
||||
ioService.post([task]() {
|
||||
try {
|
||||
task(); // Run the task
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception in thread pool task: " << e.what() << std::endl;
|
||||
} catch (...) {
|
||||
std::cerr << "Unknown exception in thread pool task!" << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool ThreadPool::isStopping() const {
|
||||
return ioService.stopped(); // Check if io_context has stopped
|
||||
}
|
||||
|
||||
void ThreadPool::waitForCompletion() {
|
||||
std::cout << "Waiting for completion...\n";
|
||||
workGuard.reset(); // Allow ioService to stop when no more tasks
|
||||
ioService.run(); // Ensure no threads are left hanging
|
||||
|
||||
for (auto &worker : workers) {
|
||||
if (worker.joinable()) worker.join();
|
||||
}
|
||||
std::cout << "All threads joined.\n";
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool() {
|
||||
waitForCompletion();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef THREADPOOL_BOOST_H
|
||||
#define THREADPOOL_BOOST_H
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
class ThreadPool {
|
||||
public:
|
||||
explicit ThreadPool(size_t numThreads);
|
||||
~ThreadPool();
|
||||
|
||||
void enqueue(std::function<void()> task);
|
||||
bool isStopping() const;
|
||||
void waitForCompletion();
|
||||
|
||||
private:
|
||||
boost::asio::io_service ioService;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> workGuard;
|
||||
std::vector<boost::thread> workers;
|
||||
};
|
||||
|
||||
#endif // THREADPOOL_BOOST_H
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user