Compare commits

..

53 Commits

Author SHA1 Message Date
Some Random Crypto Guy e2bc467618 fixed key image calculation for return_payment outputs 2025-08-05 21:11:22 +01:00
Some Random Crypto Guy cb3f1905ed updated SPARC code to support one-way payment channels, ready for Salvium Two smart contracts 2025-08-05 12:27:20 +01:00
akildemir 609615779c Merge pull request #38 from salvium/tx-parallel-scanning-fix
Fixed wallet scanning parallelisation, speed up wallet scans from chain
2025-08-04 16:08:25 +03:00
Some Random Crypto Guy b9364b5749 fixed parallel scanning of blocks for wallet - max 10 blocks at a time, because of return_payment limitations 2025-08-04 14:02:05 +01:00
akildemir 7cd7bef3ca Merge pull request #37 from salvium/tx-size-calc-fix
Carrot TX size calculation fixes
2025-08-04 13:35:48 +03:00
Some Random Crypto Guy 80bd2d1ff0 fixed calculation for Carrot TX sizes - added Salvium-specific data 2025-08-04 11:01:33 +01:00
akildemir cbe84499fb use internal notes instead of special ones for change and return enotes (#36) 2025-08-01 13:54:59 +01:00
akildemir a8ee0a90e0 Merge pull request #34 from salvium/miner-fixes
fixed miner resumption issue
2025-08-01 14:24:14 +03:00
akildemir c9de861216 Merge pull request #35 from salvium/wallet-creation-address-fix
fixed incorrect wallet address displayed for Carrot when creating a new wallet
2025-08-01 14:22:22 +03:00
Some Random Crypto Guy 0239202f09 fixed incorrect wallet address displayed for Carrot when creating a new wallet 2025-07-31 11:48:43 +01:00
Some Random Crypto Guy 2dd6208a80 added mechanism to forcibly stop miner when crossing Carrot HF boundary, and allow mining to be restarted 2025-07-31 10:39:34 +01:00
akildemir 37594fe8fa Implement return tx functionality for post-carrot (#33)
* return tx fixes

* save change output for return tx

* successfully receive returned tx

* complete post-carrot return txs
2025-07-30 15:33:35 +01:00
akildemir e5c9b05ed6 Merge pull request #31 from salvium/ringct-fix
fixed RCT types to support ECDHINFO for Salvium One; fixed input and output limits for TXs
2025-07-24 14:44:06 +03:00
akildemir 9ba621b3ae post carrot stake tx support (#32)
* add support for stake txs and payouts post-carrot

* fixes on the stake return payments

* complete post-carrot stake txs

* rename protocol_tx_data to protocol_tx_data_t
2025-07-23 14:58:11 +01:00
Some Random Crypto Guy 9e20133ed9 fixed input and output limits for TXs 2025-07-23 10:35:14 +01:00
Some Random Crypto Guy 9c11200666 fixed RCT types to support ECDHINFO for Salvium One; fixed input and output limits for TXs 2025-07-23 10:21:13 +01:00
akildemir 3e0649a8d2 Merge pull request #29 from salvium/mining-migration-from-CN-to-Carrot
fixed migration of mining services from CN to Carrot
2025-07-23 12:07:20 +03:00
akildemir b87bfe002a Merge pull request #27 from salvium/carrot-addressing
fixed Carrot subaddress displaying in CLI wallet
2025-07-23 11:57:36 +03:00
Some Random Crypto Guy f2e69594a7 fixed migration of mining from CN to Carrot, including stopping miners from using wrong address pre- and post-Carrot HF 2025-07-22 11:51:21 +01:00
Some Random Crypto Guy 7b39504050 fixed carrot syncing of mainnet 2025-07-21 19:33:17 +01:00
Some Random Crypto Guy eff99e9287 fixed Carrot subaddress display in CLI wallet 2025-07-21 19:30:47 +01:00
somerandomcryptoguy 6f8fcdab03 fixed make depends for all major platforms (#26)
* fixed 'make depends' building

* fixed capture vars for MacOS

* fixed Linux build for make depends

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-07-19 12:25:45 +01:00
akildemir c7a739d885 Merge pull request #25 from salvium/premine-scan
fixed premine scanning using Carrot
2025-07-16 11:29:01 +03:00
Some Random Crypto Guy 608962068a fixed premine scan 2025-07-15 22:04:47 +01:00
Some Random Crypto Guy e7615b4c08 Merge branch 'carrot-integration' of https://github.com/salvium/salvium into carrot-integration 2025-07-15 16:05:31 +01:00
Some Random Crypto Guy 5fa5ff8585 bumped RC version 2025-07-15 12:10:18 +01:00
akildemir 28a9338ab7 Merge pull request #24 from salvium/audit-spend-fix
fixed subaddress_map issues allowing construction of TX for spending AUDIT and STAKE post-Carrot-HF
2025-07-15 13:48:51 +03:00
Some Random Crypto Guy 1d3747e9cd fixed subaddress_map issues allowing construction of TX for spending AUDIT and STAKE post-Carrot-HF 2025-07-15 10:50:03 +01:00
akildemir c1c70ddace fix finding wrong change bug (#23) 2025-07-14 16:47:21 +01:00
akildemir 7ea690614e Merge pull request #22 from salvium/scanning-fixes
fix scanning for pre-Carrot STAKE + AUDIT TXs
2025-07-14 17:42:43 +03:00
Some Random Crypto Guy 2909fd2505 Fixed pre-scanning for AUDIT TXs 2025-07-14 14:25:28 +01:00
Some Random Crypto Guy 7dd5450419 Fixed pre-scanning for STAKE TXs 2025-07-14 13:18:54 +01:00
akildemir e3a03e9d98 add carrot subaddress derive type when inserting new subaddresses 2025-07-14 10:43:41 +03:00
akildemir dd39f94ca6 all pre-carrot txs work; Add support for return txs post-carrot. 2025-07-11 12:08:44 +03:00
akildemir 78232ff3a8 misc fixes 2025-07-07 16:55:26 +03:00
Some Random Crypto Guy aa02928620 fixed keys for pre+post Carrot mining rewards 2025-07-04 16:03:57 +01:00
Some Random Crypto Guy 52736ea0a3 bumped version number for RC 2025-07-04 10:01:32 +01:00
Some Random Crypto Guy b7cc8e8100 fixed Carrot mining; added support for dedicated Carrot wallet addresses with SC1 prefix 2025-07-03 23:08:24 +01:00
akildemir 520d4b4eab implement sparc return address functionality 2025-07-02 14:25:25 +03:00
Some Random Crypto Guy 11a296acde bumped version - rc2 2025-07-02 10:45:15 +01:00
Some Random Crypto Guy 8b1796147a fixed Carrot transfer functionality getting correct spendkeys 2025-07-01 23:12:13 +01:00
Some Random Crypto Guy bf9d0e837e fixed detection of received Carrot payments 2025-07-01 21:27:54 +01:00
Some Random Crypto Guy a9301f5573 migrated wallet_scanning tests to using carrot_and_legacy_account instead of keystore 2025-07-01 13:56:52 +01:00
Some Random Crypto Guy e6e644e600 migrated to using carrot_and_legacy_account instead of keystore 2025-07-01 13:33:53 +01:00
akildemir 130975cebd clean up previous commit 2025-06-29 15:38:14 +03:00
Some Random Crypto Guy 5306d4b94a interim checkin - working RCT Carrot TX 2025-06-27 16:05:08 +01:00
akildemir 14a6c6b8ef generate & scan correct carrot pubkeys 2025-06-24 13:19:42 +03:00
akildemir 2b31c8ad69 compile unit tests 2025-06-24 13:18:08 +03:00
akildemir ae02dcbe9a post-carrot tx scanning bug fixes 2025-06-20 11:40:46 +03:00
Some Random Crypto Guy e235199ff3 fixed crash due to accessing member based on HF version rather than TX version 2025-06-18 08:46:06 +01:00
akildemir 87ceee6c2d fully integrate carrot keys into wallet2 2025-06-17 15:40:12 +03:00
Some Random Crypto Guy 41e3e85159 fixed missing references to new RCT type RCTTypeSalviumOne 2025-06-17 13:27:02 +01:00
Some Random Crypto Guy b570a4938c added method to simple_wallet 2025-06-17 10:03:43 +01:00
91 changed files with 2707 additions and 1148 deletions
+5 -5
View File
@@ -1,4 +1,4 @@
# Salvium Zero v0.9.6
# Salvium Zero v0.9.9
Copyright (c) 2023-2024, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v0.9.6
git checkout v0.9.9
make
```
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/salvium/salvium
cd salvium
git checkout v0.9.6
git checkout v0.9.9
```
* Build:
@@ -370,10 +370,10 @@ application.
cd salvium
```
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.6'. If you don't care about the version and just want binaries from master, skip this step:
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.9'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v0.9.6
git checkout v0.9.9
```
* If you are on a 64-bit system, run:
+1 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
mingw32_CFLAGS=-pipe -pthread
mingw32_CFLAGS=-pipe
mingw32_CXXFLAGS=$(mingw32_CFLAGS)
mingw32_ARFLAGS=cr
+13 -15
View File
@@ -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
+4 -3
View File
@@ -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
+11 -5
View File
@@ -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 &&\
+2 -16
View File
@@ -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
+1
View File
@@ -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
+23 -27
View File
@@ -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 :=
+13 -9
View File
@@ -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)
+1 -1
View File
@@ -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)
+13
View File
@@ -207,6 +207,17 @@ typedef struct yield_tx_info {
crypto::public_key return_pubkey;
} yield_tx_info;
typedef struct yield_tx_info_carrot {
uint8_t version;
uint64_t block_height;
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;
#define DBF_SAFE 1
#define DBF_FAST 2
#define DBF_FASTEST 4
@@ -1924,6 +1935,8 @@ 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;
/**
* @brief set whether or not to automatically remove logs
+113 -8
View File
@@ -298,6 +298,7 @@ const char* const LMDB_YIELD_TXS = "yield_txs";
const char* const LMDB_YIELD_BLOCKS = "yield_blocks";
const char* const LMDB_AUDIT_TXS = "audit_txs";
const char* const LMDB_AUDIT_BLOCKS = "audit_blocks";
const char* const LMDB_CARROT_YIELD_TXS = "carrot_yield_txs";
const char zerokey[8] = {0};
const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey };
@@ -947,6 +948,43 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_t
return 0;
}
int BlockchainLMDB::get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const {
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
// Clear the container
yti_container.clear();
// Query for the (presumably matured) YIELD_TX_INFO information
TXN_PREFIX_RDONLY();
RCURSOR(carrot_yield_txs);
MDB_val v;
MDB_val_set(k, height);
MDB_cursor_op op = MDB_SET;
while (1)
{
int ret = mdb_cursor_get(m_cur_carrot_yield_txs, &k, &v, op);
op = MDB_NEXT_DUP;
if (ret == MDB_NOTFOUND)
break;
if (ret)
throw0(DB_ERROR(lmdb_error("Failed to enumerate yield TX info: ", ret).c_str()));
// Get the data
yield_tx_info_carrot *p = (yield_tx_info_carrot*)v.mv_data;
// Push result back into the container
yti_container.emplace_back(*p);
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
yti_container.back().block_height = height;
}
TXN_POSTFIX_RDONLY();
// Return success to caller
return 0;
}
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, uint64_t audit_total, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -1215,6 +1253,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
CURSOR(circ_supply_tally)
CURSOR(yield_txs)
CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
MDB_val_set(val_tx_id, tx_id);
MDB_val_set(val_h, tx_hash);
@@ -1321,7 +1360,12 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
}
if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::TRANSFER) {
if (tx.type == cryptonote::transaction_type::BURN ||
tx.type == cryptonote::transaction_type::CONVERT ||
tx.type == cryptonote::transaction_type::STAKE ||
tx.type == cryptonote::transaction_type::AUDIT ||
tx.type == cryptonote::transaction_type::TRANSFER)
{
// Get the current tally value for the source currency type
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(tx.source_asset_type));
@@ -1390,10 +1434,44 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
}
}
// Is there yield_tx data to add?
if (tx.type == cryptonote::transaction_type::STAKE) {
// Is there yield_tx data to add?
if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE)
{
// Create the object we are going to write to the database
yield_tx_info_carrot yield_data;
yield_data.block_height = m_height;
yield_data.tx_hash = tx_hash;
yield_data.return_pubkey = tx.protocol_tx_data.return_pubkey;
yield_data.return_address = tx.protocol_tx_data.return_address;
yield_data.return_view_tag = tx.protocol_tx_data.return_view_tag;
yield_data.return_anchor_enc = tx.protocol_tx_data.return_anchor_enc;
yield_data.locked_coins = tx.amount_burnt;
// Because LMDB is shockingly bad at handling duplicates, we have resorted to using a counter of elements
// in the first element of the struct.
MDB_val data;
MDB_val_set(val_height, m_height);
result = mdb_cursor_get(m_cur_carrot_yield_txs, &val_height, &data, MDB_SET);
if (!result)
{
mdb_size_t num_elems = 0;
result = mdb_cursor_count(m_cur_carrot_yield_txs, &num_elems);
if (result)
throw0(DB_ERROR(std::string("Failed to get number of yield TXs for height: ").append(mdb_strerror(result)).c_str()));
yield_data.block_height = num_elems;
}
else if (result != MDB_NOTFOUND)
throw0(DB_ERROR(lmdb_error("Failed to get output amount in db transaction: ", result).c_str()));
else
yield_data.block_height = 0;
// Now we know how many there are, write out the data to the DB
MDB_val_set(val_yield_tx_data, yield_data);
result = mdb_cursor_put(m_cur_carrot_yield_txs, &val_height, &val_yield_tx_data, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add tx carrot yield data to db transaction: ", result).c_str()));
} else if (tx.type == cryptonote::transaction_type::STAKE) {
// Create the object we are going to write to the database
yield_tx_info yield_data;
yield_data.block_height = m_height;
@@ -1444,7 +1522,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
if (result)
throw0(DB_ERROR( lmdb_error("Failed to add tx yield data to db transaction: ", result).c_str() ));
}
// Is there audit_tx data to add?
if (tx.type == cryptonote::transaction_type::AUDIT) {
@@ -1498,7 +1576,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
if (result)
throw0(DB_ERROR( lmdb_error("Failed to add tx audit data to db transaction: ", result).c_str() ));
}
return tx_id;
}
@@ -1522,6 +1600,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
CURSOR(circ_supply_tally)
CURSOR(yield_txs)
CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
MDB_val_set(val_h, tx_hash);
@@ -1720,7 +1799,28 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
}
// Is there yield_tx data to remove?
if (tx.type == cryptonote::transaction_type::STAKE) {
if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE) {
// Remove any yield_tx data for this transaction
MDB_val_set(val_height, m_height);
MDB_val v;
MDB_cursor_op op = MDB_SET;
while (1) {
result = mdb_cursor_get(m_cur_carrot_yield_txs, &val_height, &v, op);
if (result == MDB_NOTFOUND) {
throw1(DB_ERROR("Failed to locate carrot yield tx for removal from db transaction"));
} else if (result) {
throw1(DB_ERROR(lmdb_error("Failed to locate carrot yield_tx data for removal: ", result).c_str()));
}
op = MDB_NEXT_DUP;
const yield_tx_info_carrot yti = *(const yield_tx_info_carrot*)v.mv_data;
if (yti.tx_hash == tx_hash) {
result = mdb_cursor_del(m_cur_carrot_yield_txs, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Failed to add removal of carrot yield_tx data to db transaction: ", result).c_str()));
break;
}
}
} else if (tx.type == cryptonote::transaction_type::STAKE) {
// Remove any yield_tx data for this transaction
MDB_val_set(val_height, m_height);
MDB_val v;
@@ -2284,10 +2384,12 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
lmdb_db_open(txn, LMDB_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_yield_txs, "Failed to open db handle for m_yield_txs");
lmdb_db_open(txn, LMDB_YIELD_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_yield_blocks, "Failed to open db handle for m_yield_blocks");
lmdb_db_open(txn, LMDB_AUDIT_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_audit_txs, "Failed to open db handle for m_audit_txs");
lmdb_db_open(txn, LMDB_AUDIT_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_audit_blocks, "Failed to open db handle for m_audit_blocks");
lmdb_db_open(txn, LMDB_CARROT_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_carrot_yield_txs, "Failed to open db handle for m_carrot_yield_txs");
mdb_set_dupsort(txn, m_spent_keys, compare_hash32);
mdb_set_dupsort(txn, m_block_heights, compare_hash32);
mdb_set_dupsort(txn, m_tx_indices, compare_hash32);
@@ -2312,6 +2414,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
mdb_set_dupsort(txn, m_yield_txs, compare_uint64);
mdb_set_dupsort(txn, m_audit_txs, compare_uint64);
mdb_set_dupsort(txn, m_carrot_yield_txs, compare_uint64);
if (!(mdb_flags & MDB_RDONLY))
{
@@ -2497,6 +2600,8 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_txs: ", result).c_str()));
if (auto result = mdb_drop(txn, m_audit_blocks, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_blocks: ", result).c_str()));
if (auto result = mdb_drop(txn, m_carrot_yield_txs, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_carrot_yield_txs: ", result).c_str()));
// init with current version
MDB_val_str(k, "version");
+7 -1
View File
@@ -80,6 +80,7 @@ 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_txn_cursors;
@@ -108,6 +109,7 @@ 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
typedef struct mdb_rflags
{
@@ -137,6 +139,7 @@ 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;
} mdb_rflags;
typedef struct mdb_threadinfo
@@ -474,6 +477,7 @@ 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;
private:
MDB_env* m_env;
@@ -511,10 +515,12 @@ 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;
mutable uint64_t m_cum_size; // used in batch size estimation
mutable unsigned int m_cum_count;
std::string m_folder;
+1
View File
@@ -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; }
+1
View File
@@ -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,14 +58,14 @@ CarrotDestinationV1 carrot_and_legacy_account::cryptonote_address(const payment_
switch (resolve_derive_type(derive_type))
{
case AddressDeriveType::Carrot:
make_carrot_integrated_address_v1(carrot_account_spend_pubkey,
legacy_acb.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;
case AddressDeriveType::PreCarrot:
make_carrot_integrated_address_v1(legacy_acb.get_keys().m_account_address.m_spend_public_key,
legacy_acb.get_keys().m_account_address.m_view_public_key,
make_carrot_integrated_address_v1(get_keys().m_account_address.m_spend_public_key,
get_keys().m_account_address.m_view_public_key,
payment_id,
addr);
break;
@@ -80,15 +80,15 @@ CarrotDestinationV1 carrot_and_legacy_account::subaddress(const subaddress_index
if (!subaddress_index.index.is_subaddress())
return cryptonote_address(null_payment_id, subaddress_index.derive_type);
const cryptonote::account_keys &lkeys = legacy_acb.get_keys();
const cryptonote::account_keys &keys = get_keys();
CarrotDestinationV1 addr;
cryptonote::account_public_address cnaddr;
switch (resolve_derive_type(subaddress_index.derive_type))
{
case AddressDeriveType::Carrot:
make_carrot_subaddress_v1(carrot_account_spend_pubkey,
carrot_account_view_pubkey,
make_carrot_subaddress_v1(keys.m_carrot_account_address.m_spend_public_key,
keys.m_carrot_account_address.m_view_public_key,
s_generate_address_dev,
subaddress_index.index.major,
subaddress_index.index.minor,
@@ -96,7 +96,7 @@ CarrotDestinationV1 carrot_and_legacy_account::subaddress(const subaddress_index
break;
case AddressDeriveType::PreCarrot:
cnaddr =
lkeys.m_device->get_subaddress(lkeys, {subaddress_index.index.major, subaddress_index.index.minor});
keys.m_device->get_subaddress(keys, {subaddress_index.index.major, subaddress_index.index.minor});
addr = CarrotDestinationV1{
.address_spend_pubkey = cnaddr.m_spend_public_key,
.address_view_pubkey = cnaddr.m_view_public_key,
@@ -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,
@@ -130,7 +138,7 @@ void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_ex
const uint32_t major_index = subaddress_index.index.major;
const uint32_t minor_index = subaddress_index.index.minor;
const cryptonote::account_keys &lkeys = legacy_acb.get_keys();
const cryptonote::account_keys &keys = get_keys();
crypto::secret_key address_index_generator;
crypto::secret_key subaddress_scalar;
@@ -140,12 +148,12 @@ void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_ex
{
case AddressDeriveType::Carrot:
// s^j_gen = H_32[s_ga](j_major, j_minor)
make_carrot_index_extension_generator(s_generate_address, major_index, minor_index, address_index_generator);
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(carrot_account_spend_pubkey, address_index_generator, major_index, minor_index, subaddress_scalar);
make_carrot_subaddress_scalar(keys.m_carrot_account_address.m_spend_public_key, address_index_generator, major_index, minor_index, subaddress_scalar);
}
else
{
@@ -154,19 +162,19 @@ void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_ex
}
// k^g_a = k_gi * k^j_subscal
sc_mul(to_bytes(address_privkey_g_out), to_bytes(k_generate_image), to_bytes(subaddress_scalar));
sc_mul(to_bytes(address_privkey_g_out), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
// k^t_a = k_ps * k^j_subscal
sc_mul(to_bytes(address_privkey_t_out), to_bytes(k_prove_spend), to_bytes(subaddress_scalar));
sc_mul(to_bytes(address_privkey_t_out), to_bytes(keys.k_prove_spend), to_bytes(subaddress_scalar));
break;
case AddressDeriveType::PreCarrot:
// m = Hn(k_v || j_major || j_minor) if subaddress else 0
subaddress_extension = is_subaddress
? lkeys.get_device().get_subaddress_secret_key(lkeys.m_view_secret_key, {major_index, minor_index})
? keys.get_device().get_subaddress_secret_key(keys.m_view_secret_key, {major_index, minor_index})
: crypto::null_skey;
// k^g_a = k_s + m
sc_add(to_bytes(address_privkey_g_out), to_bytes(lkeys.m_spend_secret_key), to_bytes(subaddress_extension));
sc_add(to_bytes(address_privkey_g_out), to_bytes(keys.m_spend_secret_key), to_bytes(subaddress_extension));
// k^t_a = 0
memset(address_privkey_t_out.data, 0, sizeof(address_privkey_t_out));
@@ -284,7 +292,7 @@ void carrot_and_legacy_account::generate_subaddress_map()
{
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});
}
@@ -292,26 +300,63 @@ void carrot_and_legacy_account::generate_subaddress_map()
}
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate(const AddressDeriveType default_derive_type)
crypto::secret_key carrot_and_legacy_account::generate(
const crypto::secret_key& recovery_key,
bool recover,
bool two_random,
const AddressDeriveType default_derive_type)
{
//legacy_acb.generate();
// generate the legacy keys
crypto::secret_key retval = cryptonote::account_base::generate(recovery_key, recover, two_random);
//crypto::generate_random_bytes_thread_safe(sizeof(crypto::secret_key), to_bytes(s_master));
s_master = legacy_acb.get_keys().m_spend_secret_key;
make_carrot_provespend_key(s_master, k_prove_spend);
make_carrot_viewbalance_secret(s_master, s_view_balance);
make_carrot_generateimage_key(s_view_balance, k_generate_image);
make_carrot_generateaddress_secret(s_view_balance, s_generate_address);
// generate carrot keys
set_carrot_keys(default_derive_type);
make_carrot_spend_pubkey(k_generate_image, k_prove_spend, carrot_account_spend_pubkey);
k_view_incoming_dev.view_key_scalar_mult_ed25519(carrot_account_spend_pubkey,
carrot_account_view_pubkey);
return retval;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
{
// top level keys
m_keys.s_master = m_keys.m_spend_secret_key;
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
make_carrot_viewbalance_secret(m_keys.s_master, m_keys.s_view_balance);
// view balance keys
make_carrot_viewincoming_key(m_keys.s_view_balance, m_keys.k_view_incoming);
make_carrot_generateimage_key(m_keys.s_view_balance, m_keys.k_generate_image);
make_carrot_generateaddress_secret(m_keys.s_view_balance, m_keys.s_generate_address);
// carrot account address
make_carrot_spend_pubkey(m_keys.k_generate_image, m_keys.k_prove_spend, m_keys.m_carrot_account_address.m_spend_public_key);
k_view_incoming_dev.view_key_scalar_mult_ed25519(
m_keys.m_carrot_account_address.m_spend_public_key,
m_keys.m_carrot_account_address.m_view_public_key
);
// carrot main wallet address
m_keys.m_carrot_main_address.m_spend_public_key = m_keys.m_carrot_account_address.m_spend_public_key;
k_view_incoming_dev.view_key_scalar_mult_ed25519(
crypto::get_G(),
m_keys.m_carrot_main_address.m_view_public_key
);
this->default_derive_type = default_derive_type;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map_cn)
{
for (const auto &p : subaddress_map_cn)
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
}
//----------------------------------------------------------------------------------------------------------------------
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
{
return derive_type == AddressDeriveType::Auto ? default_derive_type : derive_type;
@@ -28,11 +28,11 @@
#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 "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"
@@ -43,30 +43,43 @@ static constexpr std::uint32_t MAX_SUBADDRESS_MINOR_INDEX = 20;
namespace carrot
{
struct carrot_and_legacy_account
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::key_image key_image;
crypto::secret_key x;
crypto::secret_key y;
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &K_o,
const crypto::public_key &K_change,
const crypto::key_image &key_image,
const crypto::secret_key &x,
const crypto::secret_key &y):
input_context(input_context),
K_o(K_o),
K_change(K_change),
key_image(key_image),
x(x),
y(y) {}
};
class carrot_and_legacy_account : public cryptonote::account_base
{
cryptonote::account_base legacy_acb;
crypto::secret_key s_master;
crypto::secret_key k_prove_spend;
crypto::secret_key s_view_balance;
crypto::secret_key k_generate_image;
crypto::secret_key s_generate_address;
crypto::public_key carrot_account_spend_pubkey;
crypto::public_key carrot_account_view_pubkey;
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(legacy_acb.get_keys().m_view_secret_key),
s_view_balance_dev(s_view_balance),
s_generate_address_dev(s_generate_address)
carrot_and_legacy_account(): k_view_incoming_dev(get_keys().k_view_incoming),
s_view_balance_dev(get_keys().s_view_balance),
s_generate_address_dev(get_keys().s_generate_address)
{}
carrot_and_legacy_account(const carrot_and_legacy_account &k) = delete;
@@ -80,7 +93,9 @@ namespace carrot
CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddress_map_cn() 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,
@@ -110,8 +125,24 @@ namespace carrot
void generate_subaddress_map();
void generate(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
crypto::secret_key generate(
const crypto::secret_key& recovery_key = crypto::secret_key(),
bool recover = false,
bool two_random = false,
const AddressDeriveType default_derive_type = AddressDeriveType::Carrot
);
void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
void insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map);
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;
};
}
+2
View File
@@ -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
+2 -1
View File
@@ -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 = 8;
static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 128;
// SPARC addressing protocol domain separators
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK[] = "SPARC return pubkey encryption mask";
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR[] = "SPARC return address scalar";
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR[] = "SPARC return index scalar";
} //namespace carrot
+4
View File
@@ -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;
};
+7
View File
@@ -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,
+4
View File
@@ -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;
};
+42
View File
@@ -211,6 +211,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_return = H_32(s_sr || input_context || Ko)
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 +232,37 @@ 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_enc_pubkey_out)
{
// K_return = k_return G + k_idx T
crypto::secret_key k_return;
crypto::secret_key k_idx = crypto::null_skey;
encrypted_return_pubkey_t return_pub;
s_view_balance_dev->make_internal_return_privkey(input_context, onetime_address, k_return);
make_sparc_return_index(to_bytes(onetime_address), input_context, onetime_address, idx, k_idx);
rct::key K_return;
rct::addKeys2(K_return,
rct::sk2rct(k_return),
rct::sk2rct(k_idx),
rct::pk2rct(crypto::get_T()));
//K_return = rct::scalarmult8(K_return);
if (!rct::isInMainSubgroup(K_return))
throw std::runtime_error("K_return is not in main subgroup");
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.bytes), "Size mismatch");
memcpy(return_pub.bytes, K_return.bytes, sizeof(encrypted_return_pubkey_t));
// return_enc = return_pub ^ m_return
encrypted_return_pubkey_t m_return;
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx, input_context, onetime_address, m_return);
return_enc_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)
+31
View File
@@ -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 k_idx, given non-secret data
* k_idx = 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
* outparam: return_index_out - k_idx
*/
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,22 @@ 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
* param: idx
* 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)
+44 -7
View File
@@ -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,11 @@ 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) {
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,6 +210,9 @@ 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)
{
@@ -210,10 +220,19 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
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) {
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 +242,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
@@ -255,6 +279,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 +296,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 +319,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
@@ -327,7 +364,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
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");
invalid_point, "this set contains an invalid onetime address");
}
// assert unique and non-trivial k_a
@@ -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
+95 -31
View File
@@ -170,6 +170,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 +178,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 +204,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/*idx*/,
return_pubkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
@@ -257,6 +268,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 +280,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 +288,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 +304,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 +324,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 +332,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 +340,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 +387,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 +396,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 +404,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;
@@ -401,13 +423,15 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
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 +449,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 +488,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
@@ -529,7 +561,39 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
// 10. construct the stake return enote
if (tx_type == cryptonote::transaction_type::STAKE) {
// 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,
+7 -3
View File
@@ -133,6 +133,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);
/**
@@ -144,9 +145,10 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
* outparam: encrypted_payment_id_out - pid_enc
*/
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);
/**
* brief: get_output_proposal_special_v1 - convert the carrot proposal to an output proposal (external selfsend)
* param: proposal -
@@ -173,6 +175,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
+204 -20
View File
@@ -34,6 +34,11 @@
//local headers
#include "destination.h"
#include "enote_utils.h"
extern "C"
{
#include "crypto/crypto-ops.h"
}
#include "crypto/generators.h"
#include "ringct/rctOps.h"
#include "scan_unsafe.h"
@@ -70,6 +75,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,
@@ -361,43 +464,124 @@ 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)
{
// 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);
bool normal_change_found = true;
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);
// make k_idx
crypto::secret_key k_idx = crypto::null_skey;
uint64_t idx = 0;
make_sparc_return_index(to_bytes(output_key), input_context, output_key, idx, k_idx);
// compute 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()));
//K_return = rct::scalarmult8(K_return);
// compute K_r = K_return + K_o
crypto::public_key K_r = rct::rct2pk(rct::addKeys(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::secret_key sum_t;
sc_add(to_bytes(sum_t), to_bytes(sender_extension_t_out), to_bytes(k_idx));
crypto::key_image key_image = account.derive_key_image(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
sum_t,
K_r
);
crypto::secret_key x, y;
account.try_searching_for_opening_for_onetime_address(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
sum_t,
x,
y
);
// save the input context & change output key
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, key_image, x, y}}});
}
// 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
View File
@@ -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
-1
View File
@@ -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
+42 -3
View File
@@ -59,10 +59,10 @@ static void store_carrot_ephemeral_pubkeys_to_extra(const EnoteContainer &enotes
if (nouts == 0)
return;
const bool use_shared_ephemeral_pubkey = nouts == 2 && 0 == memcmp(
const bool use_shared_ephemeral_pubkey = nouts == 1 || (nouts == 2 && 0 == memcmp(
&enotes.front().enote_ephemeral_pubkey,
&enotes.back().enote_ephemeral_pubkey,
sizeof(mx25519_pubkey));
sizeof(mx25519_pubkey)));
bool success = true;
if (use_shared_ephemeral_pubkey)
{
@@ -197,18 +197,32 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
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 carrot::RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id)
{
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());
cryptonote::transaction tx;
tx.pruned = true;
tx.version = 2;
tx.unlock_time = 0;
tx.source_asset_type = "SAL1";
tx.destination_asset_type = "SAL1";
tx.version = 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_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;
@@ -249,6 +263,23 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
//C_a
tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment});
//K_return
if (tx_type != cryptonote::transaction_type::STAKE) {
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.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;
}
//ephemeral pubkeys: D_e
@@ -324,6 +355,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;
}
+5
View File
@@ -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
@@ -104,6 +105,10 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
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 RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id);
/**
* brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
+16 -6
View File
@@ -326,15 +326,25 @@ std::vector<std::size_t> get_input_counts_in_preferred_order()
// 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 == 8,
//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,
"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, 3, 5, 6, 7};
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};
else
return {1, 2, 4, 8, 3, 5, 6, 7};
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};
}
//-------------------------------------------------------------------------------------------------------------------
select_inputs_func_t make_single_transfer_input_selector(
@@ -388,7 +398,7 @@ select_inputs_func_t make_single_transfer_input_selector(
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, FCMP_PLUS_PLUS_MAX_INPUTS, fee_by_input_count);
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count);
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 ("
@@ -413,7 +423,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, FCMP_PLUS_PLUS_MAX_INPUTS, fee_by_input_count);
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count);
if (!max_usable_money.first)
continue;
else if (max_usable_money.second < required_money_by_input_count.at(max_usable_money.first))
+2 -1
View File
@@ -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)
{
+9
View File
@@ -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,6 +128,10 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1
tx_proposal.key_images_sorted,
tx_proposal.sources,
tx_proposal.fee,
cryptonote::transaction_type::TRANSFER,
0, // tx_amount_burnt
{}, // change_masks
{}, // return_enote
encrypted_payment_id);
// add extra payload and sort
+4
View File
@@ -84,6 +84,10 @@ 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;
/// 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
+34 -5
View File
@@ -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,
@@ -196,7 +197,7 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
const std::size_t n_inputs = selected_inputs.size();
CARROT_CHECK_AND_THROW(n_inputs >= CARROT_MIN_TX_INPUTS,
too_few_inputs, "input selection returned too few inputs: " << n_inputs);
CARROT_CHECK_AND_THROW(n_inputs <= CARROT_MAX_TX_OUTPUTS,
CARROT_CHECK_AND_THROW(n_inputs <= CARROT_MAX_TX_INPUTS,
too_few_inputs, "input selection returned too many inputs: " << n_inputs);
CARROT_CHECK_AND_THROW(fee_per_input_count.count(n_inputs),
carrot_logic_error, "BUG: fee_per_input_count populated with holes, missing: " << n_inputs);
@@ -216,8 +217,19 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
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)
{
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 +238,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
tx_proposal_out.tx_type = tx_type;
}
//-------------------------------------------------------------------------------------------------------------------
void make_carrot_transaction_proposal_v1_transfer(
@@ -234,6 +249,7 @@ 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,
@@ -268,7 +284,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,
@@ -314,6 +331,15 @@ void make_carrot_transaction_proposal_v1_transfer(
selfsend_payment_proposals.back().proposal.amount =
boost::numeric_cast<rct::xmr_amount>(implicit_change_amount);
// 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)
{
selfsend_payment_proposals.back().proposal.enote_ephemeral_pubkey =
selfsend_payment_proposals.front().proposal.enote_ephemeral_pubkey;
selfsend_payment_proposals.erase(selfsend_payment_proposals.begin());
}
// deduct an even fee amount from all subtractable outputs
const size_t num_subtractble_normal = subtractable_normal_payment_proposals.size();
const size_t num_subtractable_selfsend = subtractable_selfsend_payment_proposals.size();
@@ -385,6 +411,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,6 +425,7 @@ 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,
@@ -477,6 +505,7 @@ void make_carrot_transaction_proposal_v1_sweep(
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
std::move(select_inputs),
std::move(carve_fees_and_balance),
change_address_spend_pubkey,
+14 -4
View File
@@ -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,6 +207,7 @@ 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,
@@ -212,6 +221,7 @@ 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,
+8
View File
@@ -276,6 +276,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};
// change this code into base 58
return get_account_address_as_str(nettype, false, addr, true);
}
//-----------------------------------------------------------------
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
{
+10 -4
View File
@@ -57,12 +57,16 @@ namespace cryptonote
crypto::secret_key s_master;
crypto::secret_key k_prove_spend;
crypto::secret_key s_view_balance;
crypto::secret_key k_view_incoming;
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)
crypto::public_key carrot_account_spend_pubkey;
crypto::public_key carrot_account_view_pubkey;
// 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)
@@ -100,6 +104,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();}
@@ -135,7 +140,8 @@ namespace cryptonote
private:
void set_null();
account_keys m_keys;
protected:
uint64_t m_creation_timestamp;
account_keys m_keys;
};
}
+37 -3
View File
@@ -197,6 +197,23 @@ namespace cryptonote
};
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()
};
class transaction_prefix
{
@@ -227,6 +244,8 @@ namespace cryptonote
// Slippage limit
uint64_t amount_slippage_limit;
protocol_tx_data_t protocol_tx_data;
BEGIN_SERIALIZE()
VARINT_FIELD(version)
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
@@ -244,8 +263,14 @@ namespace cryptonote
FIELD(return_address_list)
FIELD(return_address_change_mask)
} else {
FIELD(return_address)
FIELD(return_pubkey)
if (type == cryptonote::transaction_type::STAKE &&
version >= TRANSACTION_VERSION_CARROT)
{
FIELD(protocol_tx_data)
} else {
FIELD(return_address)
FIELD(return_pubkey)
}
}
FIELD(source_asset_type)
FIELD(destination_asset_type)
@@ -268,6 +293,10 @@ 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 = {};
source_asset_type.clear();
destination_asset_type.clear();
amount_burnt = 0;
@@ -669,6 +698,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 +713,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 +783,7 @@ 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::transaction, 0xcc);
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
@@ -764,6 +796,7 @@ 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::transaction, "tx");
VARIANT_TAG(json_archive, cryptonote::block, "block");
@@ -776,5 +809,6 @@ 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::transaction, "tx");
VARIANT_TAG(debug_archive, cryptonote::block, "block");
+38 -3
View File
@@ -155,9 +155,12 @@ namespace cryptonote {
network_type nettype
, bool subaddress
, account_public_address const & adr
, bool is_carrot
)
{
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 = 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));
}
@@ -166,9 +169,10 @@ namespace cryptonote {
network_type nettype
, account_public_address const & adr
, crypto::hash8 const & payment_id
, bool is_carrot
)
{
uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
uint64_t integrated_address_prefix = 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 +200,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 +218,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 +285,8 @@ namespace cryptonote {
LOG_PRINT_L1("Failed to validate address keys");
return false;
}
info.address.m_is_carrot = info.is_carrot;
}
else
{
@@ -285,8 +318,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;
};
@@ -87,12 +88,14 @@ namespace cryptonote {
network_type nettype
, bool subaddress
, const account_public_address& adr
, bool is_carrot = false
);
std::string get_account_integrated_address_as_str(
network_type nettype
, const account_public_address& adr
, const crypto::hash8& payment_id
, bool is_carrot = false
);
bool get_account_address_from_str(
@@ -166,6 +166,15 @@ 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::transaction_prefix &x, const boost::serialization::version_type ver)
@@ -183,8 +192,14 @@ 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.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;
@@ -209,8 +224,14 @@ 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.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;
@@ -113,7 +113,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();
@@ -646,7 +646,7 @@ namespace cryptonote
{
CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support non pruned txes");
CHECK_AND_ASSERT_MES(tx.version >= 2, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support v1 txes");
CHECK_AND_ASSERT_MES(tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus || tx.rct_signatures.type == rct::RCTTypeFullProofs,
CHECK_AND_ASSERT_MES(tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus || tx.rct_signatures.type == rct::RCTTypeFullProofs || tx.rct_signatures.type == rct::RCTTypeSalviumZero || tx.rct_signatures.type == rct::RCTTypeSalviumOne,
std::numeric_limits<uint64_t>::max(), "Unsupported rct_signatures type in get_pruned_transaction_weight");
CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin");
CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits<uint64_t>::max(), "empty vin");
+21 -3
View File
@@ -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);
+1
View File
@@ -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;
+81 -59
View File
@@ -44,9 +44,10 @@
#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 4
#define TRANSACTION_VERSION_2_OUTS 2
#define TRANSACTION_VERSION_N_OUTS 3
#define TRANSACTION_VERSION_CARROT 4
#define CURRENT_BLOCK_MAJOR_VERSION 1
#define CURRENT_BLOCK_MINOR_VERSION 1
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
@@ -292,6 +293,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;
@@ -313,24 +317,24 @@ namespace config
std::string const TREASURY_ADDRESS = "SaLvdZR6w1A21sf2Wh6jYEh1wzY4GSbT7RX6FjyPsnLsffWLrzFQeXUXJcmBLRWDzZC2YXeYe5t7qKsnrg9FpmxmEcxPHsEYfqA";
// treasury payout {tx-key, output-key} pairs
const std::vector<std::pair<std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_KEYS = {
{"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8"},
{"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c"},
{"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6"},
{"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88"},
{"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb"},
{"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b"},
{"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c"},
{"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8"},
{"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454"},
{"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42"},
{"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890"},
{"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0"},
{"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e"},
{"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c"},
{"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91"},
{"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007"},
// 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"}},
};
// Hash domain separators
@@ -396,6 +400,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;
@@ -422,24 +429,24 @@ namespace config
std::string const TREASURY_ADDRESS = "SaLvTyLFta9BiAXeUfFkKvViBkFt4ay5nEUBpWyDKewYggtsoxBbtCUVqaBjtcCDyY1euun8Giv7LLEgvztuurLo5a6Km1zskZn36";
// treasury payout {tx-key, output-key} pairs
const std::vector<std::pair<std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_KEYS = {
{"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8"},
{"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c"},
{"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6"},
{"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88"},
{"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb"},
{"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b"},
{"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c"},
{"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8"},
{"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454"},
{"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42"},
{"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890"},
{"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0"},
{"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e"},
{"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c"},
{"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91"},
{"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007"},
// 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"}}
};
}
@@ -448,6 +455,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;
@@ -471,24 +481,24 @@ namespace config
std::string const TREASURY_ADDRESS = "fuLMowH85abK8nz9BBMEem7MAfUbQu4aSHHUV9j5Z86o6Go9Lv2U5ZQiJCWPY9R9HA8p5idburazjAhCqDngLo7fYPCD9ciM9ee1A";
// treasury payout {tx-key, output-key} pairs
const std::vector<std::pair<std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_KEYS = {
{"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8"},
{"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c"},
{"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6"},
{"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88"},
{"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb"},
{"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b"},
{"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c"},
{"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8"},
{"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454"},
{"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42"},
{"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890"},
{"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0"},
{"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e"},
{"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c"},
{"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91"},
{"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007"},
// treasury payout {tx-key, output-key, anchor_enc, view_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"}},
};
}
}
@@ -508,6 +518,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;
@@ -520,7 +533,7 @@ namespace cryptonote
uint64_t TREASURY_SAL1_MINT_PERIOD;
std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> const AUDIT_HARD_FORKS;
std::string TREASURY_ADDRESS;
std::vector<std::pair<std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_KEYS;
std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA;
};
inline const config_t& get_config(network_type nettype)
{
@@ -528,6 +541,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,
@@ -540,12 +556,15 @@ namespace cryptonote
::config::TREASURY_SAL1_MINT_PERIOD,
::config::AUDIT_HARD_FORKS,
::config::TREASURY_ADDRESS,
::config::TREASURY_SAL1_MINT_OUTPUT_KEYS
::config::TREASURY_SAL1_MINT_OUTPUT_DATA
};
static const config_t testnet = {
::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,
@@ -558,12 +577,15 @@ namespace cryptonote
::config::testnet::TREASURY_SAL1_MINT_PERIOD,
::config::testnet::AUDIT_HARD_FORKS,
::config::testnet::TREASURY_ADDRESS,
::config::testnet::TREASURY_SAL1_MINT_OUTPUT_KEYS
::config::testnet::TREASURY_SAL1_MINT_OUTPUT_DATA
};
static const config_t stagenet = {
::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,
@@ -576,7 +598,7 @@ namespace cryptonote
::config::stagenet::TREASURY_SAL1_MINT_PERIOD,
::config::stagenet::AUDIT_HARD_FORKS,
::config::stagenet::TREASURY_ADDRESS,
::config::stagenet::TREASURY_SAL1_MINT_OUTPUT_KEYS
::config::stagenet::TREASURY_SAL1_MINT_OUTPUT_DATA
};
switch (nettype)
{
+220 -58
View File
@@ -1423,21 +1423,20 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
return true;
}
//------------------------------------------------------------------
std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction& tx, const uint64_t payout_index, uint8_t hf_version) const {
std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction& tx, const std::tuple<std::string, std::string, std::string, std::string>& treasury_data, uint8_t hf_version) const {
// find the treasury output
const auto treasury_output_keys = get_config(m_nettype).TREASURY_SAL1_MINT_OUTPUT_KEYS;
const auto expected_output_key = treasury_output_keys[payout_index].second;
const auto &output = std::find_if(tx.vout.begin(), tx.vout.end(), [&expected_output_key](const tx_out &o) {
const auto [tx_key, onetime_address, anchor_enc, viewtag] = treasury_data;
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_key)) {
output_key = epee::string_tools::pod_to_hex(boost::get<txout_to_key>(o.target).key);
} else if (o.target.type() == typeid(txout_to_tagged_key)) {
output_key = epee::string_tools::pod_to_hex(boost::get<txout_to_tagged_key>(o.target).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);
} else {
return false;
}
return output_key == expected_output_key;
return output_key == oa;
});
if (output == tx.vout.end()) {
@@ -1450,13 +1449,14 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
return {false, 0};
}
if (output->target.type() != typeid(txout_to_key)) {
if (output->target.type() != typeid(txout_to_carrot_v1)) {
MERROR_VER("Miner transaction contains treasury output with invalid target type");
return {false, 0};
}
if (boost::get<txout_to_key>(output->target).unlock_time != CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) {
MERROR_VER("Miner transaction contains treasury output with invalid target key");
const cryptonote::txout_to_carrot_v1 target = boost::get<txout_to_carrot_v1>(output->target);
if (target.asset_type != "SAL1") {
MERROR_VER("Miner transaction contains treasury output with invalid asset_type");
return {false, 0};
}
@@ -1468,12 +1468,14 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
{
LOG_PRINT_L3("Blockchain::" << __func__);
// validate treasury payout
size_t treasury_index_in_tx_outputs;
auto [treasury_payout_exist, treasury_payout_index] = check_treasury_payout(m_nettype, boost::get<txin_gen>(b.miner_tx.vin[0]).height, m_hardfork->get_hardforks(), version);
if (treasury_payout_exist) {
// check for treasury payouts
const uint64_t height = boost::get<txin_gen>(b.miner_tx.vin[0]).height;
const auto treasury_payout_data = get_config(m_nettype).TREASURY_SAL1_MINT_OUTPUT_DATA;
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
size_t treasury_index_in_tx_outputs = 0;
if (treasury_payout_exists) {
// check the treasury payout
auto [valid, index_in_tx_outputs] = validate_treasury_payout(b.miner_tx, treasury_payout_index, version);
auto [valid, index_in_tx_outputs] = validate_treasury_payout(b.miner_tx, treasury_payout_data.at(height), version);
if (!valid) {
MERROR_VER("Miner transaction treasury output was invalid");
return false;
@@ -1486,7 +1488,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
for(size_t i = 0; i < b.miner_tx.vout.size(); i++)
{
// skip the treasury output
if (treasury_payout_exist && (i == treasury_index_in_tx_outputs)) {
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) {
continue;
}
money_in_use += b.miner_tx.vout[i].amount;
@@ -1561,6 +1563,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) {
@@ -1569,10 +1572,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 (hf_version >= 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;
}
}
}
@@ -1605,7 +1615,36 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
}
// 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(),
false, "Invalid number of outputs in protocol_tx - aborting"
);
if (hf_version >= HF_VERSION_CARROT) {
// TODO: add other verifications for carrot yield payouts
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 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);
uint8_t hf_yield = m_hardfork->get_ideal_version(it->first.block_height);
if (hf_yield >= HF_VERSION_SALVIUM_ONE_PROOFS)
CHECK_AND_ASSERT_MES(out_asset_type == "SAL1", false, "Incorrect output asset_type (!= SAL1) detected in protocol_tx");
else
CHECK_AND_ASSERT_MES(out_asset_type == "SAL", false, "Incorrect output asset_type (!= SAL) detected in protocol_tx");
}
return true;
}
// Merge the yield and audit payouts into an iterable vector
std::vector<std::pair<yield_tx_info, uint64_t>> payouts{yield_payouts};
@@ -1864,6 +1903,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
@@ -1906,33 +1954,64 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
// 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;
std::vector<std::pair<yield_tx_info_carrot, uint64_t>> carrot_yield_payouts;
if (b.major_version >= 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;
}
}
// 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";
if (b.major_version >= HF_VERSION_CARROT) {
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;
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;
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);
}
}
@@ -2353,7 +2432,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;
}
@@ -3605,10 +3684,25 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
*/
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
// for v5, force the audit and the new SalviumOne RCT data
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) {
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;
return false;
}
} else {
if (tx.rct_signatures.type != rct::RCTTypeNull) {
MERROR_VER("NULL RCT required for coinbase TXs after v" + std::to_string(HF_VERSION_CARROT));
tvc.m_invalid_output = true;
return false;
}
}
} else if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
// for v5, force the audit and the new SalviumZero 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) {
if (tx.rct_signatures.type != rct::RCTTypeSalviumZero) {
MERROR_VER("FullProofs plus Audit data required after v" + std::to_string(HF_VERSION_SALVIUM_ONE_PROOFS));
tvc.m_invalid_output = true;
return false;
@@ -3674,7 +3768,7 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
// 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;
@@ -3682,6 +3776,14 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
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) {
@@ -3725,7 +3827,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, false, "Transaction version is not 2/3/4");
rct::rctSig &rv = tx.rct_signatures;
@@ -3748,7 +3850,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumOne)
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumZero || rv.type == rct::RCTTypeSalviumOne)
{
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size());
@@ -3924,14 +4026,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)
{
@@ -4098,8 +4192,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const rct::rctSig &rv = tx.rct_signatures;
// Check that after full proofs are enabled, the RCT version is set to enforce full proofs
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
if (hf_version >= HF_VERSION_CARROT) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) {
MERROR_VER("Unsupported rct type (Salvium One / SPARC proofs are required): " << rv.type);
return false;
}
} else if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumZero) {
MERROR_VER("Unsupported rct type (full proofs (with audit data) are required): " << rv.type);
return false;
}
@@ -4123,6 +4222,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
case rct::RCTTypeCLSAG:
case rct::RCTTypeBulletproofPlus:
case rct::RCTTypeFullProofs:
case rct::RCTTypeSalviumZero:
case rct::RCTTypeSalviumOne:
{
if (!ver_rct_non_semantics_simple_cached(tx, pubkeys, m_rct_ver_cache, RCT_CACHE_TYPE, hf_version))
@@ -4559,6 +4659,68 @@ 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
int yield_tx_result = 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, 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;
entry.second += yield_128.convert_to<uint64_t>();
}
}
// 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__);
+8 -1
View File
@@ -1175,6 +1175,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
*
@@ -1500,7 +1507,7 @@ namespace cryptonote
*
* @return bool indicating payout valid, and the index of the output within miner transaction outputs.
*/
std::tuple<bool, size_t> validate_treasury_payout(const transaction& tx, const uint64_t payout_index, uint8_t hf_version) const;
std::tuple<bool, size_t> validate_treasury_payout(const transaction& tx, const std::tuple<std::string, std::string, std::string, std::string>& treasury_data, uint8_t hf_version) const;
/**
* @brief sanity checks a miner transaction before validating an entire block
+14 -3
View File
@@ -843,7 +843,9 @@ 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_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
@@ -924,7 +926,7 @@ namespace cryptonote
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_SALVIUM_ONE_PROOFS) {
if (hf_version >= HF_VERSION_CARROT) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) {
MERROR_VER("Invalid RCT type provided");
set_semantics_failed(tx_info[n].tx_hash);
@@ -932,6 +934,14 @@ namespace cryptonote
tx_info[n].result = false;
return false;
}
} else if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumZero) {
MERROR_VER("Invalid RCT type provided");
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
return false;
}
} else if (hf_version >= HF_VERSION_ENFORCE_FULL_PROOFS) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeFullProofs) {
MERROR_VER("Invalid RCT type provided");
@@ -990,6 +1000,7 @@ namespace cryptonote
break;
case rct::RCTTypeBulletproofPlus:
case rct::RCTTypeFullProofs:
case rct::RCTTypeSalviumZero:
case rct::RCTTypeSalviumOne:
if (!is_canonical_bulletproof_plus_layout(rv.p.bulletproofs_plus))
{
@@ -1017,7 +1028,7 @@ namespace cryptonote
{
if (!tx_info[n].result)
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::RCTTypeSalviumOne)
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;
if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures,
tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt :
+59 -73
View File
@@ -337,34 +337,6 @@ namespace cryptonote
}
*/
//---------------------------------------------------------------
std::tuple<bool, uint64_t> check_treasury_payout(
network_type nettype,
uint64_t height,
const std::vector<hardfork_t>& hardforks,
const uint8_t hf_version
) {
if (hf_version >= HF_VERSION_CARROT) {
// find the hardfork height
const auto& hf = std::find_if(hardforks.begin(), hardforks.end(), [](const hardfork_t& hf) {
return hf.version == HF_VERSION_CARROT;
});
// since we are at least at the hard fork HF_VERSION_CARROT, we assume height >= hf->height
const auto diff = height - hf->height;
const auto mint_period = get_config(nettype).TREASURY_SAL1_MINT_PERIOD;
// pay per period
if (diff % mint_period == 0) {
const auto payout_index = diff / mint_period;
if (payout_index < TREASURY_SAL1_MINT_COUNT) {
return {true, payout_index};
}
}
}
return {false, 0};
}
//---------------------------------------------------------------
bool construct_protocol_tx(
const size_t height,
transaction& tx,
@@ -390,29 +362,15 @@ 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;
e.amount = 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);
}
@@ -476,7 +434,8 @@ namespace cryptonote
tx.type = cryptonote::transaction_type::MINER;
// check for treasury payouts
auto[treasury_payout_exist, treasury_payout_index] = check_treasury_payout(nettype, height, hardforks, hard_fork_version);
const auto treasury_payout_data = get_config(nettype).TREASURY_SAL1_MINT_OUTPUT_DATA;
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
uint64_t block_reward;
if(!get_block_reward(median_weight, current_block_weight, already_generated_coins, block_reward, hard_fork_version))
@@ -496,13 +455,61 @@ namespace cryptonote
{
try
{
// Build the miner payout
carrot::CarrotDestinationV1 destination;
carrot::make_carrot_main_address_v1(miner_address.m_spend_public_key,
miner_address.m_view_public_key,
destination);
CHECK_AND_ASSERT_THROW_MES(!destination.is_subaddress,
"make_single_enote_carrot_coinbase_transaction_v1: subaddress are not allowed in miner transactions");
CHECK_AND_ASSERT_THROW_MES(destination.payment_id == carrot::null_payment_id,
"make_single_enote_carrot_coinbase_transaction_v1: integrated addresses are not allowed in miner transactions");
uint64_t stake_reward = block_reward / 5;
tx = carrot::make_single_enote_carrot_coinbase_transaction_v1(destination, block_reward - stake_reward, height, extra_nonce);
const carrot::CarrotPaymentProposalV1 payment_proposal{
.destination = destination,
.amount = block_reward - stake_reward,
.asset_type = "SAL1",
.randomness = carrot::gen_janus_anchor()
};
std::vector<carrot::CarrotCoinbaseEnoteV1> enotes(treasury_payout_exists ? 2 : 1);
carrot::get_coinbase_output_proposal_v1(payment_proposal, height, enotes.front());
// Check to see if there needs to be a treasury payout
if (treasury_payout_exists) {
// Convert the strings into meaningful data
const auto [tx_public_key_str, onetime_address_str, anchor_enc_str, view_tag_str] = treasury_payout_data.at(height);
mx25519_pubkey tx_public_key;
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(tx_public_key_str, tx_public_key), "fail to deserialize treasury tx public key");
crypto::public_key onetime_address;
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(onetime_address_str, onetime_address), "fail to deserialize treasury tx onetime address");
carrot::encrypted_janus_anchor_t anchor_enc;
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(anchor_enc_str, anchor_enc), "fail to deserialize treasury tx anchor_enc");
carrot::view_tag_t view_tag;
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(view_tag_str, view_tag), "fail to deserialize treasury tx view_tag");
// Manually produce an enote for the treasury payout using the hardcoded keys
carrot::CarrotCoinbaseEnoteV1 &treasury_enote = enotes.back();
treasury_enote.onetime_address = onetime_address;
treasury_enote.amount = TREASURY_SAL1_MINT_AMOUNT;
treasury_enote.asset_type = "SAL1";
treasury_enote.anchor_enc = anchor_enc;
treasury_enote.view_tag = view_tag;
treasury_enote.enote_ephemeral_pubkey = tx_public_key;
treasury_enote.block_index = height;
// sort enotes by K_o
if (enotes[0].onetime_address > enotes[1].onetime_address) {
std::swap(enotes[0], enotes[1]);
}
}
tx = carrot::store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER, height);
tx.amount_burnt = stake_reward;
tx.invalidate_hashes();
}
@@ -573,7 +580,7 @@ namespace cryptonote
std::string asset_type = "SAL";
if (hard_fork_version >= HF_VERSION_SALVIUM_ONE_PROOFS)
asset_type = "SAL1";
cryptonote::set_tx_out(amount, asset_type, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, !treasury_payout_exist, view_tag, out);
cryptonote::set_tx_out(amount, asset_type, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, use_view_tags, view_tag, out);
tx.vout.push_back(out);
} else {
@@ -586,27 +593,6 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
// add the treasury payout if needed
if (treasury_payout_exist) {
std::vector<crypto::public_key> additional_tx_public_keys = {txkey.pub};
const auto output_keys = get_config(nettype).TREASURY_SAL1_MINT_OUTPUT_KEYS;
const auto keys = output_keys[treasury_payout_index];
crypto::public_key tx_key;
CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(keys.first, tx_key), false, "fail to deserialize treasury tx key");
crypto::public_key output_key;
CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(keys.second, output_key), false, "fail to deserialize treasury output key");
// Create the TX output for this payout
tx_out out;
cryptonote::set_tx_out(TREASURY_SAL1_MINT_AMOUNT, "SAL1", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, output_key, false, crypto::view_tag{}, out);
tx.vout.push_back(out);
// add tx pub key to tx extra
additional_tx_public_keys.push_back(tx_key);
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
}
tx.version = 2;
tx.vin.push_back(in);
tx.invalidate_hashes();
+2 -2
View File
@@ -68,6 +68,8 @@ 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;
};
@@ -75,8 +77,6 @@ namespace cryptonote
bool construct_protocol_tx(const size_t height, transaction& tx, std::vector<protocol_data_entry>& protocol_data, const uint8_t hf_version);
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, network_type nettype = network_type::FAKECHAIN, const std::vector<hardfork_t>& hardforks = {}, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
//---------------------------------------------------------------
std::tuple<bool, uint64_t> check_treasury_payout(network_type nettype, uint64_t height, const std::vector<hardfork_t>& hardforks, const uint8_t hf_version);
struct tx_source_entry
{
@@ -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 > 4 || 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
+10 -1
View File
@@ -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;
}
+30 -2
View File
@@ -1352,10 +1352,10 @@ 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);
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address, is_carrot);
req.threads_count = num_threads;
req.do_background_mining = do_background_mining;
req.ignore_battery = ignore_battery;
@@ -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;
+3 -1
View File
@@ -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();
+3 -3
View File
@@ -1930,7 +1930,7 @@ namespace hw {
// ====== Aout, Bout, AKout, C, v, k ======
kv_offset = data_offset;
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs) {
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs || type==rct::RCTTypeSalviumZero || type==rct::RCTTypeSalviumOne) {
C_offset = kv_offset+ (8)*outputs_size;
} else {
C_offset = kv_offset+ (32+32)*outputs_size;
@@ -1947,7 +1947,7 @@ namespace hw {
offset = set_command_header(INS_VALIDATE, 0x02, i+1);
//options
this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ;
this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs)?0x02:0x00;
this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs || type==rct::RCTTypeSalviumZero || type==rct::RCTTypeSalviumOne)?0x02:0x00;
offset += 1;
//is_subaddress
this->buffer_send[offset] = outKeys.is_subaddress;
@@ -1968,7 +1968,7 @@ namespace hw {
memmove(this->buffer_send+offset, data+C_offset,32);
offset += 32;
C_offset += 32;
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs) {
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs || type==rct::RCTTypeSalviumZero || type==rct::RCTTypeSalviumOne) {
//k
memset(this->buffer_send+offset, 0, 32);
offset += 32;
+4 -2
View File
@@ -857,6 +857,8 @@ static bool set_tx_rct_signatures(
rv.type = rct::RCTTypeBulletproofPlus;
else if (rct_config.bp_version == 5)
rv.type = rct::RCTTypeFullProofs;
else if (rct_config.bp_version == 6)
rv.type = rct::RCTTypeSalviumZero;
else
return false;
rv.txnFee = fee;
@@ -934,7 +936,7 @@ static bool set_tx_rct_signatures(
}
sc_sub(difference.bytes, sumpouts.bytes, sumouts.bytes);
rct::genC(rv.p_r, difference, 0);
if (rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumOne) {
if (rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumZero || rv.type == rct::RCTTypeSalviumOne) {
rv.salvium_data.pr_proof = rct::PRProof_Gen(difference);
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(rct::PRProof_Ver(rv.p_r, rv.salvium_data.pr_proof), "PRProof_Ver() failed on recently created proof");
@@ -954,7 +956,7 @@ static bool set_tx_rct_signatures(
// check balance if reconstructing the tx
else {
rv.p.pseudoOuts = unsigned_tx.rct_signatures.p.pseudoOuts;
if (rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumOne) {
if (rv.type == rct::RCTTypeFullProofs || rv.type == rct::RCTTypeSalviumZero || rv.type == rct::RCTTypeSalviumOne) {
if (!rct::PRProof_Ver(unsigned_tx.rct_signatures.p_r, unsigned_tx.rct_signatures.salvium_data.pr_proof))
return false;
rv.p_r = unsigned_tx.rct_signatures.p_r;
+6 -2
View File
@@ -1579,7 +1579,7 @@ namespace rct {
DP(rv.p_r);
// set salvium_data
if (rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumZero || rv.type == RCTTypeSalviumOne) {
if (rv.type == RCTTypeSalviumOne) {
rv.salvium_data.pr_proof = PRProof_Gen(difference);
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.p_r, rv.salvium_data.pr_proof), "PRProof_Ver() failed on recently created proof");
@@ -1611,8 +1611,12 @@ namespace rct {
{
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
rv.p.TCLSAGs[i] = make_dummy_tclsag(rv.mixRing[i].size());
else
else {
rv.p.TCLSAGs[i] = proveRctTCLSAGSimple(full_message, rv.mixRing[i], inSk[i].x, inSk[i].y, inSk[i].mask, a[i], pseudoOuts[i], index[i], hwdev);
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verRctTCLSAGSimple(full_message, rv.p.TCLSAGs[i], rv.mixRing[i], pseudoOuts[i]), "T-CLSAG verification failed");
#endif
}
}
}
+3 -2
View File
@@ -446,7 +446,7 @@ namespace rct {
return false;
for (size_t i = 0; i < outputs; ++i)
{
if (type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumZero)
if (type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumZero || type == RCTTypeSalviumOne)
{
// Since RCTTypeBulletproof2 enote types, we don't serialize the blinding factor, and only serialize the
// first 8 bytes of ecdhInfo[i].amount
@@ -504,7 +504,7 @@ namespace rct {
FIELD(outPk)
VARINT_FIELD(txnFee)
FIELD(p_r)
if (type == RCTTypeSalviumZero)
if (type == RCTTypeSalviumZero || type == RCTTypeSalviumOne)
{
FIELD(salvium_data)
}
@@ -673,6 +673,7 @@ namespace rct {
FIELD(bulletproofs_plus)
FIELD(MGs)
FIELD(CLSAGs)
FIELD(TCLSAGs)
FIELD(pseudoOuts)
END_SERIALIZE()
};
+23 -1
View File
@@ -1432,7 +1432,7 @@ namespace cryptonote
RPC_TRACKER(start_mining);
CHECK_CORE_READY();
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, nettype(), req.miner_address))
if(!get_account_address_from_str(info, m_core.get_nettype(), req.miner_address))
{
res.status = "Failed, wrong address";
LOG_PRINT_L0(res.status);
@@ -1445,6 +1445,17 @@ namespace cryptonote
return true;
}
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
if (info.is_carrot && version < HF_VERSION_CARROT) {
res.status = "Mining to Carrot wallet address, but Carrot isn't supported yet";
LOG_PRINT_L0(res.status);
return true;
} else if (!info.is_carrot && version >= HF_VERSION_CARROT) {
res.status = "Mining to CryptoNote wallet address, but Carrot wallet address is required";
LOG_PRINT_L0(res.status);
return true;
}
unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
// if we couldn't detect threads, set it to a ridiculously high number
@@ -1958,6 +1969,17 @@ namespace cryptonote
return false;
}
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
if (info.is_carrot && version < HF_VERSION_CARROT) {
error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
error_resp.message = "Mining to Carrot wallet address, but Carrot isn't supported yet";
return false;
} else if (!info.is_carrot && version >= HF_VERSION_CARROT) {
error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
error_resp.message = "Mining to CryptoNote wallet address, but Carrot wallet address is required";
return false;
}
block b;
cryptonote::blobdata blob_reserve;
size_t reserved_offset;
+45 -6
View File
@@ -277,8 +277,14 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
INSERT_INTO_JSON_OBJECT(dest, return_address_list, tx.return_address_list);
INSERT_INTO_JSON_OBJECT(dest, return_address_change_mask, tx.return_address_change_mask);
} else {
INSERT_INTO_JSON_OBJECT(dest, return_address, tx.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, tx.return_pubkey);
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
INSERT_INTO_JSON_OBJECT(dest, protocol_tx_data, tx.protocol_tx_data);
} else {
INSERT_INTO_JSON_OBJECT(dest, return_address, tx.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, tx.return_pubkey);
}
}
INSERT_INTO_JSON_OBJECT(dest, source_asset_type, tx.source_asset_type);
INSERT_INTO_JSON_OBJECT(dest, destination_asset_type, tx.destination_asset_type);
@@ -320,8 +326,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
GET_FROM_JSON_OBJECT(val, tx.return_address_list, return_address_list);
GET_FROM_JSON_OBJECT(val, tx.return_address_change_mask, return_address_change_mask);
} else {
GET_FROM_JSON_OBJECT(val, tx.return_address, return_address);
GET_FROM_JSON_OBJECT(val, tx.return_pubkey, return_pubkey);
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
GET_FROM_JSON_OBJECT(val, tx.protocol_tx_data, protocol_tx_data);
} else {
GET_FROM_JSON_OBJECT(val, tx.return_address, return_address);
GET_FROM_JSON_OBJECT(val, tx.return_pubkey, return_pubkey);
}
}
GET_FROM_JSON_OBJECT(val, tx.source_asset_type, source_asset_type);
GET_FROM_JSON_OBJECT(val, tx.destination_asset_type, destination_asset_type);
@@ -506,6 +518,33 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
}
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, version, ptd.version);
INSERT_INTO_JSON_OBJECT(dest, return_address, ptd.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, ptd.return_pubkey);
INSERT_INTO_JSON_OBJECT(dest, return_view_tag, ptd.return_view_tag);
INSERT_INTO_JSON_OBJECT(dest, return_anchor_enc, ptd.return_anchor_enc);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t& ptd)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, ptd.version, version);
GET_FROM_JSON_OBJECT(val, ptd.return_address, return_address);
GET_FROM_JSON_OBJECT(val, ptd.return_pubkey, return_pubkey);
GET_FROM_JSON_OBJECT(val, ptd.return_view_tag, return_view_tag);
GET_FROM_JSON_OBJECT(val, ptd.return_anchor_enc, return_anchor_enc);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin)
{
dest.StartObject();
@@ -1201,7 +1240,7 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig&
INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask));
INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee);
INSERT_INTO_JSON_OBJECT(dest, p_r, sig.p_r);
if (sig.type == rct::RCTTypeSalviumOne) {
if (sig.type == rct::RCTTypeSalviumZero || sig.type == rct::RCTTypeSalviumOne) {
INSERT_INTO_JSON_OBJECT(dest, salvium_data, sig.salvium_data);
} else if (sig.type == rct::RCTTypeFullProofs) {
INSERT_INTO_JSON_OBJECT(dest, pr_proof, sig.salvium_data.pr_proof);
@@ -1245,7 +1284,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
GET_FROM_JSON_OBJECT(val, sig.outPk, commitments);
GET_FROM_JSON_OBJECT(val, sig.txnFee, fee);
GET_FROM_JSON_OBJECT(val, sig.p_r, p_r);
if (sig.type == rct::RCTTypeSalviumOne) {
if (sig.type == rct::RCTTypeSalviumZero || sig.type == rct::RCTTypeSalviumOne) {
GET_FROM_JSON_OBJECT(val, sig.salvium_data, salvium_data);
} else if (sig.type == rct::RCTTypeFullProofs) {
GET_FROM_JSON_OBJECT(val, sig.salvium_data.pr_proof, pr_proof);
+3
View File
@@ -218,6 +218,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd);
void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t& ptd);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_target_v& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout);
+90 -18
View File
@@ -79,6 +79,9 @@
#include "wallet/message_store.h"
#include "QrCode.hpp"
#include "carrot_impl/subaddress_index.h"
#include "carrot_core/core_types.h"
#ifdef WIN32
#include <boost/locale.hpp>
#include <boost/filesystem.hpp>
@@ -854,6 +857,40 @@ std::string simple_wallet::get_command_usage(const std::vector<std::string> &arg
return ss.str();
}
bool simple_wallet::carrot_keys(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
// don't log
PAUSE_READLINE();
if (m_wallet->key_on_device()) {
std::cout << "secret: On device. Not available" << std::endl;
} else {
SCOPED_WALLET_UNLOCK();
printf("master secret: ");
print_secret_key(m_wallet->get_account().get_keys().s_master);
putchar('\n');
printf("prove-spend key: ");
print_secret_key(m_wallet->get_account().get_keys().k_prove_spend);
putchar('\n');
printf("view-balance secret: ");
print_secret_key(m_wallet->get_account().get_keys().s_view_balance);
putchar('\n');
printf("generate-image key: ");
print_secret_key(m_wallet->get_account().get_keys().k_generate_image);
putchar('\n');
printf("view-incoming key: ");
print_secret_key(m_wallet->get_account().get_keys().k_view_incoming);
putchar('\n');
printf("generate-address secret: ");
print_secret_key(m_wallet->get_account().get_keys().s_generate_address);
putchar('\n');
}
// TODO: print the public wallet address for the different wallet tiers
//std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
return true;
}
bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
// don't log
@@ -866,7 +903,8 @@ bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
putchar('\n');
}
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
std::cout << "CN public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
std::cout << "Carrot public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_view_public_key) << std::endl;
return true;
}
@@ -889,7 +927,8 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
putchar('\n');
}
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
std::cout << "CN public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
std::cout << "Carrot public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_spend_public_key) << std::endl;
return true;
}
@@ -3599,6 +3638,9 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("save_watch_only",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::save_watch_only, _1),
tr("Save a watch-only keys file."));
m_cmd_binder.set_handler("carrot_keys",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::carrot_keys, _1),
tr("Display the Carrot private keys."));
m_cmd_binder.set_handler("viewkey",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::viewkey, _1),
tr("Display the private view key."));
@@ -5196,8 +5238,17 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
try
{
recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file);
message_writer(console_color_white, true) << tr("Generated new wallet: ")
message_writer(console_color_white, true) << tr("Generated new legacy (CN) wallet: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
const auto addr = m_wallet->get_account().cryptonote_address(carrot::null_payment_id, carrot::AddressDeriveType::Carrot);
cryptonote::account_public_address carrot_address {
.m_spend_public_key = addr.address_spend_pubkey,
.m_view_public_key = addr.address_view_pubkey,
.m_is_carrot = true
};
message_writer(console_color_white, true) << tr("Generated new Carrot wallet: ")
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address, true);
PAUSE_READLINE();
std::cout << tr("View key: ");
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
@@ -5271,8 +5322,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
{
m_wallet->generate(m_wallet_file, std::move(rc.second).password(), address, viewkey, create_address_file);
}
message_writer(console_color_white, true) << tr("Generated new wallet: ")
message_writer(console_color_white, true) << tr("Generated new legacy wallet: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
message_writer(console_color_white, true) << tr("Generated new carrot wallet: ")
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, m_wallet->get_account().get_keys().m_carrot_account_address);
}
catch (const std::exception& e)
{
@@ -5431,9 +5485,13 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p
else if (m_wallet->is_background_wallet())
prefix = tr("Opened background wallet");
else
prefix = tr("Opened wallet");
prefix = tr("Opened legacy (CN) wallet");
message_writer(console_color_white, true) <<
prefix << ": " << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
prefix << ": " << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
prefix = tr("Opened Carrot wallet");
message_writer(console_color_white, true) <<
prefix << ": " << m_wallet->get_account().get_carrot_public_address_str(m_wallet->nettype());
if (m_wallet->get_account().get_device()) {
message_writer(console_color_white, true) << "Wallet is on device: " << m_wallet->get_account().get_device().get_name();
}
@@ -5593,7 +5651,11 @@ void simple_wallet::start_background_mining()
{
COMMAND_RPC_START_MINING::request req;
COMMAND_RPC_START_MINING::response res;
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
uint8_t hf_version = m_wallet->get_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
req.miner_address = m_wallet->get_account().get_carrot_public_address_str(m_wallet->nettype());
else
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
req.threads_count = 1;
req.do_background_mining = true;
req.ignore_battery = false;
@@ -5717,7 +5779,11 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
return true;
}
COMMAND_RPC_START_MINING::request req = AUTO_VAL_INIT(req);
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
uint8_t hf_version = m_wallet->get_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
req.miner_address = m_wallet->get_account().get_carrot_public_address_str(m_wallet->nettype());
else
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
bool ok = true;
size_t arg_size = args.size();
@@ -6320,6 +6386,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account];
success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag);
std::vector<std::string> asset_types = m_wallet->list_asset_types();
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
for (auto& asset: asset_types) {
uint64_t blocks_to_unlock, time_to_unlock;
uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, asset, false, &blocks_to_unlock, &time_to_unlock);
@@ -6343,8 +6410,10 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
m_wallet->get_transfers(transfers);
for (const auto& i : balance_per_subaddress)
{
carrot::subaddress_index_extended subaddr = {{m_current_subaddress_account, i.first},
is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot};
cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first};
std::string address_str = m_wallet->get_subaddress_as_str(subaddr_index).substr(0, 6);
std::string address_str = m_wallet->get_subaddress_as_str(subaddr).substr(0, 6);
uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == subaddr_index; });
success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
}
@@ -8084,7 +8153,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
try
{
// figure out what tx will be necessary
auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra);
auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, cryptonote::transaction_type::TRANSFER, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra);
if (ptx_vector.empty())
{
@@ -8357,8 +8426,8 @@ bool simple_wallet::return_payment(const std::vector<std::string> &args_)
asset_type = td.asset_type;
}
std::ostringstream prompt;
if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
return true;
// if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
// return true;
prompt << boost::format(tr("Returning %s %s for a total fee of %s %s. Is this okay?")) %
print_money(total_sent) % asset_type %
print_money(total_fee) % asset_type;
@@ -8789,29 +8858,31 @@ bool simple_wallet::donate(const std::vector<std::string> &args_)
fail_msg_writer() << tr("amount is wrong: ") << local_args.back() << ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max());
return true;
}
uint8_t hf_version = m_wallet->get_current_hard_fork();
// push back address, amount, payment id
std::string address_str;
std::string DONATION_ADDR = (hf_version >= HF_VERSION_CARROT) ? SALVIUM_DONATION_ADDR_CARROT : SALVIUM_DONATION_ADDR;
if (m_wallet->nettype() != cryptonote::MAINNET)
{
// if not mainnet, convert donation address string to the relevant network type
address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, SALVIUM_DONATION_ADDR))
if (!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, DONATION_ADDR))
{
fail_msg_writer() << tr("Failed to parse donation address: ") << SALVIUM_DONATION_ADDR;
fail_msg_writer() << tr("Failed to parse donation address: ") << DONATION_ADDR;
return true;
}
address_str = cryptonote::get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
}
else
{
address_str = SALVIUM_DONATION_ADDR;
address_str = DONATION_ADDR;
}
local_args.push_back(address_str);
local_args.push_back(amount_str);
if (!payment_id_str.empty())
local_args.push_back(payment_id_str);
if (m_wallet->nettype() == cryptonote::MAINNET)
message_writer() << (boost::format(tr("Donating %s %s to The Salvium Team (donate.salvium.io or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % SALVIUM_DONATION_ADDR).str();
message_writer() << (boost::format(tr("Donating %s %s to The Salvium Team (donate.salvium.io or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % DONATION_ADDR).str();
else
message_writer() << (boost::format(tr("Donating %s %s to %s.")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % address_str).str();
transfer(local_args);
@@ -10749,15 +10820,16 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
std::vector<std::string> local_args = args;
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
auto print_address_sub = [this, &transfers](uint32_t index)
auto print_address_sub = [this, &transfers, is_carrot](uint32_t index)
{
bool used = std::find_if(
transfers.begin(), transfers.end(),
[this, &index](const tools::wallet2::transfer_details& td) {
return td.m_subaddr_index == cryptonote::subaddress_index{ m_current_subaddress_account, index };
}) != transfers.end();
success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({m_current_subaddress_account, index}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
};
uint32_t index = 0;
+5 -2
View File
@@ -53,8 +53,10 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.simplewallet"
// Hardcode Monero's donation address (see #1447)
constexpr const char SALVIUM_DONATION_ADDR[] = "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H";
// Hardcode Salvium's donation address (pre-Carrot and post-Carrot)
constexpr const char SALVIUM_DONATION_ADDR[] = "SaLvdVvjgeffnxRDFoEEgeSt3jVcU49W7byT6nawvJFVio9XR7V53tU4mdo3HjjKno2WApvqxmcmKLZyoeSZZQ6jVqSGj1TLsNL";
constexpr const char SALVIUM_DONATION_ADDR_CARROT[] = "SC11sFBPrGmNuT8AiTPUW479BwkdPJwBxdjKEhZ96yDfFg3B4mawgcpE1YfCAa1zwzUiRTMP9eqB54av48ALhzUu1Q5QoPGUfh";
/*!
* \namespace cryptonote
@@ -107,6 +109,7 @@ namespace cryptonote
boost::optional<epee::wipeable_string> open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
bool carrot_keys(const std::vector<std::string> &args = std::vector<std::string>());
bool viewkey(const std::vector<std::string> &args = std::vector<std::string>());
bool spendkey(const std::vector<std::string> &args = std::vector<std::string>());
bool seed(const std::vector<std::string> &args = std::vector<std::string>());
+1 -1
View File
@@ -1,5 +1,5 @@
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
#define DEF_SALVIUM_VERSION "0.9.9-rc1"
#define DEF_SALVIUM_VERSION "0.9.9-rc4"
#define DEF_MONERO_VERSION_TAG "release"
#define DEF_MONERO_VERSION "0.18.3.4"
#define DEF_MONERO_RELEASE_NAME "One"
+185 -97
View File
@@ -55,7 +55,7 @@ namespace wallet
{
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static bool parse_tx_extra_for_scanning(const std::vector<std::uint8_t> &tx_extra,
bool parse_tx_extra_for_scanning(const std::vector<std::uint8_t> &tx_extra,
const std::size_t n_outputs,
std::vector<crypto::public_key> &main_tx_ephemeral_pubkeys_out,
std::vector<crypto::public_key> &additional_tx_ephemeral_pubkeys_out,
@@ -142,13 +142,11 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_pre_car
const std::optional<carrot::encrypted_payment_id_t> &encrypted_payment_id,
const epee::span<const crypto::key_derivation> main_derivations,
const epee::span<const crypto::key_derivation> additional_derivations,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
hw::device &hwdev)
{
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map = subaddress_keystore.get_all();
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map = account.get_subaddress_map_cn();
boost::optional<cryptonote::subaddress_receive_info> receive_info;
size_t main_deriv_idx;
for (main_deriv_idx = 0; main_deriv_idx < std::max<size_t>(1, main_derivations.size()); ++main_deriv_idx)
@@ -237,11 +235,15 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_pre_car
//j
const carrot::subaddress_index_extended subaddr_index{
.index = {receive_info->index.major, receive_info->index.minor},
.derive_type = carrot::AddressDeriveType::PreCarrot
.derive_type = carrot::AddressDeriveType::PreCarrot,
.is_return_spend_key = true
};
// add the entry to our temporary subaddress map via the keystore
subaddress_keystore.add(enote.onetime_address, {subaddr_index.index.major, subaddr_index.index.minor});
// HERE BE DRAGONS!!!
// SRCG: whilst the following code will work, it'd be better being moved to the TX_BUILDER code
// add the entry to our subaddress map in case it's a change payment (false positives won't hurt us)
account.insert_subaddresses({{enote.onetime_address, subaddr_index}});
// LAND AHOY!!!
return enote_view_incoming_scan_info_t{
.sender_extension_g = sender_extension_g,
@@ -252,33 +254,74 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_pre_car
.amount = amount,
.amount_blinding_factor = amount_blinding_factor,
.asset_type = enote.asset_type,
.main_tx_pubkey_index = main_deriv_idx
.main_tx_pubkey_index = main_deriv_idx,
.is_carrot = false,
.is_return = false
};
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_coinbase_enote(
const carrot::CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
const crypto::public_key &main_address_spend_pubkey)
const crypto::public_key &main_address_spend_pubkey,
carrot::carrot_and_legacy_account &account)
{
enote_view_incoming_scan_info_t res;
bool found_in_return = false;
if (!carrot::try_scan_carrot_coinbase_enote_receiver(enote,
s_sender_receiver_unctx,
main_address_spend_pubkey,
res.sender_extension_g,
res.sender_extension_t))
return std::nullopt;
{
// 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 std::nullopt;
found_in_return = true;
}
if (found_in_return) {
// scan the return output
crypto::public_key address_spend_pubkey;
carrot::encrypted_amount_t amount_enc;
crypto::secret_key amount_blinding_factor;
rct::xmr_amount amount;
if (!scan_return_output(
enote.onetime_address,
enote.enote_ephemeral_pubkey,
enote.view_tag,
enote.anchor_enc,
amount_enc,
std::nullopt, // no amount commitment for coinbase enotes
account.get_return_output_map_ref()
.at(enote.onetime_address).input_context,
account,
address_spend_pubkey,
amount,
amount_blinding_factor)
) {
return std::nullopt;
}
res.address_spend_pubkey = address_spend_pubkey;
res.return_address = enote.onetime_address;
res.is_return = true;
} else {
// we received a coinbase enote
res.address_spend_pubkey = main_address_spend_pubkey;
res.is_return = false;
}
res.address_spend_pubkey = main_address_spend_pubkey;
res.payment_id = crypto::null_hash;
res.subaddr_index = carrot::subaddress_index_extended{{0, 0}};
res.amount = enote.amount;
res.asset_type = enote.asset_type;
res.amount_blinding_factor = rct::I;
res.main_tx_pubkey_index = 0;
res.is_carrot = true;
return res;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -322,6 +365,8 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
res.amount_blinding_factor = rct::sk2rct(amount_blinding_factor_sk);
res.main_tx_pubkey_index = 0;
res.asset_type = enote.asset_type;
res.is_carrot = true;
res.is_return = false;
return res;
}
@@ -336,14 +381,17 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
const mx25519_pubkey &s_sender_receiver_unctx,
const crypto::public_key &main_address_spend_pubkey,
const carrot::view_incoming_key_device &k_view_dev,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore)
carrot::carrot_and_legacy_account &account)
{
enote_view_incoming_scan_info_t res;
// assume not a return output
res.is_return = false;
crypto::secret_key amount_blinding_factor_sk;
carrot::payment_id_t payment_id;
carrot::CarrotEnoteType dummy_enote_type;
carrot::janus_anchor_t internal_message;
if (!carrot::try_scan_carrot_enote_external_receiver(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
@@ -356,31 +404,48 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
amount_blinding_factor_sk,
payment_id,
dummy_enote_type))
return std::nullopt;
{
if (!carrot::try_scan_carrot_enote_internal_receiver(enote,
account,
res.sender_extension_g,
res.sender_extension_t,
res.address_spend_pubkey,
res.amount,
amount_blinding_factor_sk,
dummy_enote_type,
internal_message,
res.return_address,
res.is_return))
{
return std::nullopt;
}
}
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map = subaddress_keystore.get_all();
const auto subaddr_it = subaddress_map.find(res.address_spend_pubkey);
CHECK_AND_ASSERT_MES(subaddr_it != subaddress_map.cend(),
std::nullopt,
"view_incoming_scan_carrot_enote: carrot enote scanned successfully, "
"but the recovered address spend pubkey was not found in the subaddress map");
if (!res.is_return) {
const auto subaddr_it = account.get_subaddress_map_ref().find(res.address_spend_pubkey);
CHECK_AND_ASSERT_MES(subaddr_it != account.get_subaddress_map_ref().cend(),
std::nullopt,
"view_incoming_scan_carrot_enote: carrot enote scanned successfully, "
"but the recovered address spend pubkey was not found in the subaddress map");
const carrot::subaddress_index_extended subaddr_index = subaddr_it->second;
memset(&res.payment_id, 0, sizeof(res.payment_id));
memcpy(&res.payment_id, &payment_id, sizeof(carrot::payment_id_t));
res.subaddr_index = subaddr_index;
} else {
res.subaddr_index = {{0, 0}, carrot::AddressDeriveType::Carrot, true};
}
const carrot::subaddress_index_extended subaddr_index = {{subaddr_it->second.major, subaddr_it->second.minor}};
memset(&res.payment_id, 0, sizeof(res.payment_id));
memcpy(&res.payment_id, &payment_id, sizeof(carrot::payment_id_t));
res.subaddr_index = subaddr_index;
res.amount_blinding_factor = rct::sk2rct(amount_blinding_factor_sk);
res.main_tx_pubkey_index = 0;
res.asset_type = enote.asset_type;
res.is_carrot = true;
return res;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static void perform_ecdh_derivations(const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys,
void perform_ecdh_derivations(const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys,
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys,
const crypto::secret_key &k_view_incoming,
hw::device &hwdev,
@@ -391,7 +456,7 @@ static void perform_ecdh_derivations(const epee::span<const crypto::public_key>
main_derivations_out.clear();
additional_derivations_out.clear();
main_derivations_out.reserve(main_tx_ephemeral_pubkeys.size());
additional_derivations_out.reserve(additional_derivations_out.size());
additional_derivations_out.reserve(additional_tx_ephemeral_pubkeys.size());
if (is_carrot)
{
@@ -468,8 +533,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
const epee::span<const crypto::key_derivation> additional_derivations,
const cryptonote::account_public_address &address,
const carrot::view_incoming_key_device *k_view_dev,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
hw::device &hwdev)
{
CHECK_AND_ASSERT_MES(!main_derivations.empty() || !additional_derivations.empty(),
@@ -495,7 +559,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
encrypted_payment_id,
main_derivations,
additional_derivations,
subaddress_keystore,
account,
hwdev);
// copy long plaintext payment ID, if applicable
@@ -515,7 +579,8 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
return view_incoming_scan_carrot_coinbase_enote(enote,
s_sender_receiver_unctx,
address.m_spend_public_key);
address.m_spend_public_key,
account);
}
std::optional<enote_view_incoming_scan_info_t> operator()(const carrot::CarrotEnoteV1 &enote) const
@@ -540,7 +605,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
s_sender_receiver_unctx,
address.m_spend_public_key,
*k_view_dev,
subaddress_keystore);
account);
}
}
@@ -551,8 +616,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
const epee::span<const crypto::key_derivation> additional_derivations;
const cryptonote::account_public_address &address;
const carrot::view_incoming_key_device *k_view_dev;
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map;
tools::keystore &subaddress_keystore;
carrot::carrot_and_legacy_account &account;
hw::device &hwdev;
};
@@ -564,7 +628,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
additional_derivations,
address,
k_view_dev,
subaddress_keystore,
account,
hwdev
},
enote);
@@ -580,8 +644,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
const epee::span<const crypto::key_derivation> additional_derivations,
const cryptonote::account_public_address &address,
const carrot::view_incoming_key_device *k_view_dev,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
hw::device &hwdev)
{
MoneroEnoteVariant enote;
@@ -638,7 +701,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
additional_derivations,
address,
k_view_dev,
subaddress_keystore,
account,
hwdev);
}
//-------------------------------------------------------------------------------------------------------------------
@@ -649,8 +712,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote_from_pre
const std::size_t local_output_index,
const cryptonote::account_public_address &address,
const crypto::secret_key &k_view_incoming,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
hw::device &hwdev)
{
const bool is_carrot = carrot::is_carrot_transaction_v1(tx_prefix);
@@ -703,7 +765,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote_from_pre
epee::to_span(additional_derivations),
address,
&k_view_dev,
subaddress_keystore,
account,
hwdev);
}
//-------------------------------------------------------------------------------------------------------------------
@@ -714,9 +776,7 @@ void view_incoming_scan_transaction(
const cryptonote::blobdata &tx_extra_nonce,
const epee::span<const crypto::key_derivation> main_derivations,
const epee::span<const crypto::key_derivation> additional_derivations,
const cryptonote::account_keys &acc,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out)
{
const size_t n_outputs = tx.vout.size();
@@ -725,31 +785,35 @@ void view_incoming_scan_transaction(
"view_incoming_scan_transaction: enote scan span wrong length");
//! @TODO: HW device
carrot::view_incoming_key_ram_borrowed_device k_view_dev(acc.m_view_secret_key);
const bool is_carrot = carrot::is_carrot_transaction_v1(tx);
carrot::view_incoming_key_ram_borrowed_device k_view_dev(
is_carrot ? account.get_keys().k_view_incoming
: account.get_keys().m_view_secret_key
);
const cryptonote::account_public_address &address =
is_carrot ? account.get_keys().m_carrot_account_address : account.get_keys().m_account_address;
// do view-incoming scan for each output enotes
for (size_t local_output_index = 0; local_output_index < n_outputs; ++local_output_index)
{
auto &enote_scan_info = const_cast<std::optional<enote_view_incoming_scan_info_t>&>(enote_scan_infos_out[local_output_index]);
enote_scan_info = view_incoming_scan_enote(tx,
local_output_index,
main_tx_ephemeral_pubkeys,
additional_tx_ephemeral_pubkeys,
tx_extra_nonce,
main_derivations,
additional_derivations,
acc.m_account_address,
&k_view_dev,
subaddress_keystore,
acc.get_device());
local_output_index,
main_tx_ephemeral_pubkeys,
additional_tx_ephemeral_pubkeys,
tx_extra_nonce,
main_derivations,
additional_derivations,
address,
&k_view_dev,
account,
account.get_keys().get_device());
}
}
//-------------------------------------------------------------------------------------------------------------------
void view_incoming_scan_transaction(
const cryptonote::transaction &tx,
const cryptonote::account_keys &acc,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out)
{
// 1. parse tx extra
@@ -769,34 +833,37 @@ void view_incoming_scan_transaction(
// 2. perform ECDH derivations
std::vector<crypto::key_derivation> main_derivations;
std::vector<crypto::key_derivation> additional_derivations;
const bool is_carrot = carrot::is_carrot_transaction_v1(tx);
perform_ecdh_derivations(epee::to_span(main_tx_ephemeral_pubkeys),
epee::to_span(additional_tx_ephemeral_pubkeys),
acc.m_view_secret_key,
acc.get_device(),
carrot::is_carrot_transaction_v1(tx),
main_derivations,
additional_derivations);
epee::to_span(additional_tx_ephemeral_pubkeys),
is_carrot ? account.get_keys().k_view_incoming : account.get_keys().m_view_secret_key,
account.get_keys().get_device(),
is_carrot,
main_derivations,
additional_derivations);
// 3. view-incoming scan output enotes
view_incoming_scan_transaction(tx,
epee::to_span(main_tx_ephemeral_pubkeys),
epee::to_span(additional_tx_ephemeral_pubkeys),
tx_extra_nonce,
epee::to_span(main_derivations),
epee::to_span(additional_derivations),
acc,
subaddress_keystore,
enote_scan_infos_out);
epee::to_span(main_tx_ephemeral_pubkeys),
epee::to_span(additional_tx_ephemeral_pubkeys),
tx_extra_nonce,
epee::to_span(main_derivations),
epee::to_span(additional_derivations),
account,
enote_scan_infos_out);
}
//-------------------------------------------------------------------------------------------------------------------
std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_transaction(
const cryptonote::transaction &tx,
const cryptonote::account_keys &acc,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore)
carrot::carrot_and_legacy_account &account)
{
std::vector<std::optional<enote_view_incoming_scan_info_t>> res(tx.vout.size());
view_incoming_scan_transaction(tx, acc, subaddress_keystore, epee::to_mut_span(res));
const auto n_outputs = tx.vout.size();
std::vector<std::optional<enote_view_incoming_scan_info_t>> res(n_outputs);
if (n_outputs > 0)
{
view_incoming_scan_transaction(tx, account, epee::to_mut_span(res));
}
return res;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -821,11 +888,10 @@ std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_t
tx_extra_nonce))
MWARNING("Transaction extra has unsupported format: " << cryptonote::get_transaction_hash(tx));
tools::keystore keystore;
keystore.add(address.m_spend_public_key, {});
// 3. do view-incoming scan for each output enotes
hw::device &hwdev = hw::get_device("default");
carrot::carrot_and_legacy_account dummy_account;
dummy_account.insert_subaddresses({{address.m_spend_public_key, {{0, 0}, carrot::AddressDeriveType::Carrot, false}}});
for (size_t local_output_index = 0; local_output_index < n_outputs; ++local_output_index)
{
auto &enote_scan_info = res[local_output_index];
@@ -839,7 +905,7 @@ std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_t
custom_additional_derivations,
address,
/*k_view_dev=*/nullptr,
keystore,
dummy_account,
hwdev);
}
@@ -857,11 +923,18 @@ bool is_long_payment_id(const crypto::hash &pid)
//-------------------------------------------------------------------------------------------------------------------
std::optional<crypto::key_image> try_derive_enote_key_image(
const enote_view_incoming_scan_info_t &enote_scan_info,
const cryptonote::account_keys &acc)
{
const carrot::carrot_and_legacy_account &acc)
{
// we skip the return output key image generation here to do it in process_new_scanned_transaction.
if (!enote_scan_info.subaddr_index)
return std::nullopt;
// if we have a carrot return enote, we return already derived key image
if (enote_scan_info.is_carrot && enote_scan_info.is_return)
{
return acc.get_return_output_map_ref().at(enote_scan_info.return_address).key_image;
}
// k^j_subext
rct::key subaddress_extension;
if (enote_scan_info.subaddr_index->index.is_subaddress())
@@ -869,7 +942,13 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
const cryptonote::subaddress_index subaddr_index_cn{enote_scan_info.subaddr_index->index.major,
enote_scan_info.subaddr_index->index.minor};
subaddress_extension = rct::sk2rct(
acc.get_device().get_subaddress_secret_key(acc.m_view_secret_key, subaddr_index_cn));
acc.get_keys()
.get_device()
.get_subaddress_secret_key(
enote_scan_info.is_carrot ? acc.get_keys().k_view_incoming : acc.get_keys().m_view_secret_key,
subaddr_index_cn
)
);
}
else // !subaddr_index_cn.is_zero()
{
@@ -877,8 +956,10 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
}
// O = K^j_s + k^g_o G + k^t_o T
rct::key onetime_address = rct::scalarmultKey(rct::pk2rct(crypto::get_T()),
rct::sk2rct(enote_scan_info.sender_extension_t));
rct::key onetime_address = rct::scalarmultKey(
rct::pk2rct(crypto::get_T()),
rct::sk2rct(enote_scan_info.sender_extension_t)
);
rct::addKeys1(onetime_address, rct::sk2rct(enote_scan_info.sender_extension_g), onetime_address);
rct::addKeys(onetime_address, onetime_address, rct::pk2rct(enote_scan_info.address_spend_pubkey));
@@ -889,11 +970,18 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
//! @TODO: HW devices
// x = k_s + k^j_subext + k^g_o
rct::key x;
sc_add(x.bytes,
to_bytes(acc.m_spend_secret_key),
to_bytes(enote_scan_info.sender_extension_g));
sc_add(x.bytes, x.bytes, subaddress_extension.bytes);
if (enote_scan_info.is_carrot) {
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
enote_scan_info.sender_extension_g,
enote_scan_info.sender_extension_t,
rct::rct2pk(onetime_address));
} else {
sc_add(x.bytes,
to_bytes(acc.get_keys().m_spend_secret_key),
to_bytes(enote_scan_info.sender_extension_g));
sc_add(x.bytes, x.bytes, subaddress_extension.bytes);
}
// L = x I = (k_s + k^j_subext + k^g_o) Hp(O)
return rct::rct2ki(rct::scalarmultKey(rct::pt2rct(ki_generator), x));
}
+30 -13
View File
@@ -31,13 +31,13 @@
//local headers
#include "carrot_core/carrot_enote_types.h"
#include "carrot_core/device.h"
#include "carrot_core/account.h"
#include "carrot_impl/subaddress_index.h"
#include "crypto/crypto.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/blobdatatype.h"
#include "cryptonote_basic/subaddress_index.h"
#include "cryptonote_basic/tx_extra.h"
#include "common/keystore.h"
#include "common/variant.h"
//third party headers
@@ -75,6 +75,15 @@ struct enote_view_incoming_scan_info_t
// the cold signing code used to have a bug which added multiple main tx pubkeys to extra
std::size_t main_tx_pubkey_index;
// whether this output is to a carrot address or not
bool is_carrot;
// whether this output is a return output
bool is_return;
// Kr = K_return + K_o
crypto::public_key return_address;
};
struct PreCarrotEnote
@@ -97,6 +106,21 @@ using MoneroEnoteVariant = std::variant<PreCarrotEnote,
carrot::CarrotCoinbaseEnoteV1,
carrot::CarrotEnoteV1>;
bool parse_tx_extra_for_scanning(const std::vector<std::uint8_t> &tx_extra,
const std::size_t n_outputs,
std::vector<crypto::public_key> &main_tx_ephemeral_pubkeys_out,
std::vector<crypto::public_key> &additional_tx_ephemeral_pubkeys_out,
cryptonote::blobdata &tx_extra_nonce_out);
void perform_ecdh_derivations(const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys,
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys,
const crypto::secret_key &k_view_incoming,
hw::device &hwdev,
const bool is_carrot,
std::vector<crypto::key_derivation> &main_derivations_out,
std::vector<crypto::key_derivation> &additional_derivations_out);
std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote_from_prefix(
const cryptonote::transaction_prefix &tx_prefix,
const rct::xmr_amount amount,
@@ -104,8 +128,7 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote_from_pre
const std::size_t local_output_index,
const cryptonote::account_public_address &address,
const crypto::secret_key &k_view_incoming,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
hw::device &hwdev);
void view_incoming_scan_transaction(
@@ -115,21 +138,15 @@ void view_incoming_scan_transaction(
const cryptonote::blobdata &tx_extra_nonce,
const epee::span<const crypto::key_derivation> main_derivations,
const epee::span<const crypto::key_derivation> additional_derivations,
const cryptonote::account_keys &acc,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out);
void view_incoming_scan_transaction(
const cryptonote::transaction &tx,
const cryptonote::account_keys &acc,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore,
carrot::carrot_and_legacy_account &account,
const epee::span<std::optional<enote_view_incoming_scan_info_t>> enote_scan_infos_out);
std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_transaction(
const cryptonote::transaction &tx,
const cryptonote::account_keys &acc,
//const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
tools::keystore &subaddress_keystore);
carrot::carrot_and_legacy_account &account);
std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_transaction_as_sender(
const cryptonote::transaction &tx,
@@ -141,6 +158,6 @@ bool is_long_payment_id(const crypto::hash &pid);
std::optional<crypto::key_image> try_derive_enote_key_image(
const enote_view_incoming_scan_info_t &enote_scan_info,
const cryptonote::account_keys &acc);
const carrot::carrot_and_legacy_account &acc);
} //namespace wallet
} //namespace tools
+336 -153
View File
@@ -47,6 +47,7 @@
#include "ringct/bulletproofs_plus.h"
#include "wallet/scanning_tools.cpp"
#include "common/container_helpers.h"
#include "carrot_core/payment_proposal.cpp"
//third party headers
@@ -81,16 +82,22 @@ static bool is_transfer_usable_for_input_selection(const wallet2::transfer_detai
const uint64_t last_locked_block_index = cryptonote::get_last_locked_block_index(
td.m_tx.unlock_time, td.m_block_height);
*/
// Reject locked outputs
size_t blocks_locked_for = CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
if (td.m_tx.type == cryptonote::transaction_type::MINER || td.m_tx.type == cryptonote::transaction_type::PROTOCOL)
blocks_locked_for = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
return !td.m_spent
&& td.m_key_image_known
&& !td.m_key_image_partial
&& !td.m_frozen
&& (top_block_index >= td.m_block_height + blocks_locked_for)
// && last_locked_block_index <= top_block_index
&& td.m_subaddr_index.major == from_account
&& (from_subaddresses.empty() || from_subaddresses.count(td.m_subaddr_index.minor) == 1)
&& td.amount() >= ignore_below
&& td.amount() <= ignore_above
&& td.asset_type == "SAL1"
;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -113,7 +120,7 @@ static bool build_payment_proposals(std::vector<carrot::CarrotPaymentProposalV1>
.amount = tx_dest_entry.amount,
.enote_type = carrot::CarrotEnoteType::PAYMENT
},
.subaddr_index = {subaddr_index, carrot::AddressDeriveType::PreCarrot} // @TODO: handle carrot
.subaddr_index = {subaddr_index, carrot::AddressDeriveType::Carrot, false},
});
}
else // not *known* self-send address
@@ -173,11 +180,43 @@ static crypto::public_key find_change_address_spend_pubkey(
CHECK_AND_ASSERT_THROW_MES(change_it != subaddress_map.cend(),
"find_change_address_spend_pubkey: missing change address (index "
<< subaddr_account << ",0) in subaddress map");
const auto change_it_2 = std::find_if(std::next(change_it), subaddress_map.cend(),
[subaddr_account](const auto &p) { return p.second.major == subaddr_account && p.second.minor == 0; });
CHECK_AND_ASSERT_THROW_MES(change_it_2 == subaddress_map.cend(),
"find_change_address_spend_pubkey: provided CN subaddress map is malformed!!! At least two spend pubkeys map to "
"index " << subaddr_account << ",0 in the subaddress map!");
return change_it->first;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static crypto::public_key find_change_address_spend_pubkey(
const std::unordered_map<crypto::public_key, carrot::subaddress_index_extended> &subaddress_map,
const std::uint32_t subaddr_account)
{
const auto change_it = std::find_if(subaddress_map.cbegin(), subaddress_map.cend(),
[subaddr_account](const auto &p) {
return p.second.index.major == subaddr_account &&
p.second.index.minor == 0 &&
p.second.derive_type == carrot::AddressDeriveType::Carrot &&
p.second.is_return_spend_key == false;
});
CHECK_AND_ASSERT_THROW_MES(change_it != subaddress_map.cend(),
"find_change_address_spend_pubkey: missing change address (index "
<< subaddr_account << ",0) in subaddress map");
const auto change_it_2 = std::find_if(std::next(change_it), subaddress_map.cend(),
[subaddr_account](const auto &p) {
return p.second.index.major == subaddr_account &&
p.second.index.minor == 0 &&
p.second.derive_type == carrot::AddressDeriveType::Carrot &&
p.second.is_return_spend_key == false;
});
CHECK_AND_ASSERT_THROW_MES(change_it_2 == subaddress_map.cend(),
"find_change_address_spend_pubkey: provided subaddress map is malformed!!! At least two spend pubkeys map to "
"index " << subaddr_account << ",0 in the subaddress map!");
return change_it->first;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -400,6 +439,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
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,
const uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
wallet2::unique_index_container subtract_fee_from_outputs,
@@ -407,17 +447,16 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
{
wallet2::transfer_container unused_transfers;
w.get_transfers(unused_transfers);
const auto subaddress_map = w.get_subaddress_map_ref();
std::vector<carrot::CarrotTransactionProposalV1> tx_proposals;
tx_proposals.reserve(dsts.size() / (FCMP_PLUS_PLUS_MAX_OUTPUTS - 1) + 1);
tx_proposals.reserve(dsts.size() / (carrot::CARROT_MAX_TX_OUTPUTS - 1) + 1);
const crypto::public_key change_address_spend_pubkey
= find_change_address_spend_pubkey(subaddress_map, subaddr_account);
= find_change_address_spend_pubkey(w.get_account().get_subaddress_map_ref(), subaddr_account);
while (!dsts.empty())
{
const std::size_t num_dsts_to_complete = std::min<std::size_t>(dsts.size(), FCMP_PLUS_PLUS_MAX_OUTPUTS - 1);
const std::size_t num_dsts_to_complete = std::min<std::size_t>(dsts.size(), carrot::CARROT_MAX_TX_OUTPUTS - 1);
// build payment proposals and subtractable info from last `num_dsts_to_complete` dsts
std::vector<carrot::CarrotPaymentProposalV1> normal_payment_proposals;
@@ -430,7 +469,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const bool is_selfsend = build_payment_proposals(normal_payment_proposals,
selfsend_payment_proposals,
dst,
subaddress_map);
w.get_account().get_subaddress_map_cn());
if (subtract_fee_from_outputs.count(dsts.size() - 1))
{
if (is_selfsend)
@@ -462,9 +501,10 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
std::move(select_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::PreCarrot}, //! @TODO: handle Carrot keys
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot, false},
subtractable_normal_payment_proposals,
subtractable_selfsend_payment_proposals,
tx_proposal);
@@ -496,6 +536,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::vector<cryptonote::tx_destination_entry> &dsts,
const std::uint32_t priority,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const wallet2::unique_index_container &subtract_fee_from_outputs)
@@ -524,6 +565,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
subaddr_account,
subaddr_indices,
subtract_fee_from_outputs,
@@ -531,8 +573,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
}
//-------------------------------------------------------------------------------------------------------------------
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_sweep(
const wallet2::transfer_container &transfers,
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
wallet2 &w,
const std::vector<crypto::key_image> &input_key_images,
const cryptonote::account_public_address &address,
const bool is_subaddress,
@@ -540,12 +581,16 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
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,
const std::uint64_t top_block_index)
{
wallet2::transfer_container transfers;
w.get_transfers(transfers);
const size_t n_inputs = input_key_images.size();
CARROT_CHECK_AND_THROW(n_inputs, carrot::too_few_inputs, "no key images provided");
CARROT_CHECK_AND_THROW(n_dests_per_tx, carrot::too_few_outputs, "sweep must have at least one destination");
CARROT_CHECK_AND_THROW(n_dests_per_tx <= FCMP_PLUS_PLUS_MAX_OUTPUTS,
CARROT_CHECK_AND_THROW(n_dests_per_tx <= carrot::CARROT_MAX_TX_OUTPUTS,
carrot::too_many_outputs, "too many sweep destinations per transaction");
// Check that the key image is usable and isn't spent, collect amounts, and get subaddress account index
@@ -571,26 +616,31 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
}
const crypto::public_key change_address_spend_pubkey
= find_change_address_spend_pubkey(subaddress_map, subaddr_account);
= find_change_address_spend_pubkey(w.get_account().get_subaddress_map_ref(), subaddr_account);
// get 1 payment proposal corresponding to (address, is_subaddres)
std::vector<carrot::CarrotPaymentProposalV1> normal_payment_proposals;
std::vector<carrot::CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals;
for (size_t i = 0; i < n_dests_per_tx; ++i)
{
cryptonote::tx_destination_entry de;
de.amount = 0;
de.addr = address;
de.is_subaddress = is_subaddress;
de.asset_type = "SAL1";
const bool is_selfsend_dest = build_payment_proposals(normal_payment_proposals,
selfsend_payment_proposals,
cryptonote::tx_destination_entry(/*amount=*/0, address, is_subaddress),
subaddress_map);
de,
w.get_account().get_subaddress_map_cn());
CHECK_AND_ASSERT_THROW_MES((is_selfsend_dest && selfsend_payment_proposals.size() == i+1)
|| (!is_selfsend_dest && normal_payment_proposals.size() == i+1),
__func__ << ": BUG in build_payment_proposals: incorrect count for payment proposal lists");
}
CARROT_CHECK_AND_THROW(normal_payment_proposals.size() < FCMP_PLUS_PLUS_MAX_OUTPUTS,
CARROT_CHECK_AND_THROW(normal_payment_proposals.size() < carrot::CARROT_MAX_TX_OUTPUTS,
carrot::too_many_outputs, "too many *outgoing* sweep destinations per tx, we also need 1 self-send output");
// make `n_txs` tx proposals with `n_output` payment proposals each
const size_t n_txs = div_ceil<size_t>(n_inputs, FCMP_PLUS_PLUS_MAX_INPUTS);
const size_t n_txs = div_ceil<size_t>(n_inputs, carrot::CARROT_MAX_TX_INPUTS);
std::vector<carrot::CarrotTransactionProposalV1> tx_proposals(n_txs);
size_t ki_idx = 0;
for (carrot::CarrotTransactionProposalV1 &tx_proposal : tx_proposals)
@@ -600,7 +650,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
selfsend_payment_proposals.back().proposal.enote_type = carrot::CarrotEnoteType::CHANGE;
// collect inputs for this tx
const size_t ki_idx_end = std::min<size_t>(n_inputs, ki_idx + FCMP_PLUS_PLUS_MAX_INPUTS);
const size_t ki_idx_end = std::min<size_t>(n_inputs, ki_idx + carrot::CARROT_MAX_TX_INPUTS);
std::vector<carrot::CarrotSelectedInput> selected_inputs;
selected_inputs.reserve(n_inputs - ki_idx_end);
for (; ki_idx < ki_idx_end; ++ki_idx)
@@ -611,10 +661,18 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
std::move(selected_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::PreCarrot}, //! @TODO: handle Carrot keys
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot},
tx_proposal);
// populate the sources
std::vector<size_t> selected_transfer_indices_sorted;
for (const auto &ki: tx_proposal.key_images_sorted) {
selected_transfer_indices_sorted.push_back(w.get_transfer_details(ki));
}
tx_proposal.sources = get_sources(transfers, selected_transfer_indices_sorted, "SAL1", w);
}
CARROT_CHECK_AND_THROW(ki_idx == input_key_images.size(),
@@ -630,11 +688,9 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const bool is_subaddress,
const size_t n_dests_per_tx,
const std::uint32_t priority,
const std::vector<uint8_t> &extra)
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type)
{
wallet2::transfer_container transfers;
w.get_transfers(transfers);
const rct::xmr_amount fee_per_weight = w.get_base_fee(priority);
const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask();
@@ -644,8 +700,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::uint64_t top_block_index = current_chain_height - 1;
return make_carrot_transaction_proposals_wallet2_sweep(
transfers,
w.get_subaddress_map_ref(),
w,
input_key_images,
address,
is_subaddress,
@@ -653,12 +708,12 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
top_block_index);
}
//-------------------------------------------------------------------------------------------------------------------
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_sweep_all(
const wallet2::transfer_container &transfers,
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
wallet2 &w,
const rct::xmr_amount only_below,
const cryptonote::account_public_address &address,
const bool is_subaddress,
@@ -666,10 +721,14 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
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,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const std::uint64_t top_block_index)
{
wallet2::transfer_container transfers;
w.get_transfers(transfers);
const std::unordered_map<crypto::key_image, size_t> unburned_transfers_by_key_image =
collect_non_burned_transfers_by_key_image(transfers);
@@ -699,8 +758,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
CHECK_AND_ASSERT_THROW_MES(!input_key_images.empty(), __func__ << ": no usable transfers to sweep");
return make_carrot_transaction_proposals_wallet2_sweep(
transfers,
subaddress_map,
w,
input_key_images,
address,
is_subaddress,
@@ -708,6 +766,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
top_block_index);
}
//-------------------------------------------------------------------------------------------------------------------
@@ -719,12 +778,10 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const size_t n_dests_per_tx,
const std::uint32_t priority,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices)
{
wallet2::transfer_container transfers;
w.get_transfers(transfers);
const rct::xmr_amount fee_per_weight = w.get_base_fee(priority);
const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask();
@@ -734,8 +791,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::uint64_t top_block_index = current_chain_height - 1;
return make_carrot_transaction_proposals_wallet2_sweep_all(
transfers,
w.get_subaddress_map_ref(),
w,
only_below,
address,
is_subaddress,
@@ -743,14 +799,202 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
subaddr_account,
subaddr_indices,
top_block_index);
}
//-------------------------------------------------------------------------------------------------------------------
bool get_address_openings_x_y(
const cryptonote::transaction &tx,
const cryptonote::tx_source_entry &src,
const wallet2 &w,
crypto::secret_key &x_out,
crypto::secret_key &y_out)
{
// If the output is a return output, we can use the return output secret key
// to derive x and y directly.
const auto& return_output_map = w.get_account().get_return_output_map_ref();
if (return_output_map.find(rct::rct2pk(src.outputs[src.real_output].second.dest)) != return_output_map.end())
{
const auto &return_output = return_output_map.at(rct::rct2pk(src.outputs[src.real_output].second.dest));
x_out = return_output.x;
y_out = return_output.y;
return true;
}
const std::vector<crypto::public_key> v_pubkeys{src.real_out_tx_key};
const std::vector<crypto::public_key> v_pubkeys_empty{};
const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys = (src.real_out_tx_key == crypto::null_pkey) ? epee::to_span(v_pubkeys_empty) : epee::to_span(v_pubkeys);
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys = epee::to_span(src.real_out_additional_tx_keys);
// we have to try both internal and external derivations
bool r = false;
for (size_t i = 0; i < 2; ++i) {
// perform ECDH derivations
std::vector<crypto::key_derivation> main_derivations;
std::vector<crypto::key_derivation> additional_derivations;
if (i == 0) {
wallet::perform_ecdh_derivations(
main_tx_ephemeral_pubkeys,
additional_tx_ephemeral_pubkeys,
w.get_account().get_keys().k_view_incoming,
w.get_account().get_keys().get_device(),
src.carrot,
main_derivations,
additional_derivations
);
} else {
crypto::key_derivation main_derivation;
memcpy(main_derivation.data, w.get_account().get_keys().s_view_balance.data, sizeof(crypto::secret_key));
main_derivations.push_back(main_derivation);
}
crypto::hash s_sender_receiver;
const crypto::key_derivation &kd = main_derivations.size()
? main_derivations[0]
: additional_derivations[src.real_output_in_tx_index];
const mx25519_pubkey s_sender_receiver_unctx = carrot::raw_byte_convert<mx25519_pubkey>(kd);
// ephemeral pubkeys
const epee::span<const crypto::public_key> enote_ephemeral_pubkeys_pk =
main_tx_ephemeral_pubkeys.empty() ? additional_tx_ephemeral_pubkeys : main_tx_ephemeral_pubkeys;
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys = {
reinterpret_cast<const mx25519_pubkey*>(enote_ephemeral_pubkeys_pk.data()),
enote_ephemeral_pubkeys_pk.size()
};
const bool shared_ephemeral_pubkey = enote_ephemeral_pubkeys.size() == 1;
const size_t ephemeral_pubkey_index = shared_ephemeral_pubkey ? 0 : src.real_output_in_tx_index;
// input_context
carrot::input_context_t input_context;
if (src.coinbase) {
input_context = carrot::make_carrot_input_context_coinbase(src.block_index);
} else {
input_context = carrot::make_carrot_input_context(src.first_rct_key_image);
}
// s^ctx_sr = H_32(s_sr, D_e, input_context)
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
enote_ephemeral_pubkeys[ephemeral_pubkey_index],
input_context,
s_sender_receiver);
// get the k_og and k_ot
crypto::secret_key sender_extension_g_out;
crypto::secret_key sender_extension_t_out;
crypto::public_key address_spend_pubkey_out;
carrot::payment_id_t nominal_payment_id_out;
carrot::janus_anchor_t nominal_janus_anchor_out;
carrot::encrypted_janus_anchor_t encrypted_janus_anchor;
carrot::encrypted_payment_id_t encrypted_payment_id;
carrot::scan_carrot_dest_info(
rct::rct2pk(src.outputs[src.real_output].second.dest),
src.outputs[src.real_output].second.mask,
encrypted_janus_anchor,
encrypted_payment_id,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
nominal_payment_id_out,
nominal_janus_anchor_out
);
r = w.get_account().try_searching_for_opening_for_onetime_address(
address_spend_pubkey_out,
sender_extension_g_out,
sender_extension_t_out,
x_out,
y_out
);
// If we found the opening, we can stop here
if (r) {
break;
}
}
CHECK_AND_ASSERT_THROW_MES(r, "Failed to obtain openings for onetime address");
return true;
}
//-------------------------------------------------------------------------------------------------------------------
void encrypt_change_index(
const std::vector<carrot::CarrotPaymentProposalV1> &proposals,
const std::vector<carrot::CarrotPaymentProposalSelfSendV1> &selfsend_proposal_cores,
const crypto::key_image &tx_first_key_image,
const size_t change_index,
const std::unordered_map<crypto::public_key, size_t> &payments_indices,
std::vector<uint8_t> &change_masks_out
) {
// 1. input context: input_context = "R" || KI_1
const carrot::input_context_t input_context = carrot::make_carrot_input_context(tx_first_key_image);
// 2. collect proposals and selfsend proposals destinations
std::vector<std::tuple<crypto::public_key, size_t, bool>> destinations;
for (const auto &p : proposals) {
destinations.emplace_back(p.destination.address_spend_pubkey, payments_indices.at(p.destination.address_spend_pubkey), true);
}
for (const auto &p : selfsend_proposal_cores) {
destinations.emplace_back(p.destination_address_spend_pubkey, payments_indices.at(p.destination_address_spend_pubkey), false);
}
// 3. sort by indices
std::sort(destinations.begin(), destinations.end(),
[](const auto &a, const auto &b) {
return std::get<1>(a) < std::get<1>(b);
}
);
// 4. calculate change masks
for (const auto &d: destinations) {
// get shared secret
mx25519_pubkey eph_pubkey;
mx25519_pubkey s_sender_receiver_unctx;
if (std::get<2>(d)) {
// normal payment proposal
const auto it = std::find_if(proposals.begin(), proposals.end(),
[&d](const carrot::CarrotPaymentProposalV1 &p) {
return p.destination.address_spend_pubkey == std::get<0>(d);
});
CHECK_AND_ASSERT_THROW_MES(it != proposals.end(), "Failed to find normal payment proposal");
carrot::get_normal_proposal_ecdh_parts(
*it,
input_context,
eph_pubkey,
s_sender_receiver_unctx
);
} else {
s_sender_receiver_unctx = crypto::rand<mx25519_pubkey>();
}
// derive a scalar from the shared secret
crypto::secret_key output_index_key;
crypto::key_derivation output_index_derivation;
memcpy(output_index_derivation.data, s_sender_receiver_unctx.data, sizeof(output_index_derivation.data));
crypto::derivation_to_scalar(
output_index_derivation,
std::get<1>(d),
output_index_key
);
// Calculate the encrypted_change_index data for this output
struct {
char domain_separator[8];
crypto::secret_key output_index_key;
} eci_buf;
std::memset(eci_buf.domain_separator, 0x0, sizeof(eci_buf.domain_separator));
std::strncpy(eci_buf.domain_separator, "CHG_IDX", 8);
eci_buf.output_index_key = output_index_key;
crypto::secret_key eci_out;
keccak((uint8_t *)&eci_buf, sizeof(eci_buf), (uint8_t*)&eci_out, sizeof(eci_out));
uint8_t eci_data = change_index ^ eci_out.data[0];
change_masks_out.push_back(eci_data);
}
}
//-------------------------------------------------------------------------------------------------------------------
cryptonote::transaction finalize_all_proofs_from_transfer_details(
const carrot::CarrotTransactionProposalV1 &tx_proposal,
const cryptonote::transaction_type tx_type,
const wallet2 &w)
{
const size_t n_inputs = tx_proposal.key_images_sorted.size();
@@ -762,7 +1006,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
<< n_inputs << "-in " << n_outputs << "-out, with "
<< tx_proposal.normal_payment_proposals.size() << " normal payment proposals, "
<< tx_proposal.selfsend_payment_proposals.size() << " self-send payment proposals, and a fee of "
<< tx_proposal.fee << " pXMR");
<< cryptonote::print_money(tx_proposal.fee) << " SAL1");
wallet2::transfer_container transfers;
w.get_transfers(transfers);
@@ -776,23 +1020,28 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
//! @TODO: HW device
carrot::cryptonote_hierarchy_address_device_ram_borrowed addr_dev(
acc_keys.m_account_address.m_spend_public_key,
acc_keys.m_view_secret_key);
acc_keys.m_carrot_account_address.m_spend_public_key,
acc_keys.k_view_incoming);
// finalize enotes
LOG_PRINT_L3("Getting output enote proposals");
std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals;
carrot::encrypted_payment_id_t encrypted_payment_id;
size_t change_index;
carrot::RCTOutputEnoteProposal return_enote_out;
std::unordered_map<crypto::public_key, size_t> payments_indices;
carrot::get_output_enote_proposals(tx_proposal.normal_payment_proposals,
selfsend_payment_proposal_cores,
tx_proposal.dummy_encrypted_payment_id,
nullptr,
&w.get_account().s_view_balance_dev,
&addr_dev,
tx_proposal.key_images_sorted.at(0),
output_enote_proposals,
return_enote_out,
encrypted_payment_id,
tx_proposal.tx_type,
change_index,
payments_indices,
nullptr);
CHECK_AND_ASSERT_THROW_MES(output_enote_proposals.size() == n_outputs,
"finalize_all_proofs_from_transfer_details: unexpected number of output enote proposals");
@@ -816,15 +1065,26 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
for (size_t i = 0; i < enotes.size(); ++i)
enotes[i] = output_enote_proposals.at(i).enote;
// encrypt change index per output
std::vector<uint8_t> change_masks;
encrypt_change_index(
tx_proposal.normal_payment_proposals,
selfsend_payment_proposal_cores,
tx_proposal.key_images_sorted.at(0),
change_index,
payments_indices,
change_masks);
// serialize transaction
cryptonote::transaction tx = carrot::store_carrot_to_transaction_v1(enotes,
tx_proposal.key_images_sorted,
tx_proposal.sources,
tx_proposal.fee,
tx_proposal.tx_type,
tx_proposal.amount_burnt,
change_masks,
return_enote_out,
encrypted_payment_id);
tx.source_asset_type = "SAL1";
tx.destination_asset_type = "SAL1";
tx.type = tx_type;
// aliases
hw::device &hwdev = acc_keys.get_device();
@@ -836,97 +1096,21 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
inSk.reserve(sources.size());
std::vector<uint64_t> inamounts;
std::vector<unsigned int> index;
for (size_t i = 0; i < sources.size(); ++i)
for (const auto& src: sources)
{
amount_in += sources[i].amount;
inamounts.push_back(sources[i].amount);
index.push_back(sources[i].real_output);
amount_in += src.amount;
inamounts.push_back(src.amount);
index.push_back(src.real_output);
// inSk: (x, y, mask)
rct::carrot_ctkey ctkey;
ctkey.mask = sources[i].mask;
if (sources[i].carrot) {
const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys =
epee::to_span(std::vector<crypto::public_key>{sources[i].real_out_tx_key});
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys =
epee::to_span(sources[i].real_out_additional_tx_keys);
// 2. perform ECDH derivations
std::vector<crypto::key_derivation> main_derivations;
std::vector<crypto::key_derivation> additional_derivations;
wallet::perform_ecdh_derivations(
main_tx_ephemeral_pubkeys,
additional_tx_ephemeral_pubkeys,
acc_keys.m_view_secret_key,
hwdev,
carrot::is_carrot_transaction_v1(tx),
main_derivations,
additional_derivations
);
crypto::hash s_sender_receiver;
const crypto::key_derivation &kd = main_derivations.size()
? main_derivations[0]
: additional_derivations[sources[i].real_output_in_tx_index];
const mx25519_pubkey s_sender_receiver_unctx = carrot::raw_byte_convert<mx25519_pubkey>(kd);
// ephemeral pubkeys
const epee::span<const crypto::public_key> enote_ephemeral_pubkeys_pk =
main_tx_ephemeral_pubkeys.empty() ? additional_tx_ephemeral_pubkeys : main_tx_ephemeral_pubkeys;
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys = {
reinterpret_cast<const mx25519_pubkey*>(enote_ephemeral_pubkeys_pk.data()),
enote_ephemeral_pubkeys_pk.size()
};
const bool shared_ephemeral_pubkey = enote_ephemeral_pubkeys.size() == 1;
const size_t ephemeral_pubkey_index = shared_ephemeral_pubkey ? 0 : sources[i].real_output_in_tx_index;
// input_context
carrot::input_context_t input_context;
if (sources[i].coinbase) {
input_context = carrot::make_carrot_input_context_coinbase(sources[i].block_index);
} else {
input_context = carrot::make_carrot_input_context(sources[i].first_rct_key_image);
}
// s^ctx_sr = H_32(s_sr, D_e, input_context)
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
enote_ephemeral_pubkeys[ephemeral_pubkey_index],
input_context,
s_sender_receiver);
// get the k_og and k_ot
crypto::secret_key sender_extension_g_out;
crypto::secret_key sender_extension_t_out;
crypto::public_key address_spend_pubkey_out;
carrot::payment_id_t nominal_payment_id_out;
carrot::janus_anchor_t nominal_janus_anchor_out;
carrot::encrypted_janus_anchor_t encrypted_janus_anchor;
carrot::encrypted_payment_id_t encrypted_payment_id;
carrot::scan_carrot_dest_info(
rct::rct2pk(sources[i].outputs[sources[i].real_output].second.dest),
sources[i].outputs[sources[i].real_output].second.mask,
encrypted_janus_anchor,
encrypted_payment_id,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
nominal_payment_id_out,
nominal_janus_anchor_out
);
ctkey.mask = src.mask;
if (src.carrot) {
crypto::secret_key x, y;
bool r = w.get_carrot_account().try_searching_for_opening_for_onetime_address(
sources[i].address_spend_pubkey,
sender_extension_g_out,
sender_extension_t_out,
x,
y
);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error,
"Failed to search for opening for onetime address");
THROW_WALLET_EXCEPTION_IF(!get_address_openings_x_y(tx, src, w, x, y),
error::wallet_internal_error, "Failed to get x and y for input");
ctkey.x = rct::sk2rct(x);
ctkey.y = rct::sk2rct(y);
} else {
@@ -934,21 +1118,21 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
cryptonote::keypair in_ephemeral;
crypto::key_image img;
rct::salvium_input_data_t sid;
const auto& out_key = reinterpret_cast<const crypto::public_key&>(sources[i].outputs[sources[i].real_output].second.dest);
bool use_origin_data = (sources[i].origin_tx_data.tx_type != cryptonote::transaction_type::UNSET);
sid.origin_tx_type = sources[i].origin_tx_data.tx_type;
const auto& out_key = reinterpret_cast<const crypto::public_key&>(src.outputs[src.real_output].second.dest);
bool use_origin_data = (src.origin_tx_data.tx_type != cryptonote::transaction_type::UNSET);
sid.origin_tx_type = src.origin_tx_data.tx_type;
bool r = cryptonote::generate_key_image_helper(
w.get_account().get_keys(),
w.get_subaddress_map_ref(),
w.get_account().get_subaddress_map_cn(),
out_key,
sources[i].real_out_tx_key,
sources[i].real_out_additional_tx_keys,
sources[i].real_output_in_tx_index,
src.real_out_tx_key,
src.real_out_additional_tx_keys,
src.real_output_in_tx_index,
in_ephemeral,
img,
hwdev,
use_origin_data,
sources[i].origin_tx_data, sid
src.origin_tx_data, sid
);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image helper");
@@ -990,7 +1174,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
const carrot::RCTOutputEnoteProposal &change_enote_proposal = output_enote_proposals.at(change_index);
const carrot::input_context_t input_context = carrot::make_carrot_input_context(tx_proposal.key_images_sorted.at(0));
crypto::hash s_sender_receiver;
w.get_carrot_account().s_view_balance_dev.make_internal_sender_receiver_secret(
w.get_account().s_view_balance_dev.make_internal_sender_receiver_secret(
change_enote_proposal.enote.enote_ephemeral_pubkey,
input_context,
s_sender_receiver);
@@ -999,7 +1183,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
crypto::secret_key sender_extension_t;
carrot::make_carrot_onetime_address_extension_t(s_sender_receiver, change_enote_proposal.enote.amount_commitment, sender_extension_t);
crypto::secret_key change_x, change_y;
bool r = w.get_carrot_account().try_searching_for_opening_for_onetime_address(
bool r = w.get_account().try_searching_for_opening_for_onetime_address(
change_address_spend_pubkey,
sender_extension_g,
sender_extension_t,
@@ -1007,7 +1191,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
change_y
);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error,
"Failed to search for opening for onetime address");
"Failed to obtain opening for onetime change address");
// mixRing indexing is done the other way round for simple
rct::ctkeyM mixRing(sources.size());
@@ -1020,9 +1204,6 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
}
}
// fee
uint64_t fee = amount_in - amount_out - tx.amount_burnt;
// bpp
tx.rct_signatures.p.bulletproofs_plus.push_back(
rct::bulletproof_plus_PROVE(outamounts, output_amount_blinding_factors)
@@ -1037,12 +1218,12 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
rct::hash2rct(tx_prefix_hash),
inSk,
destinations,
tx_type,
tx_proposal.tx_type,
"SAL1",
destination_asset_types,
inamounts,
outamounts,
fee,
tx_proposal.fee,
mixRing,
index,
outSk,
@@ -1064,16 +1245,24 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
//-------------------------------------------------------------------------------------------------------------------
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view,
hw::device &hwdev)
const carrot::carrot_and_legacy_account &account)
{
const std::size_t n_inputs = tx_proposal.key_images_sorted.size();
const std::size_t n_outputs = tx_proposal.normal_payment_proposals.size() +
tx_proposal.selfsend_payment_proposals.size();
const bool shared_ephemeral_pubkey = n_outputs == 2;
CARROT_CHECK_AND_THROW(
tx_proposal.tx_type != cryptonote::transaction_type::UNSET,
carrot::missing_components,
"make_pending_carrot_tx: tx proposal has unset tx type"
);
CARROT_CHECK_AND_THROW(n_inputs >= 1, carrot::too_few_inputs, "carrot tx proposal missing inputs");
CARROT_CHECK_AND_THROW(n_outputs >= 2, carrot::too_few_outputs, "carrot tx proposal missing outputs");
if (tx_proposal.tx_type == cryptonote::transaction_type::STAKE || tx_proposal.tx_type == cryptonote::transaction_type::BURN) {
CARROT_CHECK_AND_THROW(n_outputs == 1, carrot::too_few_outputs, "carrot tx proposal doesn't have correct number of outputs");
} else {
CARROT_CHECK_AND_THROW(n_outputs >= 2, carrot::too_few_outputs, "carrot tx proposal missing outputs");
}
const crypto::key_image &tx_first_key_image = tx_proposal.key_images_sorted.at(0);
@@ -1097,16 +1286,13 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
key_images_string << ki;
}
//! @TODO: HW device
carrot::view_incoming_key_ram_borrowed_device k_view_dev(k_view);
// get order of payment proposals
std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals;
carrot::encrypted_payment_id_t encrypted_payment_id;
std::vector<std::pair<bool, std::size_t>> sorted_payment_proposal_indices;
carrot::get_output_enote_proposals_from_proposal_v1(tx_proposal,
/*s_view_balance_dev=*/nullptr,
&k_view_dev,
&account.k_view_incoming_dev,
output_enote_proposals,
encrypted_payment_id,
&sorted_payment_proposal_indices);
@@ -1136,7 +1322,7 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
if (is_selfsend)
{
dest = make_tx_destination_entry(tx_proposal.selfsend_payment_proposals.at(payment_idx.second),
k_view_dev);
account.k_view_incoming_dev);
ephemeral_privkeys.push_back(crypto::null_skey);
}
else // !is_selfsend
@@ -1196,15 +1382,12 @@ wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx(
const wallet2::transfer_container &transfers,
const wallet2 &w)
{
const auto acc_keys = w.get_account().get_keys();
wallet2::pending_tx ptx = make_pending_carrot_tx(tx_proposal,
transfers,
acc_keys.m_view_secret_key,
acc_keys.get_device());
w.get_account());
ptx.tx = finalize_all_proofs_from_transfer_details(
tx_proposal,
cryptonote::transaction_type::TRANSFER, // TODO:take this as a parameter
w
);
+10 -12
View File
@@ -63,6 +63,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
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,
const uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
wallet2::unique_index_container subtract_fee_from_outputs,
@@ -72,13 +73,13 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::vector<cryptonote::tx_destination_entry> &dsts,
const std::uint32_t priority,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const wallet2::unique_index_container &subtract_fee_from_outputs);
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_sweep(
const wallet2::transfer_container &transfers,
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
wallet2 &w,
const std::vector<crypto::key_image> &input_key_images,
const cryptonote::account_public_address &address,
const bool is_subaddress,
@@ -86,6 +87,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
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,
const std::uint64_t top_block_index);
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_sweep(
wallet2 &w,
@@ -94,11 +96,11 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const bool is_subaddress,
const size_t n_dests_per_tx,
const std::uint32_t priority,
const std::vector<uint8_t> &extra);
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type);
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_sweep_all(
const wallet2::transfer_container &transfers,
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddress_map,
wallet2 &w,
const rct::xmr_amount only_below,
const cryptonote::account_public_address &address,
const bool is_subaddress,
@@ -106,6 +108,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
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,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const std::uint64_t top_block_index);
@@ -117,23 +120,18 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const size_t n_dests_per_tx,
const std::uint32_t priority,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices);
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view,
hw::device &hwdev);
const carrot::carrot_and_legacy_account &account);
cryptonote::transaction finalize_all_proofs_from_transfer_details(
const carrot::CarrotTransactionProposalV1 &tx_proposal,
const cryptonote::transaction_type tx_type,
const wallet2 &w);
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view,
hw::device &hwdev);
wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx(
const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
+255 -257
View File
@@ -97,6 +97,8 @@ using namespace epee;
#include "net/socks_connect.h"
#include "carrot_impl/format_utils.h"
#include "tx_builder.h"
#include "scanning_tools.h"
#include "carrot_core/scan.h"
extern "C"
{
@@ -1582,10 +1584,15 @@ crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::su
return hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_subaddress_as_str(const cryptonote::subaddress_index& index) const
std::string wallet2::get_subaddress_as_str(const carrot::subaddress_index_extended& subaddr) const
{
cryptonote::account_public_address address = get_subaddress(index);
return cryptonote::get_account_address_as_str(m_nettype, !index.is_zero(), address);
carrot::CarrotDestinationV1 address = m_account.subaddress(subaddr);
// Build the cryptonote::account_public_address
account_public_address addr{address.address_spend_pubkey, address.address_view_pubkey};
// change this code into base 58
return cryptonote::get_account_address_as_str(m_nettype, address.is_subaddress, addr, subaddr.derive_type == carrot::AddressDeriveType::Carrot);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id) const
@@ -2426,7 +2433,7 @@ void wallet2::scan_key_image(const wallet::enote_view_incoming_scan_info_t &enot
}
}
ki_out = wallet::try_derive_enote_key_image(enote_scan_info, m_account.get_keys());
ki_out = wallet::try_derive_enote_key_image(enote_scan_info, m_account);
}
//----------------------------------------------------------------------------------------------------
void wallet2::process_new_transaction(
@@ -2447,11 +2454,9 @@ void wallet2::process_new_transaction(
const size_t n_outputs = tx.vout.size();
// view-incoming scan enotes
tools::keystore keystore(m_subaddresses);
const std::vector<std::optional<wallet::enote_view_incoming_scan_info_t>> enote_scan_infos =
wallet::view_incoming_scan_transaction(tx,
this->m_account.get_keys(),
keystore);
this->m_account);
// if view-incoming scan was successful, try deriving the key image
bool password_failure = false;
@@ -2534,23 +2539,20 @@ void wallet2::process_new_scanned_transaction(
}
bool notify = false;
// per receiving subaddress index
// per receiving subaddress index -> (asset type, amount, one time address)
std::unordered_map<cryptonote::subaddress_index, std::map<std::string, uint64_t>> tx_money_got_in_outs;
std::unordered_map<cryptonote::subaddress_index, amounts_container> tx_amounts_individual_outs;
std::unordered_map<cryptonote::subaddress_index, std::vector<std::tuple<uint64_t, crypto::public_key>>> tx_amounts_individual_outs;
// Note that this includes any 0-amount outputs, which we keep track of for background scanning
bool received_an_output = false;
crypto::hash payment_id = null_hash;
// for each for scanned output...
for (size_t local_output_index = 0; local_output_index < n_scanned_enotes; ++local_output_index)
{
const auto &enote_scan_info_local = enote_scan_infos[local_output_index];
bool is_protocol = (tx.type == cryptonote::transaction_type::PROTOCOL);
if (!is_protocol)
if (!enote_scan_info_local || !enote_scan_info_local->subaddr_index)
continue;
const auto &enote_scan_info = enote_scan_infos[local_output_index];
if (!enote_scan_info || !enote_scan_info->subaddr_index)
continue;
received_an_output = true;
@@ -2561,113 +2563,18 @@ void wallet2::process_new_scanned_transaction(
continue;
}
uint64_t td_origin_idx = (uint64_t)-1;
wallet::enote_view_incoming_scan_info_t enote_scan_info_protocol;
if (is_protocol) {
// Check that it is intended for us
if (!m_salvium_txs.count(onetime_address))
continue;
// Get the origin TX details
td_origin_idx = m_salvium_txs.at(onetime_address);
THROW_WALLET_EXCEPTION_IF(td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate protocol TX origin in m_transfers");
const transfer_details &td_origin = m_transfers[td_origin_idx];
THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.type != cryptonote::transaction_type::AUDIT &&
td_origin.m_tx.type != cryptonote::transaction_type::STAKE,
error::wallet_internal_error,
"incorrect TX type for protocol_tx origin in m_transfers");
// Perform the correct sanity checks needed for each type of origin TX
if (td_origin.m_tx.type == cryptonote::transaction_type::STAKE) {
// Check the age of the TX
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
THROW_WALLET_EXCEPTION_IF(td_origin.m_block_height != height - stake_lock_period - 1,
error::wallet_internal_error,
"STAKE TX payout at incorrect height: found at " + std::to_string(height) +
", but expected at " + std::to_string(td_origin.m_block_height + stake_lock_period + 1));
// Check the amount
THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.amount_burnt > tx.vout[local_output_index].amount,
error::wallet_internal_error,
"STAKE TX payout (" + print_money(tx.vout[local_output_index].amount) +
") is less than staked amount " + print_money(td_origin.m_tx.amount_burnt));
// Populate the dummy enote_scan_info struct
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(td_origin.m_tx.vout[0], enote_scan_info_protocol.address_spend_pubkey),
error::wallet_internal_error,
"failed to retrieve origin TX address spend pubkey");
enote_scan_info_protocol.amount = tx.vout[local_output_index].amount;
enote_scan_info_protocol.amount_blinding_factor = rct::I;
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_asset_type(tx.vout[local_output_index], enote_scan_info_protocol.asset_type),
error::wallet_internal_error,
"failed to retrieve output asset type");
enote_scan_info_protocol.main_tx_pubkey_index = 0;
enote_scan_info_protocol.payment_id = crypto::null_hash;
enote_scan_info_protocol.subaddr_index = carrot::subaddress_index_extended{{td_origin.m_subaddr_index.major,td_origin.m_subaddr_index.minor}};
} else if (td_origin.m_tx.type == cryptonote::transaction_type::AUDIT) {
// Check the amount
THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.amount_burnt != tx.vout[local_output_index].amount,
error::wallet_internal_error,
"AUDIT TX error - payout " + print_money(tx.vout[local_output_index].amount) +
" does not match audited amount " + print_money(td_origin.m_tx.amount_burnt));
// Populate the dummy enote_scan_info struct
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(td_origin.m_tx.vout[0], enote_scan_info_protocol.address_spend_pubkey),
error::wallet_internal_error,
"failed to retrieve origin TX address spend pubkey");
enote_scan_info_protocol.amount = tx.vout[local_output_index].amount;
enote_scan_info_protocol.amount_blinding_factor = rct::I;
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_asset_type(tx.vout[local_output_index], enote_scan_info_protocol.asset_type),
error::wallet_internal_error,
"failed to retrieve output asset type");
enote_scan_info_protocol.main_tx_pubkey_index = 0;
enote_scan_info_protocol.payment_id = crypto::null_hash;
enote_scan_info_protocol.subaddr_index = carrot::subaddress_index_extended{{td_origin.m_subaddr_index.major,td_origin.m_subaddr_index.minor}};
} else {
// Should never happen - only STAKE and AUDIT should pay out in a PROTOCOL transaction
assert(false);
}
// Get the output key for the change entry
crypto::public_key pk_locked_coins = crypto::null_pkey;
THROW_WALLET_EXCEPTION_IF(!get_output_public_key(td_origin.m_tx.vout[td_origin.m_internal_output_index], pk_locked_coins),
error::wallet_internal_error,
"Failed to get output public key for locked coins");
// At this point, we need to clear the "locked coins" count, because otherwise we will be counting yield stakes twice in our balance
//THROW_WALLET_EXCEPTION_IF(!m_locked_coins.erase(pk_locked_coins), error::wallet_internal_error, "Failed to remove protocol_tx entry from m_locked_coins");
if (!m_locked_coins.erase(pk_locked_coins)) {
LOG_ERROR("Failed to remove protocol_tx entry from m_locked_coins - possible duplicate output key detected");
}
} else {
// Check for return_payment
if (m_salvium_txs.count(enote_scan_info_local->address_spend_pubkey)) {
// Must be a return payment
td_origin_idx = m_salvium_txs[enote_scan_info_local->address_spend_pubkey];
}
}
const auto &enote_scan_info = (is_protocol) ? enote_scan_info_protocol : enote_scan_info_local;
// check for burning bug
const auto ot_it = m_pub_keys.find(onetime_address);
const transfer_details *burning_td = ot_it != m_pub_keys.cend() ? &m_transfers.at(ot_it->second) : nullptr;
const bool should_treat_as_burned = burning_td &&
(burning_td->amount() >= enote_scan_info->amount || burning_td->m_spent)
;
(burning_td->amount() >= enote_scan_info->amount || burning_td->m_spent);
if (should_treat_as_burned)
{
// We already have an older received transfer with a greater-than-or-equal amount
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(ot_it->first)
<< " from received " << print_money(enote_scan_info->amount) << " output already exists with "
<< (burning_td->m_spent ? "spent" : "unspent") << " "
<< print_money(burning_td->amount()) << " in tx " << burning_td->m_txid << ", received output ignored");
<< " from received " << print_money(enote_scan_info->amount) << " output already exists with "
<< (burning_td->m_spent ? "spent" : "unspent") << " "
<< print_money(burning_td->amount()) << " in tx " << burning_td->m_txid << ", received output ignored");
continue;
}
else if (burning_td)
@@ -2680,8 +2587,14 @@ void wallet2::process_new_scanned_transaction(
const rct::xmr_amount extra_received_money = enote_scan_info->amount - (burning_td ? burning_td->amount() : 0);
const cryptonote::subaddress_index subaddr_index_cn{
enote_scan_info->subaddr_index->index.major, enote_scan_info->subaddr_index->index.minor};
tx_money_got_in_outs[subaddr_index_cn][enote_scan_info->asset_type] += extra_received_money;
tx_amounts_individual_outs[subaddr_index_cn].push_back(extra_received_money);
// we have to save even the 0 amount enotes since we need to save their one time addresses
// for some protocol txs.
//if (enote_scan_info->amount > 0)
{
tx_money_got_in_outs[subaddr_index_cn][enote_scan_info->asset_type] += extra_received_money;
tx_amounts_individual_outs[subaddr_index_cn].push_back(std::make_tuple(extra_received_money, onetime_address));
}
// set payment id
const bool ignore_long_pid = block_version >= IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION;
@@ -2707,10 +2620,6 @@ void wallet2::process_new_scanned_transaction(
if (pool)
continue;
// expand subaddress table if applicable
if (should_expand(subaddr_index_cn))
expand_subaddresses(subaddr_index_cn);
// update m_transfers view-incoming scan info, and default values
transfer_details& td = tools::add_element(m_transfers);
td.m_block_height = height;
@@ -2727,47 +2636,12 @@ void wallet2::process_new_scanned_transaction(
td.m_recovered_spend_pubkey = enote_scan_info->address_spend_pubkey;
td.m_rct = tx.version >= 2;
td.m_frozen = false;
set_unspent(m_transfers.size() - 1);
td.m_td_origin_idx = td_origin_idx;
td.m_td_origin_idx = m_salvium_txs.find(enote_scan_info->address_spend_pubkey) != m_salvium_txs.end() ?
m_salvium_txs.at(enote_scan_info->address_spend_pubkey) : std::numeric_limits<uint64_t>::max();
// update m_transfers key image values
if (td_origin_idx != (uint64_t)-1) {
// Return payment received - rebuild the correct key image
THROW_WALLET_EXCEPTION_IF(td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate return_payment TX origin in m_transfers");
const transfer_details &td_origin = m_transfers[td_origin_idx];
hw::device &hwdev = m_account.get_device();
hw::reset_mode rst(hwdev);
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
const crypto::public_key tx_public_key = get_tx_pub_key_from_extra(tx);
const std::vector<crypto::public_key> additional_tx_public_keys = get_additional_tx_pub_keys_from_extra(tx);
keypair in_ephemeral;
crypto::key_image ki;
origin_data origin_tx_data;
origin_tx_data.tx_pub_key = get_tx_pub_key_from_extra(td_origin.m_tx);
origin_tx_data.output_index = td_origin.m_internal_output_index;
origin_tx_data.tx_type = td_origin.m_tx.type;
rct::salvium_input_data_t sid;
THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(m_account.get_keys(),
m_subaddresses,
onetime_address,
tx_public_key,
additional_tx_public_keys,
local_output_index,
in_ephemeral,
ki,
hwdev,
true,
origin_tx_data,
sid),
error::wallet_internal_error,
"failed to obtain key image for protocol_tx output");
td.m_key_image_known = true;
td.m_key_image = ki;
} else {
td.m_key_image_known = bool(output_key_images[local_output_index]);
td.m_key_image = output_key_images[local_output_index].value_or(crypto::key_image{});
}
td.m_key_image_known = bool(output_key_images[local_output_index]);
td.m_key_image = output_key_images[local_output_index].value_or(crypto::key_image{});
if (!td.m_key_image_known)
{
// we might have cold signed, and have a mapping to key images
@@ -2778,11 +2652,12 @@ void wallet2::process_new_scanned_transaction(
td.m_key_image_known = true;
}
}
if (!td.m_key_image_known && is_protocol)
// override the key image for pre-carrot PROTOCOL/RETURN tx outputs.
if (!enote_scan_info->is_carrot && td.m_td_origin_idx != std::numeric_limits<uint64_t>::max())
{
// We need to manually create the key image data
THROW_WALLET_EXCEPTION_IF(td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate protocol TX origin in m_transfers");
const transfer_details &td_origin = m_transfers[td_origin_idx];
THROW_WALLET_EXCEPTION_IF(td.m_td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate return_payment TX origin in m_transfers");
const transfer_details &td_origin = m_transfers[td.m_td_origin_idx];
hw::device &hwdev = m_account.get_device();
hw::reset_mode rst(hwdev);
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
@@ -2795,26 +2670,30 @@ void wallet2::process_new_scanned_transaction(
origin_tx_data.output_index = td_origin.m_internal_output_index;
origin_tx_data.tx_type = td_origin.m_tx.type;
rct::salvium_input_data_t sid;
THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(m_account.get_keys(),
m_subaddresses,
onetime_address,
tx_public_key,
additional_tx_public_keys,
local_output_index,
in_ephemeral,
ki,
hwdev,
true,
origin_tx_data,
sid),
error::wallet_internal_error,
"failed to obtain key image for protocol_tx output");
THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(
m_account.get_keys(),
m_account.get_subaddress_map_cn(),
onetime_address,
tx_public_key,
additional_tx_public_keys,
local_output_index,
in_ephemeral,
ki,
hwdev,
true,
origin_tx_data,
sid),
error::wallet_internal_error,
"failed to obtain key image for protocol_tx output"
);
td.m_key_image_known = true;
td.m_key_image = ki;
}
td.m_key_image_request = m_watch_only; // for view wallets, that flag means "we want to request it"
td.m_key_image_partial = m_multisig;
set_unspent(m_transfers.size() - 1);
// update m_key_images, m_pub_keys, and output_tracker_cache
if (td.m_key_image_known)
m_key_images[td.m_key_image] = m_transfers.size() - 1;
@@ -2834,44 +2713,9 @@ void wallet2::process_new_scanned_transaction(
update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1);
}
// Check for Salvium-specific logic
if (tx.type == cryptonote::transaction_type::TRANSFER) {
// Store the potential return details for a pre-Carrot return_payment
// We might store garbage entries here occasionally, but they shouldn't impact performance significantly
crypto::public_key P_change = crypto::null_pkey;
size_t change_idx = local_output_index;
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(tx.vout[change_idx], P_change), error::wallet_internal_error, "Failed to get output public key");
m_subaddresses[P_change] = subaddr_index_cn;
m_salvium_txs.insert({P_change, m_transfers.size()-1});
} else if (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) {
// The CONVERT/STAKE/AUDIT TX was created by us - therefore we need to expect an output in the PROTOCOL_TX
// It could be a refund, an audit, or a conversion
THROW_WALLET_EXCEPTION_IF(tx.vout.size() != 1, error::wallet_internal_error, "Incorrect number of outputs from CONVERT/STAKE/AUDIT TX");
// Add the change output_public_key to the list of subaddresses to check
crypto::public_key P_change = crypto::null_pkey;
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(tx.vout[0], P_change), error::wallet_internal_error, "Failed to get change output public key");
m_subaddresses[P_change] = subaddr_index_cn;//tx_scan_info[o].received->index;//{0,0};
if (block_version >= HF_VERSION_ENABLE_N_OUTS)
m_salvium_txs.insert({tx.return_address_list[local_output_index], m_transfers.size()-1});
else
m_salvium_txs.insert({tx.return_address, m_transfers.size()-1});
if (tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT) {
// Additionally, with STAKE and AUDIT TXs, we need to update our "balance staked" subtotal, because otherwise our balance is out by the staked coins until they mature!
// SRCG: must remember to deduct the number of staked coins when they mature!!
LOG_PRINT_L1("***** STAKED/AUDITED COINS : " << tx.amount_burnt << " *****");
m_locked_coins.insert({P_change, {0, tx.amount_burnt, tx.source_asset_type}});
}
}
// money received callbacks
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (!ignore_callbacks && 0 != m_callback)
if (!ignore_callbacks && 0 != m_callback && td.m_amount > 0)
m_callback->on_money_received(height, txid, tx, td.m_amount, td.asset_type, 0, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time, td.m_td_origin_idx);
}
@@ -3002,8 +2846,29 @@ void wallet2::process_new_scanned_transaction(
{
if (subaddr_account && i->first.major == *subaddr_account)
{
// tx_amounts_individual_outs.erase(i->first);
// i = tx_money_got_in_outs.erase(i);
// save the change output key in our subaddress_map, so we can receive money to it later
// from protocol or return txs
for (const auto &entry: tx_amounts_individual_outs[i->first]) {
const crypto::public_key &onetime_address = std::get<1>(entry);
carrot::AddressDeriveType derive_type;
if (use_fork_rules(HF_VERSION_CARROT, 0)) {
derive_type = carrot::AddressDeriveType::Carrot;
} else {
derive_type = carrot::AddressDeriveType::PreCarrot;
}
const carrot::subaddress_index_extended subaddr_ext = {i->first.major, i->first.minor, derive_type, true};
// save to m_subaddresses as well, so that we can populate account subaddress map
// when we open the wallet first time.
if (derive_type == carrot::AddressDeriveType::PreCarrot)
m_subaddresses_extended[onetime_address] = subaddr_ext;
// update m_salvium_txs
m_salvium_txs.insert({onetime_address, m_transfers.size()-1});
}
// delete change output from amounts
tx_amounts_individual_outs.erase(i->first);
// delete individual change asset output
for (auto assets_it = i->second.begin(); assets_it != i->second.end();) {
if (assets_it->first == source_asset)
@@ -3029,12 +2894,16 @@ void wallet2::process_new_scanned_transaction(
{
for (const auto& j : i.second)
{
amounts_container amounts;
for (const auto& entry: tx_amounts_individual_outs[i.first]) {
amounts.push_back(std::get<0>(entry));
}
payment_details payment;
payment.m_tx_hash = txid;
payment.m_fee = fee;
payment.m_asset_type = j.first;
payment.m_amount = j.second;
payment.m_amounts = tx_amounts_individual_outs[i.first];
payment.m_amounts = amounts;
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
payment.m_timestamp = ts;
@@ -3133,6 +3002,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
//----------------------------------------------------------------------------------------------------
bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) const
{
if (height == 0) return false;
// seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height && height >= m_skip_to_height);
}
@@ -3149,7 +3019,11 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b,
THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 2 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
"block transactions=" + std::to_string(bche.txs.size()) +
" not match with daemon response size=" + std::to_string(parsed_block.o_indices.indices.size()));
if (height == 0) {
m_blockchain.clear();
}
THROW_WALLET_EXCEPTION_IF(height != m_blockchain.size(), error::wallet_internal_error,
"New blockchain entry mismatch: block height " + std::to_string(height) +
" is not the expected next height " + std::to_string(m_blockchain.size()));
@@ -3470,25 +3344,28 @@ void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vect
hw::reset_mode rst(hwdev);
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
// Create a thread-safe keystore instance to allow detection of `STAKE`, `AUDIT` and `RETURN_PAYMENT` TX outputs
tools::keystore keystore(m_subaddresses);
// define view-incoming scan and key image derivation job
std::vector<std::optional<wallet::enote_view_incoming_scan_info_t>> enote_scan_infos(num_tx_outputs);
std::vector<std::optional<crypto::key_image>> output_key_images(num_tx_outputs);
bool password_failure = false;
auto tx_scan_job = [this, &enote_scan_infos, &output_key_images, &password_failure, &keystore]
auto tx_scan_job = [this, &enote_scan_infos, &output_key_images, &password_failure]
(const cryptonote::transaction &tx, size_t tx_output_idx)
{
if (tx.vout.empty())
{
MWARNING("Skipping tx without any outputs: " << get_transaction_hash(tx) << ", height: " << m_blockchain.size());
return;
}
const size_t output_span_end = tx_output_idx + tx.vout.size();
if (output_span_end > enote_scan_infos.size() || output_span_end > output_key_images.size())
{
LOG_ERROR("BUG: tx scan worker output index out of bounds, skipping...");
return;
}
wallet::view_incoming_scan_transaction(tx,
this->m_account.get_keys(),
keystore,
this->m_account,
{&enote_scan_infos[0] + tx_output_idx, tx.vout.size()});
// if view-incoming scan was successful, try deriving the key image
@@ -3505,11 +3382,25 @@ void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vect
size_t i = 0;
size_t tx_output_idx = 0;
while (i < blocks.size()) {
/*
if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
tx_scan_job(par_blk.block.miner_tx, tx_output_idx);
tx_output_idx += par_blk.block.miner_tx.vout.size();
if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
tx_scan_job(par_blk.block.protocol_tx, tx_output_idx);
tx_output_idx += par_blk.block.protocol_tx.vout.size();
for (const cryptonote::transaction &tx : par_blk.txes)
{
if (!skip_scan_for_this_block)
tx_scan_job(tx, tx_output_idx);
tx_output_idx += tx.vout.size();
*/
tools::threadpool::waiter scan_blocks_waiter(tpool);
for (size_t j = 0; j < 10; ++j)
{
const parsed_block &par_blk = parsed_blocks.at(i);
const std::uint64_t height = start_height + i;
if (i+j >= blocks.size()) break;
const parsed_block &par_blk = parsed_blocks.at(i+j);
const std::uint64_t height = start_height + i + j;
const bool skip_scan_for_this_block = should_skip_block(par_blk.block, height);
if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(par_blk.block.miner_tx), tx_output_idx));
@@ -3523,13 +3414,13 @@ void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vect
tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(tx), tx_output_idx));
tx_output_idx += tx.vout.size();
}
if (++i >= blocks.size()) break;
}
if (!scan_blocks_waiter.wait())
{
THROW_WALLET_EXCEPTION_IF(password_failure, error::password_needed);
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Unrecognized exception in enote scanning threadpool");
}
i+=10;
}
// Start processing blockchain entries with scanned outputs
@@ -3549,7 +3440,7 @@ void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vect
const epee::span<const std::optional<crypto::key_image>> output_key_images_span(
output_key_images.data() + tx_output_idx, n_block_outputs);
if(current_index >= m_blockchain.size())
if(!current_index || current_index >= m_blockchain.size())
{
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, enote_scan_infos_span, output_key_images_span, output_tracker_cache);
++blocks_added;
@@ -5521,13 +5412,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
if (r)
{
// Generate the Carrot keys here
m_carrot_account.legacy_acb = m_account;
m_carrot_account.generate();
}
if (r)
{
m_account.set_carrot_keys();
if (!m_is_background_wallet)
setup_keys(password);
else
@@ -6710,6 +6596,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if (get_num_subaddress_accounts() == 0)
add_subaddress_account(tr("Primary account"));
m_account.insert_subaddresses(m_subaddresses_extended);
if (!m_subaddresses.empty())
{
// if we have subaddresses, we need to insert them into the account
for (const auto &subaddress : m_subaddresses)
m_account.insert_subaddresses(
// TODO: we assume none of these subaddresses are return tx subaddresses
{{subaddress.first, {{subaddress.second.major, subaddress.second.minor}, carrot::AddressDeriveType::PreCarrot, false}}}
);
}
try
{
@@ -6915,9 +6812,10 @@ void wallet2::process_background_cache_on_open()
background_w2->m_background_sync_type = m_background_sync_type;
background_w2->m_custom_background_key = m_custom_background_key;
cryptonote::account_base account = m_account;
account.forget_spend_key();
background_w2->m_account = account;
// TODO: uncommet this block
// carrot::carrot_and_legacy_account account = m_account;
// account.forget_spend_key();
// background_w2->m_account = account;
// Load background cache from file
background_w2->clear();
@@ -10532,7 +10430,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
std::vector<std::pair<std::string, std::string>> circ_amounts;
THROW_WALLET_EXCEPTION_IF(!get_circulating_supply(circ_amounts), error::wallet_internal_error, "Failed to get circulating supply");
// make a normal tx
bool r = cryptonote::construct_tx_and_get_tx_key(a_keys/*m_account.get_keys()*/, m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
bool r = cryptonote::construct_tx_and_get_tx_key(a_keys/*m_account.get_keys()*/, m_account.get_subaddress_map_cn(), sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
}
@@ -10839,7 +10737,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
if (do_carrot_tx_construction)
{
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_transfer(*this, dsts, priority, extra, subaddr_account, subaddr_indices, subtract_fee_from_outputs);
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_transfer(*this, dsts, priority, extra, tx_type, subaddr_account, subaddr_indices, subtract_fee_from_outputs);
std::vector<pending_tx> ptx_vector;
ptx_vector.reserve(tx_proposals.size());
for (const auto &tx_proposal : tx_proposals)
@@ -11675,7 +11573,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
if (do_carrot_tx_construction)
{
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep_all(*this, below, address, is_subaddress, outputs, priority, extra, subaddr_account, subaddr_indices);
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep_all(*this, below, address, is_subaddress, outputs, priority, extra, tx_type, subaddr_account, subaddr_indices);
std::vector<pending_tx> ptx_vector;
ptx_vector.reserve(tx_proposals.size());
// TODO: use CLSAGS here..
@@ -11761,12 +11659,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
return create_transactions_from(address, tx_type, asset_type, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra);
}
std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
{
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
if (do_carrot_tx_construction)
{
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, {ki}, address, is_subaddress, outputs, priority, extra);
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, {ki}, address, is_subaddress, outputs, priority, extra, tx_type);
std::vector<pending_tx> ptx_vector;
ptx_vector.reserve(tx_proposals.size());
// TODO: use CLSAGS here..
@@ -11803,7 +11701,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_return(std::vector
THROW_WALLET_EXCEPTION_IF(idx >= get_num_transfer_details(), error::wallet_internal_error, tr("cannot locate return_payment origin index in m_transfers"));
const transfer_details& td_origin = get_transfer_details(idx);
const std::string asset_type = td_origin.m_tx.source_asset_type;
bool is_subaddress = true;
bool is_subaddress = false;
size_t outputs = 1;
std::vector<size_t> unused_dust_indices = {};
size_t fake_outs_count = get_min_ring_size() - 1; // Use the default ring size
@@ -11818,6 +11716,111 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_return(std::vector
THROW_WALLET_EXCEPTION_IF(td.m_txid != td_origin.m_txid, error::wallet_internal_error, tr("TX hashes do not match for inputs to return_payment"));
THROW_WALLET_EXCEPTION_IF(td.asset_type != td_origin.asset_type, error::wallet_internal_error, tr("TX asset_type values do not match for inputs to return_payment"));
}
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
if (do_carrot_tx_construction)
{
// calculate the return address
// 1. parse tx extra
std::vector<crypto::public_key> main_tx_ephemeral_pubkeys;
std::vector<crypto::public_key> additional_tx_ephemeral_pubkeys;
cryptonote::blobdata tx_extra_nonce;
if (!wallet::parse_tx_extra_for_scanning(td_origin.m_tx.extra,
td_origin.m_tx.vout.size(),
main_tx_ephemeral_pubkeys,
additional_tx_ephemeral_pubkeys,
tx_extra_nonce))
MWARNING("Return transaction extra has unsupported format: ");
THROW_WALLET_EXCEPTION_IF(main_tx_ephemeral_pubkeys.empty() && additional_tx_ephemeral_pubkeys.empty(), error::wallet_internal_error,
"Transaction missing ephemeral pubkeys");
const bool is_carrot = carrot::is_carrot_transaction_v1(td_origin.m_tx);
THROW_WALLET_EXCEPTION_IF(!is_carrot, error::wallet_internal_error,
"Return payment is only supported for Carrot transactions");
// 2. perform ECDH derivations
std::vector<crypto::key_derivation> main_derivations;
std::vector<crypto::key_derivation> additional_derivations;
wallet::perform_ecdh_derivations(
epee::to_span(main_tx_ephemeral_pubkeys),
epee::to_span(additional_tx_ephemeral_pubkeys),
m_account.get_keys().k_view_incoming,
m_account.get_keys().get_device(),
is_carrot,
main_derivations,
additional_derivations
);
// 3. input context
carrot::input_context_t input_context = carrot::gen_input_context();
if (td_origin.m_tx.vin[0].type() == typeid(cryptonote::txin_to_key)) {
input_context = carrot::make_carrot_input_context(boost::get<cryptonote::txin_to_key>(td_origin.m_tx.vin[0]).k_image);
} else if (td_origin.m_tx.vin[0].type() == typeid(cryptonote::txin_gen)) {
input_context = carrot::make_carrot_input_context_coinbase(boost::get<cryptonote::txin_gen>(td_origin.m_tx.vin[0]).height);
} else {
THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Unsupported input type for return_payment");
}
const mx25519_pubkey origin_tx_shared_secret_unctx = carrot::raw_byte_convert<mx25519_pubkey>(main_derivations[0]);
// 4. compute m_return
crypto::public_key output_key;
carrot::encrypted_return_pubkey_t m_return;
THROW_WALLET_EXCEPTION_IF(
!cryptonote::get_output_public_key(td_origin.m_tx.vout[td_origin.m_internal_output_index], output_key),
error::wallet_internal_error,
"Failed to identify output key"
);
carrot::make_sparc_return_pubkey_encryption_mask(
origin_tx_shared_secret_unctx.data,
input_context,
output_key,
m_return
);
// 5. compute K_return from m_return
carrot::encrypted_return_pubkey_t K_return;
crypto::public_key return_pub;
std::memcpy(
K_return.bytes,
td_origin.m_tx.return_address_list[td_origin.m_internal_output_index].data,
sizeof(carrot::encrypted_return_pubkey_t)
);
K_return = K_return ^ m_return;
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.data), "Size mismatch");
std::memcpy(return_pub.data, K_return.bytes, sizeof(carrot::encrypted_return_pubkey_t));
// 6. compute the change index
crypto::secret_key z_i;
derivation_to_scalar(main_derivations[0], td_origin.m_internal_output_index, z_i);
struct {
char domain_separator[8];
crypto::secret_key output_index_key;
} buf;
std::memset(buf.domain_separator, 0x0, sizeof(buf.domain_separator));
std::strncpy(buf.domain_separator, "CHG_IDX", 8);
std::memcpy(buf.output_index_key.data, z_i.data, sizeof(crypto::secret_key));
uint8_t eci_data = td_origin.m_tx.return_address_change_mask[td_origin.m_internal_output_index];
crypto::secret_key eci_out;
keccak((uint8_t *)&buf, sizeof(buf), (uint8_t*)&eci_out, sizeof(eci_out));
uint8_t change_index = eci_data ^ eci_out.data[0];
crypto::public_key change_key;
THROW_WALLET_EXCEPTION_IF(
!cryptonote::get_output_public_key(td_origin.m_tx.vout[change_index], change_key),
error::wallet_internal_error,
"Failed to identify change output"
);
// 7. Make a destination address for the return
cryptonote::account_public_address address;
address.m_spend_public_key = change_key;
address.m_view_public_key = return_pub;
return create_transactions_from(address, cryptonote::transaction_type::RETURN, asset_type, is_subaddress, outputs, transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra);
}
// pre-carrot return payment construction uses subaddress true
is_subaddress = true;
// To return a payment, we need to know the y value to process the F value
// ...but the y value is calculated differently depending on the original TX
@@ -11941,12 +11944,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (const size_t transfer_idx : unused_dust_indices)
input_key_images.push_back(m_transfers.at(transfer_idx).m_key_image);
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, input_key_images, address, is_subaddress, outputs, priority, extra);
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, input_key_images, address, is_subaddress, outputs, priority, extra, tx_type);
std::vector<pending_tx> ptx_vector;
ptx_vector.reserve(tx_proposals.size());
// TODO: use CLSAGS here..
// for (const auto &tx_proposal : tx_proposals)
// ptx_vector.push_back(tools::wallet::finalize_all_proofs_from_transfer_details_as_pending_tx(tx_proposal, *this));
for (const auto &tx_proposal : tx_proposals)
ptx_vector.push_back(tools::wallet::finalize_all_proofs_from_transfer_details_as_pending_tx(tx_proposal, *this));
return ptx_vector;
}
@@ -12888,9 +12890,9 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
received = 0;
const auto enote_scan_infos = wallet::view_incoming_scan_transaction_as_sender(tx,
{&derivation, 1},
epee::to_span(additional_derivations),
address);
{&derivation, 1},
epee::to_span(additional_derivations),
address);
for (const auto &enote_scan_info : enote_scan_infos)
if (enote_scan_info && enote_scan_info->address_spend_pubkey == address.m_spend_public_key)
@@ -13541,7 +13543,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::secret_key shared_secret;
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus || tx.rct_signatures.type == rct::RCTTypeFullProofs || tx.rct_signatures.type == rct::RCTTypeSalviumOne);
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus || tx.rct_signatures.type == rct::RCTTypeFullProofs || tx.rct_signatures.type == rct::RCTTypeSalviumZero || tx.rct_signatures.type == rct::RCTTypeSalviumOne);
amount = rct::h2d(ecdh_info.amount);
}
total += amount;
@@ -13891,8 +13893,6 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
error::wallet_internal_error,
"get_tx_pub_key_from_received_outs is not relevant for Carrot txs");
tools::keystore subaddress_keystore(m_subaddresses);
const auto enote_scan_info = wallet::view_incoming_scan_enote_from_prefix(
td.m_tx,
td.amount(),
@@ -13900,7 +13900,7 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
td.m_internal_output_index,
m_account_public_address,
m_account.get_keys().m_view_secret_key,
subaddress_keystore,
m_account,
m_account.get_device());
const size_t main_tx_pubkey_index = enote_scan_info ? enote_scan_info->main_tx_pubkey_index : 0;
@@ -14221,7 +14221,6 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
PERF_TIMER_START(import_key_images_F);
auto spent_txid = spent_txids.begin();
auto it = spent_txids.begin();
tools::keystore keystore(m_subaddresses);
for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
{
THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
@@ -14235,8 +14234,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
// get received (change) amount
std::map<std::string, uint64_t> tx_money_got_in_outs;
const auto enote_scan_infos = wallet::view_incoming_scan_transaction(spent_tx,
m_account.get_keys(),
keystore);
m_account);
for (const auto &enote_scan_info : enote_scan_infos)
if (enote_scan_info && enote_scan_info->subaddr_index)
tx_money_got_in_outs[enote_scan_info->asset_type] += enote_scan_info->amount; //! @TODO: check overflow
+33 -10
View File
@@ -47,7 +47,7 @@
#include <random>
#include "include_base_utils.h"
#include "carrot_impl/account.h"
#include "carrot_core/account.h"
#include "carrot_impl/carrot_offchain_serialization.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/account_boost_serialization.h"
@@ -391,6 +391,19 @@ private:
return output_public_key;
};
const crypto::public_key get_eph_public_key() const {
crypto::public_key eph_public_key;
eph_public_key = cryptonote::get_tx_pub_key_from_extra(m_tx);
if (eph_public_key != crypto::null_pkey)
return eph_public_key;
const auto additional_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(m_tx);
if (!additional_pub_keys.empty())
return additional_pub_keys[m_internal_output_index];
return crypto::null_pkey;
};
bool is_carrot() const
{
THROW_WALLET_EXCEPTION_IF(m_tx.vout.size() <= m_internal_output_index,
@@ -1119,11 +1132,8 @@ private:
*/
bool verify_password(const epee::wipeable_string& password) {crypto::secret_key key = crypto::null_skey; return verify_password(password, key);};
bool verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out);
cryptonote::account_base& get_account(){return m_account;}
const cryptonote::account_base& get_account()const{return m_account;}
carrot::carrot_and_legacy_account& get_carrot_account(){return m_carrot_account;}
const carrot::carrot_and_legacy_account& get_carrot_account()const{return m_carrot_account;}
carrot::carrot_and_legacy_account& get_account(){return m_account;}
const carrot::carrot_and_legacy_account& get_account()const{return m_account;}
bool is_key_encryption_enabled() const;
void encrypt_keys(const crypto::chacha_key &key);
@@ -1182,7 +1192,8 @@ private:
boost::optional<cryptonote::subaddress_index> get_subaddress_index(const cryptonote::account_public_address& address) const;
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
//std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index) const;
std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const;
void add_subaddress_account(const std::string& label);
@@ -1275,7 +1286,7 @@ private:
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_return(std::vector<size_t> transfers_indices);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, const std::string& asset_type, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, const std::vector<cryptonote::tx_destination_entry>& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const;
@@ -1405,6 +1416,7 @@ private:
if (ver < 20)
return;
a & m_subaddresses.parent();
a & m_subaddresses_extended.parent();
std::unordered_map<cryptonote::subaddress_index, crypto::public_key> dummy_subaddresses_inv;
a & dummy_subaddresses_inv;
a & m_subaddress_labels;
@@ -1471,6 +1483,7 @@ private:
FIELD(m_scanned_pool_txs[0])
FIELD(m_scanned_pool_txs[1])
FIELD(m_subaddresses)
FIELD(m_subaddresses_extended)
FIELD(m_subaddress_labels)
FIELD(m_additional_tx_keys)
FIELD(m_attributes)
@@ -1950,6 +1963,16 @@ private:
std::optional<crypto::key_image> &ki_out,
bool &password_failure_inout);
void scan_protocol_tx_output(
const cryptonote::transaction& tx,
const size_t local_output_index,
const crypto::public_key &onetime_address,
const uint64_t height,
uint64_t &td_origin_idx_out,
crypto::public_key locked_coins_pk_out,
wallet::enote_view_incoming_scan_info_t &enote_scan_info_out
);
void process_new_transaction(
const crypto::hash &txid,
const cryptonote::transaction& tx,
@@ -2098,8 +2121,7 @@ private:
bool should_expand(const cryptonote::subaddress_index &index) const;
bool spends_one_of_ours(const cryptonote::transaction &tx) const;
carrot::carrot_and_legacy_account m_carrot_account;
cryptonote::account_base m_account;
mutable carrot::carrot_and_legacy_account m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
std::string m_daemon_address;
std::string m_proxy;
@@ -2124,6 +2146,7 @@ private:
serializable_unordered_map<crypto::public_key, size_t> m_pub_keys;
cryptonote::account_public_address m_account_public_address;
serializable_unordered_map<crypto::public_key, cryptonote::subaddress_index> m_subaddresses;
serializable_unordered_map<crypto::public_key, carrot::subaddress_index_extended> m_subaddresses_extended;
std::vector<std::vector<std::string>> m_subaddress_labels;
serializable_unordered_map<crypto::hash, std::string> m_tx_notes;
serializable_unordered_map<std::string, std::string> m_attributes;
+38 -11
View File
@@ -438,7 +438,9 @@ namespace tools
entry.type = pd.m_coinbase ? "block" : "in";
entry.subaddr_index = pd.m_subaddr_index;
entry.subaddr_indices.push_back(pd.m_subaddr_index);
entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -470,7 +472,9 @@ namespace tools
entry.subaddr_index = { pd.m_subaddr_account, 0 };
for (uint32_t i: pd.m_subaddr_indices)
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -503,7 +507,9 @@ namespace tools
entry.subaddr_index = { pd.m_subaddr_account, 0 };
for (uint32_t i: pd.m_subaddr_indices)
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_tx.unlock_time);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -526,7 +532,9 @@ namespace tools
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
entry.subaddr_indices.push_back(pd.m_subaddr_index);
entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -534,6 +542,8 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
std::vector<std::string> assets_in_wallet = m_wallet->list_asset_types();
std::string asset_type = req.asset_type.empty() ? "SAL1" : boost::algorithm::to_upper_copy(req.asset_type);
// verify that the asset is in the list of in-wallet assets
@@ -590,7 +600,7 @@ namespace tools
info.account_index = account_index;
info.address_index = i;
cryptonote::subaddress_index index = {info.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
info.address = m_wallet->get_subaddress_as_str({{info.account_index, info.address_index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
info.balance = balance_per_subaddress[i];
info.unlocked_balance = unlocked_balance_per_subaddress[i].first;
info.blocks_to_unlock = unlocked_balance_per_subaddress[i].second.first;
@@ -616,6 +626,9 @@ namespace tools
bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
try
{
THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
@@ -638,7 +651,8 @@ namespace tools
res.addresses.resize(res.addresses.size() + 1);
auto& info = res.addresses.back();
const cryptonote::subaddress_index index = {req.account_index, i};
info.address = m_wallet->get_subaddress_as_str(index);
//info.address = m_wallet->get_subaddress_as_str(index);
info.address = m_wallet->get_subaddress_as_str({{req.account_index, i}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
info.label = m_wallet->get_subaddress_label(index);
info.address_index = index.minor;
info.used = std::find_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return td.m_subaddr_index == index; }) != transfers.end();
@@ -731,6 +745,9 @@ namespace tools
bool wallet_rpc_server::on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
try
{
res.total_balance = 0;
@@ -751,7 +768,8 @@ namespace tools
continue;
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major;
info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
//info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
//for (const auto& asset: asset_types) {
info.balance = m_wallet->balance(subaddr_index.major, "SAL1", req.strict_balances);
@@ -1873,7 +1891,7 @@ namespace tools
{
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, cryptonote::transaction_type::TRANSFER, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra);
if (ptx_vector.empty())
{
@@ -2084,6 +2102,9 @@ namespace tools
bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
crypto::hash payment_id;
crypto::hash8 payment_id8;
cryptonote::blobdata payment_id_blob;
@@ -2124,7 +2145,8 @@ namespace tools
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
rpc_payment.subaddr_index = payment.m_subaddr_index;
rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
res.payments.push_back(rpc_payment);
}
@@ -2134,6 +2156,9 @@ namespace tools
bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
res.payments.clear();
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
if (!m_wallet) return not_open(er);
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
@@ -2151,7 +2176,8 @@ namespace tools
rpc_payment.block_height = payment.second.m_block_height;
rpc_payment.unlock_time = payment.second.m_unlock_time;
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.second.m_subaddr_index.major, payment.second.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.second.m_unlock_time, payment.second.m_block_height);
res.payments.push_back(std::move(rpc_payment));
}
@@ -2206,7 +2232,8 @@ namespace tools
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
res.payments.push_back(std::move(rpc_payment));
}
+1 -1
View File
@@ -13,7 +13,7 @@ using namespace cryptonote;
void wallet_accessor_test::set_account(tools::wallet2 * wallet, cryptonote::account_base& account)
{
wallet->clear();
wallet->m_account = account;
// wallet->m_account = reinterpret_cast<carrot::carrot_and_legacy_account&>(account);
wallet->m_key_device_type = account.get_device().get_type();
wallet->m_account_public_address = account.get_keys().m_account_address;
+1
View File
@@ -222,6 +222,7 @@ public:
carrot::RCTOutputEnoteProposal output_proposal;
carrot::get_output_proposal_normal_v1(payment_proposal,
{},
nullptr,
output_proposal,
m_encrypted_payment_id);
m_enote = output_proposal.enote;
+15 -1
View File
@@ -115,6 +115,7 @@ TEST(carrot_core, main_address_normal_scan_completeness)
encrypted_payment_id_t encrypted_payment_id;
get_output_proposal_normal_v1(proposal,
tx_first_key_image,
nullptr, // s_view_balance_dev
enote_proposal,
encrypted_payment_id);
@@ -185,6 +186,7 @@ TEST(carrot_core, subaddress_normal_scan_completeness)
encrypted_payment_id_t encrypted_payment_id;
get_output_proposal_normal_v1(proposal,
tx_first_key_image,
nullptr, // s_view_balance_dev
enote_proposal,
encrypted_payment_id);
@@ -252,6 +254,7 @@ TEST(carrot_core, integrated_address_normal_scan_completeness)
encrypted_payment_id_t encrypted_payment_id;
get_output_proposal_normal_v1(proposal,
tx_first_key_image,
nullptr, // s_view_balance_dev
enote_proposal,
encrypted_payment_id);
@@ -320,10 +323,13 @@ TEST(carrot_core, main_address_special_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_enote;
get_output_proposal_special_v1(proposal,
keys.k_view_incoming_dev,
tx_first_key_image,
cryptonote::transaction_type::TRANSFER, // tx_type
std::nullopt,
return_enote,
enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -397,10 +403,13 @@ TEST(carrot_core, subaddress_special_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_enote;
get_output_proposal_special_v1(proposal,
keys.k_view_incoming_dev,
tx_first_key_image,
cryptonote::transaction_type::TRANSFER, // tx_type
std::nullopt,
return_enote,
enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -711,6 +720,8 @@ static void subtest_2out_transfer_get_output_enote_proposals_completeness(const
std::vector<RCTOutputEnoteProposal> enote_proposals;
encrypted_payment_id_t encrypted_payment_id;
size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals({bob_payment_proposal},
{alice_payment_proposal},
dummy_encrypted_pid,
@@ -718,8 +729,11 @@ static void subtest_2out_transfer_get_output_enote_proposals_completeness(const
&alice.k_view_incoming_dev,
tx_first_key_image,
enote_proposals,
return_enote,
encrypted_payment_id,
change_index);
cryptonote::transaction_type::TRANSFER,
change_index,
normal_payments_indices);
ASSERT_EQ(2, enote_proposals.size()); // 2-out tx
+9 -1
View File
@@ -339,6 +339,7 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx)
/*selfsend_payment_proposals=*/{},
fee_per_weight,
/*extra=*/{},
cryptonote::transaction_type::TRANSFER,
[&input_info_by_ki]
(
const boost::multiprecision::int128_t&,
@@ -375,6 +376,7 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx)
std::vector<RCTOutputEnoteProposal> output_enote_proposals;
encrypted_payment_id_t encrypted_payment_id;
size_t change_index;
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,
@@ -384,7 +386,9 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx)
tx_proposal.key_images_sorted.at(0),
output_enote_proposals,
encrypted_payment_id,
change_index);
cryptonote::transaction_type::TRANSFER,
change_index,
normal_payments_indices);
// Collect balance info and enotes
std::vector<crypto::public_key> input_onetime_addresses;
@@ -413,6 +417,10 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx)
tx_proposal.key_images_sorted,
tx_proposal.sources,
tx_proposal.fee,
tx_proposal.tx_type,
tx_proposal.amount_burnt,
{}, // change_masks
{}, // return_enote
encrypted_payment_id);
ASSERT_EQ(2, tx.version);
+8 -1
View File
@@ -158,7 +158,9 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans
make_carrot_transaction_proposal_v1_transfer(normal_payment_proposals,
selfsend_payment_proposals,
tx_preproposal.fee_per_weight,
5,
tx_preproposal.extra_extra,
cryptonote::transaction_type::TRANSFER,
make_fake_input_selection_callback(),
ss_keys.carrot_account_spend_pubkey,
{{0, 0}, AddressDeriveType::Carrot},
@@ -204,6 +206,8 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans
std::vector<RCTOutputEnoteProposal> rederived_output_enote_proposals;
encrypted_payment_id_t rederived_encrypted_payment_id;
size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals(tx_proposal.normal_payment_proposals,
modified_selfsend_payment_proposals,
*parsed_encrypted_payment_id,
@@ -211,8 +215,11 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans
&ss_keys.k_view_incoming_dev,
parsed_key_images.at(0),
rederived_output_enote_proposals,
return_enote,
rederived_encrypted_payment_id,
change_index);
cryptonote::transaction_type::TRANSFER,
change_index,
normal_payments_indices);
EXPECT_EQ(*parsed_encrypted_payment_id, rederived_encrypted_payment_id);
ASSERT_EQ(parsed_enotes.size(), rederived_output_enote_proposals.size());
for (size_t enote_idx = 0; enote_idx < parsed_enotes.size(); ++enote_idx)
+6 -1
View File
@@ -188,6 +188,8 @@ static void subtest_legacy_2out_transfer_get_output_enote_proposals_completeness
std::vector<RCTOutputEnoteProposal> enote_proposals;
encrypted_payment_id_t encrypted_payment_id;
size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals({bob_payment_proposal},
{alice_payment_proposal},
gen_encrypted_payment_id(),
@@ -195,8 +197,11 @@ static void subtest_legacy_2out_transfer_get_output_enote_proposals_completeness
&alive_k_v_dev,
tx_first_key_image,
enote_proposals,
return_enote,
encrypted_payment_id,
change_index);
cryptonote::transaction_type::TRANSFER,
change_index,
normal_payments_indices);
ASSERT_EQ(2, enote_proposals.size()); // 2-out tx
+2
View File
@@ -86,6 +86,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_
.randomness = randomness
},
tx_first_key_image,
nullptr, // s_view_balance_dev
enote_proposal_out,
encrypted_payment_id_out
);
@@ -225,6 +226,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
get_output_proposal_return_v1(
proposal_return,
tx_return_first_key_image,
nullptr, // s_view_balance_dev
enote_proposal_return,
encrypted_payment_id_return
);
+2 -3
View File
@@ -56,9 +56,8 @@ namespace test
source_amount += input.value().amount;
auto const& key = boost::get<cryptonote::txout_to_tagged_key>(input.value().target);
actual_sources.push_back(
{{}, 0, key_field.pub_key, {}, std::size_t(input.index()), input.value().amount, rct, rct::identity()}
);
cryptonote::tx_source_entry foo{{}, 0, key_field.pub_key, {}, std::size_t(input.index()), input.value().amount, rct, false, false, rct::identity(), {}, "SAL1"};
actual_sources.push_back(foo);
for (unsigned ring = 0; ring < 10; ++ring)
actual_sources.back().push_output(input.index(), key.key, input.value().amount);
@@ -407,7 +407,9 @@ cryptonote::transaction construct_carrot_pruned_transaction_fake_inputs(
normal_payment_proposals,
selfsend_payment_proposals,
fake_fee_per_weight,
5,
/*extra=*/{},
cryptonote::transaction_type::TRANSFER,
std::move(select_inputs),
acc_keys.m_account_address.m_spend_public_key,
{{0, 0}, carrot::AddressDeriveType::PreCarrot},
+22 -23
View File
@@ -42,13 +42,13 @@
//----------------------------------------------------------------------------------------------------------------------
TEST(wallet_scanning, view_scan_as_sender_mainaddr)
{
cryptonote::account_base aether;
aether.generate();
carrot::carrot_and_legacy_account aether;
aether.generate();
cryptonote::account_base bob;
bob.generate();
const cryptonote::account_public_address bob_main_addr = bob.get_keys().m_account_address;
const crypto::public_key bob_main_spend_pubkey = bob_main_addr.m_spend_public_key;
carrot::carrot_and_legacy_account bob;
bob.generate();
const cryptonote::account_public_address bob_main_addr = bob.get_keys().m_account_address;
const crypto::public_key bob_main_spend_pubkey = bob_main_addr.m_spend_public_key;
const rct::xmr_amount amount = rct::randXmrAmount(10 * COIN);
@@ -88,9 +88,10 @@ TEST(wallet_scanning, view_scan_as_sender_mainaddr)
// call view_incoming_scan_transaction with no meaningful key nor subaddresses maps,
// just with the proper ECDH
const auto enote_scan_infos = tools::wallet::view_incoming_scan_transaction_as_sender(tx,
{&main_derivation, 1},
{},
bob_main_addr);
{&main_derivation, 1},
{},
bob_main_addr
);
bool matched = false;
for (const auto &enote_scan_info : enote_scan_infos)
@@ -109,11 +110,11 @@ TEST(wallet_scanning, view_scan_as_sender_mainaddr)
//----------------------------------------------------------------------------------------------------------------------
TEST(wallet_scanning, view_scan_long_payment_id)
{
cryptonote::account_base aether;
aether.generate();
carrot::carrot_and_legacy_account aether;
aether.generate();
cryptonote::account_base bob;
bob.generate();
carrot::carrot_and_legacy_account bob;
bob.generate();
const cryptonote::account_public_address bob_main_addr = bob.get_keys().m_account_address;
const crypto::public_key bob_main_spend_pubkey = bob_main_addr.m_spend_public_key;
@@ -160,9 +161,8 @@ TEST(wallet_scanning, view_scan_long_payment_id)
// just with the proper ECDH
std::vector<std::optional<tools::wallet::enote_view_incoming_scan_info_t>> enote_scan_infos(tx.vout.size());
tools::wallet::view_incoming_scan_transaction(tx,
bob.get_keys(),
{{bob_main_spend_pubkey, {}}}, // use a fake subaddress map with just the provided address in it
epee::to_mut_span(enote_scan_infos));
bob,
epee::to_mut_span(enote_scan_infos));
bool matched = false;
for (const auto &enote_scan_info : enote_scan_infos)
@@ -182,11 +182,11 @@ TEST(wallet_scanning, view_scan_long_payment_id)
//----------------------------------------------------------------------------------------------------------------------
TEST(wallet_scanning, view_scan_short_payment_id)
{
cryptonote::account_base aether;
aether.generate();
carrot::carrot_and_legacy_account aether;
aether.generate();
cryptonote::account_base bob;
bob.generate();
carrot::carrot_and_legacy_account bob;
bob.generate();
const cryptonote::account_public_address bob_main_addr = bob.get_keys().m_account_address;
const crypto::public_key bob_main_spend_pubkey = bob_main_addr.m_spend_public_key;
@@ -237,9 +237,8 @@ TEST(wallet_scanning, view_scan_short_payment_id)
// just with the proper ECDH
std::vector<std::optional<tools::wallet::enote_view_incoming_scan_info_t>> enote_scan_infos(tx.vout.size());
tools::wallet::view_incoming_scan_transaction(tx,
bob.get_keys(),
{{bob_main_spend_pubkey, {}}}, // use a fake subaddress map with just the provided address in it
epee::to_mut_span(enote_scan_infos));
bob,
epee::to_mut_span(enote_scan_infos));
bool matched = false;
for (const auto &enote_scan_info : enote_scan_infos)
+44 -22
View File
@@ -160,16 +160,16 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_transfer_1)
const uint64_t top_block_index = std::max(transfers.front().m_block_height, transfers.back().m_block_height)
+ CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_transfer(
transfers,
{{alice.get_keys().m_account_address.m_spend_public_key, {}}},
w,
dsts,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
/*subaddr_account=*/0,
/*subaddr_indices=*/{},
/*ignore_above=*/MONEY_SUPPLY,
/*ignore_below=*/0,
{},
top_block_index);
@@ -223,16 +223,16 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_transfer_2)
carrot::mock::convert_destination_v1(bob.cryptonote_address(), out_amount)
};
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_transfer(
transfers,
alice.subaddress_map_cn(),
w,
dsts,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
/*subaddr_account=*/spending_subaddr_account,
/*subaddr_indices=*/{},
/*ignore_above=*/MONEY_SUPPLY,
/*ignore_below=*/0,
{},
top_block_index);
@@ -290,15 +290,19 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_1)
const tools::wallet2::transfer_container transfers{gen_transfer_details()};
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(
transfers,
{{alice.get_keys().m_account_address.m_spend_public_key, {}}},
// transfers,
// {{alice.get_keys().m_account_address.m_spend_public_key, {}}},
w,
{transfers.front().m_key_image},
bob.get_keys().m_account_address,
/*is_subaddress=*/false,
/*n_dests_per_tx=*/1,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
transfers.front().m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
ASSERT_EQ(1, tx_proposals.size());
const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0);
@@ -323,16 +327,19 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_2)
bob.generate();
const tools::wallet2::transfer_container transfers{gen_transfer_details()};
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(
transfers,
{{alice.get_keys().m_account_address.m_spend_public_key, {}}},
// transfers,
// {{alice.get_keys().m_account_address.m_spend_public_key, {}}},
w,
{transfers.front().m_key_image},
bob.get_keys().m_account_address,
/*is_subaddress=*/false,
/*n_dests_per_tx=*/FCMP_PLUS_PLUS_MAX_OUTPUTS - 1,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
transfers.front().m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
ASSERT_EQ(1, tx_proposals.size());
const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0);
@@ -365,16 +372,19 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_3)
alice.generate();
const tools::wallet2::transfer_container transfers{gen_transfer_details()};
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(
transfers,
{{alice.get_keys().m_account_address.m_spend_public_key, {}}},
// transfers,
// {{alice.get_keys().m_account_address.m_spend_public_key, {}}},
w,
{transfers.front().m_key_image},
alice.get_keys().m_account_address,
/*is_subaddress=*/false,
/*n_dests_per_tx=*/FCMP_PLUS_PLUS_MAX_OUTPUTS,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
transfers.front().m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
ASSERT_EQ(1, tx_proposals.size());
const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0);
@@ -441,15 +451,19 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_4)
const size_t n_dests_per_tx = 4;
// make tx proposals
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(
transfers,
{{alice.get_keys().m_account_address.m_spend_public_key, {}}},
// transfers,
// {{alice.get_keys().m_account_address.m_spend_public_key, {}}},
w,
selected_key_images,
bob.get_keys().m_account_address,
/*is_subaddress=*/false,
/*n_dests_per_tx=*/n_dests_per_tx,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
top_block_index);
ASSERT_EQ(4, tx_proposals.size());
@@ -522,15 +536,19 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_5)
const size_t n_dests_per_tx = 8;
// make tx proposals
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(
transfers,
{{alice.get_keys().m_account_address.m_spend_public_key, {}}},
// transfers,
// {{alice.get_keys().m_account_address.m_spend_public_key, {}}},
w,
selected_key_images,
alice.get_keys().m_account_address,
/*is_subaddress=*/false,
/*n_dests_per_tx=*/n_dests_per_tx,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
top_block_index);
ASSERT_EQ(8, tx_proposals.size());
@@ -606,15 +624,19 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_6)
const size_t n_dests_per_tx = 2;
// make tx proposals
tools::wallet2 w;
const std::vector<carrot::CarrotTransactionProposalV1> tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(
transfers,
{{alice.get_keys().m_account_address.m_spend_public_key, {}}},
// transfers,
// {{alice.get_keys().m_account_address.m_spend_public_key, {}}},
w,
selected_key_images,
alice.get_keys().m_account_address,
/*is_subaddress=*/false,
/*n_dests_per_tx=*/n_dests_per_tx,
/*fee_per_weight=*/1,
5,
/*extra=*/{},
/*tx_type=*/cryptonote::transaction_type::TRANSFER,
top_block_index);
ASSERT_EQ(1, tx_proposals.size());
const carrot::CarrotTransactionProposalV1 &tx_proposal = tx_proposals.at(0);