Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 465b384195 | |||
| 768ca26a8d | |||
| 6fb131a52b | |||
| 3f18c388f6 | |||
| 2f646cdc10 | |||
| a5523be459 | |||
| 81d322b2ce | |||
| 3b1a939417 | |||
| a4e7ebc591 | |||
| 25dccd5423 | |||
| d05ac7f44e | |||
| 35b1a03b3d | |||
| 7acf8068ea | |||
| 383f3d36e6 | |||
| 7d06436d08 | |||
| 3bee380b18 | |||
| 58c70115f2 | |||
| 6be4081332 | |||
| 538e4a5d1f | |||
| 3e49572539 | |||
| 05c7152ad5 | |||
| 8d31fa2842 | |||
| 2abe39f178 | |||
| a6f47a9f92 | |||
| ac13287c78 | |||
| 305b92909e | |||
| 248667016a | |||
| 10b58aac73 | |||
| 0221fe8a34 | |||
| 1ff480e64d | |||
| fd121aae19 | |||
| 0deb19c53c | |||
| 7f3e389d92 | |||
| 87da2d4661 | |||
| f6075ae9ec | |||
| c424e84f4b | |||
| 3b4efe9636 | |||
| dfa27e78c6 | |||
| 9b57fe3eae | |||
| 8f60758a3c | |||
| 3a7ec4db32 | |||
| 679bc9f0d7 | |||
| 362eb38ff8 | |||
| 6243e992cf | |||
| e872414d57 | |||
| fcaf640bcb | |||
| f5237ceaf5 | |||
| 1503ec6629 | |||
| 0f744520ad | |||
| caf52cca20 | |||
| 1c4309c400 | |||
| 9725b921a5 | |||
| 38d2515dc5 | |||
| 13efd79f88 | |||
| 0c273d3571 | |||
| d22389b37a | |||
| f9b060e552 | |||
| 7d13f90e4a | |||
| f7a75a4fdc | |||
| b9fa97daad | |||
| 6f0f5b5e83 | |||
| 6fbd3184b1 | |||
| d5fee31ec6 | |||
| 2a93e04180 | |||
| b902ec9406 | |||
| e3ba570fb1 | |||
| fac65e5093 | |||
| eebc1f1d26 | |||
| 502ece5ba3 | |||
| 62967db201 | |||
| 4b7f863c71 | |||
| 55a6f17c91 | |||
| 8a32d7f73b | |||
| dfb6a705ea | |||
| 5246138398 | |||
| cc3e6a0822 | |||
| 8e68f58eff | |||
| 0a79a4d9fd | |||
| 48fb95bdc1 | |||
| bb4d3768b2 | |||
| cea3f0f341 | |||
| 35fc88c0ec | |||
| 312413aeb0 | |||
| 6ba060e116 | |||
| 88d5e1f50e | |||
| 644a8d3b4d | |||
| e80d135c15 | |||
| a40026f941 | |||
| fb25a36bf2 | |||
| 6f3f7c8e9a | |||
| c6c35d5639 | |||
| 63433cc58f | |||
| 940c0e03f7 | |||
| 445e498fbf | |||
| 09ad75f0cc | |||
| f8d9f9335e | |||
| cd31dafa97 | |||
| 6aa32701b8 | |||
| f84a622bfa | |||
| f60b7209f8 | |||
| 3b00a41fff | |||
| 119a7fab57 | |||
| 6d0a4a4d7b | |||
| 45404ecc71 | |||
| e5bfc2f6ad | |||
| c5be51053a | |||
| 4bfb5f51bf | |||
| d7a9facee9 | |||
| 260bc3721b | |||
| 3e0457de09 | |||
| ce6f6e0593 | |||
| f1efb700d2 | |||
| dd246296b9 | |||
| 22334d7c6c | |||
| c07698fcc8 | |||
| ce69272638 | |||
| 2b95d100b3 | |||
| b7706d4def | |||
| 845d46d7b3 | |||
| e4f8929f2c | |||
| c03402d525 | |||
| d8b18b17f6 | |||
| 5347266ed9 | |||
| 419c26adbf | |||
| 7727bdb51e | |||
| 37a58646fd | |||
| 0e747e3f15 | |||
| caf8a0a962 | |||
| 1b60c08dce | |||
| 1667514896 | |||
| 041cd03098 | |||
| 0c4998b091 | |||
| 7f25459169 | |||
| 91814ebfd9 | |||
| 7b3e8007c8 | |||
| 141761957d | |||
| b2ab2f606c | |||
| 40f0420e02 | |||
| 44e54ad555 | |||
| 69407ed8a5 | |||
| 13ee838f3c | |||
| ec33664734 | |||
| bfe8c4606d | |||
| a159bed3ba | |||
| 52f2065db4 | |||
| 0fd570a2c4 | |||
| 7df7f2740d | |||
| 458af34c67 | |||
| 21a3f5ca6f | |||
| bc7a797b71 | |||
| 9e888834e7 | |||
| 9709fad4d6 | |||
| 906e55f87b | |||
| 92812621be | |||
| 82b14a776f | |||
| 54a33225a2 | |||
| d7584de579 | |||
| a4c0d85bbb | |||
| 609615779c | |||
| b9364b5749 | |||
| 7cd7bef3ca | |||
| 80bd2d1ff0 | |||
| cbe84499fb | |||
| a8ee0a90e0 | |||
| c9de861216 | |||
| 0239202f09 | |||
| 2dd6208a80 | |||
| 37594fe8fa | |||
| e5c9b05ed6 | |||
| 9ba621b3ae | |||
| 9e20133ed9 | |||
| 9c11200666 | |||
| 3e0649a8d2 | |||
| b87bfe002a | |||
| f2e69594a7 | |||
| 7b39504050 | |||
| eff99e9287 | |||
| 6f8fcdab03 | |||
| c7a739d885 | |||
| 608962068a | |||
| e7615b4c08 | |||
| 5fa5ff8585 | |||
| 28a9338ab7 | |||
| 1d3747e9cd | |||
| c1c70ddace | |||
| 7ea690614e | |||
| 2909fd2505 | |||
| 7dd5450419 | |||
| e3a03e9d98 | |||
| dd39f94ca6 | |||
| 78232ff3a8 | |||
| aa02928620 | |||
| 52736ea0a3 | |||
| b7cc8e8100 | |||
| 520d4b4eab | |||
| 24f9916287 | |||
| cd22e55296 |
@@ -22,7 +22,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build-cross:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
strategy:
|
||||
@@ -52,10 +52,10 @@ jobs:
|
||||
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"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-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"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-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"
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
${{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' }}
|
||||
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' || matrix.toolchain.host == 'aarch64-linux-gnu' }}
|
||||
with:
|
||||
name: ${{ matrix.toolchain.name }}
|
||||
path: |
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Use Ubuntu 24.04 as base
|
||||
FROM ubuntu:24.04 AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install build dependencies including cross-compilers
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential cmake pkg-config git imagemagick libcap-dev librsvg2-bin libz-dev \
|
||||
g++-mingw-w64-x86-64 clang gcc-arm-none-eabi binutils-x86-64-linux-gnu libtiff-tools \
|
||||
g++-x86-64-linux-gnu gcc-x86-64-linux-gnu binutils-aarch64-linux-gnu \
|
||||
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu crossbuild-essential-amd64 \
|
||||
libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev \
|
||||
libreadline-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \
|
||||
libprotobuf-dev protobuf-compiler libudev-dev libboost-all-dev python3 ccache doxygen graphviz \
|
||||
ca-certificates curl zip libtool gperf automake autoconf \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Clone the develop branch
|
||||
WORKDIR /opt
|
||||
RUN git clone --recursive -b develop https://github.com/salvium/salvium.git
|
||||
WORKDIR /opt/salvium
|
||||
|
||||
# make the script runnable
|
||||
RUN chmod +x make_releases.sh
|
||||
|
||||
# Make sure the output folder exists
|
||||
RUN mkdir ~/releases
|
||||
|
||||
# Expose the releases directory for copying zip files to the host
|
||||
VOLUME ["~/releases"]
|
||||
|
||||
ENTRYPOINT ["/opt/salvium/make_releases.sh"]
|
||||
|
||||
|
||||
# To access the generated zip files on your host, run the container with:
|
||||
# docker run -v ~/releases:/root/releases <image>
|
||||
# This will copy the zip files to your host's ~/salvium-releases directory.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Salvium Zero v0.9.9-rc2
|
||||
# Salvium One v1.0.7
|
||||
|
||||
Copyright (c) 2023-2024, Salvium
|
||||
Copyright (c) 2023-2025, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
Portions Copyright (c) 2012-2013 The Cryptonote developers.
|
||||
|
||||
@@ -47,7 +47,7 @@ As with many development projects, the repository on GitHub is considered to be
|
||||
|
||||
## 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.
|
||||
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
|
||||
|
||||
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
|
||||
|
||||
```bash
|
||||
cd salvium
|
||||
git checkout v0.9.9-rc2
|
||||
git checkout v1.0.7
|
||||
make
|
||||
```
|
||||
|
||||
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
|
||||
```bash
|
||||
git clone https://github.com/salvium/salvium
|
||||
cd salvium
|
||||
git checkout v0.9.9-rc2
|
||||
git checkout v1.0.7
|
||||
```
|
||||
|
||||
* Build:
|
||||
@@ -370,10 +370,10 @@ application.
|
||||
cd salvium
|
||||
```
|
||||
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.9-rc2'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.7'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v0.9.9-rc2
|
||||
git checkout v1.0.7
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
OSX_MIN_VERSION=10.13
|
||||
OSX_MIN_VERSION=11.0
|
||||
LD64_VERSION=609
|
||||
ifeq (aarch64, $(host_arch))
|
||||
CC_target=arm64-apple-$(host_os)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mingw32_CFLAGS=-pipe -pthread
|
||||
mingw32_CFLAGS=-pipe
|
||||
mingw32_CXXFLAGS=$(mingw32_CFLAGS)
|
||||
mingw32_ARFLAGS=cr
|
||||
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package=boost
|
||||
$(package)_version=1.66.0
|
||||
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.gz
|
||||
$(package)_sha256_hash=bd0df411efd9a585e5a2212275f8762079fed8842264954675a4fddc46cfcf60
|
||||
$(package)_dependencies=libiconv
|
||||
$(package)_patches=fix_aroptions.patch fix_arm_arch.patch
|
||||
$(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
|
||||
@@ -22,20 +20,20 @@ $(package)_toolset_$(host_os)=gcc
|
||||
$(package)_archiver_$(host_os)=$($(package)_ar)
|
||||
$(package)_toolset_darwin=darwin
|
||||
$(package)_archiver_darwin=$($(package)_libtool)
|
||||
$(package)_config_libraries=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 -DBOOST_ASIO_HAS_STD_STRING_VIEW=1
|
||||
$(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++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
|
||||
|
||||
define $(package)_config_cmds
|
||||
./bootstrap.sh --without-icu --with-libraries=$(boost_config_libraries)
|
||||
./bootstrap.sh --without-icu --with-libraries=$(boost_config_libraries_$(host_os))
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
|
||||
@@ -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,38 +1,34 @@
|
||||
packages:=boost openssl zeromq libiconv expat unbound
|
||||
native_packages := native_protobuf
|
||||
packages := boost openssl zeromq unbound sodium protobuf
|
||||
|
||||
# ccache is useless in gitian builds
|
||||
ifneq ($(GITIAN),1)
|
||||
native_packages := native_ccache
|
||||
ifneq ($(host_os),android)
|
||||
packages += libusb
|
||||
endif
|
||||
|
||||
hardware_packages := hidapi protobuf libusb
|
||||
hardware_native_packages := native_protobuf
|
||||
|
||||
android_native_packages = android_ndk
|
||||
android_packages = ncurses readline sodium
|
||||
|
||||
darwin_native_packages = $(hardware_native_packages)
|
||||
darwin_packages = ncurses readline sodium $(hardware_packages)
|
||||
|
||||
# not really native...
|
||||
freebsd_native_packages = freebsd_base
|
||||
freebsd_packages = ncurses readline sodium
|
||||
|
||||
linux_packages = eudev ncurses readline sodium $(hardware_packages)
|
||||
linux_native_packages = $(hardware_native_packages)
|
||||
|
||||
ifeq ($(build_tests),ON)
|
||||
packages += gtest
|
||||
ifneq ($(host_os),freebsd)
|
||||
ifneq ($(host_os),android)
|
||||
packages += hidapi
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(host_arch),riscv64)
|
||||
linux_packages += unwind
|
||||
ifneq ($(host_os),mingw32)
|
||||
packages += ncurses readline
|
||||
endif
|
||||
|
||||
mingw32_packages = icu4c sodium $(hardware_packages)
|
||||
mingw32_native_packages = $(hardware_native_packages)
|
||||
mingw32_native_packages :=
|
||||
mingw32_packages = icu4c libiconv
|
||||
|
||||
linux_native_packages :=
|
||||
linux_packages := eudev
|
||||
|
||||
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
|
||||
|
||||
@@ -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)" "$(<)"
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
Vendored
+1
-1
Submodule external/mx25519 updated: 42f36ede5f...e808a6406b
@@ -0,0 +1,5 @@
|
||||
48417220800f174a3613881a56131d25c49d344228fca124c8331e0b58a8ff06 salvium-v1.0.7-ubuntu22.04-linux-x86_64.zip
|
||||
52226551a9e1842df46cf068292cfa3c1e05328e0695cf6723365d4401af19a6 salvium-v1.0.7-ubuntu22.04-linux-aarch64.zip
|
||||
b40c3765479c9d5712e766ddd01314b63e5080472b7639d34388e6b74b36142e salvium-v1.0.7-macos-x86_64.zip
|
||||
60b05548f69040fe901e336e8fc4189a1028a8d7ded09bad555b3c854a0e8d6e salvium-v1.0.7-macos-aarch64.zip
|
||||
b2c3e0bb8254ad1f62a78d6670c6e5adba1e42bb823919faad1cbf5796d53910 salvium-v1.0.7-win64.zip
|
||||
@@ -238,7 +238,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
|
||||
LOG_PRINT_L1("Failed to get output unlock time, aborting transaction addition");
|
||||
throw std::runtime_error("Unexpected error getting output unlock_time, aborting");
|
||||
}
|
||||
if (miner_tx && tx.version == 2)
|
||||
if (miner_tx && tx.version >= 2)
|
||||
{
|
||||
cryptonote::tx_out vout = tx.vout[i];
|
||||
// TODO: avoid multiple expensive zeroCommitVartime call here + get_outs_by_last_locked_block + ver_non_input_consensus
|
||||
@@ -294,7 +294,22 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
time1 = epee::misc_utils::get_tick_count();
|
||||
|
||||
uint64_t num_rct_outs = 0;
|
||||
oracle::asset_type_counts num_rct_outs_by_asset_type;
|
||||
oracle::asset_type_counts_v2 num_rct_outs_by_asset_type;
|
||||
|
||||
// add newly created tokens
|
||||
for (const std::pair<transaction, blobdata>& tx : txs)
|
||||
{
|
||||
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type("sal" + tx.first.token_metadata.asset_type);
|
||||
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
|
||||
throw std::runtime_error("Failed to add asset_type 'sal" + tx.first.token_metadata.asset_type + "'");
|
||||
}
|
||||
|
||||
if (!is_tx_paid_for(tx.first))
|
||||
throw std::runtime_error("TX is not paid for");
|
||||
}
|
||||
|
||||
blobdata miner_bd = tx_to_blob(blk.miner_tx);
|
||||
add_transaction(blk_hash, std::make_pair(blk.miner_tx, blobdata_ref(miner_bd)));
|
||||
blobdata protocol_bd = tx_to_blob(blk.protocol_tx);
|
||||
@@ -309,12 +324,14 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
std::string asset_type;
|
||||
if (!get_output_asset_type(vout, asset_type))
|
||||
throw std::runtime_error("Failed to get output asset type");
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
|
||||
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
|
||||
num_rct_outs_by_asset_type.add(asset_type_id, 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, int64_t> slippage_counts;
|
||||
uint64_t audit_total = 0, yield_total = 0;
|
||||
uint64_t audit_total = 0, token_total = 0, yield_total = 0;
|
||||
if (blk.protocol_tx.version >= 2)
|
||||
{
|
||||
num_rct_outs += blk.protocol_tx.vout.size();
|
||||
@@ -326,10 +343,12 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
std::string asset_type;
|
||||
if (!get_output_asset_type(vout, asset_type))
|
||||
throw std::runtime_error("Failed to get output asset type");
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
|
||||
|
||||
// Update the RCT outs for the asset_type
|
||||
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
|
||||
num_rct_outs_by_asset_type.add(asset_type_id, 1);
|
||||
|
||||
// Update the RCT outs
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
|
||||
// Update the amount tallies by DEDUCTING the minted amount
|
||||
if (slippage_counts.count(asset_type) == 0)
|
||||
slippage_counts[asset_type] = 0;
|
||||
@@ -350,7 +369,9 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
throw std::runtime_error("Failed to get output asset type");
|
||||
if (vout.amount == 0) {
|
||||
++num_rct_outs;
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
|
||||
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
|
||||
num_rct_outs_by_asset_type.add(asset_type_id, 1);
|
||||
}
|
||||
|
||||
// Is this a CONVERT TX?
|
||||
@@ -367,6 +388,16 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
audit_total += tx.first.amount_burnt;
|
||||
}
|
||||
|
||||
// Is this an create_token TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
token_total += tx.first.amount_burnt;
|
||||
/*
|
||||
uint32_t asset_type_id = cryptonote::asset_id_from_type(tx.first.token_metadata.asset_type);
|
||||
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
|
||||
throw std::runtime_error("Failed to add asset_type '" + tx.first.token_metadata.asset_type + "' to RCT outputs");
|
||||
*/
|
||||
}
|
||||
|
||||
// Is this a STAKE TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::STAKE) {
|
||||
yield_total += tx.first.amount_burnt;
|
||||
@@ -420,7 +451,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, audit_total, nettype, ybi, abi);
|
||||
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, token_total, nettype, ybi, abi);
|
||||
TIME_MEASURE_FINISH(time1);
|
||||
time_add_block1 += time1;
|
||||
|
||||
|
||||
@@ -207,6 +207,29 @@ typedef struct yield_tx_info {
|
||||
crypto::public_key return_pubkey;
|
||||
} yield_tx_info;
|
||||
|
||||
typedef struct yield_tx_info_carrot {
|
||||
uint64_t block_height;
|
||||
uint8_t version;
|
||||
crypto::hash tx_hash;
|
||||
uint64_t locked_coins;
|
||||
crypto::public_key return_address;
|
||||
crypto::public_key return_pubkey;
|
||||
carrot::view_tag_t return_view_tag;
|
||||
carrot::encrypted_janus_anchor_t return_anchor_enc;
|
||||
} yield_tx_info_carrot;
|
||||
|
||||
typedef struct token_tx_info_carrot {
|
||||
uint64_t block_height;
|
||||
uint8_t version;
|
||||
crypto::hash tx_hash;
|
||||
uint64_t locked_coins;
|
||||
crypto::public_key return_address;
|
||||
crypto::public_key return_pubkey;
|
||||
carrot::view_tag_t return_view_tag;
|
||||
carrot::encrypted_janus_anchor_t return_anchor_enc;
|
||||
uint32_t asset_type_id;
|
||||
} token_tx_info_carrot;
|
||||
|
||||
#define DBF_SAFE 1
|
||||
#define DBF_FAST 2
|
||||
#define DBF_FASTEST 4
|
||||
@@ -426,11 +449,12 @@ private:
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
@@ -1201,6 +1225,20 @@ public:
|
||||
virtual std::map<std::string, uint64_t> get_circulating_supply() const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief fetch the tokens from the blockchain
|
||||
*
|
||||
* @return the current token values
|
||||
*/
|
||||
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const = 0;
|
||||
|
||||
/**
|
||||
* @brief determine if a TX has been paid for
|
||||
*
|
||||
* @return the current token values
|
||||
*/
|
||||
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const = 0;
|
||||
|
||||
/**
|
||||
* <!--
|
||||
* TODO: Rewrite (if necessary) such that all calls to remove_* are
|
||||
@@ -1924,6 +1962,10 @@ public:
|
||||
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;
|
||||
|
||||
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const = 0;
|
||||
|
||||
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief set whether or not to automatically remove logs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -80,7 +80,16 @@ typedef struct mdb_txn_cursors
|
||||
MDB_cursor *m_txc_yield_blocks;
|
||||
MDB_cursor *m_txc_audit_txs;
|
||||
MDB_cursor *m_txc_audit_blocks;
|
||||
MDB_cursor *m_txc_carrot_yield_txs;
|
||||
|
||||
MDB_cursor *m_txc_token_txs;
|
||||
MDB_cursor *m_txc_tokens;
|
||||
MDB_cursor *m_txc_asset_type_counts;
|
||||
|
||||
MDB_cursor *m_txc_rct_count_info;
|
||||
|
||||
MDB_cursor *m_txc_rollup_tx_info;
|
||||
|
||||
} mdb_txn_cursors;
|
||||
|
||||
#define m_cur_blocks m_cursors->m_txc_blocks
|
||||
@@ -108,6 +117,15 @@ typedef struct mdb_txn_cursors
|
||||
#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
|
||||
#define m_cur_carrot_yield_txs m_cursors->m_txc_carrot_yield_txs
|
||||
|
||||
#define m_cur_token_txs m_cursors->m_txc_token_txs
|
||||
#define m_cur_tokens m_cursors->m_txc_tokens
|
||||
#define m_cur_asset_type_counts m_cursors->m_txc_asset_type_counts
|
||||
|
||||
#define m_cur_rct_count_info m_cursors->m_txc_rct_count_info
|
||||
|
||||
#define m_cur_rollup_tx_info m_cursors->m_txc_rollup_tx_info
|
||||
|
||||
typedef struct mdb_rflags
|
||||
{
|
||||
@@ -137,6 +155,16 @@ typedef struct mdb_rflags
|
||||
bool m_rf_yield_blocks;
|
||||
bool m_rf_audit_txs;
|
||||
bool m_rf_audit_blocks;
|
||||
bool m_rf_carrot_yield_txs;
|
||||
|
||||
bool m_rf_token_txs;
|
||||
bool m_rf_tokens;
|
||||
bool m_rf_asset_type_counts;
|
||||
|
||||
bool m_rf_rct_count_info;
|
||||
|
||||
bool m_rf_rollup_tx_info;
|
||||
|
||||
} mdb_rflags;
|
||||
|
||||
typedef struct mdb_threadinfo
|
||||
@@ -239,6 +267,8 @@ public:
|
||||
|
||||
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const;
|
||||
|
||||
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs_old(const std::vector<uint64_t> &heights, const std::string asset_type) const;
|
||||
|
||||
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;
|
||||
|
||||
virtual uint64_t get_block_timestamp(const uint64_t& height) const;
|
||||
@@ -274,6 +304,10 @@ public:
|
||||
virtual uint64_t height() const;
|
||||
|
||||
virtual std::map<std::string, uint64_t> get_circulating_supply() const;
|
||||
|
||||
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const;
|
||||
|
||||
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const;
|
||||
|
||||
virtual bool tx_exists(const crypto::hash& h) const;
|
||||
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const;
|
||||
@@ -403,11 +437,12 @@ private:
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
@@ -474,6 +509,9 @@ private:
|
||||
|
||||
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;
|
||||
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const;
|
||||
|
||||
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const;
|
||||
|
||||
private:
|
||||
MDB_env* m_env;
|
||||
@@ -511,10 +549,20 @@ private:
|
||||
|
||||
MDB_dbi m_yield_txs;
|
||||
MDB_dbi m_yield_blocks;
|
||||
|
||||
|
||||
MDB_dbi m_audit_txs;
|
||||
MDB_dbi m_audit_blocks;
|
||||
|
||||
MDB_dbi m_carrot_yield_txs;
|
||||
|
||||
MDB_dbi m_token_txs;
|
||||
MDB_dbi m_tokens;
|
||||
MDB_dbi m_asset_type_counts;
|
||||
|
||||
MDB_dbi m_rct_count_info;
|
||||
|
||||
MDB_dbi m_rollup_tx_info;
|
||||
|
||||
mutable uint64_t m_cum_size; // used in batch size estimation
|
||||
mutable unsigned int m_cum_count;
|
||||
std::string m_folder;
|
||||
|
||||
@@ -145,6 +145,7 @@ public:
|
||||
|
||||
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 int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& 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; }
|
||||
@@ -155,15 +156,20 @@ public:
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
uint64_t audit_total,
|
||||
uint64_t token_total,
|
||||
const cryptonote::network_type nettype,
|
||||
cryptonote::yield_block_info& ybi,
|
||||
cryptonote::audit_block_info& abi
|
||||
) override { }
|
||||
|
||||
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const override { return {}; }
|
||||
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const override { return true; }
|
||||
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const override { return 0; }
|
||||
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; }
|
||||
|
||||
@@ -214,11 +214,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
#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;
|
||||
// uint64_t currsz = 0;
|
||||
// uint64_t currtxs = 0;
|
||||
// uint64_t currblks = 0;
|
||||
|
||||
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;
|
||||
|
||||
@@ -246,9 +244,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
prevtm = currtm;
|
||||
}
|
||||
skip:
|
||||
currsz += bd.size();
|
||||
uint64_t coinbase_amount;
|
||||
uint64_t tx_fee_amount = 0;
|
||||
// currsz += bd.size();
|
||||
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");
|
||||
@@ -325,10 +321,11 @@ skip:
|
||||
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++;
|
||||
// currsz += bd.size();
|
||||
// if (db->get_prunable_tx_blob(tx_id, bd))
|
||||
// currsz += bd.size();
|
||||
// currtxs++;
|
||||
db->get_prunable_tx_blob(tx_id, bd);
|
||||
|
||||
if (tx.type != cryptonote::transaction_type::TRANSFER &&
|
||||
tx.type != cryptonote::transaction_type::BURN &&
|
||||
@@ -374,7 +371,7 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
currblks++;
|
||||
// currblks++;
|
||||
|
||||
if (stop_requested)
|
||||
break;
|
||||
|
||||
@@ -211,8 +211,15 @@ int main(int argc, char* argv[])
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
||||
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
|
||||
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
|
||||
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
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -29,6 +29,7 @@
|
||||
set(carrot_core_sources
|
||||
account_secrets.cpp
|
||||
address_utils.cpp
|
||||
account.cpp
|
||||
carrot_enote_types.cpp
|
||||
core_types.cpp
|
||||
destination.cpp
|
||||
|
||||
@@ -58,8 +58,8 @@ CarrotDestinationV1 carrot_and_legacy_account::cryptonote_address(const payment_
|
||||
switch (resolve_derive_type(derive_type))
|
||||
{
|
||||
case AddressDeriveType::Carrot:
|
||||
make_carrot_integrated_address_v1(get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
get_keys().m_account_address.m_view_public_key,
|
||||
make_carrot_integrated_address_v1(get_keys().m_carrot_main_address.m_spend_public_key,
|
||||
get_keys().m_carrot_main_address.m_view_public_key,
|
||||
payment_id,
|
||||
addr);
|
||||
break;
|
||||
@@ -110,17 +110,25 @@ CarrotDestinationV1 carrot_and_legacy_account::subaddress(const subaddress_index
|
||||
return addr;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::subaddress_map_cn() const
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::get_subaddress_map_cn() const
|
||||
{
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> res;
|
||||
for (const auto &p : subaddress_map)
|
||||
if (p.second.derive_type == AddressDeriveType::PreCarrot)
|
||||
res.emplace(p.first, cryptonote::subaddress_index{p.second.index.major, p.second.index.minor});
|
||||
res.emplace(p.first, cryptonote::subaddress_index{p.second.index.major, p.second.index.minor});
|
||||
CHECK_AND_ASSERT_THROW_MES(!res.empty(),
|
||||
"carrot_and_legacy_account::subaddress_map_cn: subaddress map does not contain pre-carrot subaddresses");
|
||||
"carrot_and_legacy_account::get_subaddress_map: subaddress map does not contain subaddresses");
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
const std::unordered_map<crypto::public_key, subaddress_index_extended>& carrot_and_legacy_account::get_subaddress_map_ref() const {
|
||||
return subaddress_map;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
const std::unordered_map<crypto::public_key, return_output_info_t>&
|
||||
carrot_and_legacy_account::get_return_output_map_ref() const {
|
||||
return return_output_map;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_extended &subaddress_index,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out,
|
||||
@@ -274,17 +282,66 @@ crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::publ
|
||||
return L;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::generate_subaddress_map()
|
||||
crypto::key_image carrot_and_legacy_account::derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const
|
||||
{
|
||||
const auto it = subaddress_map.find(address_spend_pubkey);
|
||||
CHECK_AND_ASSERT_THROW_MES(it != subaddress_map.cend(),
|
||||
"carrot and legacy account: derive key image view only: cannot find subaddress");
|
||||
|
||||
const bool is_subaddress = it->second.index.is_subaddress();
|
||||
const uint32_t major_index = it->second.index.major;
|
||||
const uint32_t minor_index = it->second.index.minor;
|
||||
|
||||
const cryptonote::account_keys &keys = get_keys();
|
||||
|
||||
crypto::secret_key address_index_generator;
|
||||
crypto::secret_key subaddress_scalar;
|
||||
crypto::secret_key subaddress_extension;
|
||||
crypto::secret_key address_privkey_g;
|
||||
crypto::secret_key x;
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(it->second.derive_type == AddressDeriveType::Carrot,
|
||||
"carrot and legacy account: derive key image view only: not a Carrot address");
|
||||
|
||||
// s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||
make_carrot_index_extension_generator(keys.s_generate_address, major_index, minor_index, address_index_generator);
|
||||
|
||||
if (is_subaddress)
|
||||
{
|
||||
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||
make_carrot_subaddress_scalar(keys.m_carrot_account_address.m_spend_public_key, address_index_generator, major_index, minor_index, subaddress_scalar);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k^j_subscal = 1
|
||||
sc_1(to_bytes(subaddress_scalar));
|
||||
}
|
||||
|
||||
// k^g_a = k_gi * k^j_subscal
|
||||
sc_mul(to_bytes(address_privkey_g), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
|
||||
|
||||
// x = k^{j,g}_addr + k^g_o
|
||||
sc_add(to_bytes(x), to_bytes(address_privkey_g), to_bytes(sender_extension_g));
|
||||
|
||||
crypto::key_image L;
|
||||
crypto::generate_key_image(onetime_address, x, L);
|
||||
return L;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size)
|
||||
{
|
||||
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
|
||||
|
||||
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
|
||||
for (uint32_t major_index = 0; major_index <= lookahead_size.first; ++major_index)
|
||||
{
|
||||
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
|
||||
for (uint32_t minor_index = 0; minor_index <= lookahead_size.second; ++minor_index)
|
||||
{
|
||||
for (const AddressDeriveType derive_type : derive_types)
|
||||
{
|
||||
const subaddress_index_extended subaddr_index{{major_index, minor_index}, derive_type};
|
||||
const subaddress_index_extended subaddr_index{{major_index, minor_index}, derive_type, false};
|
||||
const CarrotDestinationV1 addr = subaddress(subaddr_index);
|
||||
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
|
||||
}
|
||||
@@ -307,6 +364,64 @@ crypto::secret_key carrot_and_legacy_account::generate(
|
||||
return retval;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::set_keys(const cryptonote::account_keys& keys, bool copy_spend_secret_keys)
|
||||
{
|
||||
// CN keys
|
||||
m_keys.m_account_address = keys.m_account_address;
|
||||
if (copy_spend_secret_keys) m_keys.m_spend_secret_key = keys.m_spend_secret_key;
|
||||
m_keys.m_view_secret_key = keys.m_view_secret_key;
|
||||
if (copy_spend_secret_keys) m_keys.m_multisig_keys = keys.m_multisig_keys;
|
||||
m_keys.m_device = keys.m_device;
|
||||
m_keys.m_encryption_iv = keys.m_encryption_iv;
|
||||
|
||||
// Carrot keys
|
||||
if (copy_spend_secret_keys) m_keys.s_master = keys.s_master;
|
||||
if (copy_spend_secret_keys) m_keys.k_prove_spend = keys.k_prove_spend;
|
||||
m_keys.s_view_balance = keys.s_view_balance;
|
||||
m_keys.k_view_incoming = keys.k_view_incoming;
|
||||
m_keys.k_generate_image = keys.k_generate_image;
|
||||
m_keys.s_generate_address = keys.s_generate_address;
|
||||
m_keys.m_carrot_account_address = keys.m_carrot_account_address;
|
||||
m_keys.m_carrot_main_address = keys.m_carrot_main_address;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key)
|
||||
{
|
||||
// top level keys
|
||||
m_keys.s_master = crypto::null_skey;
|
||||
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
|
||||
m_keys.s_view_balance = svb_key;
|
||||
|
||||
// view balance keys
|
||||
make_carrot_viewincoming_key(m_keys.s_view_balance, m_keys.k_view_incoming);
|
||||
make_carrot_generateimage_key(m_keys.s_view_balance, m_keys.k_generate_image);
|
||||
make_carrot_generateaddress_secret(m_keys.s_view_balance, m_keys.s_generate_address);
|
||||
|
||||
// carrot account address - use the provided address spend pubkey
|
||||
m_keys.m_carrot_account_address = address;
|
||||
k_view_incoming_dev.view_key_scalar_mult_ed25519(m_keys.m_carrot_account_address.m_spend_public_key,
|
||||
m_keys.m_carrot_account_address.m_view_public_key
|
||||
);
|
||||
|
||||
// carrot main wallet address
|
||||
m_keys.m_carrot_main_address = address;
|
||||
k_view_incoming_dev.view_key_scalar_mult_ed25519(crypto::get_G(),
|
||||
m_keys.m_carrot_main_address.m_view_public_key
|
||||
);
|
||||
|
||||
// Store fields for Carrot
|
||||
m_keys.m_spend_secret_key = crypto::null_skey;
|
||||
m_keys.m_view_secret_key = crypto::null_skey;
|
||||
m_keys.m_account_address = {crypto::null_pkey, crypto::null_pkey, false};
|
||||
|
||||
// Update ALL addresses to be Carrot-only
|
||||
m_keys.m_carrot_account_address.m_is_carrot = true;
|
||||
m_keys.m_carrot_main_address.m_is_carrot = true;
|
||||
|
||||
// Set the default derive type for addresses
|
||||
this->default_derive_type = AddressDeriveType::Carrot;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
|
||||
{
|
||||
// top level keys
|
||||
@@ -315,6 +430,7 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
|
||||
make_carrot_viewbalance_secret(m_keys.s_master, m_keys.s_view_balance);
|
||||
|
||||
// view balance keys
|
||||
make_carrot_viewincoming_key(m_keys.s_view_balance, m_keys.k_view_incoming);
|
||||
make_carrot_generateimage_key(m_keys.s_view_balance, m_keys.k_generate_image);
|
||||
make_carrot_generateaddress_secret(m_keys.s_view_balance, m_keys.s_generate_address);
|
||||
|
||||
@@ -324,17 +440,37 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
|
||||
m_keys.m_carrot_account_address.m_spend_public_key,
|
||||
m_keys.m_carrot_account_address.m_view_public_key
|
||||
);
|
||||
m_keys.m_carrot_account_address.m_is_carrot = true;
|
||||
|
||||
// carrot main wallet address
|
||||
m_keys.m_carrot_main_address.m_spend_public_key = m_keys.m_carrot_account_address.m_spend_public_key;
|
||||
k_view_incoming_dev.view_key_scalar_mult_ed25519(
|
||||
crypto::get_G(),
|
||||
m_keys.m_carrot_main_address.m_view_public_key
|
||||
);
|
||||
m_keys.m_carrot_main_address.m_is_carrot = true;
|
||||
|
||||
this->default_derive_type = default_derive_type;
|
||||
generate_subaddress_map();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::set_cn_subaddress_map(const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddress_map_cn)
|
||||
void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map_cn)
|
||||
{
|
||||
for (const auto &p : subaddress_map_cn)
|
||||
subaddress_map.insert({p.first, {{p.second.major, p.second.minor}, AddressDeriveType::PreCarrot}});
|
||||
for (const auto &p : subaddress_map_cn) {
|
||||
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
|
||||
if (p.second.derive_type == AddressDeriveType::PreCarrot) {
|
||||
// Create a matching Carrot address
|
||||
const subaddress_index_extended subaddr_index{{p.second.index.major, p.second.index.minor}, AddressDeriveType::Carrot, p.second.is_return_spend_key};
|
||||
const CarrotDestinationV1 addr = subaddress(subaddr_index);
|
||||
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
|
||||
{
|
||||
for (const auto &p : roi_map)
|
||||
return_output_map.insert({p.first, p.second});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
AddressDeriveType carrot_and_legacy_account::resolve_derive_type(const AddressDeriveType derive_type) const
|
||||
{
|
||||
@@ -0,0 +1,242 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "account_secrets.h"
|
||||
#include "address_utils.h"
|
||||
#include "destination.h"
|
||||
#include "device_ram_borrowed.h"
|
||||
#include "enote_utils.h"
|
||||
#include "carrot_impl/subaddress_index.h"
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/subaddress_index.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static constexpr std::uint32_t MAX_SUBADDRESS_MAJOR_INDEX = 5;
|
||||
static constexpr std::uint32_t MAX_SUBADDRESS_MINOR_INDEX = 20;
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
|
||||
struct return_output_info_t {
|
||||
input_context_t input_context;
|
||||
crypto::public_key K_o; // output onetime address
|
||||
crypto::public_key K_change; // change output onetime address
|
||||
crypto::public_key K_spend_pubkey; // change output spend pubkey
|
||||
crypto::key_image key_image;
|
||||
crypto::secret_key sum_g;
|
||||
crypto::secret_key sender_extension_t;
|
||||
|
||||
return_output_info_t() {
|
||||
// Default constructor for serialization
|
||||
input_context = input_context_t();
|
||||
K_o = crypto::public_key();
|
||||
K_change = crypto::public_key();
|
||||
K_spend_pubkey = crypto::public_key();
|
||||
key_image = crypto::key_image();
|
||||
sum_g = crypto::secret_key();
|
||||
sender_extension_t = crypto::secret_key();
|
||||
}
|
||||
|
||||
return_output_info_t(
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &K_o,
|
||||
const crypto::public_key &K_change,
|
||||
const crypto::public_key &K_spend_pubkey,
|
||||
const crypto::key_image &key_image,
|
||||
const crypto::secret_key &sum_g,
|
||||
const crypto::secret_key &sender_extension_t):
|
||||
input_context(input_context),
|
||||
K_o(K_o),
|
||||
K_change(K_change),
|
||||
K_spend_pubkey(K_spend_pubkey),
|
||||
key_image(key_image),
|
||||
sum_g(sum_g),
|
||||
sender_extension_t(sender_extension_t) {}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(input_context)
|
||||
FIELD(K_o)
|
||||
FIELD(K_change)
|
||||
FIELD(K_spend_pubkey)
|
||||
FIELD(key_image)
|
||||
FIELD(sum_g)
|
||||
FIELD(sender_extension_t)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
// Old return_output_info_t format (for deserializing version 2 wallet caches)
|
||||
struct return_output_info_retired_t {
|
||||
input_context_t input_context;
|
||||
crypto::public_key K_o;
|
||||
crypto::public_key K_change;
|
||||
crypto::key_image key_image;
|
||||
crypto::secret_key x;
|
||||
crypto::secret_key y;
|
||||
|
||||
return_output_info_retired_t() {
|
||||
input_context = input_context_t();
|
||||
K_o = crypto::public_key();
|
||||
K_change = crypto::public_key();
|
||||
key_image = crypto::key_image();
|
||||
x = crypto::secret_key();
|
||||
y = crypto::secret_key();
|
||||
}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(input_context)
|
||||
FIELD(K_o)
|
||||
FIELD(K_change)
|
||||
FIELD(key_image)
|
||||
FIELD(x)
|
||||
FIELD(y)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
|
||||
class carrot_and_legacy_account : public cryptonote::account_base
|
||||
{
|
||||
public:
|
||||
view_incoming_key_ram_borrowed_device k_view_incoming_dev;
|
||||
view_balance_secret_ram_borrowed_device s_view_balance_dev;
|
||||
generate_address_secret_ram_borrowed_device s_generate_address_dev;
|
||||
|
||||
AddressDeriveType default_derive_type;
|
||||
|
||||
carrot_and_legacy_account(): k_view_incoming_dev(get_keys().k_view_incoming),
|
||||
s_view_balance_dev(get_keys().s_view_balance),
|
||||
s_generate_address_dev(get_keys().s_generate_address)
|
||||
{}
|
||||
|
||||
void set_keys(const cryptonote::account_keys& keys, bool copy_spend_secret_keys);
|
||||
|
||||
carrot_and_legacy_account(const carrot_and_legacy_account &k) = delete;
|
||||
carrot_and_legacy_account(carrot_and_legacy_account&&) = delete;
|
||||
|
||||
carrot_and_legacy_account& operator=(const carrot_and_legacy_account&) = delete;
|
||||
carrot_and_legacy_account& operator=(carrot_and_legacy_account&&) = delete;
|
||||
|
||||
CarrotDestinationV1 cryptonote_address(const payment_id_t payment_id = null_payment_id,
|
||||
const AddressDeriveType derive_type = AddressDeriveType::Auto) const;
|
||||
|
||||
CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const;
|
||||
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> get_subaddress_map_cn() const;
|
||||
const std::unordered_map<crypto::public_key, subaddress_index_extended>& get_subaddress_map_ref() const;
|
||||
const std::unordered_map<crypto::public_key, return_output_info_t>& get_return_output_map_ref() const;
|
||||
|
||||
// brief: opening_for_subaddress - return (k^g_a, k^t_a) for j s.t. K^j_s = (k^g_a * G + k^t_a * T)
|
||||
void opening_for_subaddress(const subaddress_index_extended &subaddress_index,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out) const;
|
||||
|
||||
bool try_searching_for_opening_for_subaddress(const crypto::public_key &address_spend_pubkey,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out) const;
|
||||
|
||||
bool try_searching_for_opening_for_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
crypto::secret_key &x_out,
|
||||
crypto::secret_key &y_out) const;
|
||||
|
||||
bool can_open_fcmp_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
crypto::key_image derive_key_image(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
crypto::key_image derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
void generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size);
|
||||
|
||||
crypto::secret_key generate(
|
||||
const crypto::secret_key& recovery_key = crypto::secret_key(),
|
||||
bool recover = false,
|
||||
bool two_random = false,
|
||||
const AddressDeriveType default_derive_type = AddressDeriveType::Carrot
|
||||
);
|
||||
|
||||
void create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key);
|
||||
void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
|
||||
void insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map);
|
||||
void insert_return_output_info(
|
||||
const std::unordered_map<crypto::public_key, return_output_info_t>& input_context_map
|
||||
);
|
||||
|
||||
AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<crypto::public_key, subaddress_index_extended> subaddress_map;
|
||||
// Kr -> return_output_info
|
||||
std::unordered_map<crypto::public_key, return_output_info_t> return_output_map;
|
||||
};
|
||||
}
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
template <class Archive>
|
||||
inline typename std::enable_if<!Archive::is_loading::value, void>::type initialize_transfer_details(Archive &a, carrot::return_output_info_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
}
|
||||
template <class Archive>
|
||||
inline typename std::enable_if<Archive::is_loading::value, void>::type initialize_transfer_details(Archive &a, carrot::return_output_info_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
x.input_context = carrot::input_context_t();
|
||||
x.K_o = crypto::public_key();
|
||||
x.K_change = crypto::public_key();
|
||||
x.K_spend_pubkey = crypto::public_key();
|
||||
x.key_image = crypto::key_image();
|
||||
x.sum_g = crypto::secret_key();
|
||||
x.sender_extension_t = crypto::secret_key();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, carrot::return_output_info_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.input_context;
|
||||
a & x.K_o;
|
||||
a & x.K_change;
|
||||
a & x.K_spend_pubkey;
|
||||
a & x.key_image;
|
||||
a & x.sum_g;
|
||||
a & x.sender_extension_t;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,8 @@ struct CarrotEnoteV1 final
|
||||
mx25519_pubkey enote_ephemeral_pubkey;
|
||||
/// L_0
|
||||
crypto::key_image tx_first_key_image;
|
||||
// transaction output keys
|
||||
std::vector<crypto::public_key> tx_output_keys;
|
||||
};
|
||||
|
||||
/// equality operators
|
||||
|
||||
@@ -70,10 +70,11 @@ static constexpr const unsigned char CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR[] = "Ca
|
||||
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;
|
||||
static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 64;
|
||||
|
||||
// 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";
|
||||
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR[] = "SPARC return index scalar";
|
||||
|
||||
} //namespace carrot
|
||||
|
||||
@@ -122,6 +122,11 @@ bool operator==(const view_tag_t &a, const view_tag_t &b)
|
||||
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(rollup_binding_tag_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
janus_anchor_t gen_janus_anchor()
|
||||
{
|
||||
return crypto::rand<janus_anchor_t>();
|
||||
|
||||
@@ -106,6 +106,13 @@ struct encrypted_return_pubkey_t final
|
||||
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_BYTES];
|
||||
};
|
||||
|
||||
/// Salvium rollup binding tag
|
||||
constexpr std::size_t ROLLUP_BINDING_TAG_BYTES{8};
|
||||
struct rollup_binding_tag_t final
|
||||
{
|
||||
unsigned char bytes[ROLLUP_BINDING_TAG_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); }
|
||||
@@ -139,6 +146,10 @@ bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubke
|
||||
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);
|
||||
|
||||
/// overloaded operators: rollup binding tag
|
||||
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b);
|
||||
static inline bool operator!=(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b) { return !(a == b); }
|
||||
|
||||
/// generate a random janus anchor
|
||||
janus_anchor_t gen_janus_anchor();
|
||||
/// generate a random (non-null) payment ID
|
||||
|
||||
@@ -134,6 +134,10 @@ struct view_incoming_key_device
|
||||
const crypto::public_key &onetime_address,
|
||||
janus_anchor_t &anchor_special_out) const = 0;
|
||||
|
||||
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_incoming_key_device() = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -55,6 +55,13 @@ bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_x25519(const mx
|
||||
return make_carrot_uncontextualized_shared_key_receiver(m_k_view_incoming, D, kvD);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_incoming_key_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_k_view_incoming), input_context, onetime_address, return_privkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void view_incoming_key_ram_borrowed_device::make_janus_anchor_special(
|
||||
const mx25519_pubkey &enote_ephemeral_pubkey,
|
||||
const input_context_t &input_context,
|
||||
|
||||
@@ -60,6 +60,10 @@ public:
|
||||
const crypto::public_key &onetime_address,
|
||||
janus_anchor_t &anchor_special_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_k_view_incoming;
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@ extern "C"
|
||||
#include "crypto/wallet/crypto.h"
|
||||
#include "hash_functions.h"
|
||||
#include "int-util.h"
|
||||
#include "string_tools.h"
|
||||
#include "misc_language.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "transcript_fixed.h"
|
||||
@@ -211,6 +212,17 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const uint64_t idx,
|
||||
crypto::secret_key &return_index_out)
|
||||
{
|
||||
// k_idx = H_32(s_sr || input_context || Ko || idx)
|
||||
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR>(input_context, onetime_address, idx);
|
||||
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_index_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,
|
||||
@@ -221,6 +233,54 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
|
||||
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,
|
||||
const uint64_t idx,
|
||||
encrypted_return_pubkey_t &return_pubkey_out)
|
||||
{
|
||||
// compute k_return
|
||||
crypto::secret_key k_return;
|
||||
s_view_balance_dev->make_internal_return_privkey(input_context, onetime_address, k_return);
|
||||
|
||||
// compute k_idx
|
||||
crypto::secret_key k_idx;
|
||||
make_sparc_return_index(s_sender_receiver_unctx, input_context, onetime_address, idx, k_idx);
|
||||
|
||||
// compute m_return
|
||||
encrypted_return_pubkey_t m_return;
|
||||
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx,
|
||||
input_context,
|
||||
onetime_address,
|
||||
m_return);
|
||||
|
||||
#if 1
|
||||
// compute SPARC K_return = k_return * G
|
||||
crypto::public_key K_return;
|
||||
crypto::secret_key_to_public_key(k_return, K_return);
|
||||
|
||||
// compute return_enc
|
||||
encrypted_return_pubkey_t return_pub;
|
||||
static_assert(sizeof(K_return.data) == sizeof(return_pub.bytes), "Size mismatch");
|
||||
memcpy(return_pub.bytes, K_return.data, sizeof(encrypted_return_pubkey_t));
|
||||
#else
|
||||
// compute SPARC K_return = k_return * G + k_idx * T
|
||||
rct::key K_return;
|
||||
rct::addKeys2(K_return,
|
||||
rct::sk2rct(k_return),
|
||||
rct::sk2rct(k_idx),
|
||||
rct::pk2rct(crypto::get_T()));
|
||||
|
||||
// compute return_enc
|
||||
encrypted_return_pubkey_t return_pub;
|
||||
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.bytes), "Size mismatch");
|
||||
memcpy(return_pub.bytes, K_return.bytes, sizeof(encrypted_return_pubkey_t));
|
||||
#endif
|
||||
return_pubkey_out = return_pub ^ m_return;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
input_context_t make_carrot_input_context_coinbase(const std::uint64_t block_index)
|
||||
{
|
||||
// input_context = "C" || IntToBytes256(block_index)
|
||||
@@ -450,7 +510,6 @@ bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "core_types.h"
|
||||
#include "mx25519.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "device.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
@@ -137,6 +138,20 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
|
||||
const crypto::public_key &onetime_address,
|
||||
crypto::secret_key &return_privkey_out);
|
||||
/**
|
||||
* brief: make_sparc_return_index - return index, given non-secret data
|
||||
* m_return = H_32(s_sr || input_context || Ko || idx)
|
||||
* param: s_sender_receiver_unctx - s_sr
|
||||
* param: input_context - input_context
|
||||
* param: onetime_address - Ko
|
||||
* param: idx - idx
|
||||
* outparam: return_index_out - m_return
|
||||
*/
|
||||
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
|
||||
const input_context_t &input_context,
|
||||
const crypto::public_key &onetime_address,
|
||||
const uint64_t idx,
|
||||
crypto::secret_key &return_index_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
|
||||
@@ -148,6 +163,21 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
|
||||
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,
|
||||
const uint64_t idx,
|
||||
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)
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
|
||||
|
||||
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
|
||||
class carrot_logic_error: public 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)
|
||||
@@ -55,7 +55,7 @@ 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; };
|
||||
class carrot_runtime_error: public 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)
|
||||
|
||||
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
|
||||
}
|
||||
else if (!need_change_output)
|
||||
{
|
||||
return AdditionalOutputType::DUMMY;
|
||||
return AdditionalOutputType::CHANGE_UNIQUE;
|
||||
}
|
||||
else // num_selfsend == 1 && need_change_output
|
||||
{
|
||||
@@ -159,8 +159,11 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
const view_incoming_key_device *k_view_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
|
||||
RCTOutputEnoteProposal &return_enote_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();
|
||||
@@ -171,7 +174,14 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
// 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");
|
||||
if (tx_type == cryptonote::transaction_type::STAKE ||
|
||||
tx_type == cryptonote::transaction_type::BURN ||
|
||||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx_type == cryptonote::transaction_type::ROLLUP) {
|
||||
CARROT_CHECK_AND_THROW(num_proposals == 1, too_few_outputs, "tx doesn't have correct number of proposals");
|
||||
} else {
|
||||
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");
|
||||
|
||||
@@ -203,17 +213,30 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
// 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);
|
||||
auto &output_entry = sortable_data.emplace_back();
|
||||
output_entry.second = {false, i};
|
||||
|
||||
encrypted_payment_id_t encrypted_payment_id;
|
||||
get_output_proposal_normal_v1(normal_payment_proposals[i],
|
||||
tx_first_key_image,
|
||||
output_entry.first,
|
||||
encrypted_payment_id);
|
||||
if (tx_type == cryptonote::transaction_type::RETURN) {
|
||||
const uint64_t idx = 0;
|
||||
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)
|
||||
@@ -223,6 +246,11 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
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
|
||||
@@ -246,7 +274,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
{
|
||||
const CarrotPaymentProposalSelfSendV1 &selfsend_payment_proposal = selfsend_payment_proposals.at(i);
|
||||
|
||||
auto &output_entry = tools::add_element(sortable_data);
|
||||
auto &output_entry = sortable_data.emplace_back();
|
||||
output_entry.second = {true, i};
|
||||
|
||||
if (s_view_balance_dev != nullptr)
|
||||
@@ -255,6 +283,8 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
*s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
other_enote_ephemeral_pubkey,
|
||||
tx_type,
|
||||
return_enote_out,
|
||||
output_entry.first);
|
||||
}
|
||||
else if (k_view_dev != nullptr)
|
||||
@@ -270,10 +300,16 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
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
|
||||
@@ -287,8 +323,13 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
if (sortable_data[i].first.enote.onetime_address == change_address)
|
||||
{
|
||||
change_index_out = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -324,11 +365,11 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
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");
|
||||
}
|
||||
// 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;
|
||||
@@ -379,7 +420,7 @@ void get_coinbase_output_enotes(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
{
|
||||
get_coinbase_output_proposal_v1(normal_payment_proposals[i],
|
||||
block_index,
|
||||
tools::add_element(output_coinbase_enotes_out));
|
||||
output_coinbase_enotes_out.emplace_back());
|
||||
}
|
||||
|
||||
// assert uniqueness and non-trivial-ness of D_e
|
||||
|
||||
@@ -109,8 +109,11 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
|
||||
const view_incoming_key_device *k_view_dev,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
|
||||
RCTOutputEnoteProposal &return_enote_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
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "misc_language.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "crypto/generators.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
@@ -170,6 +171,7 @@ static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_re
|
||||
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,
|
||||
@@ -177,7 +179,8 @@ static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_re
|
||||
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)
|
||||
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,
|
||||
@@ -202,6 +205,15 @@ static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_re
|
||||
|
||||
// 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,
|
||||
0,
|
||||
return_pubkey_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -209,6 +221,7 @@ bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1
|
||||
{
|
||||
return a.destination == b.destination &&
|
||||
a.amount == b.amount &&
|
||||
a.asset_type == b.asset_type &&
|
||||
a.randomness == b.randomness;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -218,6 +231,7 @@ bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentPro
|
||||
a.amount == b.amount &&
|
||||
a.enote_type == b.enote_type &&
|
||||
a.internal_message == b.internal_message &&
|
||||
a.asset_type == b.asset_type &&
|
||||
0 == memcmp(&a.enote_ephemeral_pubkey, &b.enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -257,6 +271,7 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
|
||||
// 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;
|
||||
@@ -268,6 +283,7 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
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,
|
||||
@@ -275,8 +291,9 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
output_enote_out.onetime_address,
|
||||
dummy_encrypted_amount,
|
||||
dummy_encrypted_payment_id,
|
||||
output_enote_out.view_tag);
|
||||
|
||||
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,
|
||||
@@ -290,6 +307,7 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
@@ -309,6 +327,7 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
|
||||
// 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,
|
||||
@@ -316,6 +335,7 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
CarrotEnoteType::PAYMENT,
|
||||
output_enote_out.enote.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_view_balance_dev, // we need it to calculate the return pubkey
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
@@ -323,17 +343,19 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
encrypted_payment_id_out,
|
||||
output_enote_out.enote.view_tag);
|
||||
|
||||
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.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,
|
||||
@@ -368,6 +390,7 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
|
||||
|
||||
// 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,
|
||||
@@ -376,6 +399,7 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
|
||||
proposal.enote_type,
|
||||
enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
nullptr,
|
||||
false, // coinbase_amount_commitment
|
||||
s_sender_receiver,
|
||||
output_enote_out.amount_blinding_factor,
|
||||
@@ -383,7 +407,8 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
|
||||
output_enote_out.enote.onetime_address,
|
||||
output_enote_out.enote.amount_enc,
|
||||
dummy_encrypted_payment_id,
|
||||
output_enote_out.enote.view_tag);
|
||||
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;
|
||||
@@ -400,14 +425,100 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
|
||||
// 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.asset_type = proposal.asset_type;
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
output_enote_out.amount = proposal.amount;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void get_output_proposal_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &K_o,
|
||||
const uint64_t idx,
|
||||
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);
|
||||
/*
|
||||
// K_v = K_return - k_idx * T
|
||||
CarrotPaymentProposalV1 proposal_with_kv = proposal;
|
||||
const bool is_sparc = (k_idx != crypto::null_skey) && sc_isnonzero(to_bytes(k_idx)); // check here! for zero its not needed
|
||||
if (is_sparc)
|
||||
{
|
||||
// Calculate K_v = K_return - k_idx * T
|
||||
const rct::key K_return_rct = rct::pk2rct(proposal.destination.address_view_pubkey);
|
||||
const rct::key k_idx_T = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
|
||||
rct::key K_v_rct;
|
||||
rct::subKeys(K_v_rct, K_return_rct, k_idx_T);
|
||||
proposal_with_kv.destination.address_view_pubkey = rct::rct2pk(K_v_rct);
|
||||
}
|
||||
*/
|
||||
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
|
||||
);
|
||||
|
||||
// Override the onetime address
|
||||
crypto::secret_key k_idx;
|
||||
make_sparc_return_index(s_sender_receiver_unctx.data, input_context, K_o, idx, k_idx);
|
||||
rct::key K_r = rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey),rct::pk2rct(proposal.destination.address_view_pubkey));
|
||||
rct::key K_idx = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
|
||||
output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(K_r, K_idx));
|
||||
|
||||
// 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_return_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
RCTOutputEnoteProposal &output_enote_out,
|
||||
encrypted_payment_id_t &encrypted_payment_id_out)
|
||||
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,
|
||||
@@ -425,27 +536,30 @@ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
|
||||
// 4. build the output enote address pieces
|
||||
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: the following call needs the "destination" parameter adjusted for return_payment
|
||||
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,
|
||||
false,
|
||||
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);
|
||||
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)));
|
||||
|
||||
@@ -461,15 +575,20 @@ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
// 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 and first key image
|
||||
// 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,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
RCTOutputEnoteProposal &return_enote_out,
|
||||
RCTOutputEnoteProposal &output_enote_out)
|
||||
{
|
||||
// 1. sanity checks
|
||||
@@ -528,8 +647,40 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
|
||||
// 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.asset_type = proposal.asset_type;
|
||||
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
|
||||
output_enote_out.amount = proposal.amount;
|
||||
|
||||
// 10. construct the stake return enote
|
||||
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
// make k_return
|
||||
crypto::secret_key k_return;
|
||||
s_view_balance_dev.make_internal_return_privkey(input_context, output_enote_out.enote.onetime_address, k_return);
|
||||
|
||||
// compute K_return = k_return * G
|
||||
crypto::public_key return_pub;
|
||||
crypto::secret_key_to_public_key(k_return, return_pub);
|
||||
|
||||
// Make a destination address for the return
|
||||
CarrotDestinationV1 return_destination;
|
||||
make_carrot_main_address_v1(output_enote_out.enote.onetime_address, return_pub, return_destination);
|
||||
|
||||
// Create the return proposal, using the return address and the amount
|
||||
const CarrotPaymentProposalV1 proposal_return = CarrotPaymentProposalV1{
|
||||
.destination = return_destination,
|
||||
.amount = 0,
|
||||
.randomness = gen_janus_anchor()
|
||||
};
|
||||
|
||||
encrypted_payment_id_t encrypted_payment_id_return;
|
||||
get_output_proposal_return_v1(
|
||||
proposal_return,
|
||||
tx_first_key_image,
|
||||
nullptr, // s_view_balance_dev
|
||||
return_enote_out,
|
||||
encrypted_payment_id_return
|
||||
);
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
|
||||
|
||||
@@ -82,6 +82,8 @@ struct CarrotPaymentProposalSelfSendV1 final
|
||||
std::optional<mx25519_pubkey> enote_ephemeral_pubkey;
|
||||
/// anchor: arbitrary, pre-encrypted message for _internal_ selfsends
|
||||
std::optional<janus_anchor_t> internal_message;
|
||||
/// asset type
|
||||
std::string asset_type;
|
||||
};
|
||||
|
||||
struct RCTOutputEnoteProposal
|
||||
@@ -133,9 +135,25 @@ void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
*/
|
||||
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_paymentchannel_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_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
const crypto::key_image &tx_first_key_image,
|
||||
const view_balance_secret_device *s_view_balance_dev,
|
||||
const crypto::public_key &K_o,
|
||||
const uint64_t idx,
|
||||
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 -
|
||||
@@ -145,6 +163,7 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||
*/
|
||||
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);
|
||||
/**
|
||||
@@ -173,6 +192,8 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
|
||||
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,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
RCTOutputEnoteProposal &return_enote_out,
|
||||
RCTOutputEnoteProposal &output_enote_out);
|
||||
/**
|
||||
* brief: gen_jamtis_payment_proposal_v1 - generate a random proposal
|
||||
|
||||
+189
-21
@@ -70,6 +70,104 @@ static crypto::secret_key make_enote_ephemeral_privkey_sender(const janus_anchor
|
||||
return enote_ephemeral_privkey;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool scan_return_output(
|
||||
const crypto::public_key &return_onetime_address,
|
||||
const mx25519_pubkey &return_ephemeral_pubkey,
|
||||
const carrot::view_tag_t &return_view_tag,
|
||||
const carrot::encrypted_janus_anchor_t &return_anchor_enc,
|
||||
const carrot::encrypted_amount_t &return_amount_enc,
|
||||
const std::optional<rct::key> amount_commitment,
|
||||
const carrot::input_context_t &return_input_context,
|
||||
carrot::carrot_and_legacy_account &account,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out
|
||||
) {
|
||||
const auto &return_output_map = account.get_return_output_map_ref();
|
||||
CHECK_AND_ASSERT_MES(return_output_map.count(return_onetime_address), false, "return output not found");
|
||||
const auto &origin_tx = return_output_map.at(return_onetime_address);
|
||||
|
||||
// 1. make k_return
|
||||
crypto::secret_key k_return;
|
||||
account.s_view_balance_dev.make_internal_return_privkey(origin_tx.input_context, origin_tx.K_o, k_return);
|
||||
|
||||
// 2. compute K_return' = k_return * G
|
||||
crypto::public_key K_return;
|
||||
crypto::secret_key_to_public_key(k_return, K_return);
|
||||
|
||||
// 3. ssr
|
||||
mx25519_pubkey shared_secret_return_unctx;
|
||||
crypto::hash shared_secret_return;
|
||||
carrot::make_carrot_uncontextualized_shared_key_receiver(k_return, return_ephemeral_pubkey, shared_secret_return_unctx);
|
||||
carrot::make_carrot_sender_receiver_secret(
|
||||
shared_secret_return_unctx.data,
|
||||
return_ephemeral_pubkey,
|
||||
return_input_context,
|
||||
shared_secret_return
|
||||
);
|
||||
|
||||
// 4. verify the view_tag
|
||||
CHECK_AND_ASSERT_MES(
|
||||
carrot::test_carrot_view_tag(
|
||||
shared_secret_return_unctx.data,
|
||||
return_input_context,
|
||||
return_onetime_address,
|
||||
return_view_tag
|
||||
),
|
||||
false,
|
||||
"view tag verification failed for carrot coinbase enote"
|
||||
);
|
||||
|
||||
// 5. compute anchor_return
|
||||
carrot::janus_anchor_t recovered_anchor_return =
|
||||
carrot::decrypt_carrot_anchor(return_anchor_enc, shared_secret_return, return_onetime_address);
|
||||
|
||||
// 6. compute d_e'
|
||||
crypto::secret_key recovered_ephemeral_privkey_return;
|
||||
carrot::make_carrot_enote_ephemeral_privkey(
|
||||
recovered_anchor_return,
|
||||
return_input_context,
|
||||
origin_tx.K_change,
|
||||
carrot::null_payment_id,
|
||||
recovered_ephemeral_privkey_return
|
||||
);
|
||||
|
||||
// 7. compute D_e'
|
||||
mx25519_pubkey recovered_ephemeral_pubkey_return;
|
||||
carrot::make_carrot_enote_ephemeral_pubkey(
|
||||
recovered_ephemeral_privkey_return,
|
||||
origin_tx.K_change,
|
||||
false,
|
||||
recovered_ephemeral_pubkey_return
|
||||
);
|
||||
|
||||
// 8. verify the enote ephemeral pubkey
|
||||
CHECK_AND_ASSERT_MES(
|
||||
memcmp(recovered_ephemeral_pubkey_return.data, return_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0,
|
||||
false,
|
||||
"carrot coinbase enote protection verification failed"
|
||||
);
|
||||
|
||||
amount_out = carrot::decrypt_carrot_amount(return_amount_enc, shared_secret_return, return_onetime_address);
|
||||
address_spend_pubkey_out = origin_tx.K_change;
|
||||
|
||||
if (amount_commitment)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(
|
||||
carrot::try_recompute_carrot_amount_commitment(shared_secret_return,
|
||||
amount_out,
|
||||
address_spend_pubkey_out,
|
||||
carrot::CarrotEnoteType::PAYMENT,
|
||||
amount_commitment.value(),
|
||||
amount_blinding_factor_out
|
||||
),
|
||||
false,
|
||||
"failed to recompute carrot amount commitment for return output"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
static bool try_scan_carrot_coinbase_enote_checked(
|
||||
const CarrotCoinbaseEnoteV1 &enote,
|
||||
const mx25519_pubkey &s_sender_receiver_unctx,
|
||||
@@ -289,6 +387,11 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
CarrotEnoteType &enote_type_out,
|
||||
const bool check_pid)
|
||||
{
|
||||
epee::span<const crypto::public_key> main_address_spend_pubkeys;
|
||||
if (destination.is_subaddress)
|
||||
main_address_spend_pubkeys = {};
|
||||
else
|
||||
main_address_spend_pubkeys = {&destination.address_spend_pubkey, 1};
|
||||
crypto::public_key recovered_address_spend_pubkey;
|
||||
payment_id_t recovered_payment_id;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
@@ -297,7 +400,7 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
|
||||
if (!try_scan_carrot_enote_external_normal_checked(enote,
|
||||
encrypted_payment_id,
|
||||
s_sender_receiver_unctx,
|
||||
{&destination.address_spend_pubkey, 1},
|
||||
main_address_spend_pubkeys,
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
recovered_address_spend_pubkey,
|
||||
@@ -361,43 +464,108 @@ bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
carrot::carrot_and_legacy_account &account,
|
||||
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)
|
||||
janus_anchor_t &internal_message_out,
|
||||
crypto::public_key &return_address_out,
|
||||
bool &is_return_out)
|
||||
{
|
||||
// Determine whether this is a full wallet or a watch-only wallet
|
||||
const cryptonote::account_keys &keys = account.get_keys();
|
||||
|
||||
// input_context
|
||||
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
|
||||
|
||||
// assume that the enote is not a return output
|
||||
is_return_out = false;
|
||||
|
||||
// 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);
|
||||
account.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)
|
||||
if (nominal_view_tag == enote.view_tag) {
|
||||
// s^ctx_sr = H_32(s_vb, D_e, input_context)
|
||||
crypto::hash s_sender_receiver;
|
||||
account.s_view_balance_dev.make_internal_sender_receiver_secret(enote.enote_ephemeral_pubkey,
|
||||
input_context,
|
||||
s_sender_receiver);
|
||||
|
||||
if (!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))
|
||||
return false;
|
||||
|
||||
// we received a change output
|
||||
// save the Kr = K_change + K_return to out subaddress map
|
||||
for (const auto &output_key : enote.tx_output_keys) {
|
||||
// make k_return
|
||||
crypto::secret_key k_return;
|
||||
const carrot::input_context_t input_context = carrot::make_carrot_input_context(enote.tx_first_key_image);
|
||||
account.s_view_balance_dev.make_internal_return_privkey(input_context, output_key, k_return);
|
||||
|
||||
// compute K_return = k_return * G
|
||||
crypto::public_key K_return;
|
||||
crypto::secret_key_to_public_key(k_return, K_return);
|
||||
|
||||
// compute K_r = K_return + K_o
|
||||
crypto::public_key K_r = rct::rct2pk(rct::addKeys(rct::pk2rct(K_return), rct::pk2rct(enote.onetime_address)));
|
||||
|
||||
// calculate the key image for the return output
|
||||
crypto::secret_key sum_g;
|
||||
sc_add(to_bytes(sum_g), to_bytes(sender_extension_g_out), to_bytes(k_return));
|
||||
crypto::key_image key_image = account.derive_key_image_view_only(address_spend_pubkey_out,
|
||||
sum_g,
|
||||
sender_extension_t_out,
|
||||
K_r
|
||||
);
|
||||
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: test whether this will even work for return_payment detection
|
||||
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
|
||||
//account.insert_return_output_info({{K_r, {input_context, output_key, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
|
||||
// LAND AHOY!!!
|
||||
}
|
||||
|
||||
// janus protection checks are not needed for internal scans
|
||||
return true;
|
||||
}
|
||||
|
||||
// check for known return addresses
|
||||
const auto &return_map = account.get_return_output_map_ref();
|
||||
if (return_map.find(enote.onetime_address) == return_map.end())
|
||||
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);
|
||||
// scan the return output
|
||||
if (!scan_return_output(
|
||||
enote.onetime_address,
|
||||
enote.enote_ephemeral_pubkey,
|
||||
enote.view_tag,
|
||||
enote.anchor_enc,
|
||||
enote.amount_enc,
|
||||
enote.amount_commitment,
|
||||
carrot::make_carrot_input_context(enote.tx_first_key_image),
|
||||
account,
|
||||
address_spend_pubkey_out,
|
||||
amount_out,
|
||||
amount_blinding_factor_out))
|
||||
return false;
|
||||
|
||||
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
|
||||
// if we come here, we have a return output
|
||||
is_return_out = true;
|
||||
return_address_out = enote.onetime_address;
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace carrot
|
||||
|
||||
+19
-2
@@ -39,6 +39,7 @@
|
||||
#include "carrot_enote_types.h"
|
||||
#include "device.h"
|
||||
#include "span.h"
|
||||
#include "account.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
@@ -178,14 +179,30 @@ bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
|
||||
* return: true iff the scan process succeeded
|
||||
*/
|
||||
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
const view_balance_secret_device &s_view_balance_dev,
|
||||
carrot::carrot_and_legacy_account &account,
|
||||
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);
|
||||
janus_anchor_t &internal_message_out,
|
||||
crypto::public_key &return_address_out,
|
||||
bool &is_return_out);
|
||||
//! @TODO: try_scan_carrot_enote_internal_sender(): can't validate burning w/o passing s_sr = s_vb
|
||||
|
||||
bool scan_return_output(
|
||||
const crypto::public_key &return_onetime_address,
|
||||
const mx25519_pubkey &return_ephemeral_pubkey,
|
||||
const carrot::view_tag_t &return_view_tag,
|
||||
const carrot::encrypted_janus_anchor_t &return_anchor_enc,
|
||||
const carrot::encrypted_amount_t &return_amount_enc,
|
||||
const std::optional<rct::key> amount_commitment,
|
||||
const carrot::input_context_t &return_input_context,
|
||||
carrot::carrot_and_legacy_account &account,
|
||||
crypto::public_key &address_spend_pubkey_out,
|
||||
rct::xmr_amount &amount_out,
|
||||
crypto::secret_key &amount_blinding_factor_out
|
||||
);
|
||||
|
||||
} //namespace carrot
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(carrot_impl_sources
|
||||
account.cpp
|
||||
address_device_ram_borrowed.cpp
|
||||
address_utils_compat.cpp
|
||||
format_utils.cpp
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "carrot_core/account_secrets.h"
|
||||
#include "carrot_core/address_utils.h"
|
||||
#include "carrot_core/destination.h"
|
||||
#include "carrot_core/device_ram_borrowed.h"
|
||||
#include "carrot_core/enote_utils.h"
|
||||
#include "carrot_impl/subaddress_index.h"
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/subaddress_index.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static constexpr std::uint32_t MAX_SUBADDRESS_MAJOR_INDEX = 5;
|
||||
static constexpr std::uint32_t MAX_SUBADDRESS_MINOR_INDEX = 20;
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
class carrot_and_legacy_account : public cryptonote::account_base
|
||||
{
|
||||
public:
|
||||
view_incoming_key_ram_borrowed_device k_view_incoming_dev;
|
||||
view_balance_secret_ram_borrowed_device s_view_balance_dev;
|
||||
generate_address_secret_ram_borrowed_device s_generate_address_dev;
|
||||
|
||||
std::unordered_map<crypto::public_key, subaddress_index_extended> subaddress_map;
|
||||
AddressDeriveType default_derive_type;
|
||||
|
||||
carrot_and_legacy_account(): k_view_incoming_dev(get_keys().m_view_secret_key),
|
||||
s_view_balance_dev(get_keys().s_view_balance),
|
||||
s_generate_address_dev(get_keys().s_generate_address)
|
||||
{}
|
||||
|
||||
carrot_and_legacy_account(const carrot_and_legacy_account &k) = delete;
|
||||
carrot_and_legacy_account(carrot_and_legacy_account&&) = delete;
|
||||
|
||||
carrot_and_legacy_account& operator=(const carrot_and_legacy_account&) = delete;
|
||||
carrot_and_legacy_account& operator=(carrot_and_legacy_account&&) = delete;
|
||||
|
||||
CarrotDestinationV1 cryptonote_address(const payment_id_t payment_id = null_payment_id,
|
||||
const AddressDeriveType derive_type = AddressDeriveType::Auto) const;
|
||||
|
||||
CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const;
|
||||
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddress_map_cn() const;
|
||||
|
||||
// brief: opening_for_subaddress - return (k^g_a, k^t_a) for j s.t. K^j_s = (k^g_a * G + k^t_a * T)
|
||||
void opening_for_subaddress(const subaddress_index_extended &subaddress_index,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out,
|
||||
crypto::public_key &address_spend_pubkey_out) const;
|
||||
|
||||
bool try_searching_for_opening_for_subaddress(const crypto::public_key &address_spend_pubkey,
|
||||
crypto::secret_key &address_privkey_g_out,
|
||||
crypto::secret_key &address_privkey_t_out) const;
|
||||
|
||||
bool try_searching_for_opening_for_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
crypto::secret_key &x_out,
|
||||
crypto::secret_key &y_out) const;
|
||||
|
||||
bool can_open_fcmp_onetime_address(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
crypto::key_image derive_key_image(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
void generate_subaddress_map();
|
||||
|
||||
crypto::secret_key generate(
|
||||
const crypto::secret_key& recovery_key = crypto::secret_key(),
|
||||
bool recover = false,
|
||||
bool two_random = false,
|
||||
const AddressDeriveType default_derive_type = AddressDeriveType::Carrot
|
||||
);
|
||||
|
||||
void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
|
||||
|
||||
void set_cn_subaddress_map(const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddress_map);
|
||||
|
||||
AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const;
|
||||
};
|
||||
}
|
||||
@@ -86,6 +86,12 @@ inline void serialize(Archive &a, carrot::encrypted_return_pubkey_t &x, const bo
|
||||
}
|
||||
//---------------------------------------------------
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, carrot::rollup_binding_tag_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.bytes;
|
||||
}
|
||||
//---------------------------------------------------
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, carrot::CarrotDestinationV1 &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.address_spend_pubkey;
|
||||
|
||||
@@ -42,3 +42,4 @@ BLOB_SERIALIZER(carrot::view_tag_t);
|
||||
BLOB_SERIALIZER(carrot::encrypted_janus_anchor_t);
|
||||
BLOB_SERIALIZER(carrot::encrypted_payment_id_t);
|
||||
BLOB_SERIALIZER(carrot::encrypted_return_pubkey_t);
|
||||
BLOB_SERIALIZER(carrot::rollup_binding_tag_t);
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "carrot_core/enote_utils.h"
|
||||
#include "carrot_core/exceptions.h"
|
||||
#include "carrot_core/payment_proposal.h"
|
||||
#include "common/container_helpers.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "cryptonote_config.h"
|
||||
@@ -194,21 +193,55 @@ bool try_load_carrot_extra_v1(
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const encrypted_payment_id_t encrypted_payment_id)
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const rct::xmr_amount tx_amount_burnt,
|
||||
const std::vector<uint8_t> &change_masks,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const carrot::RCTOutputEnoteProposal &return_enote,
|
||||
const encrypted_payment_id_t encrypted_payment_id,
|
||||
const uint8_t hf_version)
|
||||
{
|
||||
const size_t nins = key_images.size();
|
||||
const size_t nouts = enotes.size();
|
||||
CHECK_AND_ASSERT_THROW_MES(nins == sources.size(), "invalid inputs/sources size");
|
||||
CHECK_AND_ASSERT_THROW_MES(change_masks.size() == nouts, "invalid change masks size. Expected: " << nouts - 1 << " got: " << change_masks.size());
|
||||
|
||||
// Sanity check asset types - all enotes and sources must be the same
|
||||
std::string asset_type = "";
|
||||
for (const auto &enote: enotes) {
|
||||
if (asset_type == "")
|
||||
asset_type = enote.asset_type;
|
||||
else
|
||||
CHECK_AND_ASSERT_THROW_MES(enote.asset_type == asset_type, "invalid asset_type in enote. Expected: " << asset_type << " got: " << enote.asset_type);
|
||||
}
|
||||
for (const auto &source: sources) {
|
||||
if (asset_type == "")
|
||||
asset_type = source.asset_type;
|
||||
else
|
||||
CHECK_AND_ASSERT_THROW_MES(source.asset_type == asset_type, "invalid asset_type in source. Expected: " << asset_type << " got: " << source.asset_type);
|
||||
}
|
||||
|
||||
cryptonote::transaction tx;
|
||||
tx.pruned = true;
|
||||
tx.version = TRANSACTION_VERSION_N_OUTS;
|
||||
tx.unlock_time = 0;
|
||||
tx.source_asset_type = asset_type;
|
||||
tx.destination_asset_type = asset_type;
|
||||
tx.version = (hf_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
|
||||
tx.type =
|
||||
tx_type == cryptonote::transaction_type::RETURN ? cryptonote::transaction_type::TRANSFER : tx_type;
|
||||
tx.amount_burnt = (
|
||||
tx.type == cryptonote::transaction_type::STAKE ||
|
||||
tx.type == cryptonote::transaction_type::BURN ||
|
||||
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx.type == cryptonote::transaction_type::ROLLUP
|
||||
) ? tx_amount_burnt : 0;
|
||||
tx.return_address_change_mask.assign(change_masks.begin(), change_masks.end());
|
||||
tx.vin.reserve(nins);
|
||||
tx.vout.reserve(nouts);
|
||||
tx.return_address_list.reserve(change_masks.size());
|
||||
tx.extra.reserve(MAX_TX_EXTRA_SIZE);
|
||||
tx.rct_signatures.type = carrot_v1_rct_type;
|
||||
tx.rct_signatures.txnFee = fee;
|
||||
@@ -226,7 +259,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
||||
//L
|
||||
tx.vin.emplace_back(cryptonote::txin_to_key{ //@TODO: can save 2 bytes by using slim input type
|
||||
.amount = 0,
|
||||
.asset_type = "SAL1",
|
||||
.asset_type = asset_type,
|
||||
.key_offsets = cryptonote::absolute_output_offsets_to_relative(key_offsets),
|
||||
.k_image = key_images.at(i)
|
||||
});
|
||||
@@ -244,13 +277,35 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
||||
}});
|
||||
|
||||
//a_enc
|
||||
rct::ecdhTuple &ecdh_tuple = tools::add_element(tx.rct_signatures.ecdhInfo);
|
||||
rct::ecdhTuple &ecdh_tuple = tx.rct_signatures.ecdhInfo.emplace_back();
|
||||
memcpy(ecdh_tuple.amount.bytes, enote.amount_enc.bytes, sizeof(ecdh_tuple.amount));
|
||||
|
||||
//C_a
|
||||
tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment});
|
||||
|
||||
//K_return
|
||||
if (tx_type != cryptonote::transaction_type::STAKE && tx_type != cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
crypto::public_key K_return;
|
||||
memcpy(K_return.data, enote.return_enc.bytes, sizeof(crypto::public_key));
|
||||
tx.return_address_list.push_back(K_return);
|
||||
}
|
||||
}
|
||||
|
||||
// store the return pubkey for stake txs
|
||||
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
tx.protocol_tx_data.version = 1;
|
||||
memcpy(tx.protocol_tx_data.return_address.data, return_enote.enote.onetime_address.data, sizeof(crypto::public_key));
|
||||
memcpy(tx.protocol_tx_data.return_pubkey.data, return_enote.enote.enote_ephemeral_pubkey.data, sizeof(crypto::public_key));
|
||||
tx.protocol_tx_data.return_view_tag = return_enote.enote.view_tag;
|
||||
tx.protocol_tx_data.return_anchor_enc = return_enote.enote.anchor_enc;
|
||||
}
|
||||
if (tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
tx.token_metadata = token;
|
||||
} else {
|
||||
tx.token_metadata = cryptonote::token_metadata_t{};
|
||||
}
|
||||
|
||||
//ephemeral pubkeys: D_e
|
||||
store_carrot_ephemeral_pubkeys_to_extra(enotes, tx.extra);
|
||||
|
||||
@@ -324,6 +379,14 @@ bool try_load_carrot_enote_from_transaction_v1(const cryptonote::transaction &tx
|
||||
|
||||
//D_e
|
||||
enote_out.enote_ephemeral_pubkey = enote_ephemeral_pubkeys[ephemeral_pubkey_index];
|
||||
|
||||
// save all output keys in order to calculate Kr values.
|
||||
for (const auto& out: tx.vout) {
|
||||
const cryptonote::txout_to_carrot_v1 * const carrot_out = boost::strict_get<cryptonote::txout_to_carrot_v1>(&out.target);
|
||||
if (carrot_out) {
|
||||
enote_out.tx_output_keys.push_back(carrot_out->key);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -382,7 +445,7 @@ cryptonote::transaction store_carrot_to_coinbase_transaction_v1(
|
||||
cryptonote::transaction tx;
|
||||
tx.type = tx_type;
|
||||
tx.pruned = false;
|
||||
tx.version = 2;
|
||||
tx.version = TRANSACTION_VERSION_CARROT;
|
||||
tx.unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
tx.vin.reserve(1);
|
||||
tx.vout.reserve(nouts);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_basic/tx_extra.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "carrot_core/payment_proposal.h"
|
||||
|
||||
//third party headers
|
||||
|
||||
@@ -101,10 +102,16 @@ bool try_load_carrot_extra_v1(
|
||||
* return: a fully populated, pruned, non-coinbase transaction containing given Carrot information
|
||||
*/
|
||||
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const encrypted_payment_id_t encrypted_payment_id);
|
||||
const std::vector<crypto::key_image> &key_images,
|
||||
const std::vector<cryptonote::tx_source_entry> &sources,
|
||||
const rct::xmr_amount fee,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const rct::xmr_amount tx_amount_burnt,
|
||||
const std::vector<uint8_t> &change_masks,
|
||||
const cryptonote::token_metadata_t &token,
|
||||
const RCTOutputEnoteProposal &return_enote,
|
||||
const encrypted_payment_id_t encrypted_payment_id,
|
||||
const uint8_t hf_version);
|
||||
/**
|
||||
* brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
|
||||
* param: tx -
|
||||
|
||||
@@ -88,7 +88,8 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
|
||||
const epee::span<const InputCandidate> input_candidates,
|
||||
const std::set<std::size_t> &selectable_inputs,
|
||||
std::size_t max_num_input_count,
|
||||
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count)
|
||||
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count,
|
||||
const bool is_token_transfer)
|
||||
{
|
||||
// Returns (N, X) where the X is the sum of the amounts of the greatest N <= max_num_input_count
|
||||
// inputs from selectable_inputs, maximizing X - F(N). F(N) is the fee for this transaction,
|
||||
@@ -96,6 +97,7 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
|
||||
// the fee, but greater than or equal to the difference of the fee compared to excluding that
|
||||
// input. If this function returns N == 0, then there aren't enough usable funds, i.e. no N
|
||||
// exists such that X - F(N) > 0.
|
||||
// For token transfers, fee is paid separately in SAL1 so we just accumulate all inputs.
|
||||
|
||||
if (fee_by_input_count.empty() || selectable_inputs.empty())
|
||||
return {0, 0};
|
||||
@@ -125,6 +127,15 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
|
||||
{
|
||||
const rct::xmr_amount amount = *amount_it;
|
||||
const rct::xmr_amount current_fee = fee_by_input_count.at(num_ins + 1);
|
||||
|
||||
// For token transfers, fee is paid separately in SAL1 - just accumulate inputs
|
||||
if (is_token_transfer)
|
||||
{
|
||||
++num_ins;
|
||||
cumulative_input_sum += amount;
|
||||
continue;
|
||||
}
|
||||
|
||||
CARROT_CHECK_AND_THROW(current_fee > last_fee,
|
||||
carrot_logic_error, "provided fee by input count is not monotonically increasing");
|
||||
const rct::xmr_amount marginal_fee_diff = current_fee - last_fee;
|
||||
@@ -318,33 +329,27 @@ std::vector<std::size_t> get_input_counts_in_preferred_order()
|
||||
// 1 or 2 randomly, then
|
||||
// other ascending powers of 2, then
|
||||
// other ascending positive numbers
|
||||
|
||||
|
||||
//! @TODO: MRL discussion about 2 vs 1 default input count when 1 input can pay. If we default
|
||||
// to 1, then that may reveal more information about the amount, and reveals that one can't pay
|
||||
// with 1 output when using 2. Vice versa, if we default to 2, then that means that one only
|
||||
// owns 1 output when using 1. It may be the most advantageous to randomly switch between
|
||||
// preferring 1 vs 2. See: https://lavalle.pl/planning/node437.html. Con to this approach: if we
|
||||
// default to 1 over 2 always then there's scenarios where we net save tx fees and proving time.
|
||||
|
||||
//static_assert(CARROT_MAX_TX_INPUTS == FCMP_PLUS_PLUS_MAX_INPUTS, "inconsistent input count max limit");
|
||||
static_assert(CARROT_MIN_TX_INPUTS == 1 && CARROT_MAX_TX_INPUTS == 128,
|
||||
// preferring 1 vs 2. See: https://lavalle.pl/planning/node437.html. Con to this approach: if we
|
||||
// default to 1 over 2 always then there's scenarios where we net save tx fees and proving time.
|
||||
|
||||
//static_assert(CARROT_MAX_TX_INPUTS == FCMP_PLUS_PLUS_MAX_INPUTS, "inconsistent input count max limit");
|
||||
static_assert(CARROT_MIN_TX_INPUTS == 1 && CARROT_MAX_TX_INPUTS == 64,
|
||||
"refactor this function for different input count limits");
|
||||
|
||||
const bool random_bit = 0 == (crypto::rand<uint8_t>() & 0x01);
|
||||
if (random_bit)
|
||||
return {2, 1, 4, 8, 16, 32, 64, 128, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73,
|
||||
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
|
||||
98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
|
||||
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127};
|
||||
return {2, 1, 4, 8, 16, 32, 64, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
|
||||
else
|
||||
return {1, 2, 4, 8, 16, 32, 64, 128, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73,
|
||||
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
|
||||
98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
|
||||
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127};
|
||||
return {1, 2, 4, 8, 16, 32, 64, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
select_inputs_func_t make_single_transfer_input_selector(
|
||||
@@ -378,11 +383,12 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
|
||||
// 3. Calculate minimum required input money sum for a given input count
|
||||
const bool subtract_fee = flags & IS_KNOWN_FEE_SUBTRACTABLE;
|
||||
const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
|
||||
std::map<std::size_t, boost::multiprecision::uint128_t> required_money_by_input_count;
|
||||
for (const auto &fee_and_input_count : fee_by_input_count)
|
||||
{
|
||||
required_money_by_input_count[fee_and_input_count.first] =
|
||||
nominal_output_sum + (subtract_fee ? 0 : fee_and_input_count.second);
|
||||
nominal_output_sum + ((subtract_fee || is_token_transfer) ? 0 : fee_and_input_count.second);
|
||||
}
|
||||
const boost::multiprecision::uint128_t absolute_minimum_required_money
|
||||
= required_money_by_input_count.cbegin()->second;
|
||||
@@ -396,9 +402,10 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
"Not enough money in all inputs (" << cryptonote::print_money(total_candidate_money)
|
||||
<< ") to fund minimum output sum (" << cryptonote::print_money(absolute_minimum_required_money) << ')');
|
||||
|
||||
// const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
|
||||
std::set<std::size_t> all_idxs; for (std::size_t i = 0; i < input_candidates.size(); ++i) all_idxs.insert(i);
|
||||
const std::pair<std::size_t, boost::multiprecision::uint128_t> max_usable_money =
|
||||
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count);
|
||||
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
|
||||
CARROT_CHECK_AND_THROW(max_usable_money.second >= absolute_minimum_required_money,
|
||||
not_enough_usable_money,
|
||||
"Not enough usable money in top " << max_usable_money.first << " inputs ("
|
||||
@@ -423,7 +430,7 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
|
||||
// Skip if not enough money in this selectable set for max number of tx inputs...
|
||||
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
|
||||
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count);
|
||||
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
|
||||
if (!max_usable_money.first)
|
||||
continue;
|
||||
else if (max_usable_money.second < required_money_by_input_count.at(max_usable_money.first))
|
||||
@@ -447,7 +454,7 @@ select_inputs_func_t make_single_transfer_input_selector(
|
||||
|
||||
// Skip if not enough money in this selectable set for exact number of inputs...
|
||||
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
|
||||
input_candidate_subset, n_inputs, fee_by_input_count);
|
||||
input_candidate_subset, n_inputs, fee_by_input_count, is_token_transfer);
|
||||
if (max_usable_money.first != n_inputs)
|
||||
continue;
|
||||
else if (max_usable_money.second < required_money)
|
||||
|
||||
@@ -52,14 +52,17 @@ struct InputCandidate
|
||||
namespace InputSelectionFlags
|
||||
{
|
||||
// Quantum forward secrecy (ON = unsafe)
|
||||
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0;
|
||||
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1;
|
||||
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0; // 000..00000001
|
||||
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1; // 000..00000010
|
||||
static constexpr std::uint32_t ALLOW_MIXED_INTERNAL_EXTERNAL = 1 << 2;
|
||||
static constexpr std::uint32_t ALLOW_MIXED_CARROT_PRE_CARROT = 1 << 3;
|
||||
|
||||
// Amount handling
|
||||
static constexpr std::uint32_t IS_KNOWN_FEE_SUBTRACTABLE = 1 << 4;
|
||||
static constexpr std::uint32_t ALLOW_DUST = 1 << 5;
|
||||
|
||||
// Token transfer (fee is paid separately in SAL1 via rollup tx, so no fee for input selection)
|
||||
static constexpr std::uint32_t IS_TOKEN_TRANSFER = 1 << 6;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,10 +77,11 @@ struct subaddress_index_extended
|
||||
{
|
||||
subaddress_index index;
|
||||
AddressDeriveType derive_type;
|
||||
bool is_return_spend_key;
|
||||
};
|
||||
static inline bool operator==(const subaddress_index_extended &a, const subaddress_index_extended &b)
|
||||
{
|
||||
return a.index == b.index && a.derive_type == b.derive_type;
|
||||
return a.index == b.index && a.derive_type == b.derive_type && a.is_return_spend_key == b.is_return_spend_key;
|
||||
}
|
||||
static inline bool operator!=(const subaddress_index_extended &a, const subaddress_index_extended &b)
|
||||
{
|
||||
|
||||
@@ -60,6 +60,8 @@ void get_output_enote_proposals_from_proposal_v1(const CarrotTransactionProposal
|
||||
|
||||
// derive enote proposals
|
||||
size_t change_index;
|
||||
RCTOutputEnoteProposal return_enote_out;
|
||||
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
|
||||
get_output_enote_proposals(tx_proposal.normal_payment_proposals,
|
||||
selfsend_payment_proposal_cores,
|
||||
tx_proposal.dummy_encrypted_payment_id,
|
||||
@@ -67,8 +69,11 @@ void get_output_enote_proposals_from_proposal_v1(const CarrotTransactionProposal
|
||||
k_view_dev,
|
||||
tx_proposal.key_images_sorted.at(0),
|
||||
output_enote_proposals_out,
|
||||
return_enote_out,
|
||||
encrypted_payment_id_out,
|
||||
tx_proposal.tx_type,
|
||||
change_index,
|
||||
normal_payments_indices,
|
||||
payment_proposal_order_out);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -123,7 +128,13 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1
|
||||
tx_proposal.key_images_sorted,
|
||||
tx_proposal.sources,
|
||||
tx_proposal.fee,
|
||||
encrypted_payment_id);
|
||||
cryptonote::transaction_type::TRANSFER,
|
||||
0, // tx_amount_burnt
|
||||
{}, // change_masks
|
||||
{}, // return_enote
|
||||
{0},
|
||||
encrypted_payment_id,
|
||||
/*hf_version=*/10);
|
||||
|
||||
// add extra payload and sort
|
||||
if (!tx_proposal.extra.empty())
|
||||
|
||||
@@ -67,8 +67,8 @@ struct CarrotPaymentProposalVerifiableSelfSendV1
|
||||
*
|
||||
* For exact details on what goes into the signable transaction hash, see `rct::get_pre_mlsag_hash`.
|
||||
*/
|
||||
struct CarrotTransactionProposalV1
|
||||
{
|
||||
struct CarrotTransactionProposalV1
|
||||
{
|
||||
/// Key images sorted in std::greater order
|
||||
std::vector<crypto::key_image> key_images_sorted;
|
||||
// sources in the same order as key_images_sorted.
|
||||
@@ -84,9 +84,23 @@ struct CarrotTransactionProposalV1
|
||||
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||
/// Fee to miner
|
||||
rct::xmr_amount fee;
|
||||
/// transaction type, e.g.
|
||||
cryptonote::transaction_type tx_type;
|
||||
/// how much money tx burns
|
||||
rct::xmr_amount amount_burnt;
|
||||
|
||||
/// used if this is a CREATE_TOKEN transaction
|
||||
cryptonote::token_metadata_t token;
|
||||
|
||||
/// This field is truly "extra". It should contain only tx.extra fields that aren't present in a
|
||||
/// normal Carrot transaction, i.e. NOT ephemeral pubkeys nor encrypted PIDs
|
||||
std::vector<std::uint8_t> extra;
|
||||
};
|
||||
|
||||
/// Used if this is a TOKEN_TRANSFER or ROLLUP transaction
|
||||
carrot::rollup_binding_tag_t rollup_binding_tag;
|
||||
|
||||
/// Used if this is a ROLLUP transaction
|
||||
cryptonote::layer2_rollup_data_t layer2_rollup_data;
|
||||
};
|
||||
|
||||
} //namespace carrot
|
||||
|
||||
@@ -90,7 +90,7 @@ static void append_additional_payment_proposal_if_necessary(
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
std::uint64_t get_carrot_default_tx_extra_size(const std::size_t n_outputs)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(n_outputs <= FCMP_PLUS_PLUS_MAX_OUTPUTS,
|
||||
CHECK_AND_ASSERT_THROW_MES(n_outputs <= CARROT_MAX_TX_OUTPUTS,
|
||||
"get_carrot_default_tx_extra_size: n_outputs too high: " << n_outputs);
|
||||
CHECK_AND_ASSERT_THROW_MES(n_outputs >= CARROT_MIN_TX_OUTPUTS,
|
||||
"get_carrot_default_tx_extra_size: n_outputs too low: " << n_outputs);
|
||||
@@ -117,6 +117,7 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
select_inputs_func_t &&select_inputs,
|
||||
carve_fees_and_balance_func_t &&carve_fees_and_balance,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
@@ -208,16 +209,44 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
|
||||
input_amount_sum += selected_input.amount;
|
||||
|
||||
// callback to balance the outputs with the fee and input sum
|
||||
carve_fees_and_balance(input_amount_sum, tx_proposal_out.fee, normal_payment_proposals, selfsend_payment_proposals);
|
||||
std::string asset_type = "SAL1"; // default to SAL1
|
||||
if (!normal_payment_proposals.empty()) {
|
||||
asset_type = normal_payment_proposals.at(0).asset_type;
|
||||
} else if (!selfsend_payment_proposals.empty()) {
|
||||
asset_type = selfsend_payment_proposals.at(0).proposal.asset_type;
|
||||
}
|
||||
uint64_t fee = tx_proposal_out.fee;
|
||||
if (asset_type != "SAL1") {
|
||||
if (tx_type != cryptonote::transaction_type::BURN || asset_type != "SAL") {
|
||||
CARROT_CHECK_AND_THROW(cryptonote::is_valid_custom_asset_type(asset_type),
|
||||
carrot_logic_error, "make_carrot_transaction_proposal_v1: invalid asset type in payment proposals: " << asset_type);
|
||||
}
|
||||
|
||||
fee = 0; //fee is always in SAL1
|
||||
}
|
||||
carve_fees_and_balance(input_amount_sum, fee, normal_payment_proposals, selfsend_payment_proposals);
|
||||
|
||||
// sanity check balance
|
||||
input_amount_sum -= tx_proposal_out.fee;
|
||||
input_amount_sum -= fee;
|
||||
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
|
||||
input_amount_sum -= normal_payment_proposal.amount;
|
||||
for (const CarrotPaymentProposalVerifiableSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals)
|
||||
input_amount_sum -= selfsend_payment_proposal.proposal.amount;
|
||||
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
|
||||
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
|
||||
|
||||
if (tx_type != cryptonote::transaction_type::STAKE &&
|
||||
tx_type != cryptonote::transaction_type::BURN &&
|
||||
tx_type != cryptonote::transaction_type::CREATE_TOKEN &&
|
||||
tx_type != cryptonote::transaction_type::ROLLUP)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
|
||||
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
|
||||
} else {
|
||||
tx_proposal_out.amount_burnt = input_amount_sum.convert_to<uint64_t>();
|
||||
CHECK_AND_ASSERT_THROW_MES(tx_proposal_out.amount_burnt >= 0,
|
||||
"make_carrot_transaction_proposal_v1: post-carved transaction burnt amount is negative: "
|
||||
<< tx_proposal_out.amount_burnt);
|
||||
input_amount_sum -= tx_proposal_out.amount_burnt;
|
||||
}
|
||||
|
||||
// collect and sort key images
|
||||
tx_proposal_out.key_images_sorted.reserve(selected_inputs.size());
|
||||
@@ -226,6 +255,9 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
|
||||
std::sort(tx_proposal_out.key_images_sorted.begin(),
|
||||
tx_proposal_out.key_images_sorted.end(),
|
||||
std::greater{}); // consensus rules dictate inputs sorted in *reverse* lexicographical order since v7
|
||||
|
||||
// set the transaction type & new asset type
|
||||
tx_proposal_out.tx_type = tx_type;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
void make_carrot_transaction_proposal_v1_transfer(
|
||||
@@ -234,11 +266,13 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
select_inputs_func_t &&select_inputs,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::set<std::size_t> &subtractable_normal_payment_proposals,
|
||||
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out)
|
||||
{
|
||||
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals = selfsend_payment_proposals_in;
|
||||
@@ -259,7 +293,8 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
.proposal = CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = 0,
|
||||
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE
|
||||
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE,
|
||||
.asset_type = asset_type
|
||||
},
|
||||
.subaddr_index = change_address_index
|
||||
});
|
||||
@@ -268,7 +303,8 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
carve_fees_and_balance_func_t carve_fees_and_balance =
|
||||
[
|
||||
&subtractable_normal_payment_proposals,
|
||||
&subtractable_selfsend_payment_proposals
|
||||
&subtractable_selfsend_payment_proposals,
|
||||
&tx_type
|
||||
]
|
||||
(
|
||||
const boost::multiprecision::uint128_t &input_sum_amount,
|
||||
@@ -377,6 +413,19 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(fee_remainder == 0,
|
||||
"make unsigned transaction transfer subtractable: bug: fee remainder at end of carve function");
|
||||
|
||||
// remove the self send payment we have made to ourself now that we have our change payment.
|
||||
if (tx_type == cryptonote::transaction_type::STAKE ||
|
||||
tx_type == cryptonote::transaction_type::BURN ||
|
||||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx_type == cryptonote::transaction_type::ROLLUP)
|
||||
{
|
||||
selfsend_payment_proposals.back().proposal.enote_ephemeral_pubkey =
|
||||
selfsend_payment_proposals.front().proposal.enote_ephemeral_pubkey;
|
||||
|
||||
selfsend_payment_proposals.erase(selfsend_payment_proposals.begin());
|
||||
}
|
||||
|
||||
}; //end carve_fees_and_balance
|
||||
|
||||
// make unsigned transaction with fee carving callback
|
||||
@@ -385,6 +434,7 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
tx_type,
|
||||
std::forward<select_inputs_func_t>(select_inputs),
|
||||
std::move(carve_fees_and_balance),
|
||||
change_address_spend_pubkey,
|
||||
@@ -398,9 +448,11 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
std::vector<CarrotSelectedInput> &&selected_inputs,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out)
|
||||
{
|
||||
// sanity check payment proposals are provided
|
||||
@@ -423,6 +475,19 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
CHECK_AND_ASSERT_THROW_MES(bool(normal_payment_proposals.size()) ^ bool(selfsend_payment_proposals.size()),
|
||||
"make carrot transaction proposal v1 sweep: both normal and self-send payment proposals are provided");
|
||||
|
||||
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals_inout{selfsend_payment_proposals};
|
||||
//if (tx_type == cryptonote::transaction_type::RETURN) {
|
||||
selfsend_payment_proposals_inout.push_back(carrot::CarrotPaymentProposalVerifiableSelfSendV1{
|
||||
.proposal = carrot::CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = change_address_spend_pubkey,
|
||||
.amount = 0,
|
||||
.enote_type = carrot::CarrotEnoteType::CHANGE,
|
||||
.asset_type = asset_type
|
||||
},
|
||||
.subaddr_index = change_address_index
|
||||
});
|
||||
// }
|
||||
|
||||
const bool is_selfsend_sweep = !selfsend_payment_proposals.empty();
|
||||
|
||||
// define input selection callback, which is just a shuttle for `selected_inputs`
|
||||
@@ -473,10 +538,11 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
|
||||
// make unsigned transaction with sweep carving callback and selected inputs
|
||||
make_carrot_transaction_proposal_v1(normal_payment_proposals,
|
||||
selfsend_payment_proposals,
|
||||
selfsend_payment_proposals_inout,
|
||||
fee_per_weight,
|
||||
fee_quantization_mask,
|
||||
extra,
|
||||
tx_type,
|
||||
std::move(select_inputs),
|
||||
std::move(carve_fees_and_balance),
|
||||
change_address_spend_pubkey,
|
||||
|
||||
@@ -92,19 +92,26 @@ static size_t estimate_rct_tx_size_carrot(int n_inputs, int mixin, int n_outputs
|
||||
size += 1 + 6;
|
||||
|
||||
// vin
|
||||
size += n_inputs * (1+6+4+(mixin+1)*2+32);
|
||||
size += n_inputs * (1+1+4+(mixin+1)*2+32);
|
||||
|
||||
// vout
|
||||
size += n_outputs * (3+4+16+32);
|
||||
size += n_outputs * (1+1+32+4+3+16);
|
||||
|
||||
// extra
|
||||
size += extra_size;
|
||||
|
||||
// rct signatures
|
||||
|
||||
// type
|
||||
size += 1;
|
||||
|
||||
// amount_burnt
|
||||
size += 8;
|
||||
|
||||
// return_address data
|
||||
// NOTE: this will be wrong by 21 bytes for STAKE TXs using `protocol_tx_data_t`
|
||||
size += n_outputs * (32 + 1);
|
||||
|
||||
// rct signatures
|
||||
|
||||
// rangeSigs
|
||||
if (bulletproof || bulletproof_plus)
|
||||
{
|
||||
@@ -187,6 +194,7 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
select_inputs_func_t &&select_inputs,
|
||||
carve_fees_and_balance_func_t &&carve_fees_and_balance,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
@@ -199,11 +207,13 @@ void make_carrot_transaction_proposal_v1_transfer(
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
select_inputs_func_t &&select_inputs,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::set<std::size_t> &subtractable_normal_payment_proposals,
|
||||
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out);
|
||||
|
||||
void make_carrot_transaction_proposal_v1_sweep(
|
||||
@@ -212,9 +222,11 @@ void make_carrot_transaction_proposal_v1_sweep(
|
||||
const rct::xmr_amount fee_per_weight,
|
||||
const rct::xmr_amount fee_quantization_mask,
|
||||
const std::vector<uint8_t> &extra,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
std::vector<CarrotSelectedInput> &&selected_inputs,
|
||||
const crypto::public_key &change_address_spend_pubkey,
|
||||
const subaddress_index_extended &change_address_index,
|
||||
const std::string &asset_type,
|
||||
CarrotTransactionProposalV1 &tx_proposal_out);
|
||||
|
||||
} //namespace carrot
|
||||
|
||||
@@ -183,28 +183,33 @@ namespace cryptonote
|
||||
bool checkpoints::init_default_checkpoints(network_type nettype)
|
||||
{
|
||||
if (nettype == MAINNET) {
|
||||
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
|
||||
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
|
||||
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
|
||||
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
|
||||
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
|
||||
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
|
||||
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
|
||||
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
|
||||
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
|
||||
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
|
||||
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
|
||||
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
|
||||
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
|
||||
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
|
||||
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
|
||||
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
|
||||
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
|
||||
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
|
||||
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
|
||||
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
|
||||
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
|
||||
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
|
||||
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
|
||||
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
|
||||
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
|
||||
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
|
||||
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
|
||||
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
|
||||
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
|
||||
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
|
||||
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
|
||||
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
|
||||
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
|
||||
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
|
||||
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
|
||||
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
|
||||
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
|
||||
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
|
||||
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
|
||||
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
|
||||
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
|
||||
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
|
||||
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
|
||||
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
|
||||
ADD_CHECKPOINT2(100000, "ff4e8ec805d5bfbcd01f350ac071be1d944ba73e0d27e37d12acb549902b3f3d", "0xDA97F5697F7D8"); // 3845539075979224
|
||||
ADD_CHECKPOINT2(150000, "c43281eb5b2a41ee77a4465735e4820c6d14d473b568df7987541afc48f18568", "0x12BDA9ED687AAF"); // 5275087110961839
|
||||
ADD_CHECKPOINT2(225000, "7648405f7cfb24341d9580275b518bb3713c68e970f547faa0b3bcae450d9ec2", "0x18CD5F1B7BA43A"); // 6981207807730746
|
||||
ADD_CHECKPOINT2(300000, "a18ca65464c1f2d876dd3a00643c9be265655d6bca364eccc5e3d628b9a5cd2c", "0x1F0C2A50FE631C"); // 8739100165038876
|
||||
ADD_CHECKPOINT2(375000, "07f0c907cc5cb44cef88e5899a411adddd4b0a4419210906e0583efdcafb499f", "0x240C7F9742F265"); // 10146841299710565
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -138,13 +138,6 @@ bool keys_match_internal_values(const std::unordered_map<KeyT, ValueT> &map, Pre
|
||||
|
||||
return true;
|
||||
}
|
||||
/// convenience wrapper for getting the last element after emplacing back
|
||||
template <typename ContainerT>
|
||||
typename ContainerT::value_type& add_element(ContainerT &container)
|
||||
{
|
||||
container.emplace_back();
|
||||
return container.back();
|
||||
}
|
||||
/// convenience erasor for unordered maps: std::erase_if(std::unordered_map) is C++20
|
||||
template <typename KeyT, typename ValueT, typename PredT>
|
||||
void for_all_in_map_erase_if(std::unordered_map<KeyT, ValueT> &map_inout, PredT predicate)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define DEBUG_BREAK() asm volatile("int $3")
|
||||
#elif defined(__aarch64__)
|
||||
#define DEBUG_BREAK() asm volatile("brk #0xf000")
|
||||
#elif defined(__arm__)
|
||||
#define DEBUG_BREAK() asm volatile(".inst 0xe7f001f0") // Encoding for bkpt #0xf01, common for ARM32
|
||||
#else
|
||||
#error "Unsupported architecture for DEBUG_BREAK"
|
||||
#endif
|
||||
@@ -71,6 +71,7 @@ monero_add_library(cncrypto
|
||||
target_link_libraries(cncrypto
|
||||
PUBLIC
|
||||
epee
|
||||
mx25519_static
|
||||
randomx
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${SODIUM_LIBRARY}
|
||||
|
||||
@@ -887,4 +887,7 @@ const ge_p3 ge_p3_H = {
|
||||
{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},
|
||||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
|
||||
};
|
||||
};
|
||||
|
||||
// Precomputed sqrt(-486664) mod p as 32-byte little-endian array
|
||||
const fe fe_sqrt_m486664 = {12222970, 8312128, 11511410, -9067497, 15300785, 241793, -25456130, -14121551, 12187136, -3972024};
|
||||
|
||||
+125
-2
@@ -104,6 +104,21 @@ void fe_1(fe h) {
|
||||
h[9] = 0;
|
||||
}
|
||||
|
||||
int fe_equal(const fe a, const fe b)
|
||||
{
|
||||
fe t;
|
||||
fe_sub(t, a, b);
|
||||
return fe_isnonzero(t) == 0;
|
||||
}
|
||||
|
||||
void ge_from_xy(ge_p3 *out, const fe x, const fe y)
|
||||
{
|
||||
fe_1(out->Z); // Z = 1
|
||||
fe_copy(out->X, x); // X = x
|
||||
fe_copy(out->Y, y); // Y = y
|
||||
fe_mul(out->T, x, y); // T = x*y
|
||||
}
|
||||
|
||||
/* From fe_add.c */
|
||||
|
||||
/*
|
||||
@@ -365,7 +380,7 @@ int fe_isnegative(const fe f) {
|
||||
|
||||
/* From fe_isnonzero.c, modified */
|
||||
|
||||
static int fe_isnonzero(const fe f) {
|
||||
int fe_isnonzero(const fe f) {
|
||||
unsigned char s[32];
|
||||
fe_tobytes(s, f);
|
||||
return (((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] |
|
||||
@@ -3967,6 +3982,114 @@ int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Precomputed sqrt(-1) from Monero (fe_sqrtm1 in crypto-ops-data.c)
|
||||
extern const fe fe_sqrtm1;
|
||||
extern const fe fe_sqrt_m486664;
|
||||
|
||||
// Function to recover v from u (returns 0 on success, -1 if not on curve)
|
||||
int fe_sqrt_mont(fe v_out, const fe u_in) {
|
||||
fe rhs;
|
||||
fe t0, t1, t2;
|
||||
fe candidate, c2, check;
|
||||
|
||||
// Compute rhs = u^3 + A u^2 + u, A=486662
|
||||
fe A_fe;
|
||||
fe_frombytes_vartime(A_fe, (const unsigned char[]){0x06, 0x6D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); // Correct little-endian 486662
|
||||
|
||||
fe_sq(t0, u_in); // t0 = u^2
|
||||
fe_mul(t1, t0, u_in); // t1 = u^3
|
||||
fe_mul(t2, t0, A_fe); // t2 = A u^2
|
||||
fe_add(rhs, t1, t2); // u^3 + A u^2
|
||||
fe_add(rhs, rhs, u_in); // + u
|
||||
|
||||
// The exponentiation chain for (p+3)/8
|
||||
fe_1(t1);
|
||||
fe_sq(t0, t1);
|
||||
fe_mul(t0, t0, t1);
|
||||
fe_sq(candidate, t0);
|
||||
fe_mul(candidate, candidate, t1);
|
||||
fe_mul(candidate, candidate, rhs);
|
||||
fe_pow22523(candidate, candidate);
|
||||
fe_mul(candidate, candidate, t0);
|
||||
fe_mul(candidate, candidate, rhs);
|
||||
|
||||
// Check c^2 == rhs or -rhs
|
||||
fe_sq(c2, candidate);
|
||||
fe_sub(check, c2, rhs);
|
||||
if (fe_isnonzero(check)) {
|
||||
fe_add(check, c2, rhs);
|
||||
if (fe_isnonzero(check)) {
|
||||
return -1; // Not a quadratic residue
|
||||
}
|
||||
fe_mul(candidate, candidate, fe_sqrtm1); // Adjust with sqrt(-1)
|
||||
}
|
||||
|
||||
// Output v (principal root; flip to -v if needed for your map)
|
||||
fe_copy(v_out, candidate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v) {
|
||||
fe inv_v, temp;
|
||||
fe t1, t2, inv_t2;
|
||||
fe one;
|
||||
fe_1(one);
|
||||
|
||||
fe_invert(inv_v, v); // 1/v
|
||||
fe_mul(temp, u, inv_v); // u/v
|
||||
fe_mul(x_out, fe_sqrt_m486664, temp); // sqrt(-486664) * (u/v)
|
||||
|
||||
fe_sub(t1, u, one); // u - 1
|
||||
fe_add(t2, u, one); // u + 1
|
||||
fe_invert(inv_t2, t2);
|
||||
fe_mul(y_out, t1, inv_t2); // (u-1)/(u+1)
|
||||
}
|
||||
|
||||
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y) {
|
||||
fe t1, t2, inv_t2;
|
||||
fe one;
|
||||
fe_1(one);
|
||||
|
||||
fe_add(t1, one, y); // 1 + y
|
||||
fe_sub(t2, one, y); // 1 - y
|
||||
fe_invert(inv_t2, t2);
|
||||
fe_mul(u_out, t1, inv_t2); // (1+y)/(1-y)
|
||||
|
||||
fe inv_x;
|
||||
fe_invert(inv_x, x); // 1/x
|
||||
fe_mul(t1, u_out, inv_x); // u / x
|
||||
fe_mul(v_out, fe_sqrt_m486664, t1); // sqrt(-486664) * (u/x)
|
||||
}
|
||||
|
||||
// Usage: Add two Montgomery points
|
||||
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2) {
|
||||
ge_p3 P_ed, Q_ed, sum_ed;
|
||||
|
||||
// Convert to Edwards
|
||||
fe x1, y1, x2, y2;
|
||||
mont_to_ed(x1, y1, u1, v1);
|
||||
mont_to_ed(x2, y2, u2, v2);
|
||||
|
||||
// Load into ge_p3 (assume Z=1, T=x*y for affine)
|
||||
fe_1(P_ed.Z); fe_mul(P_ed.T, x1, y1); fe_copy(P_ed.X, x1); fe_copy(P_ed.Y, y1);
|
||||
fe_1(Q_ed.Z); fe_mul(Q_ed.T, x2, y2); fe_copy(Q_ed.X, x2); fe_copy(Q_ed.Y, y2);
|
||||
|
||||
// Add using ge_
|
||||
ge_cached Q_cached;
|
||||
ge_p3_to_cached(&Q_cached, &Q_ed);
|
||||
ge_p1p1 sum_p1p1;
|
||||
ge_add(&sum_p1p1, &P_ed, &Q_cached);
|
||||
ge_p1p1_to_p3(&sum_ed, &sum_p1p1);
|
||||
|
||||
// Convert back (normalize to affine: divide by Z)
|
||||
fe inv_z;
|
||||
fe_invert(inv_z, sum_ed.Z);
|
||||
fe x_out, y_out;
|
||||
fe_mul(x_out, sum_ed.X, inv_z);
|
||||
fe_mul(y_out, sum_ed.Y, inv_z);
|
||||
ed_to_mont(u3, v3, x_out, y_out);
|
||||
}
|
||||
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
|
||||
// https://eprint.iacr.org/2008/522
|
||||
// X == T == 0 and Y/Z == 1
|
||||
@@ -4063,4 +4186,4 @@ void fe_dbl(fe h, const fe f)
|
||||
// Reduce the output for safety to ensure the result can be used as input to
|
||||
// fe_add or fe_sub without an extra call to fe_reduce
|
||||
fe_reduce(h, h_res);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -176,6 +176,11 @@ int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
|
||||
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
|
||||
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
|
||||
|
||||
int fe_sqrt_mont(fe v_out, const fe u_in);
|
||||
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v);
|
||||
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y);
|
||||
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2);
|
||||
|
||||
// internal
|
||||
uint64_t load_3(const unsigned char *in);
|
||||
uint64_t load_4(const unsigned char *in);
|
||||
@@ -192,10 +197,14 @@ void fe_sq(fe h, const fe f);
|
||||
void fe_sub(fe h, const fe f, const fe g);
|
||||
void fe_0(fe h);
|
||||
void fe_1(fe h);
|
||||
int fe_equal(const fe a, const fe b);
|
||||
void ge_from_xy(ge_p3 *out, const fe x, const fe y);
|
||||
|
||||
int fe_isnonzero(const fe f);
|
||||
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
|
||||
|
||||
void fe_ed_y_derivatives_to_wei_x(unsigned char *wei_x, const fe inv_one_minus_y, const fe one_plus_y);
|
||||
|
||||
void fe_reduce(fe reduced_f, const fe f);
|
||||
void fe_dbl(fe h, const fe f);
|
||||
void fe_dbl(fe h, const fe f);
|
||||
|
||||
+558
-1
@@ -41,6 +41,7 @@
|
||||
#include "common/varint.h"
|
||||
#include "warnings.h"
|
||||
#include "crypto.h"
|
||||
#include "mx25519.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
@@ -90,6 +91,16 @@ namespace crypto {
|
||||
return &reinterpret_cast<const unsigned char &>(scalar);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
boost::mutex &get_random_lock()
|
||||
{
|
||||
static boost::mutex random_lock;
|
||||
@@ -504,6 +515,391 @@ namespace crypto {
|
||||
memwipe(&k, sizeof(k));
|
||||
}
|
||||
|
||||
void crypto_ops::generate_carrot_tx_proof(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u-coordinate
|
||||
const public_key &A, // Ed25519
|
||||
const boost::optional<public_key> &B, // Ed if present
|
||||
const public_key &D, // X25519 u-coordinate
|
||||
const secret_key &r,
|
||||
const secret_key &a,
|
||||
signature &sig)
|
||||
{
|
||||
// Check if we are sender or receiver
|
||||
if (r != crypto::null_skey) {
|
||||
// SENDER
|
||||
generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
|
||||
return;
|
||||
}
|
||||
|
||||
// RECEIVER
|
||||
|
||||
// Load points (A and B and R) into ge_p3
|
||||
ge_p3 A_p3;
|
||||
ge_p3 B_p3;
|
||||
ge_p3 R_p3;
|
||||
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
throw std::runtime_error("recipient view pubkey is invalid");
|
||||
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
throw std::runtime_error("recipient spend pubkey is invalid");
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
{
|
||||
// Debug check D == a*R
|
||||
mx25519_pubkey D_x25519;
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&D_x25519,
|
||||
reinterpret_cast<const mx25519_privkey*>(&a),
|
||||
reinterpret_cast<const mx25519_pubkey*>(&R));
|
||||
public_key dbg_D;
|
||||
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
|
||||
assert(D == dbg_D);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// 1. Pick random nonce k
|
||||
//
|
||||
crypto::secret_key k;
|
||||
random_scalar(k);
|
||||
|
||||
static const public_key zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 u-coord
|
||||
buf.R = R; // X25519 u-coord
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
//
|
||||
// 2. Compute X = ConvertPointE(k*G or k*B)
|
||||
//
|
||||
ge_p3 kB_or_kG_p3;
|
||||
if (B)
|
||||
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&kB_or_kG_p3, &k);
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 3. Compute Y = k*R
|
||||
//
|
||||
mx25519_pubkey Y;
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&Y,
|
||||
reinterpret_cast<const mx25519_privkey*>(&k),
|
||||
reinterpret_cast<const mx25519_pubkey*>(&R));
|
||||
memcpy(&buf.Y, &Y, sizeof(mx25519_pubkey));
|
||||
|
||||
// ---------- Extract and lift R ----------
|
||||
fe u_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
|
||||
fe v_R_cand;
|
||||
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
|
||||
throw std::runtime_error("R not on curve");
|
||||
|
||||
fe x1, y1, x2, y2, v_R_neg;
|
||||
ge_p3 R_ed1, R_ed2;
|
||||
|
||||
// +v (principal)
|
||||
mont_to_ed(x1, y1, u_R, v_R_cand);
|
||||
ge_from_xy(&R_ed1, x1, y1);
|
||||
|
||||
// -v
|
||||
fe_neg(v_R_neg, v_R_cand);
|
||||
mont_to_ed(x2, y2, u_R, v_R_neg);
|
||||
ge_from_xy(&R_ed2, x2, y2);
|
||||
|
||||
// Arbitrarily choose R_sign = true (principal v from fe_sqrt_mont)
|
||||
bool R_sign = true;
|
||||
ge_p3 R_ed_correct = R_ed1; // +v
|
||||
|
||||
// ---------- Extract and lift D (consistent with chosen R_sign) ----------
|
||||
fe u_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
|
||||
fe v_D_cand;
|
||||
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
|
||||
throw std::runtime_error("D not on curve");
|
||||
|
||||
fe x3, y3, x4, y4, v_D_neg;
|
||||
|
||||
// Compute D_ed_true = a * R_ed_correct
|
||||
ge_p3 D_ed_true;
|
||||
ge_scalarmult_p3(&D_ed_true, &a, &R_ed_correct);
|
||||
|
||||
// Normalize to affine for matching
|
||||
fe inv_z;
|
||||
fe_invert(inv_z, D_ed_true.Z);
|
||||
fe xd_true, yd_true;
|
||||
fe_mul(xd_true, D_ed_true.X, inv_z);
|
||||
fe_mul(yd_true, D_ed_true.Y, inv_z);
|
||||
|
||||
// +v for D
|
||||
mont_to_ed(x3, y3, u_D, v_D_cand);
|
||||
bool D_match1 = fe_equal(x3, xd_true) && fe_equal(y3, yd_true); // Affine match (mont_to_ed gives affine x,y)
|
||||
|
||||
// -v for D
|
||||
fe_neg(v_D_neg, v_D_cand);
|
||||
mont_to_ed(x4, y4, u_D, v_D_neg);
|
||||
bool D_match2 = fe_equal(x4, xd_true) && fe_equal(y4, yd_true);
|
||||
|
||||
bool D_sign = false;
|
||||
if (D_match1)
|
||||
D_sign = true;
|
||||
else if (D_match2)
|
||||
D_sign = false;
|
||||
else
|
||||
throw std::runtime_error("D lift mismatch with computed D_ed_true");
|
||||
|
||||
// Pack signs (MSB is set to [1] for outbound, [0] for inbound
|
||||
sig.sign_mask =
|
||||
(R_sign ? 0x01 : 0x00) |
|
||||
(D_sign ? 0x02 : 0x00);
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
|
||||
//
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
|
||||
|
||||
//
|
||||
// 8. Compute response z = k - c*a
|
||||
//
|
||||
sc_mulsub(&sig.r, &sig.c, &unwrap(a), &k);
|
||||
|
||||
memwipe(&k, sizeof(k));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
assert(ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
void crypto_ops::generate_carrot_tx_proof_as_sender(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u-coordinate
|
||||
const public_key &A, // Ed25519
|
||||
const boost::optional<public_key> &B, // Ed if present
|
||||
const public_key &D, // X25519 u-coordinate
|
||||
const secret_key &r,
|
||||
const secret_key &a,
|
||||
signature &sig)
|
||||
{
|
||||
// Load only Ed points (A and B) into ge_p3
|
||||
ge_p3 A_p3;
|
||||
ge_p3 B_p3;
|
||||
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
throw std::runtime_error("recipient view pubkey is invalid");
|
||||
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
throw std::runtime_error("recipient spend pubkey is invalid");
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
{
|
||||
assert(sc_check(&r) == 0);
|
||||
|
||||
// Debug check R == ConvertPointE(r*G or r*B)
|
||||
public_key dbg_R;
|
||||
ge_p3 dbg_R_p3;
|
||||
|
||||
if (B)
|
||||
ge_scalarmult_p3(&dbg_R_p3, &r, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&dbg_R_p3, &r);
|
||||
|
||||
mx25519_pubkey R_x25519;
|
||||
ge_p3_to_x25519(R_x25519.data, &dbg_R_p3);
|
||||
|
||||
memcpy(&dbg_R, &R_x25519, sizeof(mx25519_pubkey));
|
||||
assert(R == dbg_R);
|
||||
|
||||
// Debug check D == ConvertPointE(r*A)
|
||||
public_key dbg_D;
|
||||
ge_p3 dbg_D_p3;
|
||||
ge_scalarmult_p3(&dbg_D_p3, &r, &A_p3);
|
||||
|
||||
mx25519_pubkey D_x25519;
|
||||
ge_p3_to_x25519(D_x25519.data, &dbg_D_p3);
|
||||
|
||||
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
|
||||
assert(D == dbg_D);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// 1. Pick random nonce k
|
||||
//
|
||||
ec_scalar k;
|
||||
random_scalar(k);
|
||||
|
||||
static const ec_point zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 u-coord
|
||||
buf.R = R; // X25519 u-coord
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
//
|
||||
// 2. Compute X = ConvertPointE(k*G or k*B)
|
||||
//
|
||||
ge_p3 kB_or_kG_p3;
|
||||
if (B)
|
||||
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&kB_or_kG_p3, &k);
|
||||
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 3. Compute Y = ConvertPointE(k*A)
|
||||
//
|
||||
ge_p3 kA_p3;
|
||||
ge_scalarmult_p3(&kA_p3, &k, &A_p3);
|
||||
|
||||
mx25519_pubkey Y_x25519;
|
||||
ge_p3_to_x25519(Y_x25519.data, &kA_p3);
|
||||
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 4. Compute true Ed points R_ed_true and D_ed_true
|
||||
//
|
||||
ge_p3 R_ed_true, D_ed_true;
|
||||
|
||||
if (B)
|
||||
ge_scalarmult_p3(&R_ed_true, &r, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&R_ed_true, &r);
|
||||
|
||||
ge_scalarmult_p3(&D_ed_true, &r, &A_p3);
|
||||
|
||||
//
|
||||
// 5. Determine sign bits for R and D
|
||||
//
|
||||
|
||||
// ---------- Extract and lift R ----------
|
||||
fe u_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
|
||||
fe v_R_cand;
|
||||
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
|
||||
throw std::runtime_error("R not on curve");
|
||||
|
||||
fe x1, y1, x2, y2, v_R_neg, v_D_neg;
|
||||
mont_to_ed(x1, y1, u_R, v_R_cand);
|
||||
|
||||
fe_neg(v_R_neg, v_R_cand);
|
||||
mont_to_ed(x2, y2, u_R, v_R_neg);
|
||||
|
||||
// Compute affine Edwards coords of R_ed_true
|
||||
fe inv_z, xr_true, yr_true;
|
||||
fe_invert(inv_z, R_ed_true.Z);
|
||||
fe_mul(xr_true, R_ed_true.X, inv_z);
|
||||
fe_mul(yr_true, R_ed_true.Y, inv_z);
|
||||
|
||||
bool R_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
|
||||
bool R_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
|
||||
|
||||
if (!R_match1 && !R_match2)
|
||||
throw std::runtime_error("R mapping mismatch");
|
||||
|
||||
bool R_sign = R_match1;
|
||||
|
||||
// ---------- Extract and lift D ----------
|
||||
fe u_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
|
||||
fe v_D_cand;
|
||||
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
|
||||
throw std::runtime_error("D not on curve");
|
||||
|
||||
mont_to_ed(x1, y1, u_D, v_D_cand);
|
||||
|
||||
fe_neg(v_D_neg, v_D_cand);
|
||||
mont_to_ed(x2, y2, u_D, v_D_neg);
|
||||
|
||||
fe_invert(inv_z, D_ed_true.Z);
|
||||
fe_mul(xr_true, D_ed_true.X, inv_z);
|
||||
fe_mul(yr_true, D_ed_true.Y, inv_z);
|
||||
|
||||
bool D_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
|
||||
bool D_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
|
||||
|
||||
if (!D_match1 && !D_match2)
|
||||
throw std::runtime_error("D mapping mismatch");
|
||||
|
||||
bool D_sign = D_match1;
|
||||
|
||||
//
|
||||
// 6. Pack sign bits into signature, include in challenge hash
|
||||
//
|
||||
sig.sign_mask =
|
||||
(R_sign ? 0x01 : 0x00) |
|
||||
(D_sign ? 0x02 : 0x00) |
|
||||
0x80;
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
|
||||
//
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
|
||||
|
||||
//
|
||||
// 8. Compute response z = k - c*r
|
||||
//
|
||||
sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
|
||||
|
||||
memwipe(&k, sizeof(k));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
assert(ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Verify a proof: either v1 (version == 1) or v2 (version == 2)
|
||||
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
|
||||
// sanity check
|
||||
@@ -608,6 +1004,167 @@ namespace crypto {
|
||||
return sc_isnonzero(&c2) == 0;
|
||||
}
|
||||
|
||||
// R and D are provided in X25519 format (u-coordinate), A and B in Ed25519.
|
||||
bool crypto_ops::check_carrot_tx_proof(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u
|
||||
const public_key &A, // Ed25519 viewkey
|
||||
const boost::optional<public_key> &B, // Ed25519 spendkey if any
|
||||
const public_key &D, // X25519 u
|
||||
const signature &sig)
|
||||
{
|
||||
ge_p3 A_p3, B_p3;
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
return false;
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
return false;
|
||||
|
||||
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
|
||||
return false;
|
||||
|
||||
// Extract sign bits and direction flag
|
||||
const bool R_sign = (sig.sign_mask & 0x01) != 0;
|
||||
const bool D_sign = (sig.sign_mask & 0x02) != 0;
|
||||
const bool outbound = (sig.sign_mask & 0x80) != 0;
|
||||
|
||||
//
|
||||
// 1. Reconstruct R_ed and D_ed from X25519 u-coords + sign bits
|
||||
//
|
||||
|
||||
// ----- R -----
|
||||
fe u_R, v_R_candidate, v_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
if (fe_sqrt_mont(v_R_candidate, u_R) != 0)
|
||||
return false;
|
||||
if (R_sign) fe_copy(v_R, v_R_candidate);
|
||||
else fe_neg(v_R, v_R_candidate);
|
||||
|
||||
fe x_R, y_R;
|
||||
mont_to_ed(x_R, y_R, u_R, v_R);
|
||||
ge_p3 R_ed;
|
||||
ge_from_xy(&R_ed, x_R, y_R); // Z=1, T=X*Y
|
||||
|
||||
// ----- D -----
|
||||
fe u_D, v_D_candidate, v_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
if (fe_sqrt_mont(v_D_candidate, u_D) != 0)
|
||||
return false;
|
||||
if (D_sign) fe_copy(v_D, v_D_candidate);
|
||||
else fe_neg(v_D, v_D_candidate);
|
||||
|
||||
fe x_D, y_D;
|
||||
mont_to_ed(x_D, y_D, u_D, v_D);
|
||||
ge_p3 D_ed;
|
||||
ge_from_xy(&D_ed, x_D, y_D);
|
||||
|
||||
//
|
||||
// 2. Compute X'
|
||||
// If inbound proof, X`= z*G + c*A (or z*B + c*A)
|
||||
// If outbound proof, X`= z*G + c*R_ed (or z*B + c*R_ed)
|
||||
//
|
||||
|
||||
ge_p3 c_p3;
|
||||
if (outbound)
|
||||
ge_scalarmult_p3(&c_p3, &sig.c, &R_ed);
|
||||
else
|
||||
ge_scalarmult_p3(&c_p3, &sig.c, &A_p3);
|
||||
|
||||
ge_p1p1 X_p1p1;
|
||||
if (B)
|
||||
{
|
||||
// Subaddress: X' = c*A + z*B
|
||||
ge_p3 rB_p3;
|
||||
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
|
||||
ge_cached rB_cached;
|
||||
ge_p3_to_cached(&rB_cached, &rB_p3);
|
||||
ge_add(&X_p1p1, &c_p3, &rB_cached);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Main address: X' = c*R_ed + z*G
|
||||
ge_p3 rG_p3;
|
||||
ge_scalarmult_base(&rG_p3, &sig.r);
|
||||
ge_cached rG_cached;
|
||||
ge_p3_to_cached(&rG_cached, &rG_p3);
|
||||
ge_add(&X_p1p1, &c_p3, &rG_cached);
|
||||
}
|
||||
|
||||
ge_p3 X_ed_p3;
|
||||
ge_p1p1_to_p3(&X_ed_p3, &X_p1p1);
|
||||
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &X_ed_p3);
|
||||
|
||||
//
|
||||
// 3. Compute Y'
|
||||
// If inbound, Y' = c*D_ed + z*R
|
||||
// If outbound, Y' = c*D_ed + z*A
|
||||
//
|
||||
|
||||
ge_p3 cD_p3;
|
||||
ge_scalarmult_p3(&cD_p3, &sig.c, &D_ed);
|
||||
|
||||
ge_p3 z_p3;
|
||||
if (outbound)
|
||||
ge_scalarmult_p3(&z_p3, &sig.r, &A_p3);
|
||||
else
|
||||
ge_scalarmult_p3(&z_p3, &sig.r, &R_ed);
|
||||
|
||||
ge_cached z_cached;
|
||||
ge_p3_to_cached(&z_cached, &z_p3);
|
||||
|
||||
ge_p1p1 Y_p1p1;
|
||||
ge_add(&Y_p1p1, &cD_p3, &z_cached);
|
||||
|
||||
ge_p3 Y_ed_p3;
|
||||
ge_p1p1_to_p3(&Y_ed_p3, &Y_p1p1);
|
||||
|
||||
mx25519_pubkey Y_x25519;
|
||||
ge_p3_to_x25519(Y_x25519.data, &Y_ed_p3);
|
||||
|
||||
//
|
||||
// 4. Rebuild the hash transcript exactly as the prover did
|
||||
//
|
||||
|
||||
static const ec_point zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 (same bytes as prover)
|
||||
buf.R = R; // X25519
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 5. Recompute challenge and compare with sig.c
|
||||
//
|
||||
|
||||
ec_scalar c2;
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), c2);
|
||||
sc_sub(&c2, &c2, &sig.c);
|
||||
return sc_isnonzero(&c2) == 0;
|
||||
}
|
||||
|
||||
static void hash_to_ec(const public_key &key, ge_p3 &res) {
|
||||
hash h;
|
||||
ge_p2 point;
|
||||
@@ -796,4 +1353,4 @@ POP_WARNINGS
|
||||
ki.data[31] ^= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-3
@@ -85,6 +85,7 @@ namespace crypto {
|
||||
|
||||
POD_CLASS signature {
|
||||
ec_scalar c, r;
|
||||
uint8_t sign_mask;
|
||||
friend class crypto_ops;
|
||||
};
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace crypto {
|
||||
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
|
||||
sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 &&
|
||||
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(key_image_y) == 32 &&
|
||||
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
sizeof(signature) == 65 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
|
||||
class crypto_ops {
|
||||
crypto_ops();
|
||||
@@ -131,8 +132,14 @@ namespace crypto {
|
||||
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
static void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
friend void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
|
||||
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
|
||||
static bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
|
||||
friend bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
|
||||
static void derive_key_image_generator(const public_key &, ec_point &);
|
||||
friend void derive_key_image_generator(const public_key &, ec_point &);
|
||||
static void generate_key_image(const public_key &, const secret_key &, key_image &);
|
||||
@@ -260,10 +267,28 @@ namespace crypto {
|
||||
inline void generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
|
||||
crypto_ops::generate_tx_proof_v1(prefix_hash, R, A, B, D, r, sig);
|
||||
}
|
||||
/* Generation of a carrot tx proof; for carrot transactions, D is in X25519 domain (D = r*ConvertPointE(A))
|
||||
* instead of Ed25519 domain (D = r*A). This version applies ConvertPointE transformation.
|
||||
*/
|
||||
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
|
||||
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
inline void generate_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
|
||||
crypto_ops::generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
|
||||
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version);
|
||||
}
|
||||
|
||||
/* Verification of a carrot tx proof; R and D should be in Ed25519 domain for verification,
|
||||
*/
|
||||
inline bool check_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
|
||||
return crypto_ops::check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
}
|
||||
/*
|
||||
inline bool check_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
|
||||
return crypto_ops::check_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, sig);
|
||||
}
|
||||
*/
|
||||
inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) {
|
||||
crypto_ops::derive_key_image_generator(pub, ki_gen);
|
||||
}
|
||||
@@ -378,4 +403,4 @@ CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe)
|
||||
CRYPTO_MAKE_HASHABLE(key_image)
|
||||
CRYPTO_MAKE_HASHABLE(key_image_y)
|
||||
CRYPTO_MAKE_COMPARABLE(signature)
|
||||
CRYPTO_MAKE_COMPARABLE(view_tag)
|
||||
CRYPTO_MAKE_COMPARABLE(view_tag)
|
||||
|
||||
@@ -89,12 +89,24 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size()));
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (8 + m_multisig_keys.size()));
|
||||
const char *ptr = key_stream.data();
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_spend_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_master.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_prove_spend.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_view_balance.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_view_incoming.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_generate_image.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_generate_address.data[i] ^= *ptr++;
|
||||
for (crypto::secret_key &k: m_multisig_keys)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
@@ -116,11 +128,20 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 8);
|
||||
const char *ptr = key_stream.data();
|
||||
ptr += sizeof(crypto::secret_key);
|
||||
ptr += sizeof(crypto::secret_key); // Skip m_spend_secret_key
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
ptr += (2 * sizeof(crypto::secret_key)); // Skip s_master, k_prove_spend
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_view_balance.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_view_incoming.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_generate_image.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_generate_address.data[i] ^= *ptr++;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
|
||||
@@ -151,6 +172,8 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
void account_base::forget_spend_key()
|
||||
{
|
||||
m_keys.m_spend_secret_key = crypto::secret_key();
|
||||
m_keys.s_master = m_keys.m_spend_secret_key;
|
||||
m_keys.k_prove_spend = m_keys.m_spend_secret_key;
|
||||
m_keys.m_multisig_keys.clear();
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
@@ -163,6 +186,7 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
"Unexpected derived public spend key");
|
||||
|
||||
m_keys.m_spend_secret_key = spend_secret_key;
|
||||
m_keys.s_master = m_keys.m_spend_secret_key;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random)
|
||||
@@ -276,6 +300,14 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
//TODO: change this code into base 58
|
||||
return get_account_address_as_str(nettype, false, m_keys.m_account_address);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
std::string account_base::get_carrot_public_address_str(network_type nettype) const
|
||||
{
|
||||
// Build the cryptonote::account_public_address
|
||||
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key, true};
|
||||
// change this code into base 58
|
||||
return get_account_address_as_str(nettype, false, addr);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
|
||||
{
|
||||
|
||||
@@ -61,9 +61,12 @@ namespace cryptonote
|
||||
crypto::secret_key k_generate_image;
|
||||
crypto::secret_key s_generate_address;
|
||||
|
||||
// carrot public keys (minus K^0_v, which is shared with legacy K^0_v)
|
||||
// carrot public account address (K_s, K_v)
|
||||
account_public_address m_carrot_account_address;
|
||||
|
||||
// carrot main address (K^0_s, K^0_v)
|
||||
account_public_address m_carrot_main_address;
|
||||
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(m_account_address)
|
||||
@@ -72,6 +75,11 @@ namespace cryptonote
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
|
||||
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
|
||||
if (m_account_address.m_spend_public_key == crypto::null_pkey) {
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(s_view_balance)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(k_view_incoming)
|
||||
KV_SERIALIZE(m_carrot_account_address)
|
||||
}
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
void encrypt(const crypto::chacha_key &key);
|
||||
@@ -101,6 +109,7 @@ namespace cryptonote
|
||||
bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys);
|
||||
const account_keys& get_keys() const;
|
||||
std::string get_public_address_str(network_type nettype) const;
|
||||
std::string get_carrot_public_address_str(network_type nettype) const;
|
||||
std::string get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const;
|
||||
|
||||
hw::device& get_device() const {return m_keys.get_device();}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "serialization/debug_archive.h"
|
||||
#include "serialization/crypto.h"
|
||||
#include "serialization/keyvalue_serialization.h" // eepe named serialization
|
||||
#include "serialization/pair.h"
|
||||
#include "serialization/string.h"
|
||||
#include "carrot_core/core_types.h"
|
||||
#include "carrot_impl/carrot_chain_serialization.h"
|
||||
@@ -193,8 +194,116 @@ namespace cryptonote
|
||||
VARINT_FIELD(amount)
|
||||
FIELD(target)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class protocol_tx_data_t {
|
||||
public:
|
||||
uint8_t version;
|
||||
crypto::public_key return_address;
|
||||
crypto::public_key return_pubkey;
|
||||
carrot::view_tag_t return_view_tag;
|
||||
carrot::encrypted_janus_anchor_t return_anchor_enc;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
FIELD(return_address)
|
||||
FIELD(return_pubkey)
|
||||
FIELD(return_view_tag)
|
||||
FIELD(return_anchor_enc)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct erc_token_t
|
||||
{
|
||||
uint8_t version;
|
||||
std::string contract_address;
|
||||
std::string lockbox_address;
|
||||
std::string ticker;
|
||||
uint64_t erc20_asset_id; // NOTE: this is NOT the Salvium `asset_type_id`!!!
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
FIELD(contract_address)
|
||||
FIELD(lockbox_address)
|
||||
FIELD(ticker)
|
||||
VARINT_FIELD(erc20_asset_id)
|
||||
END_SERIALIZE()
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(contract_address)
|
||||
KV_SERIALIZE(lockbox_address)
|
||||
KV_SERIALIZE(ticker)
|
||||
KV_SERIALIZE(erc20_asset_id)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct sal_token_t
|
||||
{
|
||||
uint8_t version;
|
||||
uint64_t supply;
|
||||
uint64_t size;
|
||||
std::string name;
|
||||
std::string url;
|
||||
crypto::hash signature;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
VARINT_FIELD(supply)
|
||||
VARINT_FIELD(size)
|
||||
FIELD(name)
|
||||
FIELD(url)
|
||||
FIELD(signature)
|
||||
END_SERIALIZE()
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(supply)
|
||||
KV_SERIALIZE(size)
|
||||
KV_SERIALIZE(name)
|
||||
KV_SERIALIZE(url)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
#define TOKEN_TYPE_UNSET 0
|
||||
#define TOKEN_TYPE_ERC20 1
|
||||
#define TOKEN_TYPE_SAL 2
|
||||
|
||||
typedef boost::variant<erc_token_t, sal_token_t> token_v;
|
||||
|
||||
struct token_metadata_t
|
||||
{
|
||||
uint8_t version;
|
||||
std::string asset_type;
|
||||
token_v token;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
FIELD(asset_type)
|
||||
FIELD(token)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class layer2_rollup_tx_t {
|
||||
public:
|
||||
crypto::hash tx_prefix_hash;
|
||||
crypto::key_image first_key_image;
|
||||
uint64_t tx_fee;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(tx_prefix_hash)
|
||||
FIELD(first_key_image)
|
||||
VARINT_FIELD(tx_fee)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class layer2_rollup_data_t {
|
||||
public:
|
||||
uint8_t version;
|
||||
std::vector<layer2_rollup_tx_t> txs;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
FIELD(txs)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class transaction_prefix
|
||||
@@ -227,6 +336,12 @@ namespace cryptonote
|
||||
// Slippage limit
|
||||
uint64_t amount_slippage_limit;
|
||||
|
||||
protocol_tx_data_t protocol_tx_data;
|
||||
|
||||
carrot::rollup_binding_tag_t rollup_binding_tag;
|
||||
token_metadata_t token_metadata;
|
||||
layer2_rollup_data_t layer2_rollup_data;
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
VARINT_FIELD(version)
|
||||
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
|
||||
@@ -244,14 +359,30 @@ namespace cryptonote
|
||||
FIELD(return_address_list)
|
||||
FIELD(return_address_change_mask)
|
||||
} else {
|
||||
FIELD(return_address)
|
||||
FIELD(return_pubkey)
|
||||
if ((type == cryptonote::transaction_type::STAKE || type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(version >= TRANSACTION_VERSION_CARROT)) {
|
||||
FIELD(protocol_tx_data)
|
||||
} else {
|
||||
FIELD(return_address)
|
||||
FIELD(return_pubkey)
|
||||
}
|
||||
}
|
||||
FIELD(source_asset_type)
|
||||
FIELD(destination_asset_type)
|
||||
VARINT_FIELD(amount_slippage_limit)
|
||||
}
|
||||
}
|
||||
if (version < TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
return true;
|
||||
}
|
||||
if (type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
FIELD(token_metadata)
|
||||
} else if (type == cryptonote::transaction_type::TRANSFER) {
|
||||
FIELD(rollup_binding_tag)
|
||||
} else if (type == cryptonote::transaction_type::ROLLUP) {
|
||||
FIELD(rollup_binding_tag)
|
||||
FIELD(layer2_rollup_data)
|
||||
}
|
||||
END_SERIALIZE()
|
||||
|
||||
public:
|
||||
@@ -268,10 +399,18 @@ namespace cryptonote
|
||||
return_address_list.clear();
|
||||
return_address_change_mask.clear();
|
||||
return_pubkey = crypto::null_pkey;
|
||||
protocol_tx_data.return_address = crypto::null_pkey;
|
||||
protocol_tx_data.return_pubkey = crypto::null_pkey;
|
||||
protocol_tx_data.return_view_tag = {};
|
||||
protocol_tx_data.return_anchor_enc = {};
|
||||
token_metadata.version = 0;
|
||||
source_asset_type.clear();
|
||||
destination_asset_type.clear();
|
||||
amount_burnt = 0;
|
||||
amount_slippage_limit = 0;
|
||||
rollup_binding_tag = {0};
|
||||
token_metadata = {};
|
||||
layer2_rollup_data = {};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -535,6 +674,91 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct token_block_info {
|
||||
uint64_t block_height;
|
||||
std::map<uint32_t, uint64_t> token_info; // Replaces the vector
|
||||
|
||||
token_block_info() = default; // default ctor
|
||||
|
||||
explicit token_block_info(const uint64_t h)
|
||||
: block_height(h) {}
|
||||
|
||||
void clear() {
|
||||
block_height = 0;
|
||||
token_info.clear();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> serialize() const noexcept {
|
||||
constexpr uint8_t version = 1; // Bump if format changes
|
||||
uint32_t sz = static_cast<uint32_t>(token_info.size());
|
||||
size_t total_size = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
|
||||
std::vector<uint8_t> buf(total_size);
|
||||
uint8_t* ptr = buf.data();
|
||||
|
||||
std::memcpy(ptr, &version, sizeof(uint8_t));
|
||||
ptr += sizeof(uint8_t);
|
||||
|
||||
std::memcpy(ptr, &block_height, sizeof(uint64_t));
|
||||
ptr += sizeof(uint64_t);
|
||||
|
||||
std::memcpy(ptr, &sz, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
for (const auto& p : token_info) { // Iterates in sorted order
|
||||
std::memcpy(ptr, &p.first, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
std::memcpy(ptr, &p.second, sizeof(uint64_t));
|
||||
ptr += sizeof(uint64_t);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void deserialize(const uint8_t* data, size_t len) {
|
||||
if (len < sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
std::memcpy(&version, data, sizeof(uint8_t));
|
||||
data += sizeof(uint8_t);
|
||||
len -= sizeof(uint8_t);
|
||||
|
||||
if (version == 0) { // Legacy: No height/version (your current format)
|
||||
// Handle old blobs: Caller must set block_height externally
|
||||
block_height = 0; // Or throw if height is mandatory
|
||||
} else if (version == 1) {
|
||||
if (len < sizeof(uint64_t) + sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
std::memcpy(&block_height, data, sizeof(uint64_t));
|
||||
data += sizeof(uint64_t);
|
||||
len -= sizeof(uint64_t);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported version");
|
||||
}
|
||||
|
||||
uint32_t sz;
|
||||
std::memcpy(&sz, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
len -= sizeof(uint32_t);
|
||||
|
||||
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
|
||||
throw std::runtime_error("Invalid serialized data length");
|
||||
}
|
||||
|
||||
token_info.clear();
|
||||
for (uint32_t i = 0; i < sz; ++i) {
|
||||
uint32_t k;
|
||||
uint64_t v;
|
||||
std::memcpy(&k, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
std::memcpy(&v, data, sizeof(uint64_t));
|
||||
data += sizeof(uint64_t);
|
||||
token_info[k] = v; // Map handles sorting/uniquness
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct yield_block_info {
|
||||
uint64_t block_height;
|
||||
uint64_t slippage_total_this_block;
|
||||
@@ -669,6 +893,7 @@ namespace cryptonote
|
||||
{
|
||||
crypto::public_key m_spend_public_key;
|
||||
crypto::public_key m_view_public_key;
|
||||
bool m_is_carrot;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(m_spend_public_key)
|
||||
@@ -683,7 +908,8 @@ namespace cryptonote
|
||||
bool operator==(const account_public_address& rhs) const
|
||||
{
|
||||
return m_spend_public_key == rhs.m_spend_public_key &&
|
||||
m_view_public_key == rhs.m_view_public_key;
|
||||
m_view_public_key == rhs.m_view_public_key &&
|
||||
m_is_carrot == rhs.m_is_carrot;
|
||||
}
|
||||
|
||||
bool operator!=(const account_public_address& rhs) const
|
||||
@@ -752,6 +978,10 @@ VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_carrot_v1, 0x4);
|
||||
VARIANT_TAG(binary_archive, cryptonote::protocol_tx_data_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::token_metadata_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::erc_token_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::sal_token_t, 0x1);
|
||||
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
|
||||
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
|
||||
|
||||
@@ -764,6 +994,10 @@ VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_tagged_key, "tagged_key");
|
||||
VARIANT_TAG(json_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
|
||||
VARIANT_TAG(json_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
|
||||
VARIANT_TAG(json_archive, cryptonote::token_metadata_t, "token_metadata");
|
||||
VARIANT_TAG(json_archive, cryptonote::erc_token_t, "erc_token");
|
||||
VARIANT_TAG(json_archive, cryptonote::sal_token_t, "sal_token");
|
||||
VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
|
||||
VARIANT_TAG(json_archive, cryptonote::block, "block");
|
||||
|
||||
@@ -776,5 +1010,9 @@ VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_tagged_key, "tagged_key");
|
||||
VARIANT_TAG(debug_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
|
||||
VARIANT_TAG(debug_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
|
||||
VARIANT_TAG(debug_archive, cryptonote::token_metadata_t, "token_metadata");
|
||||
VARIANT_TAG(debug_archive, cryptonote::erc_token_t, "erc_token");
|
||||
VARIANT_TAG(debug_archive, cryptonote::sal_token_t, "sal_token");
|
||||
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
|
||||
VARIANT_TAG(debug_archive, cryptonote::block, "block");
|
||||
|
||||
@@ -157,7 +157,9 @@ namespace cryptonote {
|
||||
, account_public_address const & adr
|
||||
)
|
||||
{
|
||||
uint64_t address_prefix = subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t address_prefix = adr.m_is_carrot
|
||||
? (subaddress ? get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX)
|
||||
: (subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
|
||||
|
||||
return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr));
|
||||
}
|
||||
@@ -168,7 +170,7 @@ namespace cryptonote {
|
||||
, crypto::hash8 const & payment_id
|
||||
)
|
||||
{
|
||||
uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t integrated_address_prefix = adr.m_is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
|
||||
integrated_address iadr = {
|
||||
adr, payment_id
|
||||
@@ -196,6 +198,9 @@ namespace cryptonote {
|
||||
uint64_t address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t subaddress_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
|
||||
uint64_t carrot_address_prefix = get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t carrot_integrated_address_prefix = get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t carrot_subaddress_prefix = get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX;
|
||||
|
||||
if (2 * sizeof(public_address_outer_blob) != str.size())
|
||||
{
|
||||
@@ -211,21 +216,45 @@ namespace cryptonote {
|
||||
{
|
||||
info.is_subaddress = false;
|
||||
info.has_payment_id = true;
|
||||
info.is_carrot = false;
|
||||
}
|
||||
else if (address_prefix == prefix)
|
||||
{
|
||||
info.is_subaddress = false;
|
||||
info.has_payment_id = false;
|
||||
info.is_carrot = false;
|
||||
}
|
||||
else if (subaddress_prefix == prefix)
|
||||
{
|
||||
info.is_subaddress = true;
|
||||
info.has_payment_id = false;
|
||||
info.is_carrot = false;
|
||||
}
|
||||
else if (carrot_integrated_address_prefix == prefix)
|
||||
{
|
||||
info.is_subaddress = false;
|
||||
info.has_payment_id = true;
|
||||
info.is_carrot = true;
|
||||
}
|
||||
else if (carrot_address_prefix == prefix)
|
||||
{
|
||||
info.is_subaddress = false;
|
||||
info.has_payment_id = false;
|
||||
info.is_carrot = true;
|
||||
}
|
||||
else if (carrot_subaddress_prefix == prefix)
|
||||
{
|
||||
info.is_subaddress = true;
|
||||
info.has_payment_id = false;
|
||||
info.is_carrot = true;
|
||||
}
|
||||
else {
|
||||
LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix
|
||||
<< " or " << integrated_address_prefix
|
||||
<< " or " << subaddress_prefix);
|
||||
<< " or " << subaddress_prefix
|
||||
<< " or " << carrot_address_prefix
|
||||
<< " or " << carrot_integrated_address_prefix
|
||||
<< " or " << carrot_subaddress_prefix);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -254,6 +283,8 @@ namespace cryptonote {
|
||||
LOG_PRINT_L1("Failed to validate address keys");
|
||||
return false;
|
||||
}
|
||||
|
||||
info.address.m_is_carrot = info.is_carrot;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -285,8 +316,10 @@ namespace cryptonote {
|
||||
|
||||
//we success
|
||||
info.address = blob.m_address;
|
||||
info.address.m_is_carrot = false;
|
||||
info.is_subaddress = false;
|
||||
info.has_payment_id = false;
|
||||
info.is_carrot = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace cryptonote {
|
||||
account_public_address address;
|
||||
bool is_subaddress;
|
||||
bool has_payment_id;
|
||||
bool is_carrot;
|
||||
crypto::hash8 payment_id;
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "crypto/crypto.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "serialization/containers.h"
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@@ -73,6 +74,11 @@ namespace boost
|
||||
a & reinterpret_cast<char (&)[sizeof(crypto::key_image)]>(x);
|
||||
}
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, carrot::input_context_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & reinterpret_cast<char (&)[sizeof(carrot::input_context_t)]>(x);
|
||||
}
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, crypto::view_tag &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & reinterpret_cast<char (&)[sizeof(crypto::view_tag)]>(x);
|
||||
@@ -166,6 +172,59 @@ namespace boost
|
||||
a & x.target;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::protocol_tx_data_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.return_address;
|
||||
a & x.return_pubkey;
|
||||
a & x.return_view_tag;
|
||||
a & x.return_anchor_enc;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::erc_token_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.contract_address;
|
||||
a & x.lockbox_address;
|
||||
a & x.ticker;
|
||||
a & x.erc20_asset_id;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::sal_token_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.supply;
|
||||
a & x.size;
|
||||
a & x.name;
|
||||
a & x.url;
|
||||
a & x.signature;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::token_metadata_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.asset_type;
|
||||
a & x.token;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::layer2_rollup_tx_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.tx_prefix_hash;
|
||||
a & x.first_key_image;
|
||||
a & x.tx_fee;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::layer2_rollup_data_t &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.version;
|
||||
a & x.txs;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
|
||||
@@ -183,14 +242,29 @@ namespace boost
|
||||
a & x.return_address_list;
|
||||
a & x.return_address_change_mask;
|
||||
} else {
|
||||
a & x.return_address;
|
||||
a & x.return_pubkey;
|
||||
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(x.version >= TRANSACTION_VERSION_CARROT)) {
|
||||
a & x.protocol_tx_data;
|
||||
} else {
|
||||
a & x.return_address;
|
||||
a & x.return_pubkey;
|
||||
}
|
||||
}
|
||||
a & x.source_asset_type;
|
||||
a & x.destination_asset_type;
|
||||
a & x.amount_slippage_limit;
|
||||
}
|
||||
}
|
||||
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
a & x.token_metadata;
|
||||
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
|
||||
a & x.rollup_binding_tag;
|
||||
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
|
||||
a & x.rollup_binding_tag;
|
||||
a & x.layer2_rollup_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
@@ -209,13 +283,28 @@ namespace boost
|
||||
a & x.return_address_list;
|
||||
a & x.return_address_change_mask;
|
||||
} else {
|
||||
a & x.return_address;
|
||||
a & x.return_pubkey;
|
||||
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
|
||||
(x.version >= TRANSACTION_VERSION_CARROT)) {
|
||||
a & x.protocol_tx_data;
|
||||
} else {
|
||||
a & x.return_address;
|
||||
a & x.return_pubkey;
|
||||
}
|
||||
}
|
||||
a & x.source_asset_type;
|
||||
a & x.destination_asset_type;
|
||||
a & x.amount_slippage_limit;
|
||||
}
|
||||
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
a & x.token_metadata;
|
||||
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
|
||||
a & x.rollup_binding_tag;
|
||||
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
|
||||
a & x.rollup_binding_tag;
|
||||
a & x.layer2_rollup_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x.version == 1)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "wipeable_string.h"
|
||||
#include "string_tools.h"
|
||||
@@ -43,6 +44,7 @@
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "oracle/asset_types.h"
|
||||
#include "common/debugging.h"
|
||||
|
||||
using namespace epee;
|
||||
|
||||
@@ -113,7 +115,7 @@ namespace cryptonote
|
||||
const bool plus = (
|
||||
rv.type == rct::RCTTypeBulletproofPlus ||
|
||||
rv.type == rct::RCTTypeFullProofs ||
|
||||
rv.type == rct::RCTTypeSalviumZero ||
|
||||
/*rv.type == rct::RCTTypeSalviumZero ||*/
|
||||
rv.type == rct::RCTTypeSalviumOne);
|
||||
const uint64_t bp_base = (32 * ((plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
|
||||
const size_t n_outputs = tx.vout.size();
|
||||
@@ -1114,7 +1116,37 @@ namespace cryptonote
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (asset_type_id)
|
||||
{
|
||||
// Check to see if 1st byte is permitted
|
||||
std::string s, s_prefix;
|
||||
switch (asset_type_id >> 24) {
|
||||
case 0x01:
|
||||
// We have a user-generated token
|
||||
s_prefix = "sal";
|
||||
break;
|
||||
case 0x02:
|
||||
s_prefix = "erc";
|
||||
break;
|
||||
default:
|
||||
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
|
||||
}
|
||||
|
||||
// Break up the remaining 3 bytes into 4 chunks of base36/64
|
||||
static const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
uint32_t asset_type_id_temp = asset_type_id & 0x00FFFFFF;
|
||||
for (int i=0; i<4; ++i) {
|
||||
uint8_t val = asset_type_id_temp & 0x0000003F;
|
||||
if (val >= 36)
|
||||
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
|
||||
s.push_back(alphabet[val]);
|
||||
asset_type_id_temp >>= 6;
|
||||
}
|
||||
std::reverse(s.begin(), s.end());
|
||||
return s_prefix + s;
|
||||
}
|
||||
// Should probably throw() here
|
||||
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
|
||||
return "";
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
@@ -1129,11 +1161,162 @@ namespace cryptonote
|
||||
} else if (asset_type == "") {
|
||||
return 0x00000000;
|
||||
} else {
|
||||
// Should probably throw() here
|
||||
return static_cast<uint32_t>(-1);
|
||||
if (asset_type.length() != 7) {
|
||||
LOG_ERROR("Custom asset type '" << asset_type << "' has invalid length.");
|
||||
return 0x00000000;
|
||||
}
|
||||
static const std::string alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
// Check the 4-char type
|
||||
std::string s_type = asset_type.substr(3);
|
||||
std::transform(s_type.begin(), s_type.end(), s_type.begin(),
|
||||
[](unsigned char c){ return std::toupper(c); });
|
||||
uint32_t asset_id = 0x00000000;
|
||||
for (int i=0; i<s_type.length(); ++i) {
|
||||
uint8_t idx = alphabet.find(s_type.at(i));
|
||||
if (idx == std::string::npos || idx >= 36) {
|
||||
LOG_ERROR("Custom asset type contains invalid char.");
|
||||
return 0x00000000;
|
||||
}
|
||||
asset_id = (asset_id << 6) | (idx & 0x3F);
|
||||
}
|
||||
|
||||
// Check the 3-char prefix
|
||||
std::string s_prefix = asset_type.substr(0,3);
|
||||
std::transform(s_prefix.begin(), s_prefix.end(), s_prefix.begin(),
|
||||
[](unsigned char c){ return std::tolower(c); });
|
||||
if (s_prefix == "sal") {
|
||||
asset_id |= 0x01000000;
|
||||
} else if (s_prefix == "erc") {
|
||||
asset_id |= 0x02000000;
|
||||
} else {
|
||||
LOG_ERROR("Custom asset type has invalid prefix.");
|
||||
return 0x00000000;
|
||||
}
|
||||
return asset_id;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool is_asset_type_token(const std::string& asset_type)
|
||||
{
|
||||
// SAL, SAL1, BURN are base asset types
|
||||
// erc prefix is not included here
|
||||
if (asset_type.length() >= 3) {
|
||||
std::string prefix = asset_type.substr(0, 3);
|
||||
return (prefix == "sal");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool is_valid_asset_type(const std::string& asset_type) {
|
||||
// This method does NOT throw()
|
||||
try {
|
||||
uint32_t asset_id = asset_id_from_type(asset_type);
|
||||
std::string asset_type_check = asset_type_from_id(asset_id);
|
||||
return (asset_type_check == asset_type);
|
||||
}
|
||||
catch (std::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool is_valid_custom_asset_type(const std::string& asset_type)
|
||||
{
|
||||
if (!is_valid_asset_type(asset_type))
|
||||
return false;
|
||||
|
||||
// double check with reserved IDs
|
||||
uint32_t id = asset_id_from_type(asset_type);
|
||||
if (id == 0x53414C00 || id == 0x53414C31 || id == 0x4255524E || id == 0x00000000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
uint64_t get_token_creation_price(const std::string& ticker)
|
||||
{
|
||||
static const std::map<std::string, uint64_t> premium_tickers = {
|
||||
// add more
|
||||
{"USDT", 10000 * COIN},
|
||||
{"USDC", 10000 * COIN},
|
||||
{"WBTC", 10000 * COIN},
|
||||
{"DOGE", 10000 * COIN},
|
||||
{"SHIB", 10000 * COIN},
|
||||
{"AVAX", 10000 * COIN},
|
||||
{"ATOM", 10000 * COIN},
|
||||
{"NEAR", 10000 * COIN},
|
||||
{"TRON", 10000 * COIN},
|
||||
{"HBAR", 10000 * COIN},
|
||||
{"AAVE", 10000 * COIN},
|
||||
{"FLOW", 10000 * COIN},
|
||||
{"EGLD", 10000 * COIN},
|
||||
{"KLAY", 10000 * COIN},
|
||||
{"LUNA", 10000 * COIN},
|
||||
{"DASH", 10000 * COIN},
|
||||
{"NANO", 10000 * COIN},
|
||||
{"CORE", 10000 * COIN},
|
||||
{"BEAM", 10000 * COIN},
|
||||
{"DYDX", 10000 * COIN},
|
||||
{"COMP", 10000 * COIN},
|
||||
{"SAND", 10000 * COIN},
|
||||
{"MANA", 10000 * COIN},
|
||||
{"RUNE", 10000 * COIN},
|
||||
{"PYTH", 10000 * COIN},
|
||||
{"ARKM", 10000 * COIN},
|
||||
{"BLUR", 10000 * COIN},
|
||||
{"STRK", 10000 * COIN},
|
||||
{"PEPE", 10000 * COIN},
|
||||
{"BONK", 10000 * COIN},
|
||||
{"VIUM", 10000 * COIN},
|
||||
{"GOLD", 10000 * COIN},
|
||||
{"SILV", 10000 * COIN},
|
||||
{"CASH", 10000 * COIN},
|
||||
{"EURO", 10000 * COIN},
|
||||
{"PESO", 10000 * COIN},
|
||||
{"BOND", 10000 * COIN},
|
||||
{"FUND", 10000 * COIN},
|
||||
{"BANK", 10000 * COIN},
|
||||
{"SWAP", 10000 * COIN},
|
||||
{"LEND", 10000 * COIN},
|
||||
{"LOAN", 10000 * COIN},
|
||||
{"NOTE", 10000 * COIN},
|
||||
{"HOLD", 10000 * COIN},
|
||||
{"BULL", 10000 * COIN},
|
||||
{"BEAR", 10000 * COIN},
|
||||
{"TECH", 10000 * COIN},
|
||||
{"DATA", 10000 * COIN},
|
||||
{"HASH", 10000 * COIN},
|
||||
{"NODE", 10000 * COIN},
|
||||
{"BYTE", 10000 * COIN},
|
||||
{"GRID", 10000 * COIN},
|
||||
{"CODE", 10000 * COIN},
|
||||
{"META", 10000 * COIN},
|
||||
{"WEB3", 10000 * COIN},
|
||||
{"NFTS", 10000 * COIN},
|
||||
{"DEFI", 10000 * COIN},
|
||||
{"LAND", 10000 * COIN},
|
||||
{"REAL", 10000 * COIN},
|
||||
{"RENT", 10000 * COIN},
|
||||
{"FARM", 10000 * COIN},
|
||||
{"OILX", 10000 * COIN},
|
||||
{"ENRG", 10000 * COIN},
|
||||
{"FUEL", 10000 * COIN},
|
||||
{"VOTE", 10000 * COIN},
|
||||
{"PASS", 10000 * COIN},
|
||||
{"LOCK", 10000 * COIN},
|
||||
};
|
||||
|
||||
// is it a premium ticker
|
||||
auto it = premium_tickers.find(ticker);
|
||||
if (it != premium_tickers.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// not premium
|
||||
return 1000 * COIN;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
/**
|
||||
* The various scenarios that are permitted for Salvium are more extensive than
|
||||
* they are for Zepyhr / Havan. Specifically, we permit:
|
||||
@@ -1381,48 +1564,31 @@ namespace cryptonote
|
||||
|
||||
for (const auto &o: tx.vout)
|
||||
{
|
||||
if (hf_version >= HF_VERSION_CARROT)
|
||||
{
|
||||
// from v11, require outputs be carrot outputs
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
|
||||
} else {
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (tx.type != cryptonote::transaction_type::PROTOCOL) {
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key) ||
|
||||
o.target.type() == typeid(txout_to_tagged_key) ||
|
||||
o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_key or txout_to_tagged_key or txout_to_carrot_v1 in protocol transaction");
|
||||
// require all outputs in a tx be of the same type
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == tx.vout[0].target.type(), false, "non-matching variant types: "
|
||||
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
|
||||
<< "expected matching variant types in protocol transaction");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// require outputs be of type txout_to_key OR txout_to_tagged_key
|
||||
// to allow grace period before requiring all to be txout_to_tagged_key
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key) || o.target.type() == typeid(txout_to_tagged_key), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_key or txout_to_tagged_key in transaction");
|
||||
|
||||
// require all outputs in a tx be of the same type
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == tx.vout[0].target.type(), false, "non-matching variant types: "
|
||||
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
|
||||
<< "expected matching variant types in transaction");
|
||||
}
|
||||
|
||||
// Verify the asset type
|
||||
std::string asset_type;
|
||||
CHECK_AND_ASSERT_MES(cryptonote::get_output_asset_type(o, asset_type), false, "failed to get asset type");
|
||||
if (hf_version < HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
// Prior to the first audit, ONLY SAL was supported
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
|
||||
} else {
|
||||
if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
// HERE BE DRAGONS!!!
|
||||
// SRCG: This will NOT always be the case - when we add an audit for SALx it'll need to support that as well
|
||||
// The CHANGE for an AUDIT TX must be SAL (and 0 value, and unspendable, and to the origin wallet, and ...)
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
|
||||
// LAND AHOY!!!
|
||||
} else if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
if (hf_version < HF_VERSION_AUDIT1_PAUSE) {
|
||||
// PROTOCOL TXs are responsible for paying out SAL and SAL1 during the first AUDIT
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL1" || asset_type == "SAL", false, "wrong output asset type:" << asset_type);
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
|
||||
}
|
||||
} else {
|
||||
// All other TX types must only spend + create SAL1 (MINER, TRANSFER)
|
||||
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,10 @@ namespace cryptonote
|
||||
uint64_t get_outs_money_amount(const transaction& tx);
|
||||
std::string asset_type_from_id(const uint32_t asset_type_id);
|
||||
uint32_t asset_id_from_type(const std::string asset_type);
|
||||
bool is_valid_custom_asset_type(const std::string& asset_type);
|
||||
bool is_valid_asset_type(const std::string& asset_type);
|
||||
bool is_asset_type_token(const std::string& asset_type);
|
||||
uint64_t get_token_creation_price(const std::string& ticker);
|
||||
bool get_tx_asset_types(const transaction& tx, const crypto::hash &txid, std::string& source, std::string& destination, const bool is_miner_tx);
|
||||
bool get_output_public_key(const cryptonote::tx_out& out, crypto::public_key& output_public_key);
|
||||
boost::optional<crypto::view_tag> get_output_view_tag(const cryptonote::tx_out& out);
|
||||
|
||||
@@ -104,7 +104,9 @@ namespace cryptonote
|
||||
}
|
||||
|
||||
|
||||
miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):m_stop(1),
|
||||
miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):
|
||||
m_forced_stop(1),
|
||||
m_stop(1),
|
||||
m_template{},
|
||||
m_template_no(0),
|
||||
m_diffic(0),
|
||||
@@ -176,6 +178,8 @@ namespace cryptonote
|
||||
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
|
||||
{
|
||||
LOG_ERROR("Failed to get_block_template(), stopping mining");
|
||||
m_forced_stop = true;
|
||||
m_stop = true;
|
||||
return false;
|
||||
}
|
||||
set_block_template(bl, di, height, expected_reward);
|
||||
@@ -185,7 +189,17 @@ namespace cryptonote
|
||||
bool miner::on_idle()
|
||||
{
|
||||
m_update_block_template_interval.do_call([&](){
|
||||
if(is_mining())request_block_template();
|
||||
if(is_mining()) {
|
||||
// Request the block template
|
||||
request_block_template();
|
||||
} else {
|
||||
// Check for forced stop
|
||||
if (m_forced_stop) {
|
||||
if (!m_threads_active) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -393,8 +407,12 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
request_block_template();//lets update block template
|
||||
if (!request_block_template()) {
|
||||
LOG_ERROR("Unable to start miner - unknown error");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_forced_stop = false;
|
||||
m_stop = false;
|
||||
m_thread_index = 0;
|
||||
set_is_background_mining_enabled(do_background);
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace cryptonote
|
||||
};
|
||||
|
||||
|
||||
std::atomic<bool> m_forced_stop;
|
||||
std::atomic<bool> m_stop;
|
||||
epee::critical_section m_template_lock;
|
||||
block m_template;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#define TX_EXTRA_NONCE 0x02
|
||||
#define TX_EXTRA_MERGE_MINING_TAG 0x03
|
||||
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
|
||||
#define TX_EXTRA_TAG_TOKEN 0x80
|
||||
#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE
|
||||
|
||||
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
|
||||
@@ -167,6 +168,15 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_extra_token
|
||||
{
|
||||
cryptonote::token_metadata_t token;
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
FIELD(token)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_extra_mysterious_minergate
|
||||
{
|
||||
std::string data;
|
||||
@@ -180,7 +190,7 @@ namespace cryptonote
|
||||
// varint tag;
|
||||
// varint size;
|
||||
// varint data[];
|
||||
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate> tx_extra_field;
|
||||
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate, tx_extra_token> tx_extra_field;
|
||||
}
|
||||
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING);
|
||||
@@ -189,3 +199,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);
|
||||
VARIANT_TAG(binary_archive, cryptonote::tx_extra_token, TX_EXTRA_TAG_TOKEN);
|
||||
|
||||
+46
-36
@@ -44,9 +44,11 @@
|
||||
#define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000
|
||||
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
|
||||
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
|
||||
#define CURRENT_TRANSACTION_VERSION 3
|
||||
#define CURRENT_TRANSACTION_VERSION 5
|
||||
#define TRANSACTION_VERSION_2_OUTS 2
|
||||
#define TRANSACTION_VERSION_N_OUTS 3
|
||||
#define TRANSACTION_VERSION_CARROT 4
|
||||
#define TRANSACTION_VERSION_ENABLE_TOKENS 5
|
||||
#define CURRENT_BLOCK_MAJOR_VERSION 1
|
||||
#define CURRENT_BLOCK_MINOR_VERSION 1
|
||||
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
||||
@@ -87,8 +89,10 @@
|
||||
#define PREMINE_AMOUNT_UPFRONT ((uint64_t)650000000000000ull) // 3.4% of MONEY_SUPPLY
|
||||
#define PREMINE_AMOUNT_MONTHLY ((uint64_t)65000000000000ull) // 8.6%/24 of MONEY_SUPPLY
|
||||
|
||||
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)65000000000000ull) // 650K
|
||||
#define TREASURY_SAL1_MINT_COUNT 16 // 16 times
|
||||
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
|
||||
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
|
||||
|
||||
#define CREATE_TOKEN_LOCK_PERIOD 10
|
||||
|
||||
#define DIFFICULTY_TARGET_V2 120 // seconds
|
||||
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
|
||||
@@ -243,13 +247,14 @@
|
||||
#define HF_VERSION_AUDIT2 8
|
||||
#define HF_VERSION_AUDIT2_PAUSE 9
|
||||
#define HF_VERSION_CARROT 10
|
||||
#define HF_VERSION_ENABLE_TOKENS 11
|
||||
|
||||
#define HF_VERSION_REQUIRE_VIEW_TAGS 255
|
||||
#define HF_VERSION_ENABLE_CONVERT 255
|
||||
#define HF_VERSION_ENABLE_ORACLE 255
|
||||
#define HF_VERSION_SLIPPAGE_YIELD 255
|
||||
|
||||
#define TESTNET_VERSION 14
|
||||
#define TESTNET_VERSION 16
|
||||
#define STAGENET_VERSION 1
|
||||
|
||||
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
|
||||
@@ -292,6 +297,9 @@ namespace config
|
||||
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0x3ef318; // SaLv
|
||||
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0x55ef318; // SaLvi
|
||||
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0xf5ef318; // SaLvs
|
||||
uint64_t const CARROT_PUBLIC_ADDRESS_BASE58_PREFIX = 0x180c96; // SC1
|
||||
uint64_t const CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0x2ccc96; // SC1i
|
||||
uint64_t const CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0x314c96; // SC1s
|
||||
uint16_t const P2P_DEFAULT_PORT = 19080;
|
||||
uint16_t const RPC_DEFAULT_PORT = 19081;
|
||||
uint16_t const ZMQ_RPC_DEFAULT_PORT = 19082;
|
||||
@@ -315,22 +323,14 @@ namespace config
|
||||
|
||||
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
|
||||
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
|
||||
{1000000ULL, {"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8", "00", "00"}},
|
||||
{1020000ULL, {"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c", "00", "00"}},
|
||||
{1040000ULL, {"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6", "00", "00"}},
|
||||
{1060000ULL, {"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88", "00", "00"}},
|
||||
{1080000ULL, {"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb", "00", "00"}},
|
||||
{1100000ULL, {"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b", "00", "00"}},
|
||||
{1120000ULL, {"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c", "00", "00"}},
|
||||
{1140000ULL, {"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8", "00", "00"}},
|
||||
{1160000ULL, {"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454", "00", "00"}},
|
||||
{1180000ULL, {"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42", "00", "00"}},
|
||||
{1200000ULL, {"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890", "00", "00"}},
|
||||
{1220000ULL, {"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0", "00", "00"}},
|
||||
{1240000ULL, {"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e", "00", "00"}},
|
||||
{1260000ULL, {"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c", "00", "00"}},
|
||||
{1280000ULL, {"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91", "00", "00"}},
|
||||
{1300000ULL, {"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007", "00", "00"}},
|
||||
{334750, {"1b2cd3ff56aa77c0cbab0473bfb96697ebdd0b25ad230136bfe41d5dc1ef265f","718cf02eabca157fd7ad7f8537db217624bfe1ca99dd09e758357e7000a5e57a","789cca3def51fb879eb7fbca271869b7","79bd0c"}},
|
||||
{356350, {"b51acbf35265d09f3cfb83dcabde2746991ddf0d30b5a4ecc34043b349a77031","9dc0d2e9534cdccf83494687c55c67c8c1b29834acf97cce53124a08a9549231","588ebc2918d06c009a18a28a8ab76694","ab8c23"}},
|
||||
{377950, {"771c6865980493846cbb049b34f72b937878cf799ae1126775df35064cf53526","60d340613c7721aeb03f2de1b56e2916d6cb023cc668acaca00e1606e0bb7f64","198bb0e5880c8abf8108158284ca7637","9b93f2"}},
|
||||
{399550, {"d9159f9bb654230382a69bb7a739c14f9d506c21dba5be9f4cdf66126ed1b24e","c3b34e7f9977f31f659abd9306b650416a674ed7bcfb8c75b257040a1a06b8b9","6f0de181716e0a9e100228d58f11d42c","4c87fd"}},
|
||||
{421150, {"1e2ab1ae204e7a9375f5b57fed2524e2d1a4702c4bbc6d420eb65d92af44c60c","826ecca0e2837860a6d2be89a410ff6aab22c5f5a66bd0a318de3abe646aa26a","bad663aa8253c40d00de0a2d1dfca60f","4a04d7"}},
|
||||
{442750, {"18267940a2b37c82850f3fb83d935281ee5cc436b0797ec10ca26c9cc3c0ef01","77bdec7bbc4cabd84cc8cf0666e1fdfe299480b9b2a4cf52cc959d728fa899ab","a8807121e33471cf18aa82af4826c9a7","d35d35"}},
|
||||
{464350, {"9ab50223bfecec2508e8233540dc13c3fc7e15a8d77ecb90939afee96859724d","675a1b3f2a2cd2bd0e7ec073c426125d95e12ad13e541bf019925c481c2bca15","591d2f2331832c347654ab255ad97e2d","d87c9c"}},
|
||||
{485950, {"c0d7299fe0b873687e7319f1a0d1f67712ef3ccf6a579d96c5f99743add6f029","dd3d17b5145b56377e023c169ae858cdf48bd6c23e8b2a77e02765ce316e3ca7","6a9e74df01762e2b32db1dc4d69dc3e9","75debe"}}
|
||||
};
|
||||
|
||||
// Hash domain separators
|
||||
@@ -396,6 +396,9 @@ namespace config
|
||||
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0x15beb318; // SaLvT
|
||||
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0xd055eb318; // SaLvTi
|
||||
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0xa59eb318; // SaLvTs
|
||||
uint64_t const CARROT_PUBLIC_ADDRESS_BASE58_PREFIX = 0x254c96; // SC1T
|
||||
uint64_t const CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0x1ac50c96; // SC1Ti
|
||||
uint64_t const CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0x3c54c96; // SC1Ts
|
||||
uint16_t const P2P_DEFAULT_PORT = 29080;
|
||||
uint16_t const RPC_DEFAULT_PORT = 29081;
|
||||
uint16_t const ZMQ_RPC_DEFAULT_PORT = 29082;
|
||||
@@ -424,22 +427,14 @@ namespace config
|
||||
|
||||
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
|
||||
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
|
||||
{1100, {"310fe378b82e2475a87c83eef07c57d92fa0731d5c499258e774852158276968","f17c5a62efcc96710f2a173538fe5e79b190b44ab6facc01a619f0e227ac18d1","eb613855f58d093ae9d527e8ab69b46a","6c74f9"}},
|
||||
{1120, {"adb74cea72d46ebe5a45e5886189e71c493308686f076ccfc053cb9b4f55b656","b40e958589238cc48a5a7f03f1f28406a13b2f724efe831cfec43c06bb0fcb60","77a51cc3a6c3c53d631cadad72f57c1d","e5fec0"}},
|
||||
{1140, {"c5e9a67c695ddcab05b980de0c8cc7d9d5b9c09af58047843bfd7d29a038f671","6c3253bd837b899b87aedc97114feb9c10c2d255ce59b7ba5b470817a37d16df","fcf6e1907ea30dbafd510e33a37cc2dc","fa5c45"}},
|
||||
{1160, {"e95c8b7fc3d0e7e34d544e97d3b526e94f70acb7d85cb0053b3cb830e863ac38","3a5ae93e922985ea1a126ff8fd35f6cbf39fa556525e15a049765510eb1360e1","7ea6d62add9387a28f3dbed5dbac120b","c26ac2"}},
|
||||
{1180, {"9d453235a25e470df2d7ceaa8f8d4216a8939a0a93bc0c43f1b902cb461ee82d","2e990ed2de4d745389d88aa9868634ce430f09d2c0cb5ebc2ee8b9bcc23e221a","807e07241a48b97acb537241822a97b8","eb37c0"}},
|
||||
{1200, {"92dbd7da158d4bcb4f002dcc505ed4c49ac4e4782724b506a5977119c80c4869","4505a9be31c2ac6ad5faa2ea8332b85b1a3efee84d0456740d208b8188ad842a","8870c1f88de25b5443d8323759f386d9","443c65"}},
|
||||
{1220, {"1e4d5c44a112c3ac89ab2c7065bb2c8c094779b3a5d696652198f7dd05f88b77","01f56bf134652852688e30dd9559bdb5fe2edd046bed99dbea23b655d2475b89","da088fc1e1c2f2d0e348f06f2079e8ed","d7c7bf"}},
|
||||
{1240, {"91ba144c930a65996b2d7c3b296217c692225756392b580a323838e48b357452","f2d5f4f2aaa599a52c557270cbc5cde138b753a98297d975b74a7223a9b80b13","0dc3a42cb1f36768a09c316790936356","172c22"}},
|
||||
{1260, {"1d626ede8d2e34e888384655b30942a25be272a3ea58d49835265e6933a91a52","05666de1b136236f0682e128c0c3ae84578a6a4b233a578dd676e581a582fe19","c81ccf7f93e01e7872543907de8d17d0","21dd94"}},
|
||||
{1280, {"85b10b5ae72ab59281f47be4edede17b7a017dc7ddd839813e3a12c0b2a39874","d34d259e2fee2b0c977242c473cf83997920c1e64ff7a9c364b2bf4abba4d5cc","43de6ce100ca3eeb6734fc44cc9f7dbc","574e2f"}},
|
||||
{1300, {"c699ca0dcbe538e6be9e997acb50a4c13557d1e8cf3a105ab47406cb68300656","50c5e0fa60907c2c2b66748562193b7f5bfcba269eb398c5c975c9e125933942","05aeb41f8863152a7e0e36f216ecbb12","e413dd"}},
|
||||
{1320, {"76bb126d6e0e5d3d7ee7ccabd194a248cbe006ae7d57f89c31c6826f8e7b4851","e1f2277b378630d92bc03003e3d206899404afde342fcb3a9b1790f798c8b5a4","26274ad1bbf5d483f2c747754b898873","a2df70"}},
|
||||
{1340, {"5738dc94686bfffe1789b695785f270f6f616b1a52e071e975cf915063233b1a","982b4848ed74f2a5d899f778b182c35f40535579349435b466ff98c3fce678e7","67bd9b9a434b9028564a065a30ae34fa","36ceec"}},
|
||||
{1360, {"473b6f6941d6eafaef007d3d932763b6e39c1428d3ba76dfff452ddc67f93b48","76e3b1280b959672d86c357e6f5fcd8a74e61a0a948a0541a0eb384060705bac","74b5ad8847471e3d8723ad3e101b8a13","5e5671"}},
|
||||
{1380, {"6dae9a14e24b96caf8f7acb0edad936a4ecaa194a61b3785a2b1d08cbca0a158","26ca204c10481ed2447447e3893e0c46c9f01df16c3f3a671cf9e6054176ee5d","7c04e4ed28bfac12a0050bdae44f9979","4c23ad"}},
|
||||
{1400, {"a5837480d8e17ddb9b29baaf5671545d59d18b546de0d7092768882cff94d125","dc76eb3892b3721da947fc71de8ab30ef32651f1b6bb1a45eb7b80882217a5ac","bb490940a3cdf4a17fd0bec0b578531f","84d899"}}
|
||||
{1100, {"71336a480440ed24a53b2cfd5a5292d1e618c3e843637227883ea2cc42fb346f","a135f59a05ea9e33539e4502b187b4789cc7fba79616c6902a902cc6601f0359","7f1c6970232254a9b13f7f063df2a853","62073c"}},
|
||||
{1120, {"2cc49b182addc0106b601c9876c01a4b06532c05f9dd9179b2c4f47e5c7c0d74","fd62e59324389f37d0bb628a39f413c11be34d572ec3a40f465e008b9dfd5e0c","fbf8584222e299bb748009eaf2177123","b76a8d"}},
|
||||
{1140, {"50f1dd5244bb6fdc62b5a18b868a4dc9e9ad15f4a7a54e5a98084a3a5213c846","942de90a3347df3e962d9b0a2745f8aedb265a8caf73aabef9aa713f4ec3e493","933702e29d680bf21ad559a2d3243621","dae32b"}},
|
||||
{1160, {"114a1fef3a03c81b3b6308fb4e51db42d86945d8f71ee8798749a4aa2d8bfe57","fd77f0289b7e25ae68c86f4cd2c72ac0211a4d0e7a31c84b8d58a33bbd3c511b","d8048bb76b9313768a4c69100cba6d43","46b0a3"}},
|
||||
{1180, {"cf7fe9d97a7a4cb881bff1d37bb6981bcb0691649c465d660522a43f14b32849","b76908d56108e7111374f60993e9411def7bb2899118f20f49e67a801936a1ad","f203a5acd48f93f1451d32d70716e585","d72097"}},
|
||||
{1200, {"415e8fa4fb6bed8bc59591883f5e098dde6179e980eccfc4cbb3a64c4168cf47","5426c588d7c5750c76b526ef268b45f7414e2ddea40218ee92d123564c5114c4","fb1926bbef8c70ebd796ae77dffca07d","351edf"}},
|
||||
{1220, {"1f47cce00e8e1c77cb3ff007491d261dae75de5bbfdbe0f4913d74a8503aaa5e","4d8aac299306989aa169982d89457852a1bab858c668c9b740d96b682bb1961b","d8c0be7926ce704290cfd8d5e0500f33","750003"}},
|
||||
{1240, {"3afd93c684638d54e68f5d8d417863035f12482251feea5cf7515725a2bb0f4f","7895e6baded473b093b57993ae3497a47b0ea78cf35b37d1387f226f445d9018","76a0c37e6f3d9d508c4bce1d7162514e","09e146"}}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -448,6 +443,9 @@ namespace config
|
||||
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0x149eb318; // SaLvS
|
||||
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0xf343eb318; // SaLvSi
|
||||
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0x2d47eb318; // SaLvSs
|
||||
uint64_t const CARROT_PUBLIC_ADDRESS_BASE58_PREFIX = 0x24cc96; // SC1S
|
||||
uint64_t const CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0x1a848c96; // SC1Si
|
||||
uint64_t const CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0x384cc96; // SC1Ss
|
||||
uint16_t const P2P_DEFAULT_PORT = 39080;
|
||||
uint16_t const RPC_DEFAULT_PORT = 39081;
|
||||
uint16_t const ZMQ_RPC_DEFAULT_PORT = 39082;
|
||||
@@ -508,6 +506,9 @@ namespace cryptonote
|
||||
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
|
||||
uint64_t const CARROT_PUBLIC_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t const CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t const CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX;
|
||||
uint16_t const P2P_DEFAULT_PORT;
|
||||
uint16_t const RPC_DEFAULT_PORT;
|
||||
uint16_t const ZMQ_RPC_DEFAULT_PORT;
|
||||
@@ -528,6 +529,9 @@ namespace cryptonote
|
||||
::config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
::config::CARROT_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
::config::P2P_DEFAULT_PORT,
|
||||
::config::RPC_DEFAULT_PORT,
|
||||
::config::ZMQ_RPC_DEFAULT_PORT,
|
||||
@@ -546,6 +550,9 @@ namespace cryptonote
|
||||
::config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::CARROT_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
::config::testnet::P2P_DEFAULT_PORT,
|
||||
::config::testnet::RPC_DEFAULT_PORT,
|
||||
::config::testnet::ZMQ_RPC_DEFAULT_PORT,
|
||||
@@ -564,6 +571,9 @@ namespace cryptonote
|
||||
::config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::CARROT_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX,
|
||||
::config::stagenet::P2P_DEFAULT_PORT,
|
||||
::config::stagenet::RPC_DEFAULT_PORT,
|
||||
::config::stagenet::ZMQ_RPC_DEFAULT_PORT,
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "tx_pool.h"
|
||||
#include "blockchain.h"
|
||||
#include "blockchain_db/locked_txn.h"
|
||||
@@ -66,6 +67,8 @@
|
||||
#include "net/http_client.h"
|
||||
#include "storages/http_abstract_invoke.h"
|
||||
|
||||
#include "common/debugging.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
|
||||
|
||||
@@ -877,7 +880,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> difficulties;
|
||||
uint64_t height;
|
||||
auto new_top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||
top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||
++height;
|
||||
|
||||
uint8_t version = get_current_hard_fork_version();
|
||||
@@ -976,11 +979,11 @@ size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_heig
|
||||
if (start_height_opt) {
|
||||
start_height = *start_height_opt;
|
||||
} else {
|
||||
bool found = false;
|
||||
// bool found;
|
||||
for (size_t i=0; i<num_mainnet_hard_forks; ++i) {
|
||||
if (version == mainnet_hard_forks[i].version) {
|
||||
start_height = mainnet_hard_forks[i].height;
|
||||
found = true;
|
||||
//found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1348,6 +1351,14 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.version > 1, false, "Invalid coinbase transaction version");
|
||||
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "miner transaction has wrong version");
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
|
||||
} else if (hf_version >= HF_VERSION_CARROT) {
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_CARROT, false, "miner transaction has wrong version");
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
|
||||
}
|
||||
|
||||
// for v2 txes (ringct), we only accept empty rct signatures for miner transactions,
|
||||
if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE && b.miner_tx.version >= 2)
|
||||
{
|
||||
@@ -1398,6 +1409,24 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version");
|
||||
|
||||
// Work out what the HF version _was_ when the STAKE outputs were created
|
||||
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
uint8_t hf_version_submitted = get_ideal_hard_fork_version(height - stake_lock_period - 1);
|
||||
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "protocol transaction has wrong version");
|
||||
hf_version_submitted = hf_version;
|
||||
} else if (hf_version == HF_VERSION_CARROT) {
|
||||
if (hf_version_submitted >= HF_VERSION_CARROT || b.protocol_tx.vout.size() == 0) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == 2, false, "protocol transaction has wrong version");
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
|
||||
} else {
|
||||
hf_version_submitted = hf_version;
|
||||
}
|
||||
|
||||
// for v2 txes (ringct), we only accept empty rct signatures for protocol transactions,
|
||||
if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE && b.protocol_tx.version >= 2)
|
||||
{
|
||||
@@ -1418,8 +1447,7 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version_submitted), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@@ -1427,8 +1455,8 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
|
||||
|
||||
// find the treasury output
|
||||
const auto [tx_key, onetime_address, anchor_enc, viewtag] = treasury_data;
|
||||
//const auto expected_output_key = std::get<1>(treasury_output_data);
|
||||
const auto &output = std::find_if(tx.vout.begin(), tx.vout.end(), [&onetime_address](const tx_out &o) {
|
||||
const auto &oa = onetime_address; // Alias to support capture compatibility on MacOS
|
||||
const auto &output = std::find_if(tx.vout.begin(), tx.vout.end(), [&oa](const tx_out &o) {
|
||||
std::string output_key;
|
||||
if (o.target.type() == typeid(txout_to_carrot_v1)) {
|
||||
output_key = epee::string_tools::pod_to_hex(boost::get<txout_to_carrot_v1>(o.target).key);
|
||||
@@ -1436,7 +1464,7 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
|
||||
return false;
|
||||
}
|
||||
|
||||
return output_key == onetime_address;
|
||||
return output_key == oa;
|
||||
});
|
||||
|
||||
if (output == tx.vout.end()) {
|
||||
@@ -1506,6 +1534,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENABLE_TOKENS:
|
||||
if (b.miner_tx.amount_burnt > 0) {
|
||||
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
|
||||
money_in_use += b.miner_tx.amount_burnt;
|
||||
@@ -1538,7 +1567,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// SRCG
|
||||
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version)
|
||||
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
@@ -1563,6 +1592,7 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
|
||||
// Get the staking data for the block that matured this time
|
||||
cryptonote::yield_block_info ybi_matured;
|
||||
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
|
||||
std::vector<std::pair<yield_tx_info_carrot, uint64_t>> carrot_yield_payouts;
|
||||
uint64_t matured_height = height - stake_lock_period - 1;
|
||||
bool ok = get_ybi_entry(matured_height, ybi_matured);
|
||||
if (!ok) {
|
||||
@@ -1571,10 +1601,17 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
|
||||
} else if (ybi_matured.locked_coins_this_block == 0) {
|
||||
LOG_PRINT_L1("Block at height: " << height << " - no yield payouts due - skipping");
|
||||
} else {
|
||||
// Iterate over the cached data for block yield, calculating the yield payouts due
|
||||
if (!calculate_yield_payouts(matured_height, yield_payouts)) {
|
||||
LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting");
|
||||
return false;
|
||||
// Iterate over the cached data for block yield, calculating the yield payouts due
|
||||
if (get_ideal_hard_fork_version(matured_height) >= HF_VERSION_CARROT) {
|
||||
if (!calculate_yield_payouts(matured_height, carrot_yield_payouts)) {
|
||||
LOG_ERROR("Block at height: " << height << " - Failed to obtain carrot yield payout information - aborting");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!calculate_yield_payouts(matured_height, yield_payouts)) {
|
||||
LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1606,8 +1643,133 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
|
||||
}
|
||||
}
|
||||
|
||||
// Count CREATE_TOKEN transactions if we're at the right hard fork version
|
||||
size_t create_token_count = 0;
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !txs.empty()) {
|
||||
for (const auto& tx_pair : txs) {
|
||||
const transaction& tx = tx_pair.first;
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
create_token_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check we have the correct number of entries
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting");
|
||||
CHECK_AND_ASSERT_MES(
|
||||
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size() + create_token_count,
|
||||
false, "Invalid number of outputs in protocol_tx - aborting"
|
||||
);
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
|
||||
size_t output_idx = 0;
|
||||
for (auto it = carrot_yield_payouts.begin(); it != carrot_yield_payouts.end(); it++, output_idx++) {
|
||||
// Verify the output key
|
||||
crypto::public_key out_key;
|
||||
cryptonote::get_output_public_key(b.protocol_tx.vout[output_idx], out_key);
|
||||
CHECK_AND_ASSERT_MES(out_key == it->first.return_address, false, "Incorrect output key detected in protocol_tx");
|
||||
|
||||
// Verify the return pubkey
|
||||
if (b.protocol_tx.vout.size() > 1) {
|
||||
const auto additional_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.protocol_tx.extra);
|
||||
CHECK_AND_ASSERT_MES(additional_pubkeys.size() > output_idx, false, "Missing return pubkey detected in protocol_tx");
|
||||
CHECK_AND_ASSERT_MES(additional_pubkeys[output_idx] == it->first.return_pubkey, false, "Incorrect return pubkey detected in protocol_tx");
|
||||
} else {
|
||||
const auto main_pubkey = cryptonote::get_tx_pub_key_from_extra(b.protocol_tx.extra);
|
||||
CHECK_AND_ASSERT_MES(main_pubkey == it->first.return_pubkey, false, "Incorrect return pubkey detected in protocol_tx");
|
||||
}
|
||||
|
||||
// Verify the output amount
|
||||
uint64_t expected_amount = it->second;
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == expected_amount, false, "Incorrect output amount detected in protocol_tx. expected_amount: " << expected_amount);
|
||||
|
||||
// Verify the output asset type
|
||||
std::string out_asset_type;
|
||||
cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type);
|
||||
CHECK_AND_ASSERT_MES(out_asset_type == "SAL1", false, "Incorrect output asset_type (!= SAL1) detected in protocol_tx");
|
||||
|
||||
// Verify the view tag
|
||||
CHECK_AND_ASSERT_MES(
|
||||
boost::get<cryptonote::txout_to_carrot_v1>(
|
||||
b.protocol_tx.vout[output_idx].target
|
||||
).view_tag == it->first.return_view_tag, false, "Incorrect view tag detected in protocol_tx"
|
||||
);
|
||||
|
||||
// Verify the anchor encrypted
|
||||
CHECK_AND_ASSERT_MES(
|
||||
boost::get<cryptonote::txout_to_carrot_v1>(
|
||||
b.protocol_tx.vout[output_idx].target
|
||||
).encrypted_janus_anchor == it->first.return_anchor_enc, false, "Incorrect anchor detected in protocol_tx"
|
||||
);
|
||||
}
|
||||
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
// Validate create_coin transaction outputs
|
||||
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_data;
|
||||
|
||||
for (const auto& tx_pair : txs) {
|
||||
const transaction& tx = tx_pair.first;
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
create_token_data.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& ct_data : create_token_data) {
|
||||
// Verify the output key
|
||||
crypto::public_key out_key;
|
||||
cryptonote::get_output_public_key(b.protocol_tx.vout[output_idx], out_key);
|
||||
CHECK_AND_ASSERT_MES(out_key == ct_data.first.return_address, false, "Incorrect CREATE_TOKEN output key detected in protocol_tx");
|
||||
|
||||
// Verify the return pubkey
|
||||
if (b.protocol_tx.vout.size() > 1) {
|
||||
const auto additional_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.protocol_tx.extra);
|
||||
CHECK_AND_ASSERT_MES(additional_pubkeys.size() > output_idx, false, "Missing CREATE_TOKEN return pubkey detected in protocol_tx");
|
||||
CHECK_AND_ASSERT_MES(additional_pubkeys[output_idx] == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN return pubkey detected in protocol_tx");
|
||||
} else {
|
||||
const auto main_pubkey = cryptonote::get_tx_pub_key_from_extra(b.protocol_tx.extra);
|
||||
CHECK_AND_ASSERT_MES(main_pubkey == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN return pubkey detected in protocol_tx");
|
||||
}
|
||||
|
||||
// Verify the correct metadata type is provided
|
||||
CHECK_AND_ASSERT_MES(ct_data.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
|
||||
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data.second.token);
|
||||
|
||||
// Verify the output amount
|
||||
uint64_t hi, lo;
|
||||
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
|
||||
lo = mul128(token.supply, COIN, &hi);
|
||||
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == lo, false, "Incorrect CREATE_TOKEN output amount detected in protocol_tx");
|
||||
|
||||
// Verify the output asset type (should be the new asset type from protocol_tx_data)
|
||||
std::string expected_asset_type = "sal" + ct_data.second.asset_type;
|
||||
std::string out_asset_type;
|
||||
cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type);
|
||||
CHECK_AND_ASSERT_MES(out_asset_type == expected_asset_type, false, "Incorrect CREATE_TOKEN output asset_type detected in protocol_tx");
|
||||
|
||||
// Validate custom asset type format
|
||||
CHECK_AND_ASSERT_MES(cryptonote::is_valid_custom_asset_type(out_asset_type), false, "CREATE_TOKEN asset type is invalid");
|
||||
|
||||
// Verify the view tag
|
||||
CHECK_AND_ASSERT_MES(
|
||||
boost::get<cryptonote::txout_to_carrot_v1>(
|
||||
b.protocol_tx.vout[output_idx].target
|
||||
).view_tag == ct_data.first.return_view_tag, false, "Incorrect CREATE_TOKEN view tag detected in protocol_tx"
|
||||
);
|
||||
|
||||
// Verify the anchor encrypted
|
||||
CHECK_AND_ASSERT_MES(
|
||||
boost::get<cryptonote::txout_to_carrot_v1>(
|
||||
b.protocol_tx.vout[output_idx].target
|
||||
).encrypted_janus_anchor == ct_data.first.return_anchor_enc, false, "Incorrect CREATE_TOKEN anchor detected in protocol_tx"
|
||||
);
|
||||
|
||||
output_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the yield and audit payouts into an iterable vector
|
||||
std::vector<std::pair<yield_tx_info, uint64_t>> payouts{yield_payouts};
|
||||
@@ -1866,6 +2028,15 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
b.timestamp = median_ts;
|
||||
}
|
||||
|
||||
// Verify that we aren't mixing Carrot and non-Carrot
|
||||
if (b.major_version >= HF_VERSION_CARROT && !miner_address.m_is_carrot) {
|
||||
LOG_ERROR("mining to CryptoNote wallet address, but Carrot wallet address is required");
|
||||
return false;
|
||||
} else if (b.major_version < HF_VERSION_CARROT && miner_address.m_is_carrot) {
|
||||
LOG_ERROR("mining to Carrot wallet address, but Carrot isn't supported yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, uint64_t> circ_supply = get_db().get_circulating_supply();
|
||||
|
||||
// Check if we are supposed to be obtaining PRs from the Oracle
|
||||
@@ -1889,52 +2060,112 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
|
||||
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
|
||||
|
||||
std::vector<cryptonote::protocol_data_entry> protocol_entries;
|
||||
size_t txs_weight;
|
||||
uint64_t fee;
|
||||
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
|
||||
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_entries;
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, create_token_entries, b.major_version))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the protocol transaction entries for CREATE_TOKEN TXs
|
||||
if (b.major_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
for (const auto& ct_data_entry: create_token_entries) {
|
||||
CHECK_AND_ASSERT_MES(ct_data_entry.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
|
||||
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data_entry.second.token);
|
||||
cryptonote::protocol_data_entry entry;
|
||||
uint64_t hi, lo;
|
||||
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
|
||||
CHECK_AND_ASSERT_MES(token.supply > 0, false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
|
||||
lo = mul128(token.supply, COIN, &hi);
|
||||
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
|
||||
entry.amount_burnt = cryptonote::get_token_creation_price(ct_data_entry.second.asset_type);
|
||||
entry.amount_minted = lo;
|
||||
entry.amount_slippage_limit = 0;
|
||||
entry.source_asset = "SAL1";
|
||||
entry.destination_asset = "sal" + ct_data_entry.second.asset_type;
|
||||
entry.type = cryptonote::transaction_type::CREATE_TOKEN;
|
||||
entry.return_address = ct_data_entry.first.return_address;
|
||||
entry.return_pubkey = ct_data_entry.first.return_pubkey;
|
||||
entry.return_view_tag = ct_data_entry.first.return_view_tag;
|
||||
entry.return_anchor_enc = ct_data_entry.first.return_anchor_enc;
|
||||
entry.is_carrot = true;
|
||||
entry.origin_height = height;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if there are any matured YIELD TXs
|
||||
uint64_t yield_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
uint64_t start_height = (height > yield_lock_period) ? height - yield_lock_period - 1 : 0;
|
||||
|
||||
std::vector<cryptonote::protocol_data_entry> protocol_entries;
|
||||
cryptonote::yield_block_info ybi_matured;
|
||||
bool ok = get_ybi_entry(start_height, ybi_matured);
|
||||
if (ok && ybi_matured.locked_coins_this_block > 0) {
|
||||
|
||||
// Iterate over the cached data for block yield, calculating the yield payouts due
|
||||
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
|
||||
if (!calculate_yield_payouts(start_height, yield_payouts)) {
|
||||
LOG_ERROR("Failed to obtain yield payout information - aborting");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Work out what the asset_type should be based on height of submission
|
||||
uint8_t hf_submitted = m_hardfork->get_ideal_version(start_height);
|
||||
|
||||
// Create the protocol_metadata entries here
|
||||
for (const auto& yield_entry: yield_payouts) {
|
||||
cryptonote::protocol_data_entry entry;
|
||||
entry.amount_burnt = yield_entry.second;
|
||||
entry.amount_minted = 0;
|
||||
entry.amount_slippage_limit = 0;
|
||||
if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
entry.source_asset = "SAL1";
|
||||
entry.destination_asset = "SAL1";
|
||||
} else {
|
||||
entry.source_asset = "SAL";
|
||||
entry.destination_asset = "SAL";
|
||||
// Iterate over the cached data for block yield, calculating the yield payouts due
|
||||
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
|
||||
std::vector<std::pair<yield_tx_info_carrot, uint64_t>> carrot_yield_payouts;
|
||||
if (hf_submitted >= HF_VERSION_CARROT) {
|
||||
if (!calculate_yield_payouts(start_height, carrot_yield_payouts)) {
|
||||
LOG_ERROR("Failed to obtain yield payout information - aborting");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!calculate_yield_payouts(start_height, yield_payouts)) {
|
||||
LOG_ERROR("Failed to obtain yield payout information - aborting");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the protocol_metadata entries here
|
||||
if (!carrot_yield_payouts.empty()) {
|
||||
for (const auto& yield_entry: carrot_yield_payouts) {
|
||||
cryptonote::protocol_data_entry entry;
|
||||
entry.amount_burnt = yield_entry.second;
|
||||
entry.amount_minted = 0;
|
||||
entry.amount_slippage_limit = 0;
|
||||
if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
entry.source_asset = "SAL1";
|
||||
entry.destination_asset = "SAL1";
|
||||
} else {
|
||||
entry.source_asset = "SAL";
|
||||
entry.destination_asset = "SAL";
|
||||
}
|
||||
entry.return_address = yield_entry.first.return_address;
|
||||
entry.type = cryptonote::transaction_type::STAKE;
|
||||
entry.return_pubkey = yield_entry.first.return_pubkey;
|
||||
entry.origin_height = start_height;
|
||||
entry.return_view_tag = yield_entry.first.return_view_tag;
|
||||
entry.return_anchor_enc = yield_entry.first.return_anchor_enc;
|
||||
entry.is_carrot = true;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
} else {
|
||||
for (const auto& yield_entry: yield_payouts) {
|
||||
cryptonote::protocol_data_entry entry;
|
||||
entry.amount_burnt = yield_entry.second;
|
||||
entry.amount_minted = 0;
|
||||
entry.amount_slippage_limit = 0;
|
||||
if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
entry.source_asset = "SAL1";
|
||||
entry.destination_asset = "SAL1";
|
||||
} else {
|
||||
entry.source_asset = "SAL";
|
||||
entry.destination_asset = "SAL";
|
||||
}
|
||||
entry.return_address = yield_entry.first.return_address;
|
||||
entry.type = cryptonote::transaction_type::STAKE;
|
||||
entry.P_change = yield_entry.first.P_change;
|
||||
entry.return_pubkey = yield_entry.first.return_pubkey;
|
||||
entry.origin_height = start_height;
|
||||
entry.is_carrot = false;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
entry.return_address = yield_entry.first.return_address;
|
||||
entry.type = cryptonote::transaction_type::STAKE;
|
||||
entry.P_change = yield_entry.first.P_change;
|
||||
entry.return_pubkey = yield_entry.first.return_pubkey;
|
||||
entry.origin_height = start_height;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1978,6 +2209,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
entry.P_change = audit_entry.first.P_change;
|
||||
entry.return_pubkey = audit_entry.first.return_pubkey;
|
||||
entry.origin_height = matured_audit_height;
|
||||
entry.is_carrot = false;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
@@ -2004,7 +2236,6 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
*/
|
||||
|
||||
// Time to construct the protocol_tx
|
||||
uint64_t protocol_fee = 0;
|
||||
address_parse_info treasury_address_info;
|
||||
ok = cryptonote::get_account_address_from_str(treasury_address_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS);
|
||||
CHECK_AND_ASSERT_MES(ok, false, "Failed to obtain treasury address info");
|
||||
@@ -2355,7 +2586,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||
|
||||
if(!prevalidate_protocol_transaction(b, bei.height, hf_version))
|
||||
{
|
||||
MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has incorrect miner transaction.");
|
||||
MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has incorrect protocol transaction.");
|
||||
bvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
@@ -2835,7 +3066,7 @@ bool Blockchain::get_pricing_record(oracle::pricing_record &pr, std::map<std::st
|
||||
|
||||
bool r = false;
|
||||
const uint64_t height = get_current_blockchain_height();
|
||||
const uint8_t hf_version = m_hardfork->get_current_version();
|
||||
// const uint8_t hf_version = m_hardfork->get_current_version();
|
||||
|
||||
epee::net_utils::http::http_simple_client http_client;
|
||||
COMMAND_RPC_GET_PRICING_RECORD::request req = AUTO_VAL_INIT(req);
|
||||
@@ -3608,8 +3839,8 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
||||
*/
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
// from v11, force the new SalviumOne RCT data
|
||||
if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
// from v10, force the new SalviumOne RCT data
|
||||
if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::CREATE_TOKEN || tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) {
|
||||
MERROR_VER("SalviumOne data required after v" + std::to_string(HF_VERSION_CARROT));
|
||||
tvc.m_invalid_output = true;
|
||||
@@ -3664,6 +3895,100 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
const uint8_t hf_version = m_hardfork->get_current_version();
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
|
||||
// Get the valid tokens according to the DB
|
||||
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
|
||||
return true;
|
||||
} else if (
|
||||
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
|
||||
tx.type == cryptonote::transaction_type::ROLLUP ||
|
||||
tx.type == cryptonote::transaction_type::STAKE
|
||||
) {
|
||||
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
|
||||
MERROR_VER("Invalid source/dest asset type for CREATE_TOKEN / ROLLUP / STAKE - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (tx.type == cryptonote::transaction_type::BURN) {
|
||||
if ((tx.source_asset_type != "SAL" && tx.source_asset_type != "SAL1") || tx.destination_asset_type != tx.source_asset_type) {
|
||||
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected (matching) SAL/SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
|
||||
if (tx.source_asset_type != tx.destination_asset_type) {
|
||||
MERROR_VER("Mismatched asset types for TRANSFER - provided source asset: " << tx.source_asset_type << ", provided destination asset: " << tx.destination_asset_type << ", expected them to match");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
} else if (!(tx.source_asset_type == "SAL1" || mapTokens.count(tx.source_asset_type))) {
|
||||
MERROR_VER("Invalid source asset type for TRANSFER - provided source asset: " << tx.source_asset_type << ", expected valid token type or SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
} else if (tx.destination_asset_type != "SAL1" && !mapTokens.count(tx.destination_asset_type)) {
|
||||
MERROR_VER("Invalid destination asset type for TRANSFER - provided destination asset: " << tx.destination_asset_type << ", expected valid token type or SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::CONVERT) {
|
||||
MERROR_VER("AUDIT and CONVERT transaction types are not allowed in this hardfork version:" << std::to_string(HF_VERSION_ENABLE_TOKENS));
|
||||
} else {
|
||||
MERROR_VER("Unknown transaction type: " << tx.type << ".");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
|
||||
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL1" << ", provided source asset: " << tx.source_asset_type << ", expected SAL1");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
// }
|
||||
} else if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
// protocol and miner txs don't have source and destination asset types
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
|
||||
return true;
|
||||
} else if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL" && tx.destination_asset_type == "SAL", false, "wrong source/destination asset type: provided source asset " << tx.source_asset_type << " expected SAL and provided destination asset " << tx.destination_asset_type << " expected SAL");
|
||||
} else if (tx.type == cryptonote::transaction_type::BURN) {
|
||||
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "BURN") {
|
||||
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL1 and BURN respectively");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1" && tx.destination_asset_type == "SAL1", false, "wrong source/destination asset type: provided source asset: " << tx.source_asset_type << " expected SAL1 and provided destination asset: " << tx.destination_asset_type << " expected SAL1");
|
||||
}
|
||||
} else {
|
||||
if (tx.type == cryptonote::transaction_type::BURN) {
|
||||
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "BURN") {
|
||||
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL and BURN respectively");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//only SAL existed before hf 6
|
||||
else{
|
||||
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "SAL") {
|
||||
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL" << ", provided source asset: " << tx.source_asset_type << ", expected SAL");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verification_context &tvc) const
|
||||
{
|
||||
@@ -3683,15 +4008,34 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
|
||||
}
|
||||
|
||||
// Check for v1 TXs - genesis block protocol_tx exception required!
|
||||
if (tx.version == 1 && epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(tx)) == "4f78ff511e860acd03138737a71505eb62eb78b620e180e58c8e13ed0e1e3e19") {
|
||||
if (tx.version == 1) {
|
||||
MERROR("v1 TXs are not permitted");
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the order of checking now, because we're using >=
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.type == cryptonote::transaction_type::TRANSFER ||
|
||||
tx.type == cryptonote::transaction_type::RETURN ||
|
||||
tx.type == cryptonote::transaction_type::ROLLUP ||
|
||||
tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
if (tx.version < TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
}
|
||||
} else {
|
||||
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
}
|
||||
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
|
||||
// Allowed multiple asset_types for `vout` entries
|
||||
} else {
|
||||
// No mixing of asset_types permitted - verify here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After v2 allow N-out TXs for TRANSFER ONLY
|
||||
if (hf_version >= HF_VERSION_ENABLE_N_OUTS) {
|
||||
if (hf_version >= HF_VERSION_ENABLE_N_OUTS && hf_version < HF_VERSION_CARROT) {
|
||||
if (tx.version >= TRANSACTION_VERSION_N_OUTS && tx.type != cryptonote::transaction_type::TRANSFER) {
|
||||
MERROR("N-out TXs are only permitted for TRANSFER TX type");
|
||||
tvc.m_version_mismatch = true;
|
||||
@@ -3699,6 +4043,22 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
|
||||
}
|
||||
}
|
||||
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
|
||||
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_ENABLE_TOKENS));
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hf_version == HF_VERSION_CARROT) {
|
||||
if (tx.version != TRANSACTION_VERSION_CARROT) {
|
||||
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_CARROT));
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure CONVERT TXs are disabled until we are ready - belt and braces!
|
||||
if (hf_version < HF_VERSION_ENABLE_CONVERT) {
|
||||
if (tx.type == cryptonote::transaction_type::CONVERT) {
|
||||
@@ -3708,6 +4068,45 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure CREATE_TOKEN TXs are disabled until we are ready - belt and braces!
|
||||
if (hf_version < HF_VERSION_ENABLE_TOKENS) {
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
MERROR("CREATE_TOKEN TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
if (tx.type == cryptonote::transaction_type::ROLLUP) {
|
||||
MERROR("ROLLUP TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
|
||||
tvc.m_version_mismatch = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
// Make sure we are supposed to accept AUDIT txs at this point
|
||||
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(m_nettype).AUDIT_HARD_FORKS;
|
||||
CHECK_AND_ASSERT_MES(audit_hard_forks.find(hf_version) != audit_hard_forks.end(), false, "trying to audit outside an audit fork");
|
||||
std::string expected_asset_type = audit_hard_forks.at(hf_version).second.first;
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == expected_asset_type, false, "trying to spend " << tx.source_asset_type << " coins in an AUDIT TX");
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
// Check that the specific asset_type being created isn't already in our list of tokens
|
||||
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
|
||||
std::string asset_type = "sal" + tx.token_metadata.asset_type;
|
||||
if (mapTokens.count(asset_type)) {
|
||||
MERROR("TX attempting to create '" + asset_type + "', which already exists");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
// Validate token metadata //! check */
|
||||
CHECK_AND_ASSERT_MES(tx.token_metadata.token.type() == typeid(cryptonote::sal_token_t), false, "Invalid CREATE_TOKEN metadata type");
|
||||
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(tx.token_metadata.token);
|
||||
CHECK_AND_ASSERT_MES(token.supply > 0 && token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN");
|
||||
// Validate the amount burnt matches the token creation price
|
||||
CHECK_AND_ASSERT_MES(tx.amount_burnt == cryptonote::get_token_creation_price(tx.token_metadata.asset_type), false, "Invalid fee paid for CREATE_TOKEN");
|
||||
}
|
||||
|
||||
// Check for invalid TX types
|
||||
if (tx.type == cryptonote::transaction_type::UNSET || tx.type > cryptonote::transaction_type::MAX) {
|
||||
MERROR("TX type `" + std::to_string(tx.type) + "' is not supported");
|
||||
@@ -3742,7 +4141,7 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
|
||||
bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys, const uint8_t &hf_version)
|
||||
{
|
||||
PERF_TIMER(expand_transaction_2);
|
||||
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3, false, "Transaction version is not 2/3");
|
||||
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3 || tx.version == 4 || tx.version == 5, false, "Transaction version is not 2/3/5");
|
||||
|
||||
rct::rctSig &rv = tx.rct_signatures;
|
||||
|
||||
@@ -3887,11 +4286,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||
const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
|
||||
if (in_to_key.amount == 0)
|
||||
{
|
||||
// always consider rct inputs mixable. Even if there's not enough rct
|
||||
// inputs on the chain to mix with, this is going to be the case for
|
||||
// just a few blocks right after the fork at most
|
||||
++n_mixable;
|
||||
}
|
||||
// don't always consider rct inputs mixable!
|
||||
uint64_t n_outputs = m_db->get_num_outputs_of_asset_type(in_to_key.asset_type);
|
||||
if (n_outputs <= min_mixin)
|
||||
++n_unmixable;
|
||||
else
|
||||
++n_mixable;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
|
||||
@@ -3941,14 +4342,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||
return false;
|
||||
}
|
||||
|
||||
// min/max tx version based on HF, and we accept v1 txes if having a non mixable
|
||||
const size_t max_tx_version = (hf_version >= HF_VERSION_ENABLE_N_OUTS) ? TRANSACTION_VERSION_N_OUTS : TRANSACTION_VERSION_2_OUTS;
|
||||
if (tx.version > max_tx_version)
|
||||
{
|
||||
MERROR_VER("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version);
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
const size_t min_tx_version = (n_unmixable > 0 ? 1 : 2);
|
||||
if (tx.version < min_tx_version)
|
||||
{
|
||||
@@ -3979,20 +4372,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::AUDIT) {
|
||||
// Make sure we are supposed to accept AUDIT txs at this point
|
||||
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(m_nettype).AUDIT_HARD_FORKS;
|
||||
CHECK_AND_ASSERT_MES(audit_hard_forks.find(hf_version) != audit_hard_forks.end(), false, "trying to audit outside an audit fork");
|
||||
std::string expected_asset_type = audit_hard_forks.at(hf_version).second.first;
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == expected_asset_type, false, "trying to spend " << tx.source_asset_type << " coins in an AUDIT TX");
|
||||
} else {
|
||||
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size());
|
||||
std::vector < uint64_t > results;
|
||||
results.resize(tx.vin.size(), 0);
|
||||
@@ -4387,8 +4766,7 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
|
||||
//------------------------------------------------------------------
|
||||
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
|
||||
{
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const uint64_t db_height = m_db->height();
|
||||
// const uint64_t db_height = m_db->height();
|
||||
|
||||
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
|
||||
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
|
||||
@@ -4539,7 +4917,7 @@ bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vecto
|
||||
// Get the AUDIT TX information for matured staked coins
|
||||
std::vector<cryptonote::yield_tx_info> audit_entries;
|
||||
// We get the audit_tx_info from the block where they entered the chain
|
||||
int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries);
|
||||
m_db->get_audit_tx_info(start_height, audit_entries);
|
||||
if (!audit_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -4582,6 +4960,72 @@ bool Blockchain::get_abi_entry(const uint64_t height, cryptonote::audit_block_in
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info_carrot, uint64_t>>& yield_container)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
// Clear the yield payout amounts
|
||||
yield_container.clear();
|
||||
|
||||
// Get the YIELD TX information for matured staked coins
|
||||
std::vector<cryptonote::yield_tx_info_carrot> yield_entries;
|
||||
// We get the yield_tx_info from the block _before_ they started to accrue yield
|
||||
m_db->get_carrot_yield_tx_info(start_height, yield_entries);
|
||||
if (!yield_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
LOG_ERROR("calculate_yield_payouts() called for carrot, but no yield TXs found at height " << start_height << " - aborting");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build a blacklist of staking TXs _not_ to pay out for
|
||||
const std::set<std::string> txs_blacklist = {};
|
||||
|
||||
// Get the YBI information for the 21,600 blocks that the matured TX(s), we can calculate yield
|
||||
for (const auto& entry: yield_entries) {
|
||||
// Check to see if this entry is in the blacklist
|
||||
if (txs_blacklist.find(epee::string_tools::pod_to_hex(entry.tx_hash)) != txs_blacklist.end()) {
|
||||
LOG_ERROR("calculate_yield_payouts() found blacklisted TX at height " << start_height << " - skipping payout");
|
||||
continue;
|
||||
}
|
||||
yield_container.emplace_back(std::make_pair(entry, entry.locked_coins));
|
||||
}
|
||||
|
||||
// Iterate over the cached yield_block_info data
|
||||
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
for (uint64_t idx = start_height+1; idx <= start_height + yield_lock_period; ++idx) {
|
||||
// Get the next block
|
||||
if (m_yield_block_info_cache.count(idx) == 0) {
|
||||
LOG_ERROR("failed to locate yield information for block height " << idx <<" - aborting");
|
||||
return false;
|
||||
}
|
||||
yield_block_info ybi = m_yield_block_info_cache[idx];
|
||||
if (ybi.slippage_total_this_block == 0) continue;
|
||||
if (ybi.locked_coins_tally == 0) continue;
|
||||
|
||||
boost::multiprecision::int128_t slippage_128 = ybi.slippage_total_this_block;
|
||||
|
||||
// Get the total number of coins locked at this height
|
||||
boost::multiprecision::int128_t locked_total_128 = ybi.locked_coins_tally;
|
||||
|
||||
// Iterate over the yield_container, adding each proportion of the yield
|
||||
for (auto& entry: yield_container) {
|
||||
boost::multiprecision::int128_t locked_coins_128 = entry.first.locked_coins;
|
||||
boost::multiprecision::int128_t yield_128 = (slippage_128 * locked_coins_128) / locked_total_128;
|
||||
uint64_t yield_u64 = boost::numeric_cast<uint64_t>(yield_128);
|
||||
|
||||
if (entry.second + yield_u64 < entry.second) {
|
||||
throw std::overflow_error("uint64_t addition overflow");
|
||||
}
|
||||
entry.second += yield_u64;
|
||||
}
|
||||
}
|
||||
|
||||
// Return success to caller
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_container)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
@@ -4592,7 +5036,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
|
||||
// Get the YIELD TX information for matured staked coins
|
||||
std::vector<cryptonote::yield_tx_info> yield_entries;
|
||||
// We get the yield_tx_info from the block _before_ they started to accrue yield
|
||||
int yield_tx_result = m_db->get_yield_tx_info(start_height, yield_entries);
|
||||
m_db->get_yield_tx_info(start_height, yield_entries);
|
||||
if (!yield_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -4632,10 +5076,14 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
|
||||
|
||||
// Iterate over the yield_container, adding each proportion of the yield
|
||||
for (auto& entry: yield_container) {
|
||||
|
||||
boost::multiprecision::int128_t locked_coins_128 = entry.first.locked_coins;
|
||||
boost::multiprecision::int128_t yield_128 = (slippage_128 * locked_coins_128) / locked_total_128;
|
||||
entry.second += yield_128.convert_to<uint64_t>();
|
||||
uint64_t yield_u64 = boost::numeric_cast<uint64_t>(yield_128);
|
||||
|
||||
if (entry.second + yield_u64 < entry.second) {
|
||||
throw std::overflow_error("uint64_t addition overflow");
|
||||
}
|
||||
entry.second += yield_u64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4755,6 +5203,18 @@ bool Blockchain::get_ybi_entry(const uint64_t height, cryptonote::yield_block_in
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::is_tx_paid_for(const cryptonote::transaction& tx)
|
||||
{
|
||||
// Is it a TRANSFER?
|
||||
if (tx.type != cryptonote::transaction_type::TRANSFER)
|
||||
return true;
|
||||
// Is is a TOKEN?
|
||||
if (!cryptonote::is_asset_type_token(tx.source_asset_type))
|
||||
return true;
|
||||
// Has the TRANSFER TOKEN been paid for?
|
||||
return m_db->is_tx_paid_for(tx);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
//TODO: revisit, has changed a bit on upstream
|
||||
bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const
|
||||
{
|
||||
@@ -5129,6 +5589,16 @@ leave:
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check the TX type
|
||||
tx_verification_context tvc;
|
||||
if (!check_tx_type_and_version(tx, tvc)) {
|
||||
MERROR("Block with id: " << id << " failed to pass transaction type and version check for transaction id: " << tx_id);
|
||||
bvc.m_verifivation_failed = true;
|
||||
return_tx_to_pool(txs);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
TIME_MEASURE_FINISH(cc);
|
||||
t_checktx += cc;
|
||||
fee_summary += fee;
|
||||
@@ -5161,7 +5631,7 @@ leave:
|
||||
TIME_MEASURE_FINISH(vmt);
|
||||
|
||||
TIME_MEASURE_START(vpt);
|
||||
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version()))
|
||||
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version(), txs))
|
||||
{
|
||||
MERROR_VER("Block with id: " << id << " has incorrect protocol transaction");
|
||||
bvc.m_verifivation_failed = true;
|
||||
@@ -5205,7 +5675,7 @@ leave:
|
||||
|
||||
// Update the YBI cache data
|
||||
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
|
||||
// uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
|
||||
if (new_height > yield_lock_period) {
|
||||
if (m_yield_block_info_cache.count(new_height - yield_lock_period - 2) != 0) {
|
||||
m_yield_block_info_cache.erase(new_height - yield_lock_period - 2);
|
||||
@@ -5323,7 +5793,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
|
||||
const uint64_t db_height = m_db->height();
|
||||
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
|
||||
|
||||
const uint8_t hf_version = get_current_hard_fork_version();
|
||||
// const uint8_t hf_version = get_current_hard_fork_version();
|
||||
|
||||
if (db_height < CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE)
|
||||
return block_weight;
|
||||
@@ -5970,7 +6440,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
} while(0); \
|
||||
|
||||
// generate sorted tables for all amounts and absolute offsets
|
||||
size_t tx_index = 0, block_index = 0;
|
||||
size_t tx_index = 0; //, block_index = 0;
|
||||
for (const auto &entry : blocks_entry)
|
||||
{
|
||||
if (m_cancel)
|
||||
@@ -6041,7 +6511,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
|
||||
}
|
||||
}
|
||||
++block_index;
|
||||
//++block_index;
|
||||
}
|
||||
|
||||
// sort and remove duplicate absolute_offsets in offset_map
|
||||
@@ -6313,7 +6783,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
|
||||
static const char expected_block_hashes_hash[] = "7e6c9efd949afb36bce062bbb720605f0dd67b03c0124e5167c527fa59c15ce3";
|
||||
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
|
||||
{
|
||||
if (get_checkpoints == nullptr || !m_fast_sync)
|
||||
|
||||
@@ -726,6 +726,19 @@ namespace cryptonote
|
||||
*/
|
||||
bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc) const;
|
||||
|
||||
/**
|
||||
* @brief check a transaction's input/output/TX asset types conform to current standards
|
||||
*
|
||||
* This function checks, for example at the time of this writing, that
|
||||
* the asset types are supported by the given TX for the current HF version on-chain.
|
||||
*
|
||||
* @param tx the transaction to check the asset types of
|
||||
* @param tvc returned info about tx verification
|
||||
*
|
||||
* @return false if the TX version and/or type is unsupported, otherwise true
|
||||
*/
|
||||
bool check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const;
|
||||
|
||||
/**
|
||||
* @brief check that a transaction's version & type conforms to current standards
|
||||
*
|
||||
@@ -1175,6 +1188,13 @@ namespace cryptonote
|
||||
*/
|
||||
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_payouts);
|
||||
|
||||
/**
|
||||
* calculate the yield payouts
|
||||
*
|
||||
* @return TRUE if the payouts were calculated successfully, FALSE otherwise
|
||||
*/
|
||||
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info_carrot, uint64_t>>& yield_payouts);
|
||||
|
||||
/**
|
||||
* @brief get the ABI entry for a particular height from the cache
|
||||
*
|
||||
@@ -1226,6 +1246,8 @@ namespace cryptonote
|
||||
*/
|
||||
bool validate_ybi_cache();
|
||||
|
||||
bool is_tx_paid_for(const cryptonote::transaction& tx);
|
||||
|
||||
#ifndef IN_UNIT_TESTS
|
||||
private:
|
||||
#endif
|
||||
@@ -1557,10 +1579,11 @@ namespace cryptonote
|
||||
* @param b the block containing the miner transaction to be validated
|
||||
* @param height the blockchain's weight
|
||||
* @param version hard fork version for that transaction
|
||||
* @param txs create_coin transactions in the block
|
||||
*
|
||||
* @return false if anything is found wrong with the protocol transaction, otherwise true
|
||||
*/
|
||||
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version);
|
||||
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs = std::vector<std::pair<transaction, blobdata>>());
|
||||
|
||||
/**
|
||||
* @brief reverts the blockchain to its previous state following a failed switch
|
||||
|
||||
@@ -843,7 +843,10 @@ namespace cryptonote
|
||||
bad_semantics_txes_lock.unlock();
|
||||
|
||||
uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
const size_t max_tx_version = (hf_version >= HF_VERSION_ENABLE_N_OUTS) ? TRANSACTION_VERSION_N_OUTS : TRANSACTION_VERSION_2_OUTS;
|
||||
const size_t max_tx_version = hf_version >= HF_VERSION_ENABLE_TOKENS ? TRANSACTION_VERSION_ENABLE_TOKENS :
|
||||
hf_version >= HF_VERSION_CARROT ? TRANSACTION_VERSION_CARROT :
|
||||
hf_version >= HF_VERSION_ENABLE_N_OUTS ? TRANSACTION_VERSION_N_OUTS :
|
||||
TRANSACTION_VERSION_2_OUTS;
|
||||
if (tx.version == 0 || tx.version > max_tx_version)
|
||||
{
|
||||
// v2 is the latest one we know
|
||||
@@ -902,6 +905,7 @@ namespace cryptonote
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block)
|
||||
{
|
||||
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
bool ret = true;
|
||||
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
|
||||
{
|
||||
@@ -923,7 +927,6 @@ namespace cryptonote
|
||||
if (tx_info[n].tx->version < 2)
|
||||
continue;
|
||||
const rct::rctSig &rv = tx_info[n].tx->rct_signatures;
|
||||
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) {
|
||||
MERROR_VER("Invalid RCT type provided");
|
||||
@@ -948,7 +951,11 @@ namespace cryptonote
|
||||
tx_info[n].result = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool need_rollup = false;
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
|
||||
}
|
||||
switch (rv.type) {
|
||||
case rct::RCTTypeNull:
|
||||
// coinbase should not come here, so we reject for all other types
|
||||
@@ -963,7 +970,8 @@ namespace cryptonote
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
|
||||
0
|
||||
0,
|
||||
need_rollup
|
||||
))
|
||||
{
|
||||
MERROR_VER("rct signature semantics check failed");
|
||||
@@ -1020,7 +1028,7 @@ namespace cryptonote
|
||||
}
|
||||
if (!rvv.empty())
|
||||
{
|
||||
LOG_PRINT_L1("One transaction among this group has bad semantics, verifying one at a time");
|
||||
LOG_PRINT_L1("Verifying transactions one at a time");
|
||||
ret = false;
|
||||
for (size_t n = 0; n < tx_info.size(); ++n)
|
||||
{
|
||||
@@ -1028,12 +1036,19 @@ namespace cryptonote
|
||||
continue;
|
||||
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus && tx_info[n].tx->rct_signatures.type != rct::RCTTypeFullProofs && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumZero && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumOne)
|
||||
continue;
|
||||
bool need_rollup = false;
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
|
||||
}
|
||||
if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures,
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
|
||||
0
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CREATE_TOKEN ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::ROLLUP ? tx_info[n].tx->amount_burnt :
|
||||
0,
|
||||
need_rollup
|
||||
))
|
||||
{
|
||||
set_semantics_failed(tx_info[n].tx_hash);
|
||||
@@ -1247,6 +1262,13 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !m_blockchain_storage.is_tx_paid_for(tx))
|
||||
{
|
||||
MERROR_VER("tx has not been paid for by ROLLUP");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -346,14 +346,30 @@ namespace cryptonote
|
||||
|
||||
// Clear the TX contents
|
||||
tx.set_null();
|
||||
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? 3 : 2;
|
||||
bool carrot_found = false;
|
||||
bool noncarrot_found = false;
|
||||
tx.type = cryptonote::transaction_type::PROTOCOL;
|
||||
|
||||
// Force the TX type to 2
|
||||
tx.version = 2;
|
||||
// Scan the protocol_data to make sure all or none are Carrot
|
||||
for (auto const& entry: protocol_data) {
|
||||
if (entry.is_carrot) carrot_found = true;
|
||||
else noncarrot_found = true;
|
||||
}
|
||||
|
||||
const bool do_carrot = hard_fork_version >= HF_VERSION_CARROT;
|
||||
if (do_carrot)
|
||||
if (carrot_found && noncarrot_found) {
|
||||
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
|
||||
return false;
|
||||
}
|
||||
if (carrot_found && hard_fork_version < HF_VERSION_CARROT) {
|
||||
LOG_ERROR("Carrot outputs found in CryptoNote protocol transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (carrot_found || (!noncarrot_found && hard_fork_version >= HF_VERSION_CARROT))
|
||||
{
|
||||
// Ensure the TX version is correct
|
||||
tx.version = TRANSACTION_VERSION_CARROT;
|
||||
try
|
||||
{
|
||||
// Create a vector of enotes
|
||||
@@ -362,34 +378,21 @@ namespace cryptonote
|
||||
|
||||
// Iterate over the protocol_data we received, creating an enote for each entry
|
||||
for (auto const& entry: protocol_data) {
|
||||
|
||||
carrot::CarrotDestinationV1 destination;
|
||||
carrot::make_carrot_main_address_v1(entry.P_change,
|
||||
entry.return_address,
|
||||
destination);
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(!destination.is_subaddress,
|
||||
"construct_protocol_tx: subaddress are not allowed in miner transactions");
|
||||
CHECK_AND_ASSERT_THROW_MES(destination.payment_id == carrot::null_payment_id,
|
||||
"construct_protocol_tx: integrated addresses are not allowed in miner transactions");
|
||||
|
||||
LOG_PRINT_L2(((entry.type == cryptonote::transaction_type::STAKE) ? "Yield TX payout submitted " : "Audit TX payout submitted ") << entry.amount_burnt << entry.source_asset);
|
||||
|
||||
const carrot::CarrotPaymentProposalV1 payment_proposal{
|
||||
.destination = destination,
|
||||
.amount = entry.amount_burnt,
|
||||
.asset_type = "SAL1",
|
||||
.randomness = carrot::gen_janus_anchor()
|
||||
};
|
||||
|
||||
// Build the proposal
|
||||
carrot::CarrotCoinbaseEnoteV1 e;
|
||||
get_coinbase_output_proposal_v1(payment_proposal, height, e);
|
||||
e.onetime_address = entry.return_address;
|
||||
// amount_minted for CREATE_TOKEN, amount_burnt for STAKE/AUDIT
|
||||
e.amount = (entry.type == cryptonote::transaction_type::CREATE_TOKEN) ? entry.amount_minted : entry.amount_burnt;
|
||||
e.asset_type = entry.destination_asset;
|
||||
e.view_tag = entry.return_view_tag;
|
||||
e.anchor_enc = entry.return_anchor_enc;
|
||||
e.block_index = height;
|
||||
memcpy(e.enote_ephemeral_pubkey.data, entry.return_pubkey.data, sizeof(crypto::public_key));
|
||||
enotes.push_back(e);
|
||||
}
|
||||
|
||||
tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL, height);
|
||||
tx.amount_burnt = 0;
|
||||
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
|
||||
tx.invalidate_hashes();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -410,14 +413,29 @@ namespace cryptonote
|
||||
std::vector<crypto::public_key> additional_tx_public_keys;
|
||||
for (auto const& entry: protocol_data) {
|
||||
if (entry.type == cryptonote::transaction_type::STAKE) {
|
||||
|
||||
// PAYOUT
|
||||
LOG_PRINT_L2("Yield TX payout submitted " << entry.amount_burnt << entry.source_asset);
|
||||
|
||||
if (entry.is_carrot) {
|
||||
tx_out out;
|
||||
out.amount = entry.amount_burnt;
|
||||
out.target = txout_to_carrot_v1 {
|
||||
.key = entry.return_address,
|
||||
.asset_type = entry.destination_asset,
|
||||
.view_tag = entry.return_view_tag,
|
||||
.encrypted_janus_anchor = entry.return_anchor_enc,
|
||||
};
|
||||
|
||||
// Create the TX output for this refund
|
||||
tx_out out;
|
||||
cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out);
|
||||
additional_tx_public_keys.push_back(entry.return_pubkey);
|
||||
tx.vout.push_back(out);
|
||||
additional_tx_public_keys.push_back(entry.return_pubkey);
|
||||
tx.vout.push_back(out);
|
||||
} else {
|
||||
// Create the TX output for this refund
|
||||
tx_out out;
|
||||
cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out);
|
||||
additional_tx_public_keys.push_back(entry.return_pubkey);
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
} else if (entry.type == cryptonote::transaction_type::AUDIT) {
|
||||
// PAYOUT
|
||||
LOG_PRINT_L2("Audit TX payout submitted " << entry.amount_burnt << entry.source_asset);
|
||||
@@ -523,7 +541,7 @@ namespace cryptonote
|
||||
}
|
||||
|
||||
tx = carrot::store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER, height);
|
||||
|
||||
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
|
||||
tx.amount_burnt = stake_reward;
|
||||
tx.invalidate_hashes();
|
||||
}
|
||||
@@ -581,6 +599,7 @@ namespace cryptonote
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENABLE_TOKENS:
|
||||
// SRCG: subtract 20% that will be rewarded to staking users
|
||||
CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero");
|
||||
tx.amount_burnt = amount / 5;
|
||||
|
||||
@@ -68,7 +68,10 @@ namespace cryptonote
|
||||
uint8_t type;
|
||||
crypto::public_key P_change;
|
||||
crypto::public_key return_pubkey;
|
||||
carrot::view_tag_t return_view_tag;
|
||||
carrot::encrypted_janus_anchor_t return_anchor_enc;
|
||||
uint64_t origin_height;
|
||||
bool is_carrot;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "common/perf_timer.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "crypto/duration.h"
|
||||
#include "common/debugging.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "txpool"
|
||||
@@ -266,6 +267,20 @@ namespace cryptonote
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the TX asset types
|
||||
if (!m_blockchain.check_tx_asset_types(tx, tvc)) {
|
||||
LOG_PRINT_L1("Transaction with id= "<< id << " has invalid asset type(s)");
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// HERE BE DRAGONS!!!
|
||||
// Check that CREATE_TOKEN txs are unique in the pool
|
||||
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
|
||||
// TODO: ...scan the existing entries - requires either a registry of CREATE_TOKEN TXs, or to interatively process the pool
|
||||
}
|
||||
// LAND AHOY!!!
|
||||
|
||||
// assume failure during verification steps until success is certain
|
||||
tvc.m_verifivation_failed = true;
|
||||
@@ -1505,6 +1520,10 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that `transfer token` TXs have been paid for before accepting them into a block template
|
||||
if (!m_blockchain.is_tx_paid_for(lazy_tx()))
|
||||
return false;
|
||||
|
||||
//transaction is ok.
|
||||
return true;
|
||||
}
|
||||
@@ -1614,7 +1633,7 @@ namespace cryptonote
|
||||
//---------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------
|
||||
//TODO: investigate whether boolean return is appropriate
|
||||
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
|
||||
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
@@ -1630,7 +1649,6 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5;
|
||||
@@ -1638,6 +1656,15 @@ namespace cryptonote
|
||||
|
||||
LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
|
||||
|
||||
// Store list of tokens being created
|
||||
std::set<std::string> tokens;
|
||||
|
||||
// Get the list of already-created tokens that are in the DB
|
||||
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_blockchain.get_db().get_tokens();
|
||||
for (const auto &entry: mapTokens) {
|
||||
tokens.insert(entry.second.asset_type);
|
||||
}
|
||||
|
||||
LockedTXN lock(m_blockchain.get_db());
|
||||
|
||||
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
|
||||
@@ -1749,6 +1776,49 @@ namespace cryptonote
|
||||
LOG_PRINT_L2(" key images already seen");
|
||||
continue;
|
||||
}
|
||||
if (version < HF_VERSION_CARROT && tx.version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
LOG_PRINT_L2(" is a Carrot transaction - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
if (version >= HF_VERSION_CARROT && tx.version < TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
LOG_PRINT_L2(" is not a Carrot transaction - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for ROLLUP TXs
|
||||
if (tx.type == cryptonote::transaction_type::ROLLUP)
|
||||
{
|
||||
if (version < HF_VERSION_ENABLE_TOKENS) {
|
||||
LOG_PRINT_L2(" is a ROLLUP transaction before they are permitted - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check and include create token entries
|
||||
if (tx.type == transaction_type::CREATE_TOKEN)
|
||||
{
|
||||
if (version < HF_VERSION_ENABLE_TOKENS) {
|
||||
LOG_PRINT_L2(" is a CREATE_TOKEN transaction before they are permitted - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string asset_type = "sal" + tx.token_metadata.asset_type;
|
||||
if (!cryptonote::is_valid_asset_type(asset_type)) {
|
||||
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an invalid asset_type - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check to see if token is already being created
|
||||
if (tokens.find(tx.token_metadata.asset_type) != tokens.end()) {
|
||||
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an asset_type already in the block template - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
tokens.insert(tx.token_metadata.asset_type);
|
||||
create_token_entries.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
|
||||
}
|
||||
|
||||
bl.tx_hashes.push_back(sorted_it->second);
|
||||
total_weight += meta.weight;
|
||||
|
||||
@@ -226,11 +226,12 @@ namespace cryptonote
|
||||
* @param total_weight return-by-reference the total weight of the new block
|
||||
* @param fee return-by-reference the total of fees from the included transactions
|
||||
* @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
|
||||
* @param create_token_entries list of create coin entries to include in protocol tx
|
||||
* @param version hard fork version to use for consensus rules
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
|
||||
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version);
|
||||
|
||||
/**
|
||||
* @brief get a list of all transactions in the pool
|
||||
|
||||
@@ -187,7 +187,7 @@ bool ver_rct_non_semantics_simple_cached
|
||||
// mixring. Future versions of the protocol may differ in this regard, but if this assumptions
|
||||
// holds true in the future, enable the verification hash by modifying the `untested_tx`
|
||||
// condition below.
|
||||
const bool untested_tx = tx.version > 3 || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
|
||||
const bool untested_tx = tx.version > TRANSACTION_VERSION_ENABLE_TOKENS || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
|
||||
VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here.");
|
||||
|
||||
// Don't cache older (or newer) rctSig types
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace cryptonote
|
||||
STAKE = 6,
|
||||
RETURN = 7,
|
||||
AUDIT = 8,
|
||||
MAX = 8
|
||||
CREATE_TOKEN = 9,
|
||||
ROLLUP = 10,
|
||||
MAX = 10
|
||||
};
|
||||
}
|
||||
|
||||
@@ -374,6 +374,8 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_carrot = m_executor.current_hard_fork_version() >= HF_VERSION_CARROT;
|
||||
|
||||
cryptonote::address_parse_info info;
|
||||
cryptonote::network_type nettype = cryptonote::MAINNET;
|
||||
if(!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, args.front()))
|
||||
@@ -420,6 +422,13 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
|
||||
tools::fail_msg_writer() << "subaddress for mining reward is not yet supported!" << std::endl;
|
||||
return true;
|
||||
}
|
||||
if (info.is_carrot && !is_carrot) {
|
||||
tools::fail_msg_writer() << "mining to Carrot wallet address, but Carrot isn't supported yet" << std::endl;
|
||||
return true;
|
||||
} else if (!info.is_carrot && is_carrot) {
|
||||
tools::fail_msg_writer() << "mining to CryptoNote wallet address, but Carrot wallet address is required" << std::endl;
|
||||
return true;
|
||||
}
|
||||
if(nettype != cryptonote::MAINNET)
|
||||
std::cout << "Mining to a " << (nettype == cryptonote::TESTNET ? "testnet" : "stagenet") << " address, make sure this is intentional!" << std::endl;
|
||||
uint64_t threads_count = 1;
|
||||
@@ -470,7 +479,7 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
|
||||
}
|
||||
}
|
||||
|
||||
m_executor.start_mining(info.address, threads_count, nettype, do_background_mining, ignore_battery);
|
||||
m_executor.start_mining(info.address, threads_count, nettype, do_background_mining, ignore_battery, info.is_carrot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1352,7 +1352,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining, bool ignore_battery) {
|
||||
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining, bool ignore_battery, bool is_carrot) {
|
||||
cryptonote::COMMAND_RPC_START_MINING::request req;
|
||||
cryptonote::COMMAND_RPC_START_MINING::response res;
|
||||
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address);
|
||||
@@ -1650,6 +1650,34 @@ bool t_rpc_command_executor::in_peers(bool set, uint32_t limit)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t t_rpc_command_executor::current_hard_fork_version()
|
||||
{
|
||||
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req;
|
||||
cryptonote::COMMAND_RPC_HARD_FORK_INFO::response res;
|
||||
std::string fail_message = "Unsuccessful";
|
||||
epee::json_rpc::error error_resp;
|
||||
|
||||
req.version = 0;
|
||||
|
||||
if (m_is_rpc)
|
||||
{
|
||||
if (!m_rpc_client->json_rpc_request(req, res, "hard_fork_info", fail_message.c_str()))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_rpc_server->on_hard_fork_info(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, res.status);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return res.version;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::hard_fork_info(uint8_t version)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req;
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
|
||||
bool print_transaction_pool_stats();
|
||||
|
||||
bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining = false, bool ignore_battery = false);
|
||||
bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining = false, bool ignore_battery = false, bool is_carrot = false);
|
||||
|
||||
bool stop_mining();
|
||||
|
||||
@@ -128,6 +128,8 @@ public:
|
||||
|
||||
bool in_peers(bool set, uint32_t limit);
|
||||
|
||||
uint8_t current_hard_fork_version();
|
||||
|
||||
bool hard_fork_info(uint8_t version);
|
||||
|
||||
bool print_bans();
|
||||
|
||||
@@ -202,6 +202,10 @@ namespace hw {
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
crypto::signature &sig) = 0;
|
||||
|
||||
virtual void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) = 0;
|
||||
|
||||
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
|
||||
|
||||
virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0;
|
||||
|
||||
@@ -282,6 +282,12 @@ namespace hw {
|
||||
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
|
||||
}
|
||||
|
||||
void device_default::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) {
|
||||
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
|
||||
bool device_default::open_tx(crypto::secret_key &tx_key) {
|
||||
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
|
||||
tx_key = txkey.sec;
|
||||
|
||||
@@ -112,6 +112,10 @@ namespace hw {
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
crypto::signature &sig) override;
|
||||
|
||||
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) override;
|
||||
|
||||
bool open_tx(crypto::secret_key &tx_key) override;
|
||||
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
|
||||
|
||||
|
||||
@@ -1433,6 +1433,14 @@ namespace hw {
|
||||
#endif
|
||||
}
|
||||
|
||||
void device_ledger::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) {
|
||||
// to-do: For now, carrot tx proofs are not supported
|
||||
AUTO_LOCK_CMD();
|
||||
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
|
||||
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
|
||||
AUTO_LOCK_CMD();
|
||||
this->lock();
|
||||
|
||||
@@ -253,6 +253,10 @@ namespace hw {
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
crypto::signature &sig) override;
|
||||
|
||||
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) override;
|
||||
|
||||
bool open_tx(crypto::secret_key &tx_key) override;
|
||||
|
||||
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
|
||||
|
||||
@@ -58,6 +58,9 @@ const hardfork_t mainnet_hard_forks[] = {
|
||||
|
||||
// version 9 starts from block 179200, which is on or around the 10th of March, 2025. Fork time finalised on 2025-02-24. No fork voting occurs for the v9 fork.
|
||||
{ 9, 179200, 0, 1740393800 },
|
||||
|
||||
// version 10 Carrot - including treasury mint - starts from block 334750, which is on or around the 13th of October, 2025. Fork time finalised on 2025-09-29. No fork voting occurs for the v10 fork.
|
||||
{10, 334750, 0, 1759142500 },
|
||||
};
|
||||
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
|
||||
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
@@ -92,6 +95,9 @@ const hardfork_t testnet_hard_forks[] = {
|
||||
|
||||
// version 10 Carrot - including treasury mint - starts from block 1100
|
||||
{10, 1100, 0, 1739780005 },
|
||||
|
||||
// version 11
|
||||
{11, 1200, 0, 1739880005 },
|
||||
};
|
||||
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
|
||||
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
#include <cstdint> // For uint8_t, etc.
|
||||
#include <cstring> // For memcpy
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -74,4 +76,122 @@ namespace oracle {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class asset_type_counts_v2
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// Fields
|
||||
std::map<uint32_t, uint64_t> asset_counts;
|
||||
|
||||
asset_type_counts_v2() noexcept
|
||||
: asset_counts{}
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t operator[](const uint32_t asset_type_id) const noexcept
|
||||
{
|
||||
if (asset_counts.count(asset_type_id) == 0)
|
||||
return 0;
|
||||
else
|
||||
return asset_counts.at(asset_type_id);
|
||||
}
|
||||
|
||||
bool add(const uint32_t asset_type_id, const uint64_t amount)
|
||||
{
|
||||
if (asset_counts.count(asset_type_id) == 0)
|
||||
return false;
|
||||
asset_counts[asset_type_id] += amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_asset_type(const uint32_t asset_type_id)
|
||||
{
|
||||
if (asset_counts.count(asset_type_id) != 0)
|
||||
return false;
|
||||
asset_counts[asset_type_id] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint64_t> get()
|
||||
{
|
||||
return asset_counts;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> get_asset_types() const noexcept
|
||||
{
|
||||
std::vector<uint32_t> asset_type_ids;
|
||||
for (const auto& entry: asset_counts) {
|
||||
asset_type_ids.push_back(entry.first);
|
||||
}
|
||||
return asset_type_ids;
|
||||
}
|
||||
|
||||
bool has_asset_type(const uint32_t asset_type_id) const noexcept
|
||||
{
|
||||
return asset_counts.count(asset_type_id) == 1;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> serialize() const noexcept {
|
||||
constexpr uint8_t version = 1; // Bump if format changes
|
||||
uint32_t sz = static_cast<uint32_t>(asset_counts.size());
|
||||
size_t total_size = sizeof(uint8_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
|
||||
std::vector<uint8_t> buf(total_size);
|
||||
uint8_t* ptr = buf.data();
|
||||
|
||||
std::memcpy(ptr, &version, sizeof(uint8_t));
|
||||
ptr += sizeof(uint8_t);
|
||||
|
||||
std::memcpy(ptr, &sz, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
|
||||
for (const auto& p : asset_counts) { // Iterates in sorted order
|
||||
std::memcpy(ptr, &p.first, sizeof(uint32_t));
|
||||
ptr += sizeof(uint32_t);
|
||||
std::memcpy(ptr, &p.second, sizeof(uint64_t));
|
||||
ptr += sizeof(uint64_t);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void deserialize(const uint8_t* data, size_t len) {
|
||||
if (len < sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
std::memcpy(&version, data, sizeof(uint8_t));
|
||||
data += sizeof(uint8_t);
|
||||
len -= sizeof(uint8_t);
|
||||
|
||||
if (version == 1) {
|
||||
if (len < sizeof(uint32_t)) {
|
||||
throw std::runtime_error("Invalid serialized data");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported version");
|
||||
}
|
||||
|
||||
uint32_t sz;
|
||||
std::memcpy(&sz, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
len -= sizeof(uint32_t);
|
||||
|
||||
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
|
||||
throw std::runtime_error("Invalid serialized data length");
|
||||
}
|
||||
|
||||
asset_counts.clear();
|
||||
for (uint32_t i = 0; i < sz; ++i) {
|
||||
uint32_t k;
|
||||
uint64_t v;
|
||||
std::memcpy(&k, data, sizeof(uint32_t));
|
||||
data += sizeof(uint32_t);
|
||||
std::memcpy(&v, data, sizeof(uint64_t));
|
||||
data += sizeof(uint64_t);
|
||||
asset_counts[k] = v; // Map handles sorting/uniquness
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+4
-22
@@ -733,9 +733,9 @@ namespace nodetool
|
||||
std::set<std::string> full_addrs;
|
||||
if (m_nettype == cryptonote::TESTNET)
|
||||
{
|
||||
full_addrs.insert("72.5.43.63:29080");
|
||||
full_addrs.insert("195.85.114.217:29080");
|
||||
full_addrs.insert("206.188.197.72:29080");
|
||||
full_addrs.insert("165.22.6.67:29080");
|
||||
full_addrs.insert("165.22.15.162:29080");
|
||||
full_addrs.insert("162.243.163.207:29080");
|
||||
}
|
||||
else if (m_nettype == cryptonote::STAGENET)
|
||||
{
|
||||
@@ -881,12 +881,6 @@ namespace nodetool
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
return {
|
||||
"xwvz3ekocr3dkyxfkmgm2hvbpzx2ysqmaxgter7znnqrhoicygkfswid.onion:18083",
|
||||
"4pixvbejrvihnkxmduo2agsnmc3rrulrqc7s3cbwwrep6h6hrzsibeqd.onion:18083",
|
||||
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
|
||||
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
|
||||
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
|
||||
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -894,10 +888,6 @@ namespace nodetool
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
return {
|
||||
"s3l6ke4ed3df466khuebb4poienoingwof7oxtbo6j4n56sghe3a.b32.i2p:18080",
|
||||
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080",
|
||||
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p:18080",
|
||||
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p:18080",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -2054,20 +2044,13 @@ namespace nodetool
|
||||
return true;
|
||||
|
||||
static const std::vector<std::string> dns_urls = {
|
||||
"blocklist.moneropulse.se"
|
||||
, "blocklist.moneropulse.org"
|
||||
, "blocklist.moneropulse.net"
|
||||
, "blocklist.moneropulse.no"
|
||||
, "blocklist.moneropulse.fr"
|
||||
, "blocklist.moneropulse.de"
|
||||
, "blocklist.moneropulse.ch"
|
||||
};
|
||||
|
||||
std::vector<std::string> records;
|
||||
if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
|
||||
return true;
|
||||
|
||||
unsigned good = 0, bad = 0;
|
||||
unsigned good = 0;
|
||||
for (const auto& record : records)
|
||||
{
|
||||
std::vector<std::string> ips;
|
||||
@@ -2091,7 +2074,6 @@ namespace nodetool
|
||||
continue;
|
||||
}
|
||||
MWARNING("Invalid IP address or subnet from DNS blocklist: " << ip << " - " << parsed_addr.error());
|
||||
++bad;
|
||||
}
|
||||
}
|
||||
if (good > 0)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user