Compare commits

..

125 Commits

Author SHA1 Message Date
Codex Bot 1998d79c3e Keep tx info sync without polyseed API drift
ci/gh-actions/depends / Cross-Mac aarch64 (push) Failing after 7m38s
ci/gh-actions/depends / ARM v7 (push) Has been cancelled
ci/gh-actions/depends / i686 Linux (push) Has been cancelled
ci/gh-actions/depends / i686 Win (push) Has been cancelled
ci/gh-actions/depends / RISCV 64bit (push) Has been cancelled
ci/gh-actions/depends / Cross-Mac x86_64 (push) Has been cancelled
ci/gh-actions/depends / x86_64 Freebsd (push) Has been cancelled
ci/gh-actions/depends / x86_64 Linux (push) Has been cancelled
ci/gh-actions/depends / Win64 (push) Has been cancelled
ci/gh-actions/depends / ARM v8 (push) Has been cancelled
2026-04-06 18:40:08 +02:00
Codex Bot ea4d5a9132 Fix subdirectory linker flag checks
ci/gh-actions/depends / Cross-Mac aarch64 (push) Has been cancelled
ci/gh-actions/depends / ARM v8 (push) Has been cancelled
ci/gh-actions/depends / ARM v7 (push) Has been cancelled
ci/gh-actions/depends / i686 Linux (push) Has been cancelled
ci/gh-actions/depends / i686 Win (push) Has been cancelled
ci/gh-actions/depends / RISCV 64bit (push) Has been cancelled
ci/gh-actions/depends / Cross-Mac x86_64 (push) Has been cancelled
ci/gh-actions/depends / x86_64 Freebsd (push) Has been cancelled
ci/gh-actions/depends / x86_64 Linux (push) Has been cancelled
ci/gh-actions/depends / Win64 (push) Has been cancelled
2026-04-06 18:38:28 +02:00
Codex Bot 585f7b9d05 Add store tx info and return addresses API
ci/gh-actions/depends / Cross-Mac aarch64 (push) Has been cancelled
ci/gh-actions/depends / ARM v8 (push) Has been cancelled
ci/gh-actions/depends / ARM v7 (push) Has been cancelled
ci/gh-actions/depends / i686 Linux (push) Has been cancelled
ci/gh-actions/depends / i686 Win (push) Has been cancelled
ci/gh-actions/depends / RISCV 64bit (push) Has been cancelled
ci/gh-actions/depends / Cross-Mac x86_64 (push) Has been cancelled
ci/gh-actions/depends / x86_64 Freebsd (push) Has been cancelled
ci/gh-actions/depends / x86_64 Linux (push) Has been cancelled
ci/gh-actions/depends / Win64 (push) Has been cancelled
2026-04-06 18:36:27 +02:00
Some Random Crypto Guy b7c707bd4e added hashes file for v1.1.1 2026-04-02 11:02:12 +01:00
Some Random Crypto Guy b57d2c338c bumped version to v1.1.1 2026-04-02 08:48:00 +01:00
Some Random Crypto Guy 28069fc84b Merge branch 'display-rollup-tx-amount-burnt' 2026-04-02 08:44:47 +01:00
Some Random Crypto Guy d7ec62cdbe fixed RPC-based mining issues with block cache template for some miners 2026-04-02 08:43:48 +01:00
auruya 244fc7acaa display amount_burnt for rollup txs 2026-04-01 13:24:10 +03:00
Some Random Crypto Guy 3716330ac7 added hashes file for mainnet release 2026-03-30 14:42:23 +01:00
Some Random Crypto Guy c4e7f6e0f0 implementation of treasury block reward; various fixes for P2Pool and solo-xmrig miners 2026-03-30 12:50:49 +01:00
Some Random Crypto Guy c0862ea66e bumped testnet to (hopefully) final version for Milestone-1 2026-03-27 11:09:25 +00:00
Some Random Crypto Guy 67b41240d2 improved miner_tx security relating to treasury block reward; updated fast-sync checkpoints to block 450000 2026-03-27 11:01:13 +00:00
Some Random Crypto Guy 16110603a1 updated 'fork date set' timestamp to correct value 2026-03-26 08:57:52 +00:00
Some Random Crypto Guy 83934efaad treasury reward payout added for HF11 2026-03-25 19:55:35 +00:00
Some Random Crypto Guy 6fb167308c fixed small asset_type issue for sweep; added HF11 block height 2026-03-23 10:16:26 +00:00
Some Random Crypto Guy d6f95156bc added 1.1.0-rc6 hashes; added consensus block on SAL* tokens 2026-03-19 16:34:50 +00:00
Some Random Crypto Guy 35804afc90 bumped RC version; reset testnet for RC6 2026-03-18 09:30:15 +00:00
Some Random Crypto Guy bc59547714 fix to prevent nefarious AI agents run by Tiamak from paying reduced rollup fees. Thanks for the assist, Tiamak! :-) 2026-03-18 09:27:36 +00:00
Some Random Crypto Guy f2b6068d8b added RC5 hashes file 2026-03-17 14:41:13 +00:00
Some Random Crypto Guy c820967cba fixed issues with ROLLUP circulating supply changes; fixed BURN SAL for HF11+; bumped RC version 2026-03-17 13:34:27 +00:00
Some Random Crypto Guy eadaa21e83 added hashes file for v1.1.0-rc4 2026-03-13 13:18:26 +00:00
Some Random Crypto Guy 465b384195 fixed regression of TESTNET_VERSION var 2026-03-10 21:01:54 +00:00
Some Random Crypto Guy 768ca26a8d Merge branch 'develop' of https://github.com/salvium/salvium into develop 2026-03-10 20:58:15 +00:00
Some Random Crypto Guy 6fb131a52b bumped testnet version; bumped miner and protocol TX versions for HF11; added some verification 2026-03-10 20:57:54 +00:00
auruya 3f18c388f6 add tx type and version check to block verification 2026-03-10 18:06:16 +03:00
auruya 2f646cdc10 use tx version 5 for miner and protocol txs in ENABLE_TOKENS hardfork 2026-03-10 18:03:04 +03:00
Some Random Crypto Guy a5523be459 bumped testnet version to prevent collision with old remnants; bumped version number; fixed duplicate CREATE_TOKEN issues 2026-03-10 13:56:13 +00:00
Some Random Crypto Guy 81d322b2ce Merge branch 'main' into develop 2026-03-10 13:54:58 +00:00
Some Random Crypto Guy 3b1a939417 added missing header file (thanks gitignore); bumped version 2026-03-05 14:36:25 +00:00
Some Random Crypto Guy a4e7ebc591 added missing test code 2026-03-05 14:10:49 +00:00
Some Random Crypto Guy 25dccd5423 updated some RPC param names; made some RPC params optional; bumped version 2026-03-05 11:17:06 +00:00
Some Random Crypto Guy d05ac7f44e initial import of v1.1 code 2026-03-04 14:38:59 +00:00
Some Random Crypto Guy 35b1a03b3d Merge branch 'main' into develop 2025-12-10 12:00:13 +00:00
Some Random Crypto Guy 7acf8068ea added hashes list 2025-12-10 11:53:35 +00:00
auruya 383f3d36e6 Fix difficulty cache in get_difficulty_for_next_block (#73) 2025-12-10 10:20:29 +00:00
Some Random Crypto Guy 7d06436d08 Merge branch 'main' of https://github.com/salvium/salvium 2025-12-10 10:09:48 +00:00
somerandomcryptoguy 3bee380b18 add cross-compilation dependencies to Dockerfile.salvium (#78)
Co-authored-by: auruya <dream.glorix@gmail.com>
2025-12-10 10:08:53 +00:00
auruya 58c70115f2 fix compiler warnings 2025-12-10 10:06:10 +00:00
Some Random Crypto Guy 6be4081332 fixed self-subaddress issue 2025-12-10 10:01:31 +00:00
auruya 538e4a5d1f misc fixes 2025-12-10 09:55:53 +00:00
Some Random Crypto Guy 3e49572539 updated checkpoints ready for next release 2025-12-10 09:53:02 +00:00
Some Random Crypto Guy 05c7152ad5 Merge branch 'add-account-all-command' 2025-12-10 09:22:14 +00:00
Some Random Crypto Guy 8d31fa2842 fixed Carrot TX proof for self-send to subaddress 2025-12-09 11:40:52 +00:00
Some Random Crypto Guy 2abe39f178 Merge branch 'carrot-tx-proof-support' 2025-12-09 09:26:42 +00:00
Some Random Crypto Guy a6f47a9f92 Merge branch 'carrot-tx-proof-support' into develop 2025-12-08 19:29:38 +00:00
Some Random Crypto Guy ac13287c78 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-12-08 16:36:19 +00:00
Some Random Crypto Guy 305b92909e fixed InProofV3 calcs / checks 2025-12-08 16:36:12 +00:00
auruya 248667016a fix freshly unlocked output being excluded from transactions 2025-12-04 17:33:37 +03:00
Some Random Crypto Guy 10b58aac73 fixed sweeping to own subaddress 2025-12-03 16:10:54 +00:00
Some Random Crypto Guy 0221fe8a34 fixed TX proof generation for multiple destinations 2025-12-02 20:29:28 +00:00
Some Random Crypto Guy 1ff480e64d added sanity checks on Carrot vs CN; fixed InProofV2 for CN 2025-12-02 14:59:07 +00:00
Some Random Crypto Guy fd121aae19 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-12-02 13:02:47 +00:00
Some Random Crypto Guy 0deb19c53c tidied TX proof code; removed large chunks of commented-out cruft 2025-12-02 13:02:39 +00:00
auruya 7f3e389d92 add carrot tx proof known values tests 2025-12-02 15:43:32 +03:00
Some Random Crypto Guy 87da2d4661 extended unit test scenarios for Carrot TX proofs 2025-12-01 20:23:27 +00:00
Some Random Crypto Guy f6075ae9ec simple unit test for Carrot TX proofs 2025-12-01 20:13:49 +00:00
Some Random Crypto Guy c424e84f4b cleaned up wallet code; fixed unit test 2025-12-01 15:44:23 +00:00
Some Random Crypto Guy 3b4efe9636 new fe_ functions for reversing point compression 2025-12-01 14:52:48 +00:00
auruya dfa27e78c6 add check_carrot_tx_proof fn 2025-11-27 14:08:39 +03:00
Some Random Crypto Guy 9b57fe3eae fixed lambda func with boost::optional 2025-11-26 09:56:13 +00:00
Some Random Crypto Guy 8f60758a3c interim checkin - pretty sure this proof cannot work without curve translation using ConvertPointE() 2025-11-25 11:57:57 +00:00
Some Random Crypto Guy 3a7ec4db32 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-11-25 09:22:36 +00:00
auruya 679bc9f0d7 update carrot tx proof support 2025-11-25 12:08:02 +03:00
Some Random Crypto Guy 362eb38ff8 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-11-21 12:37:48 +00:00
auruya 6243e992cf add get carrot tx proof support for sender 2025-11-21 14:50:31 +03:00
auruya e872414d57 add get carrot tx proof support for sender 2025-11-21 13:20:02 +03:00
auruya fcaf640bcb Add carrot tx proof support (get_tx_proof and check_tx_proof) 2025-11-17 19:58:40 +03:00
Some Random Crypto Guy f5237ceaf5 Merge branch 'move-xy-calculation-to-txbuilder' 2025-11-14 15:52:15 +00:00
Some Random Crypto Guy 1503ec6629 fixed CLI auto-refresh when rescan_bc 2025-11-14 15:42:58 +00:00
auruya 0f744520ad wallet cache migration v2 to v3 2025-11-14 17:26:50 +03:00
Some Random Crypto Guy caf52cca20 fixed spending of return_payment - forces rescan_bc of wallet on first load 2025-11-13 20:52:20 +00:00
auruya 1c4309c400 calculate x, y in tx_builder 2025-11-13 15:55:08 +03:00
Some Random Crypto Guy 9725b921a5 fixed scan refresh issue when Carrot keys are still encrypted 2025-11-12 14:46:41 +00:00
Some Random Crypto Guy 38d2515dc5 Merge branch 'develop' 2025-11-10 14:26:53 +00:00
Some Random Crypto Guy 13efd79f88 Merge branch 'fix-view-only-wallet' into develop 2025-11-10 13:34:36 +00:00
Some Random Crypto Guy 0c273d3571 possible fix for STAKE amount in GUI app - thanks, Tiamak 2025-11-10 12:28:41 +00:00
Some Random Crypto Guy d22389b37a added return_output_info collection to SVB scanning 2025-11-10 11:55:59 +00:00
auruya f9b060e552 add view only scan support 2025-11-10 14:36:38 +03:00
auruya 7d13f90e4a encrypt carrot keys 2025-11-10 14:35:48 +03:00
Some Random Crypto Guy f7a75a4fdc added help information 2025-11-04 11:38:32 +00:00
auruya b9fa97daad add account all command to display all accounts 2025-11-04 13:02:11 +03:00
Some Random Crypto Guy 6f0f5b5e83 fixed view-only wallet support 2025-11-03 17:12:27 +00:00
Some Random Crypto Guy 6fbd3184b1 fixed lookahead 2025-10-31 11:37:15 +00:00
Some Random Crypto Guy d5fee31ec6 partial fix for view-only wallet scanning 2025-10-29 21:14:36 +00:00
Some Random Crypto Guy 2a93e04180 removed erroneous Tor/i2p/blocklist entries 2025-10-29 20:48:36 +00:00
Some Random Crypto Guy b902ec9406 password issue fixed (kludgy) 2025-10-29 16:46:04 +00:00
Some Random Crypto Guy e3ba570fb1 publishing ARMv8 binaries 2025-10-29 15:34:00 +00:00
Some Random Crypto Guy fac65e5093 possible fix for GH action for cross-compilation for MacOS 2025-10-29 12:59:51 +00:00
Some Random Crypto Guy eebc1f1d26 Merge branch 'fix-check-tx-key' into develop 2025-10-28 11:39:56 +00:00
Some Random Crypto Guy 502ece5ba3 fixed random check_tx_key issues 2025-10-27 13:17:26 +00:00
Some Random Crypto Guy 62967db201 fixed merge of scanning code 2025-10-23 13:11:16 +01:00
Some Random Crypto Guy 4b7f863c71 fix for show_transfers not showing SC1 addresses; fixed proposal asset_type handling 2025-10-23 13:10:10 +01:00
auruya 55a6f17c91 fix check_tx_key 2025-10-23 14:40:13 +03:00
auruya 8a32d7f73b fix check_tx_key for multi-destination txs with additional derivations 2025-10-21 17:23:16 +03:00
Some Random Crypto Guy dfb6a705ea Merge branch 'blockchain-stats-testnet-fix' into develop 2025-10-21 12:23:25 +01:00
Some Random Crypto Guy 5246138398 fixed salvium-blockchain-stats for non-mainnet chains 2025-10-21 12:10:52 +01:00
Some Random Crypto Guy cc3e6a0822 Merge branch 'main' into develop 2025-10-21 12:09:43 +01:00
Some Random Crypto Guy 8e68f58eff Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-10-21 12:09:15 +01:00
auruya 0a79a4d9fd fix check_tx_key for carrot tx using x25519 derivation 2025-10-21 10:26:20 +03:00
Some Random Crypto Guy 48fb95bdc1 bumped version to v1.0.6 2025-10-16 14:58:02 +01:00
Some Random Crypto Guy bb4d3768b2 Merge branch 'wallet-ui-improvements' 2025-10-16 14:52:47 +01:00
Some Random Crypto Guy cea3f0f341 fixed Carrot wallet exceptions reporting 'unknown error'; fixed wallet.address.txt file to contain Carrot keys as well; fixed sending to subaddress from same wallet 2025-10-16 14:51:49 +01:00
Some Random Crypto Guy 35fc88c0ec bumped version 2025-10-15 13:45:16 +01:00
Some Random Crypto Guy 312413aeb0 Merge branch 'hotfix-subaddress-limit-removal-optimisation' 2025-10-15 13:31:28 +01:00
Some Random Crypto Guy 6ba060e116 fixed subaddress lookahead generation for Carrot; optimised to reduce scanning times 2025-10-15 13:29:04 +01:00
Some Random Crypto Guy 88d5e1f50e updated translation bundles; bumped version 2025-10-14 13:55:35 +01:00
Some Random Crypto Guy 644a8d3b4d Merge branch 'hotfix-carrot-address-display-in-cli' 2025-10-14 13:52:50 +01:00
Some Random Crypto Guy e80d135c15 Merge branch 'hotfix-return-from-multiple-dest-transaction' 2025-10-14 13:51:46 +01:00
Some Random Crypto Guy a40026f941 fixed return_payment when receiving Carrot output from a multiple-destination transaction 2025-10-14 13:51:04 +01:00
Some Random Crypto Guy fb25a36bf2 fixes for Carrot address display in CLI wallet 2025-10-14 10:44:26 +01:00
Some Random Crypto Guy 6f3f7c8e9a Merge branch 'hotfix-Carrot-get-tx-key' 2025-10-14 06:20:16 +01:00
Some Random Crypto Guy c6c35d5639 fixed get_tx_key implementation for Carrot (kludgy) 2025-10-14 06:19:45 +01:00
Some Random Crypto Guy 63433cc58f bumped version to v1.0.3 2025-10-14 05:05:55 +01:00
Some Random Crypto Guy 940c0e03f7 Merge branch 'hotfix-sweepall-to-self' 2025-10-14 05:04:03 +01:00
Some Random Crypto Guy 445e498fbf Merge branch 'hotfix-additional-cn-address-checks' 2025-10-14 05:03:51 +01:00
Some Random Crypto Guy 09ad75f0cc additional CN/Carrot address checks implemented in wallet 2025-10-14 05:02:28 +01:00
Some Random Crypto Guy f8d9f9335e fixed issue with post-Carrot-HF sweep_all to self 2025-10-13 21:17:45 +01:00
Some Random Crypto Guy cd31dafa97 updated ubuntu version for GH action runner 2025-10-13 19:17:52 +01:00
Some Random Crypto Guy 6aa32701b8 fixed get_block_template RPC call to handle payouts for treasury and miner 2025-10-13 16:20:39 +01:00
Some Random Crypto Guy f84a622bfa attempt to bump GH builds to use Ubuntu 22.04 2025-10-12 14:31:46 +01:00
Some Random Crypto Guy 4bfb5f51bf Merge branch 'develop' 2025-10-04 13:38:50 +01:00
Some Random Crypto Guy 260bc3721b bumped version 2025-10-03 21:18:52 +01:00
Some Random Crypto Guy 3e0457de09 Merge branch 'develop' 2025-10-03 21:17:54 +01:00
Some Random Crypto Guy 24f9916287 Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-05-08 09:21:06 +01:00
Some Random Crypto Guy cd22e55296 removed compilation warnings from scanner 2025-03-19 11:26:47 +00:00
170 changed files with 49626 additions and 43342 deletions
+4 -4
View File
@@ -22,7 +22,7 @@ env:
jobs:
build-cross:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
CCACHE_TEMPDIR: /tmp/.ccache-temp
strategy:
@@ -52,10 +52,10 @@ jobs:
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
- name: "Cross-Mac x86_64"
host: "x86_64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "Cross-Mac aarch64"
host: "aarch64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "x86_64 Freebsd"
host: "x86_64-unknown-freebsd"
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
@@ -105,7 +105,7 @@ jobs:
${{env.CCACHE_SETTINGS}}
make depends target=${{ matrix.toolchain.host }} -j2
- uses: actions/upload-artifact@v4
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' || matrix.toolchain.host == 'aarch64-linux-gnu' }}
with:
name: ${{ matrix.toolchain.name }}
path: |
+1 -12
View File
@@ -6,22 +6,11 @@
url = https://github.com/trezor/trezor-common.git
[submodule "external/randomx"]
path = external/randomx
url = https://github.com/MrCyjaneK/RandomX
branch = cyjan-fix-ios
[submodule "external/utf8proc"]
path = external/utf8proc
url = https://github.com/JuliaStrings/utf8proc.git
[submodule "external/polyseed"]
path = external/polyseed
url = https://github.com/tevador/polyseed.git
url = https://github.com/tevador/RandomX
[submodule "external/supercop"]
path = external/supercop
url = https://github.com/monero-project/supercop
branch = monero
[submodule "external/bc-ur"]
path = external/bc-ur
url = https://github.com/MrCyjaneK/bc-ur
branch = misc
[submodule "external/miniupnp"]
path = external/miniupnp
url = https://github.com/miniupnp/miniupnp
View File
+15 -20
View File
@@ -45,6 +45,10 @@ if (POLICY CMP0148)
endif()
include(FindPythonInterp)
if (IOS)
INCLUDE(CmakeLists_IOS.txt)
endif()
project(salvium)
option (USE_CCACHE "Use ccache if a usable instance is found" ON)
@@ -96,7 +100,6 @@ set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES) # boost: no template named 'unary_function' in namespace 'std'; did you mean '__unary_function'?
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
@@ -222,9 +225,9 @@ function(forbid_undefined_symbols)
cmake_minimum_required(VERSION 3.5)
project(test)
option(EXPECT_SUCCESS "" ON)
file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }")
file(WRITE "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }")
if (EXPECT_SUCCESS)
file(APPEND "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ")
file(APPEND "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ")
endif()
add_library(l0 SHARED incorrect_source.cpp)
add_library(l1 MODULE incorrect_source.cpp)
@@ -364,15 +367,12 @@ if(NOT MANUAL_SUBMODULES)
endfunction ()
message(STATUS "Checking submodules")
# check_submodule(external/bc-ur)
check_submodule(external/miniupnp)
check_submodule(external/rapidjson)
check_submodule(external/trezor-common)
check_submodule(external/randomx)
check_submodule(external/supercop)
check_submodule(external/mx25519)
check_submodule(external/polyseed)
check_submodule(external/utf8proc)
endif()
endif()
@@ -390,7 +390,7 @@ else()
endif()
list(INSERT CMAKE_MODULE_PATH 0
"${CMAKE_CURRENT_SOURCE_DIR}/cmake")
"${CMAKE_SOURCE_DIR}/cmake")
if (NOT DEFINED ENV{DEVELOPER_LOCAL_TOOLS})
message(STATUS "Could not find DEVELOPER_LOCAL_TOOLS in env (not required)")
@@ -459,7 +459,7 @@ elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
set(BSDI TRUE)
endif()
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include external/polyseed/include external/utf8proc)
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include)
if(APPLE)
cmake_policy(SET CMP0042 NEW)
@@ -692,21 +692,16 @@ include_directories(${LMDB_INCLUDE})
include_directories(${LIBUNWIND_INCLUDE})
link_directories(${LIBUNWIND_LIBRARY_DIRS})
if (HIDAPI_DUMMY)
add_definitions(-DHIDAPI_DUMMY)
# Final setup for hid
if (HIDAPI_FOUND)
message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}")
add_definitions(-DHAVE_HIDAPI)
include_directories(${HIDAPI_INCLUDE_DIR})
link_directories(${LIBHIDAPI_LIBRARY_DIRS})
else()
# Final setup for hid
if (HIDAPI_FOUND)
message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}")
add_definitions(-DHAVE_HIDAPI)
include_directories(${HIDAPI_INCLUDE_DIR})
link_directories(${LIBHIDAPI_LIBRARY_DIRS})
else()
message(STATUS "Could not find HIDAPI")
endif()
message(STATUS "Could not find HIDAPI")
endif()
# Trezor support check
include(CheckTrezor)
+4 -2
View File
@@ -3,14 +3,16 @@ FROM ubuntu:24.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies
# Install build dependencies including cross-compilers
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake pkg-config git imagemagick libcap-dev librsvg2-bin libz-dev \
g++-mingw-w64-x86-64 clang gcc-arm-none-eabi binutils-x86-64-linux-gnu libtiff-tools \
g++-x86-64-linux-gnu gcc-x86-64-linux-gnu binutils-aarch64-linux-gnu \
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu crossbuild-essential-amd64 \
libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev \
libreadline-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \
libprotobuf-dev protobuf-compiler libudev-dev libboost-all-dev python3 ccache doxygen graphviz \
ca-certificates curl zip libtool gperf \
ca-certificates curl zip libtool gperf automake autoconf \
&& rm -rf /var/lib/apt/lists/*
# Clone the develop branch
+5 -5
View File
@@ -1,4 +1,4 @@
# Salvium One v1.0.0
# Salvium One v1.1.1
Copyright (c) 2023-2025, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v1.0.0
git checkout v1.1.1
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 v1.0.0
git checkout v1.1.1
```
* 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. 'v1.0.0'. If you don't care about the version and just want binaries from master, skip this step:
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.1.1'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v1.0.0
git checkout v1.1.1
```
* If you are on a 64-bit system, run:
+1 -1
View File
@@ -28,7 +28,7 @@
#pragma once
#include "memwipe.h"
#include <stdint.h>
#include <boost/utility/string_ref.hpp>
#include <string>
+1
View File
@@ -162,6 +162,7 @@ namespace epee
{
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
static_assert(alignof(T) == 1, "type may have padding");
return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)};
}
-7
View File
@@ -34,7 +34,6 @@
#include <string>
#include "memwipe.h"
#include "fnv1.h"
#include "serialization/keyvalue_serialization.h"
namespace epee
{
@@ -76,12 +75,6 @@ namespace epee
bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; }
wipeable_string &operator=(wipeable_string &&other);
wipeable_string &operator=(const wipeable_string &other);
char& operator[](size_t idx);
const char& operator[](size_t idx) const;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer)
END_KV_SERIALIZE_MAP()
private:
void grow(size_t sz, size_t reserved = 0);
-10
View File
@@ -261,14 +261,4 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other)
return *this;
}
char& wipeable_string::operator[](size_t idx) {
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
return buffer[idx];
}
const char& wipeable_string::operator[](size_t idx) const {
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
return buffer[idx];
}
}
-3
View File
@@ -69,8 +69,5 @@ endif()
add_subdirectory(db_drivers)
add_subdirectory(easylogging++)
add_subdirectory(qrcodegen)
add_subdirectory(polyseed EXCLUDE_FROM_ALL)
add_subdirectory(utf8proc EXCLUDE_FROM_ALL)
add_subdirectory(bc-ur)
add_subdirectory(randomx EXCLUDE_FROM_ALL)
add_subdirectory(mx25519)
-1
Submodule external/bc-ur deleted from d82e7c753e
Submodule external/polyseed deleted from bd79f5014c
Submodule external/utf8proc deleted from 3de4596fbe
+5
View File
@@ -0,0 +1,5 @@
48417220800f174a3613881a56131d25c49d344228fca124c8331e0b58a8ff06 salvium-v1.0.7-ubuntu22.04-linux-x86_64.zip
52226551a9e1842df46cf068292cfa3c1e05328e0695cf6723365d4401af19a6 salvium-v1.0.7-ubuntu22.04-linux-aarch64.zip
b40c3765479c9d5712e766ddd01314b63e5080472b7639d34388e6b74b36142e salvium-v1.0.7-macos-x86_64.zip
60b05548f69040fe901e336e8fc4189a1028a8d7ded09bad555b3c854a0e8d6e salvium-v1.0.7-macos-aarch64.zip
b2c3e0bb8254ad1f62a78d6670c6e5adba1e42bb823919faad1cbf5796d53910 salvium-v1.0.7-win64.zip
+5
View File
@@ -0,0 +1,5 @@
e032e42ebac862bf90d71f0a231d9f3ddb5583e321ec153ee05144d87629980b salvium-v1.1.0-macos-aarch64.zip
0f31f09cf7be38b50a35510172bc94b7a4fb07c7107c79c4f055754c282c99f5 salvium-v1.1.0-macos-x86_64.zip
4424fd93391daab7eee47c54ab7aad7810a16f4c866209d41ba016d984605ffb salvium-v1.1.0-ubuntu22.04-linux-aarch64.zip
d1a5138f892189dfccc1d51d72ce24147fe6f1a2a5d465d350651426a71282a4 salvium-v1.1.0-ubuntu22.04-linux-x86_64.zip
bb9c9175726b82e061a6a332a27a6845805be4779928df3abbbd5c0f54691c9a salvium-v1.1.0-win64.zip
+5
View File
@@ -0,0 +1,5 @@
d0a8b0515ae2ee79849cbc17b0639bb7859e30efcd50e5b058540874cd0919ce salvium-v1.1.0-rc4-ubuntu22.04-linux-x86_64.zip
6528b8b23f09c574fc9383b48b88e87f99609ff5ce1b727872b5554505a69d77 salvium-v1.1.0-rc4-ubuntu22.04-linux-aarch64.zip
00ca183c47f852b8b30e4b87ce3b3536a0e97866e6027bf25d389b4a1bc9c471 salvium-v1.1.0-rc4-macos-x86_64.zip
50480b1043e9b5901576ab45f926b0b51a5d21237c6da549603ad1f13819e6ed salvium-v1.1.0-rc4-macos-aarch64.zip
adf96eee17e16f318fc047721acb5bfa8ae7ed1c0ff8d022da8ab5df64d8ce3f salvium-v1.1.0-rc4-win64.zip
+5
View File
@@ -0,0 +1,5 @@
23a03277e922c3f41ba6ddb0efc7581c3287d9f3faa2ddd19cc2d018a6797701 salvium-v1.1.0-rc5-ubuntu22.04-linux-x86_64.zip
94409b190eae890792b2d04cfffe148828dc23d658cd54a8d7802b12a9f274a4 salvium-v1.1.0-rc5-ubuntu22.04-linux-aarch64.zip
76cc02603cb21cd0729f0e5c9a39bb32310428b15580458ec8d74dcba39c9319 salvium-v1.1.0-rc5-macos-x86_64.zip
dc477718bfb370ecbafebf3e1736df5978200302f13542374f1b1fd43e07ab45 salvium-v1.1.0-rc5-macos-aarch64.zip
0e9b81eeaa32a4709a1cbd4c198721455a3c8cc86c1f1915cfc74af7885f8fee salvium-v1.1.0-rc5-win64.zip
+5
View File
@@ -0,0 +1,5 @@
b712adec8fa6bbcbe461b0748649e4c9e4fb61934fc1adb064779afed28c0758 salvium-v1.1.0-rc6-macos-aarch64.zip
8635955ff784f936a8b8de5be267e5dcdc0b55dfbf0b6f65ba24d098d8b8ab00 salvium-v1.1.0-rc6-macos-x86_64.zip
79b65ba9a074aeba87f03d83a07c3a6c51815d376049b3136a38c1a33260bfac salvium-v1.1.0-rc6-ubuntu22.04-linux-aarch64.zip
9031763ad60d1c8c572c76c5cb51a96b1680b9708c287264388f3d411bda464f salvium-v1.1.0-rc6-ubuntu22.04-linux-x86_64.zip
e3a8e53e092041ed9ed32c98d5d0ecf548f42232402748f862371ba21e1f1f9f salvium-v1.1.0-rc6-win64.zip
+5
View File
@@ -0,0 +1,5 @@
d22bbe19fa5e7eb7ebaf95eee336d73cbd59ded7b5e737213e89a8b84d5791eb salvium-v1.1.1-macos-aarch64.zip
262e2bdffc3c4fee89ec79451a6181175671db5874726d359077c4479754076e salvium-v1.1.1-macos-x86_64.zip
72b37fa30df6b136dba380b88e9ccc4d66804d8286a221a87e944e337f4ba593 salvium-v1.1.1-ubuntu22.04-linux-aarch64.zip
33321419bb426507de0f5de7c1977e436ca34bf4db620fa00eede1fa318b7994 salvium-v1.1.1-ubuntu22.04-linux-x86_64.zip
33e7c1e1bc4e5a1f9c40482eba993a5efb97a8feedfb36dca863bbb5bd9e794f salvium-v1.1.1-win64.zip
-1
View File
@@ -98,7 +98,6 @@ add_subdirectory(net)
add_subdirectory(hardforks)
add_subdirectory(blockchain_db)
add_subdirectory(mnemonics)
add_subdirectory(polyseed)
add_subdirectory(rpc)
add_subdirectory(seraphis_crypto)
if(NOT IOS)
+39 -8
View File
@@ -294,7 +294,22 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
time1 = epee::misc_utils::get_tick_count();
uint64_t num_rct_outs = 0;
oracle::asset_type_counts num_rct_outs_by_asset_type;
oracle::asset_type_counts_v2 num_rct_outs_by_asset_type;
// add newly created tokens
for (const std::pair<transaction, blobdata>& tx : txs)
{
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN)
{
uint32_t asset_type_id = cryptonote::asset_id_from_type("sal" + tx.first.token_metadata.asset_type);
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
throw std::runtime_error("Failed to add asset_type 'sal" + tx.first.token_metadata.asset_type + "'");
}
if (!is_tx_paid_for(tx.first))
throw std::runtime_error("TX is not paid for");
}
blobdata miner_bd = tx_to_blob(blk.miner_tx);
add_transaction(blk_hash, std::make_pair(blk.miner_tx, blobdata_ref(miner_bd)));
blobdata protocol_bd = tx_to_blob(blk.protocol_tx);
@@ -309,12 +324,14 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
std::string asset_type;
if (!get_output_asset_type(vout, asset_type))
throw std::runtime_error("Failed to get output asset type");
num_rct_outs_by_asset_type.add(asset_type, 1);
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
num_rct_outs_by_asset_type.add(asset_type_id, 1);
}
}
std::map<std::string, int64_t> slippage_counts;
uint64_t audit_total = 0, yield_total = 0;
uint64_t audit_total = 0, token_total = 0, yield_total = 0;
if (blk.protocol_tx.version >= 2)
{
num_rct_outs += blk.protocol_tx.vout.size();
@@ -326,10 +343,12 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
std::string asset_type;
if (!get_output_asset_type(vout, asset_type))
throw std::runtime_error("Failed to get output asset type");
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
// Update the RCT outs for the asset_type
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
num_rct_outs_by_asset_type.add(asset_type_id, 1);
// Update the RCT outs
num_rct_outs_by_asset_type.add(asset_type, 1);
// Update the amount tallies by DEDUCTING the minted amount
if (slippage_counts.count(asset_type) == 0)
slippage_counts[asset_type] = 0;
@@ -350,7 +369,9 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
throw std::runtime_error("Failed to get output asset type");
if (vout.amount == 0) {
++num_rct_outs;
num_rct_outs_by_asset_type.add(asset_type, 1);
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
num_rct_outs_by_asset_type.add(asset_type_id, 1);
}
// Is this a CONVERT TX?
@@ -367,6 +388,16 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
audit_total += tx.first.amount_burnt;
}
// Is this an create_token TX?
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN) {
token_total += tx.first.amount_burnt;
/*
uint32_t asset_type_id = cryptonote::asset_id_from_type(tx.first.token_metadata.asset_type);
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
throw std::runtime_error("Failed to add asset_type '" + tx.first.token_metadata.asset_type + "' to RCT outputs");
*/
}
// Is this a STAKE TX?
if (tx.first.type == cryptonote::transaction_type::STAKE) {
yield_total += tx.first.amount_burnt;
@@ -420,7 +451,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
// call out to subclass implementation to add the block & metadata
time1 = epee::misc_utils::get_tick_count();
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, nettype, ybi, abi);
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, token_total, nettype, ybi, abi);
TIME_MEASURE_FINISH(time1);
time_add_block1 += time1;
+30 -1
View File
@@ -218,6 +218,18 @@ typedef struct yield_tx_info_carrot {
carrot::encrypted_janus_anchor_t return_anchor_enc;
} yield_tx_info_carrot;
typedef struct token_tx_info_carrot {
uint64_t block_height;
uint8_t version;
crypto::hash tx_hash;
uint64_t locked_coins;
crypto::public_key return_address;
crypto::public_key return_pubkey;
carrot::view_tag_t return_view_tag;
carrot::encrypted_janus_anchor_t return_anchor_enc;
uint32_t asset_type_id;
} token_tx_info_carrot;
#define DBF_SAFE 1
#define DBF_FAST 2
#define DBF_FASTEST 4
@@ -437,11 +449,12 @@ private:
const difficulty_type& cumulative_difficulty,
const uint64_t& coins_generated,
uint64_t num_rct_outs,
oracle::asset_type_counts& cum_rct_by_asset_type,
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi
@@ -1212,6 +1225,20 @@ public:
virtual std::map<std::string, uint64_t> get_circulating_supply() const = 0;
/**
* @brief fetch the tokens from the blockchain
*
* @return the current token values
*/
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const = 0;
/**
* @brief determine if a TX has been paid for
*
* @return the current token values
*/
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const = 0;
/**
* <!--
* TODO: Rewrite (if necessary) such that all calls to remove_* are
@@ -1937,6 +1964,8 @@ public:
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const = 0;
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const = 0;
/**
* @brief set whether or not to automatically remove logs
File diff suppressed because it is too large Load Diff
+43 -1
View File
@@ -82,6 +82,14 @@ typedef struct mdb_txn_cursors
MDB_cursor *m_txc_audit_blocks;
MDB_cursor *m_txc_carrot_yield_txs;
MDB_cursor *m_txc_token_txs;
MDB_cursor *m_txc_tokens;
MDB_cursor *m_txc_asset_type_counts;
MDB_cursor *m_txc_rct_count_info;
MDB_cursor *m_txc_rollup_tx_info;
} mdb_txn_cursors;
#define m_cur_blocks m_cursors->m_txc_blocks
@@ -111,6 +119,14 @@ typedef struct mdb_txn_cursors
#define m_cur_audit_blocks m_cursors->m_txc_audit_blocks
#define m_cur_carrot_yield_txs m_cursors->m_txc_carrot_yield_txs
#define m_cur_token_txs m_cursors->m_txc_token_txs
#define m_cur_tokens m_cursors->m_txc_tokens
#define m_cur_asset_type_counts m_cursors->m_txc_asset_type_counts
#define m_cur_rct_count_info m_cursors->m_txc_rct_count_info
#define m_cur_rollup_tx_info m_cursors->m_txc_rollup_tx_info
typedef struct mdb_rflags
{
bool m_rf_txn;
@@ -140,6 +156,15 @@ typedef struct mdb_rflags
bool m_rf_audit_txs;
bool m_rf_audit_blocks;
bool m_rf_carrot_yield_txs;
bool m_rf_token_txs;
bool m_rf_tokens;
bool m_rf_asset_type_counts;
bool m_rf_rct_count_info;
bool m_rf_rollup_tx_info;
} mdb_rflags;
typedef struct mdb_threadinfo
@@ -242,6 +267,8 @@ public:
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const;
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs_old(const std::vector<uint64_t> &heights, const std::string asset_type) const;
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const;
virtual uint64_t get_block_timestamp(const uint64_t& height) const;
@@ -277,6 +304,10 @@ public:
virtual uint64_t height() const;
virtual std::map<std::string, uint64_t> get_circulating_supply() const;
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const;
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const;
virtual bool tx_exists(const crypto::hash& h) const;
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const;
@@ -406,11 +437,12 @@ private:
const difficulty_type& cumulative_difficulty,
const uint64_t& coins_generated,
uint64_t num_rct_outs,
oracle::asset_type_counts& cum_rct_by_asset_type,
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi
@@ -479,6 +511,8 @@ private:
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const;
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const;
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const;
private:
MDB_env* m_env;
@@ -521,6 +555,14 @@ private:
MDB_dbi m_carrot_yield_txs;
MDB_dbi m_token_txs;
MDB_dbi m_tokens;
MDB_dbi m_asset_type_counts;
MDB_dbi m_rct_count_info;
MDB_dbi m_rollup_tx_info;
mutable uint64_t m_cum_size; // used in batch size estimation
mutable unsigned int m_cum_count;
std::string m_folder;
+6 -1
View File
@@ -156,15 +156,20 @@ public:
const difficulty_type& cumulative_difficulty,
const uint64_t& coins_generated,
uint64_t num_rct_outs,
oracle::asset_type_counts& cum_rct_by_asset_type,
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi
) override { }
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const override { return {}; }
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const override { return true; }
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const override { return 0; }
virtual cryptonote::block get_block_from_height(const uint64_t& height) const override { return cryptonote::block(); }
virtual void set_hard_fork_version(uint64_t height, uint8_t version) override {}
virtual uint8_t get_hard_fork_version(uint64_t height) const override { return 0; }
+10 -13
View File
@@ -214,11 +214,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
#define MAX_RINGS 0xffffffff
struct tm prevtm = {0}, currtm;
uint64_t prevsz = 0, currsz = 0;
uint64_t prevtxs = 0, currtxs = 0;
uint64_t currblks = 0;
uint32_t txhr[24] = {0};
unsigned int i;
// uint64_t currsz = 0;
// uint64_t currtxs = 0;
// uint64_t currblks = 0;
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
@@ -246,9 +244,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
prevtm = currtm;
}
skip:
currsz += bd.size();
uint64_t coinbase_amount;
uint64_t tx_fee_amount = 0;
// currsz += bd.size();
std::set<std::string> used_assets, miner_tx_assets, protocol_tx_assets;
std::map<size_t, std::vector<std::string>> used_tx_versions;
used_assets.insert("SAL");
@@ -325,10 +321,11 @@ skip:
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX detected" << delimiter << std::endl;
continue;
}
currsz += bd.size();
if (db->get_prunable_tx_blob(tx_id, bd))
currsz += bd.size();
currtxs++;
// currsz += bd.size();
// if (db->get_prunable_tx_blob(tx_id, bd))
// currsz += bd.size();
// currtxs++;
db->get_prunable_tx_blob(tx_id, bd);
if (tx.type != cryptonote::transaction_type::TRANSFER &&
tx.type != cryptonote::transaction_type::BURN &&
@@ -374,7 +371,7 @@ skip:
}
}
currblks++;
// currblks++;
if (stop_requested)
break;
@@ -211,8 +211,15 @@ int main(int argc, char* argv[])
throw std::runtime_error("Failed to initialize a database");
}
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
boost::filesystem::path folder(opt_data_dir);
if (opt_stagenet) {
folder /= std::to_string(STAGENET_VERSION);
} else if (opt_testnet) {
folder /= std::to_string(TESTNET_VERSION);
}
folder /= db->get_db_name();
LOG_PRINT_L0("Loading blockchain from folder " << folder << " ...");
const std::string filename = folder.string();
try
{
Binary file not shown.
+73 -9
View File
@@ -282,13 +282,62 @@ crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::publ
return L;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate_subaddress_map()
crypto::key_image carrot_and_legacy_account::derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const
{
const auto it = subaddress_map.find(address_spend_pubkey);
CHECK_AND_ASSERT_THROW_MES(it != subaddress_map.cend(),
"carrot and legacy account: derive key image view only: cannot find subaddress");
const bool is_subaddress = it->second.index.is_subaddress();
const uint32_t major_index = it->second.index.major;
const uint32_t minor_index = it->second.index.minor;
const cryptonote::account_keys &keys = get_keys();
crypto::secret_key address_index_generator;
crypto::secret_key subaddress_scalar;
crypto::secret_key subaddress_extension;
crypto::secret_key address_privkey_g;
crypto::secret_key x;
CHECK_AND_ASSERT_THROW_MES(it->second.derive_type == AddressDeriveType::Carrot,
"carrot and legacy account: derive key image view only: not a Carrot address");
// s^j_gen = H_32[s_ga](j_major, j_minor)
make_carrot_index_extension_generator(keys.s_generate_address, major_index, minor_index, address_index_generator);
if (is_subaddress)
{
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
make_carrot_subaddress_scalar(keys.m_carrot_account_address.m_spend_public_key, address_index_generator, major_index, minor_index, subaddress_scalar);
}
else
{
// k^j_subscal = 1
sc_1(to_bytes(subaddress_scalar));
}
// k^g_a = k_gi * k^j_subscal
sc_mul(to_bytes(address_privkey_g), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
// x = k^{j,g}_addr + k^g_o
sc_add(to_bytes(x), to_bytes(address_privkey_g), to_bytes(sender_extension_g));
crypto::key_image L;
crypto::generate_key_image(onetime_address, x, L);
return L;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size)
{
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
for (uint32_t major_index = 0; major_index <= lookahead_size.first; ++major_index)
{
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
for (uint32_t minor_index = 0; minor_index <= lookahead_size.second; ++minor_index)
{
for (const AddressDeriveType derive_type : derive_types)
{
@@ -337,7 +386,7 @@ void carrot_and_legacy_account::set_keys(const cryptonote::account_keys& keys, b
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key)
{
{
// top level keys
m_keys.s_master = crypto::null_skey;
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
@@ -359,9 +408,18 @@ void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_pu
k_view_incoming_dev.view_key_scalar_mult_ed25519(crypto::get_G(),
m_keys.m_carrot_main_address.m_view_public_key
);
// Store fields for Carrot
m_keys.m_spend_secret_key = crypto::null_skey;
m_keys.m_view_secret_key = crypto::null_skey;
m_keys.m_account_address = {crypto::null_pkey, crypto::null_pkey, false};
// Update ALL addresses to be Carrot-only
m_keys.m_carrot_account_address.m_is_carrot = true;
m_keys.m_carrot_main_address.m_is_carrot = true;
// Set the default derive type for addresses
this->default_derive_type = AddressDeriveType::Carrot;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
@@ -393,13 +451,19 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
m_keys.m_carrot_main_address.m_is_carrot = true;
this->default_derive_type = default_derive_type;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::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}});
for (const auto &p : subaddress_map_cn) {
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
if (p.second.derive_type == AddressDeriveType::PreCarrot) {
// Create a matching Carrot address
const subaddress_index_extended subaddr_index{{p.second.index.major, p.second.index.minor}, AddressDeriveType::Carrot, p.second.is_return_spend_key};
const CarrotDestinationV1 addr = subaddress(subaddr_index);
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
}
}
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
+61 -21
View File
@@ -48,12 +48,59 @@ namespace carrot
input_context_t input_context;
crypto::public_key K_o; // output onetime address
crypto::public_key K_change; // change output onetime address
crypto::public_key K_spend_pubkey; // change output spend pubkey
crypto::key_image key_image;
crypto::secret_key sum_g;
crypto::secret_key sender_extension_t;
return_output_info_t() {
// Default constructor for serialization
input_context = input_context_t();
K_o = crypto::public_key();
K_change = crypto::public_key();
K_spend_pubkey = crypto::public_key();
key_image = crypto::key_image();
sum_g = crypto::secret_key();
sender_extension_t = crypto::secret_key();
}
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &K_o,
const crypto::public_key &K_change,
const crypto::public_key &K_spend_pubkey,
const crypto::key_image &key_image,
const crypto::secret_key &sum_g,
const crypto::secret_key &sender_extension_t):
input_context(input_context),
K_o(K_o),
K_change(K_change),
K_spend_pubkey(K_spend_pubkey),
key_image(key_image),
sum_g(sum_g),
sender_extension_t(sender_extension_t) {}
BEGIN_SERIALIZE_OBJECT()
FIELD(input_context)
FIELD(K_o)
FIELD(K_change)
FIELD(K_spend_pubkey)
FIELD(key_image)
FIELD(sum_g)
FIELD(sender_extension_t)
END_SERIALIZE()
};
// Old return_output_info_t format (for deserializing version 2 wallet caches)
struct return_output_info_retired_t {
input_context_t input_context;
crypto::public_key K_o;
crypto::public_key K_change;
crypto::key_image key_image;
crypto::secret_key x;
crypto::secret_key y;
return_output_info_t() {
// Default constructor for serialization
return_output_info_retired_t() {
input_context = input_context_t();
K_o = crypto::public_key();
K_change = crypto::public_key();
@@ -62,20 +109,6 @@ namespace carrot
y = crypto::secret_key();
}
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &K_o,
const crypto::public_key &K_change,
const crypto::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) {}
BEGIN_SERIALIZE_OBJECT()
FIELD(input_context)
FIELD(K_o)
@@ -144,7 +177,12 @@ namespace carrot
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const;
void generate_subaddress_map();
crypto::key_image derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const;
void generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size);
crypto::secret_key generate(
const crypto::secret_key& recovery_key = crypto::secret_key(),
@@ -183,9 +221,10 @@ namespace boost
x.input_context = carrot::input_context_t();
x.K_o = crypto::public_key();
x.K_change = crypto::public_key();
x.K_spend_pubkey = crypto::public_key();
x.key_image = crypto::key_image();
x.x = crypto::secret_key();
x.y = crypto::secret_key();
x.sum_g = crypto::secret_key();
x.sender_extension_t = crypto::secret_key();
}
template <class Archive>
@@ -194,9 +233,10 @@ namespace boost
a & x.input_context;
a & x.K_o;
a & x.K_change;
a & x.K_spend_pubkey;
a & x.key_image;
a & x.x;
a & x.y;
a & x.sum_g;
a & x.sender_extension_t;
}
}
}
+1
View File
@@ -75,5 +75,6 @@ static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 64;
// SPARC addressing protocol domain separators
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK[] = "SPARC return pubkey encryption mask";
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR[] = "SPARC return address scalar";
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR[] = "SPARC return index scalar";
} //namespace carrot
+5
View File
@@ -122,6 +122,11 @@ bool operator==(const view_tag_t &a, const view_tag_t &b)
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b)
{
return memcmp(&a, &b, sizeof(rollup_binding_tag_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
janus_anchor_t gen_janus_anchor()
{
return crypto::rand<janus_anchor_t>();
+11
View File
@@ -106,6 +106,13 @@ struct encrypted_return_pubkey_t final
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_BYTES];
};
/// Salvium rollup binding tag
constexpr std::size_t ROLLUP_BINDING_TAG_BYTES{8};
struct rollup_binding_tag_t final
{
unsigned char bytes[ROLLUP_BINDING_TAG_BYTES];
};
/// overloaded operators: address tag
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b);
static inline bool operator!=(const janus_anchor_t &a, const janus_anchor_t &b) { return !(a == b); }
@@ -139,6 +146,10 @@ bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubke
static inline bool operator!=(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b) { return !(a == b); }
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
/// overloaded operators: rollup binding tag
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b);
static inline bool operator!=(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b) { return !(a == b); }
/// generate a random janus anchor
janus_anchor_t gen_janus_anchor();
/// generate a random (non-null) payment ID
+53 -14
View File
@@ -39,6 +39,7 @@ extern "C"
#include "crypto/wallet/crypto.h"
#include "hash_functions.h"
#include "int-util.h"
#include "string_tools.h"
#include "misc_language.h"
#include "ringct/rctOps.h"
#include "transcript_fixed.h"
@@ -211,6 +212,17 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const uint64_t idx,
crypto::secret_key &return_index_out)
{
// k_idx = H_32(s_sr || input_context || Ko || idx)
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR>(input_context, onetime_address, idx);
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_index_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
@@ -222,22 +234,50 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &onetime_address,
encrypted_return_pubkey_t &return_pubkey_out)
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)
{
// K_return = k_return G ^ m_return
// compute k_return
crypto::secret_key k_return;
crypto::public_key return_pub;
encrypted_return_pubkey_t K_return;
encrypted_return_pubkey_t m_return;
s_view_balance_dev->make_internal_return_privkey(input_context, onetime_address, k_return);
crypto::secret_key_to_public_key(k_return, return_pub);
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.data), "Size mismatch");
memcpy(K_return.bytes, return_pub.data, sizeof(encrypted_return_pubkey_t));
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx, input_context, onetime_address, m_return);
return_pubkey_out = K_return ^ m_return;
// compute k_idx
crypto::secret_key k_idx;
make_sparc_return_index(s_sender_receiver_unctx, input_context, onetime_address, idx, k_idx);
// compute m_return
encrypted_return_pubkey_t m_return;
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx,
input_context,
onetime_address,
m_return);
#if 1
// compute SPARC K_return = k_return * G
crypto::public_key K_return;
crypto::secret_key_to_public_key(k_return, K_return);
// compute return_enc
encrypted_return_pubkey_t return_pub;
static_assert(sizeof(K_return.data) == sizeof(return_pub.bytes), "Size mismatch");
memcpy(return_pub.bytes, K_return.data, sizeof(encrypted_return_pubkey_t));
#else
// compute SPARC K_return = k_return * G + k_idx * T
rct::key K_return;
rct::addKeys2(K_return,
rct::sk2rct(k_return),
rct::sk2rct(k_idx),
rct::pk2rct(crypto::get_T()));
// compute return_enc
encrypted_return_pubkey_t return_pub;
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.bytes), "Size mismatch");
memcpy(return_pub.bytes, K_return.bytes, sizeof(encrypted_return_pubkey_t));
#endif
return_pubkey_out = return_pub ^ m_return;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
@@ -470,7 +510,6 @@ bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
// vt' = H_3(s_sr || input_context || Ko)
view_tag_t nominal_view_tag;
make_carrot_view_tag(s_sender_receiver_unctx, input_context, onetime_address, nominal_view_tag);
// vt' ?= vt
return nominal_view_tag == view_tag;
}
+19 -4
View File
@@ -138,6 +138,20 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out);
/**
* brief: make_sparc_return_index - return index, given non-secret data
* m_return = H_32(s_sr || input_context || Ko || idx)
* param: s_sender_receiver_unctx - s_sr
* param: input_context - input_context
* param: onetime_address - Ko
* param: idx - idx
* outparam: return_index_out - m_return
*/
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const uint64_t idx,
crypto::secret_key &return_index_out);
/**
* brief: make_sparc_return_pubkey_encryption_mask - used for hiding return pubkey
* m_return = H_32(s_sr || input_context || Ko)
* param: s_sender_receiver_unctx - s_sr
@@ -159,10 +173,11 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
* outparam: return_pubkey_mask_out - K_return
*/
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &onetime_address,
encrypted_return_pubkey_t &return_pubkey_out);
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)
+3 -3
View File
@@ -41,9 +41,9 @@
namespace carrot
{
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
class carrot_logic_error: public std::logic_error { using std::logic_error::logic_error; };
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
@@ -55,7 +55,7 @@ CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error)
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
class carrot_runtime_error: public std::runtime_error { using std::runtime_error::runtime_error; };
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
+10 -6
View File
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
}
else if (!need_change_output)
{
return AdditionalOutputType::DUMMY;
return AdditionalOutputType::CHANGE_UNIQUE;
}
else // num_selfsend == 1 && need_change_output
{
@@ -174,7 +174,10 @@ 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;
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::BURN) {
if (tx_type == cryptonote::transaction_type::STAKE ||
tx_type == cryptonote::transaction_type::BURN ||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
tx_type == cryptonote::transaction_type::ROLLUP) {
CARROT_CHECK_AND_THROW(num_proposals == 1, too_few_outputs, "tx doesn't have correct number of proposals");
} else {
CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals");
@@ -221,11 +224,12 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
encrypted_payment_id_t encrypted_payment_id;
if (tx_type == cryptonote::transaction_type::RETURN) {
const uint64_t idx = 0;
get_output_proposal_return_v1(normal_payment_proposals[i],
tx_first_key_image,
s_view_balance_dev,
output_entry.first,
encrypted_payment_id);
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,
+95 -7
View File
@@ -36,6 +36,7 @@
#include "misc_language.h"
#include "misc_log_ex.h"
#include "ringct/rctOps.h"
#include "crypto/generators.h"
//third party headers
@@ -208,10 +209,11 @@ static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_re
// 4. construct the return pubkey
if (s_view_balance_dev != nullptr)
make_sparc_return_pubkey(s_sender_receiver_unctx.data,
input_context,
s_view_balance_dev,
onetime_address_out,
return_pubkey_out);
input_context,
s_view_balance_dev,
onetime_address_out,
0,
return_pubkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
@@ -219,6 +221,7 @@ bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1
{
return a.destination == b.destination &&
a.amount == b.amount &&
a.asset_type == b.asset_type &&
a.randomness == b.randomness;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -228,6 +231,7 @@ bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentPro
a.amount == b.amount &&
a.enote_type == b.enote_type &&
a.internal_message == b.internal_message &&
a.asset_type == b.asset_type &&
0 == memcmp(&a.enote_ephemeral_pubkey, &b.enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
}
//-------------------------------------------------------------------------------------------------------------------
@@ -421,11 +425,95 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
// 8. save the enote ephemeral pubkey, first tx key image, and amount
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.asset_type = proposal.asset_type;
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &K_o,
const uint64_t idx,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out)
{
// 1. sanity checks
CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor,
missing_randomness, "invalid randomness for janus anchor (zero).");
// 2. input context: input_context = "R" || KI_1
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
/*
// K_v = K_return - k_idx * T
CarrotPaymentProposalV1 proposal_with_kv = proposal;
const bool is_sparc = (k_idx != crypto::null_skey) && sc_isnonzero(to_bytes(k_idx)); // check here! for zero its not needed
if (is_sparc)
{
// Calculate K_v = K_return - k_idx * T
const rct::key K_return_rct = rct::pk2rct(proposal.destination.address_view_pubkey);
const rct::key k_idx_T = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
rct::key K_v_rct;
rct::subKeys(K_v_rct, K_return_rct, k_idx_T);
proposal_with_kv.destination.address_view_pubkey = rct::rct2pk(K_v_rct);
}
*/
mx25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
get_normal_proposal_ecdh_parts(proposal,
input_context,
output_enote_out.enote.enote_ephemeral_pubkey,
s_sender_receiver_unctx);
// 4. build the output enote address pieces
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
encrypted_return_pubkey_t return_pubkey;
get_external_output_proposal_parts(
s_sender_receiver_unctx,
proposal.destination.address_spend_pubkey,
proposal.destination.payment_id,
proposal.amount,
CarrotEnoteType::PAYMENT,
output_enote_out.enote.enote_ephemeral_pubkey,
input_context,
s_view_balance_dev,
false, // coinbase_amount_commitment
s_sender_receiver,
output_enote_out.amount_blinding_factor,
output_enote_out.enote.amount_commitment,
output_enote_out.enote.onetime_address,
output_enote_out.enote.amount_enc,
encrypted_payment_id_out,
output_enote_out.enote.view_tag,
return_pubkey
);
// Override the onetime address
crypto::secret_key k_idx;
make_sparc_return_index(s_sender_receiver_unctx.data, input_context, K_o, idx, k_idx);
rct::key K_r = rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey),rct::pk2rct(proposal.destination.address_view_pubkey));
rct::key K_idx = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(K_r, K_idx));
// Recalculate the view tag : vt = H_3(s_sr || input_context || Ksra)
make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, output_enote_out.enote.onetime_address, output_enote_out.enote.view_tag);
// Recalculate a_enc = BytesToInt64(a) XOR m_a
output_enote_out.enote.amount_enc = encrypt_carrot_amount(proposal.amount, s_sender_receiver, output_enote_out.enote.onetime_address);
// Recalculate anchor_enc = anchor XOR m_anchor
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness, s_sender_receiver, output_enote_out.enote.onetime_address);
// Recalculate the pid_enc = pid XOR m_pid
encrypted_payment_id_out = encrypt_legacy_payment_id(proposal.destination.payment_id, s_sender_receiver, output_enote_out.enote.onetime_address);
// 6. save the amount & first key image & asset type
output_enote_out.amount = proposal.amount;
output_enote_out.enote.asset_type = proposal.asset_type;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
// disable returning an already returned tx.
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
@@ -559,12 +647,12 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
// 9. save the enote ephemeral pubkey, first tx key image, and amount
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.asset_type = proposal.asset_type;
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
// 10. construct the stake return enote
if (tx_type == cryptonote::transaction_type::STAKE) {
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
// make k_return
crypto::secret_key k_return;
s_view_balance_dev.make_internal_return_privkey(input_context, output_enote_out.enote.onetime_address, k_return);
+21 -4
View File
@@ -82,6 +82,8 @@ struct CarrotPaymentProposalSelfSendV1 final
std::optional<mx25519_pubkey> enote_ephemeral_pubkey;
/// anchor: arbitrary, pre-encrypted message for _internal_ selfsends
std::optional<janus_anchor_t> internal_message;
/// asset type
std::string asset_type;
};
struct RCTOutputEnoteProposal
@@ -137,6 +139,21 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out);
/**
* brief: get_output_proposal_paymentchannel_v1 - convert the carrot proposal to an output proposal
* param: proposal -
* param: tx_first_key_image -
* param: k_view_dev -
* outparam: output_enote_out -
* outparam: encrypted_payment_id_out - pid_enc
*/
void get_output_proposal_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &K_o,
const uint64_t idx,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out);
/**
* brief: get_output_proposal_return_v1 - convert the carrot proposal to an output proposal
* param: proposal -
* param: tx_first_key_image -
@@ -145,10 +162,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,
const view_balance_secret_device *s_view_balance_dev,
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 -
+19 -19
View File
@@ -387,6 +387,11 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
CarrotEnoteType &enote_type_out,
const bool check_pid)
{
epee::span<const crypto::public_key> main_address_spend_pubkeys;
if (destination.is_subaddress)
main_address_spend_pubkeys = {};
else
main_address_spend_pubkeys = {&destination.address_spend_pubkey, 1};
crypto::public_key recovered_address_spend_pubkey;
payment_id_t recovered_payment_id;
CarrotEnoteType recovered_enote_type;
@@ -395,7 +400,7 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
if (!try_scan_carrot_enote_external_normal_checked(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
{&destination.address_spend_pubkey, 1},
main_address_spend_pubkeys,
sender_extension_g_out,
sender_extension_t_out,
recovered_address_spend_pubkey,
@@ -470,6 +475,9 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
crypto::public_key &return_address_out,
bool &is_return_out)
{
// Determine whether this is a full wallet or a watch-only wallet
const cryptonote::account_keys &keys = account.get_keys();
// input_context
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
@@ -488,7 +496,6 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
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,
@@ -518,24 +525,17 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
// calculate the key image for the return output
crypto::secret_key sum_g;
sc_add(to_bytes(sum_g), to_bytes(sender_extension_g_out), to_bytes(k_return));
crypto::key_image key_image = account.derive_key_image(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
sender_extension_t_out,
K_r
);
crypto::key_image key_image = account.derive_key_image_view_only(address_spend_pubkey_out,
sum_g,
sender_extension_t_out,
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,
sender_extension_t_out,
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}}});
// HERE BE DRAGONS!!!
// SRCG: test whether this will even work for return_payment detection
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
//account.insert_return_output_info({{K_r, {input_context, output_key, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
// LAND AHOY!!!
}
// janus protection checks are not needed for internal scans
@@ -86,6 +86,12 @@ inline void serialize(Archive &a, carrot::encrypted_return_pubkey_t &x, const bo
}
//---------------------------------------------------
template <class Archive>
inline void serialize(Archive &a, carrot::rollup_binding_tag_t &x, const boost::serialization::version_type ver)
{
a & x.bytes;
}
//---------------------------------------------------
template <class Archive>
inline void serialize(Archive &a, carrot::CarrotDestinationV1 &x, const boost::serialization::version_type ver)
{
a & x.address_spend_pubkey;
@@ -42,3 +42,4 @@ BLOB_SERIALIZER(carrot::view_tag_t);
BLOB_SERIALIZER(carrot::encrypted_janus_anchor_t);
BLOB_SERIALIZER(carrot::encrypted_payment_id_t);
BLOB_SERIALIZER(carrot::encrypted_return_pubkey_t);
BLOB_SERIALIZER(carrot::rollup_binding_tag_t);
+42 -17
View File
@@ -193,31 +193,51 @@ bool try_load_carrot_extra_v1(
}
//-------------------------------------------------------------------------------------------------------------------
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
const std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const 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 std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const cryptonote::token_metadata_t &token,
const carrot::RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id,
const uint8_t hf_version)
{
const size_t nins = key_images.size();
const size_t nouts = enotes.size();
CHECK_AND_ASSERT_THROW_MES(nins == sources.size(), "invalid inputs/sources size");
CHECK_AND_ASSERT_THROW_MES(change_masks.size() == nouts, "invalid change masks size. Expected: " << nouts - 1 << " got: " << change_masks.size());
// Sanity check asset types - all enotes and sources must be the same
std::string asset_type = "";
for (const auto &enote: enotes) {
if (asset_type == "")
asset_type = enote.asset_type;
else
CHECK_AND_ASSERT_THROW_MES(enote.asset_type == asset_type, "invalid asset_type in enote. Expected: " << asset_type << " got: " << enote.asset_type);
}
for (const auto &source: sources) {
if (asset_type == "")
asset_type = source.asset_type;
else
CHECK_AND_ASSERT_THROW_MES(source.asset_type == asset_type, "invalid asset_type in source. Expected: " << asset_type << " got: " << source.asset_type);
}
cryptonote::transaction tx;
tx.pruned = true;
tx.unlock_time = 0;
tx.source_asset_type = "SAL1";
tx.destination_asset_type = "SAL1";
tx.version = TRANSACTION_VERSION_CARROT;
tx.source_asset_type = asset_type;
tx.destination_asset_type = asset_type;
tx.version = (hf_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
tx.type =
tx_type == cryptonote::transaction_type::RETURN ? cryptonote::transaction_type::TRANSFER : tx_type;
tx.amount_burnt = (
tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN
) ? tx_amount_burnt : 0;
tx.type == cryptonote::transaction_type::STAKE ||
tx.type == cryptonote::transaction_type::BURN ||
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
tx.type == cryptonote::transaction_type::ROLLUP
) ? tx_amount_burnt : 0;
tx.return_address_change_mask.assign(change_masks.begin(), change_masks.end());
tx.vin.reserve(nins);
tx.vout.reserve(nouts);
@@ -239,7 +259,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
//L
tx.vin.emplace_back(cryptonote::txin_to_key{ //@TODO: can save 2 bytes by using slim input type
.amount = 0,
.asset_type = "SAL1",
.asset_type = asset_type,
.key_offsets = cryptonote::absolute_output_offsets_to_relative(key_offsets),
.k_image = key_images.at(i)
});
@@ -264,7 +284,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment});
//K_return
if (tx_type != cryptonote::transaction_type::STAKE) {
if (tx_type != cryptonote::transaction_type::STAKE && tx_type != cryptonote::transaction_type::CREATE_TOKEN) {
crypto::public_key K_return;
memcpy(K_return.data, enote.return_enc.bytes, sizeof(crypto::public_key));
tx.return_address_list.push_back(K_return);
@@ -272,7 +292,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
}
// store the return pubkey for stake txs
if (tx_type == cryptonote::transaction_type::STAKE)
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN)
{
tx.protocol_tx_data.version = 1;
memcpy(tx.protocol_tx_data.return_address.data, return_enote.enote.onetime_address.data, sizeof(crypto::public_key));
@@ -280,7 +300,12 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
tx.protocol_tx_data.return_view_tag = return_enote.enote.view_tag;
tx.protocol_tx_data.return_anchor_enc = return_enote.enote.anchor_enc;
}
if (tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
tx.token_metadata = token;
} else {
tx.token_metadata = cryptonote::token_metadata_t{};
}
//ephemeral pubkeys: D_e
store_carrot_ephemeral_pubkeys_to_extra(enotes, tx.extra);
+10 -8
View File
@@ -102,14 +102,16 @@ bool try_load_carrot_extra_v1(
* return: a fully populated, pruned, non-coinbase transaction containing given Carrot information
*/
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
const std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const 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);
const std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const cryptonote::token_metadata_t &token,
const RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id,
const uint8_t hf_version);
/**
* brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
* param: tx -
+18 -5
View File
@@ -88,7 +88,8 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
const epee::span<const InputCandidate> input_candidates,
const std::set<std::size_t> &selectable_inputs,
std::size_t max_num_input_count,
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count)
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count,
const bool is_token_transfer)
{
// Returns (N, X) where the X is the sum of the amounts of the greatest N <= max_num_input_count
// inputs from selectable_inputs, maximizing X - F(N). F(N) is the fee for this transaction,
@@ -96,6 +97,7 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
// the fee, but greater than or equal to the difference of the fee compared to excluding that
// input. If this function returns N == 0, then there aren't enough usable funds, i.e. no N
// exists such that X - F(N) > 0.
// For token transfers, fee is paid separately in SAL1 so we just accumulate all inputs.
if (fee_by_input_count.empty() || selectable_inputs.empty())
return {0, 0};
@@ -125,6 +127,15 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
{
const rct::xmr_amount amount = *amount_it;
const rct::xmr_amount current_fee = fee_by_input_count.at(num_ins + 1);
// For token transfers, fee is paid separately in SAL1 - just accumulate inputs
if (is_token_transfer)
{
++num_ins;
cumulative_input_sum += amount;
continue;
}
CARROT_CHECK_AND_THROW(current_fee > last_fee,
carrot_logic_error, "provided fee by input count is not monotonically increasing");
const rct::xmr_amount marginal_fee_diff = current_fee - last_fee;
@@ -372,11 +383,12 @@ select_inputs_func_t make_single_transfer_input_selector(
// 3. Calculate minimum required input money sum for a given input count
const bool subtract_fee = flags & IS_KNOWN_FEE_SUBTRACTABLE;
const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
std::map<std::size_t, boost::multiprecision::uint128_t> required_money_by_input_count;
for (const auto &fee_and_input_count : fee_by_input_count)
{
required_money_by_input_count[fee_and_input_count.first] =
nominal_output_sum + (subtract_fee ? 0 : fee_and_input_count.second);
nominal_output_sum + ((subtract_fee || is_token_transfer) ? 0 : fee_and_input_count.second);
}
const boost::multiprecision::uint128_t absolute_minimum_required_money
= required_money_by_input_count.cbegin()->second;
@@ -390,9 +402,10 @@ select_inputs_func_t make_single_transfer_input_selector(
"Not enough money in all inputs (" << cryptonote::print_money(total_candidate_money)
<< ") to fund minimum output sum (" << cryptonote::print_money(absolute_minimum_required_money) << ')');
// const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
std::set<std::size_t> all_idxs; for (std::size_t i = 0; i < input_candidates.size(); ++i) all_idxs.insert(i);
const std::pair<std::size_t, boost::multiprecision::uint128_t> max_usable_money =
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count);
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
CARROT_CHECK_AND_THROW(max_usable_money.second >= absolute_minimum_required_money,
not_enough_usable_money,
"Not enough usable money in top " << max_usable_money.first << " inputs ("
@@ -417,7 +430,7 @@ select_inputs_func_t make_single_transfer_input_selector(
// Skip if not enough money in this selectable set for max number of tx inputs...
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count);
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
if (!max_usable_money.first)
continue;
else if (max_usable_money.second < required_money_by_input_count.at(max_usable_money.first))
@@ -441,7 +454,7 @@ select_inputs_func_t make_single_transfer_input_selector(
// Skip if not enough money in this selectable set for exact number of inputs...
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
input_candidate_subset, n_inputs, fee_by_input_count);
input_candidate_subset, n_inputs, fee_by_input_count, is_token_transfer);
if (max_usable_money.first != n_inputs)
continue;
else if (max_usable_money.second < required_money)
+5 -2
View File
@@ -52,14 +52,17 @@ struct InputCandidate
namespace InputSelectionFlags
{
// Quantum forward secrecy (ON = unsafe)
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0;
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1;
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0; // 000..00000001
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1; // 000..00000010
static constexpr std::uint32_t ALLOW_MIXED_INTERNAL_EXTERNAL = 1 << 2;
static constexpr std::uint32_t ALLOW_MIXED_CARROT_PRE_CARROT = 1 << 3;
// Amount handling
static constexpr std::uint32_t IS_KNOWN_FEE_SUBTRACTABLE = 1 << 4;
static constexpr std::uint32_t ALLOW_DUST = 1 << 5;
// Token transfer (fee is paid separately in SAL1 via rollup tx, so no fee for input selection)
static constexpr std::uint32_t IS_TOKEN_TRANSFER = 1 << 6;
}
/**
+3 -1
View File
@@ -132,7 +132,9 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1
0, // tx_amount_burnt
{}, // change_masks
{}, // return_enote
encrypted_payment_id);
{0},
encrypted_payment_id,
/*hf_version=*/10);
// add extra payload and sort
if (!tx_proposal.extra.empty())
+13 -3
View File
@@ -67,8 +67,8 @@ struct CarrotPaymentProposalVerifiableSelfSendV1
*
* For exact details on what goes into the signable transaction hash, see `rct::get_pre_mlsag_hash`.
*/
struct CarrotTransactionProposalV1
{
struct CarrotTransactionProposalV1
{
/// Key images sorted in std::greater order
std::vector<crypto::key_image> key_images_sorted;
// sources in the same order as key_images_sorted.
@@ -89,8 +89,18 @@ struct CarrotTransactionProposalV1
/// how much money tx burns
rct::xmr_amount amount_burnt;
/// used if this is a CREATE_TOKEN transaction
cryptonote::token_metadata_t token;
/// This field is truly "extra". It should contain only tx.extra fields that aren't present in a
/// normal Carrot transaction, i.e. NOT ephemeral pubkeys nor encrypted PIDs
std::vector<std::uint8_t> extra;
};
/// Used if this is a TOKEN_TRANSFER or ROLLUP transaction
carrot::rollup_binding_tag_t rollup_binding_tag;
/// Used if this is a ROLLUP transaction
cryptonote::layer2_rollup_data_t layer2_rollup_data;
};
} //namespace carrot
+44 -7
View File
@@ -209,17 +209,35 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
input_amount_sum += selected_input.amount;
// callback to balance the outputs with the fee and input sum
carve_fees_and_balance(input_amount_sum, tx_proposal_out.fee, normal_payment_proposals, selfsend_payment_proposals);
std::string asset_type = "SAL1"; // default to SAL1
if (!normal_payment_proposals.empty()) {
asset_type = normal_payment_proposals.at(0).asset_type;
} else if (!selfsend_payment_proposals.empty()) {
asset_type = selfsend_payment_proposals.at(0).proposal.asset_type;
}
uint64_t fee = tx_proposal_out.fee;
if (asset_type != "SAL1") {
// check here
if (tx_type != cryptonote::transaction_type::BURN || asset_type != "SAL") {
CARROT_CHECK_AND_THROW(cryptonote::is_valid_custom_asset_type(asset_type),
carrot_logic_error, "make_carrot_transaction_proposal_v1: invalid asset type in payment proposals: " << asset_type);
}
fee = 0; //fee is always in SAL1
}
carve_fees_and_balance(input_amount_sum, fee, normal_payment_proposals, selfsend_payment_proposals);
// sanity check balance
input_amount_sum -= tx_proposal_out.fee;
input_amount_sum -= fee;
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
input_amount_sum -= normal_payment_proposal.amount;
for (const CarrotPaymentProposalVerifiableSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals)
input_amount_sum -= selfsend_payment_proposal.proposal.amount;
if (tx_type != cryptonote::transaction_type::STAKE &&
tx_type != cryptonote::transaction_type::BURN)
tx_type != cryptonote::transaction_type::BURN &&
tx_type != cryptonote::transaction_type::CREATE_TOKEN &&
tx_type != cryptonote::transaction_type::ROLLUP)
{
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
@@ -239,7 +257,7 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
tx_proposal_out.key_images_sorted.end(),
std::greater{}); // consensus rules dictate inputs sorted in *reverse* lexicographical order since v7
// set the transaction type
// set the transaction type & new asset type
tx_proposal_out.tx_type = tx_type;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -255,6 +273,7 @@ void make_carrot_transaction_proposal_v1_transfer(
const subaddress_index_extended &change_address_index,
const std::set<std::size_t> &subtractable_normal_payment_proposals,
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out)
{
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals = selfsend_payment_proposals_in;
@@ -275,7 +294,8 @@ void make_carrot_transaction_proposal_v1_transfer(
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = 0,
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE,
.asset_type = asset_type
},
.subaddr_index = change_address_index
});
@@ -397,10 +417,13 @@ void make_carrot_transaction_proposal_v1_transfer(
// remove the self send payment we have made to ourself now that we have our change payment.
if (tx_type == cryptonote::transaction_type::STAKE ||
tx_type == cryptonote::transaction_type::BURN)
tx_type == cryptonote::transaction_type::BURN ||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
tx_type == cryptonote::transaction_type::ROLLUP)
{
selfsend_payment_proposals.back().proposal.enote_ephemeral_pubkey =
selfsend_payment_proposals.front().proposal.enote_ephemeral_pubkey;
selfsend_payment_proposals.erase(selfsend_payment_proposals.begin());
}
@@ -430,6 +453,7 @@ void make_carrot_transaction_proposal_v1_sweep(
std::vector<CarrotSelectedInput> &&selected_inputs,
const crypto::public_key &change_address_spend_pubkey,
const subaddress_index_extended &change_address_index,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out)
{
// sanity check payment proposals are provided
@@ -452,6 +476,19 @@ void make_carrot_transaction_proposal_v1_sweep(
CHECK_AND_ASSERT_THROW_MES(bool(normal_payment_proposals.size()) ^ bool(selfsend_payment_proposals.size()),
"make carrot transaction proposal v1 sweep: both normal and self-send payment proposals are provided");
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals_inout{selfsend_payment_proposals};
//if (tx_type == cryptonote::transaction_type::RETURN) {
selfsend_payment_proposals_inout.push_back(carrot::CarrotPaymentProposalVerifiableSelfSendV1{
.proposal = carrot::CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = 0,
.enote_type = carrot::CarrotEnoteType::CHANGE,
.asset_type = asset_type
},
.subaddr_index = change_address_index
});
// }
const bool is_selfsend_sweep = !selfsend_payment_proposals.empty();
// define input selection callback, which is just a shuttle for `selected_inputs`
@@ -502,7 +539,7 @@ void make_carrot_transaction_proposal_v1_sweep(
// make unsigned transaction with sweep carving callback and selected inputs
make_carrot_transaction_proposal_v1(normal_payment_proposals,
selfsend_payment_proposals,
selfsend_payment_proposals_inout,
fee_per_weight,
fee_quantization_mask,
extra,
+2
View File
@@ -213,6 +213,7 @@ void make_carrot_transaction_proposal_v1_transfer(
const subaddress_index_extended &change_address_index,
const std::set<std::size_t> &subtractable_normal_payment_proposals,
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out);
void make_carrot_transaction_proposal_v1_sweep(
@@ -225,6 +226,7 @@ void make_carrot_transaction_proposal_v1_sweep(
std::vector<CarrotSelectedInput> &&selected_inputs,
const crypto::public_key &change_address_spend_pubkey,
const subaddress_index_extended &change_address_index,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out);
} //namespace carrot
+1 -5
View File
@@ -28,11 +28,7 @@
if(APPLE)
if(DEPENDS)
if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
else()
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
endif()
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
else()
find_library(IOKIT_LIBRARY IOKit)
mark_as_advanced(IOKIT_LIBRARY)
+27 -22
View File
@@ -183,28 +183,33 @@ namespace cryptonote
bool checkpoints::init_default_checkpoints(network_type nettype)
{
if (nettype == MAINNET) {
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
ADD_CHECKPOINT2(100000, "ff4e8ec805d5bfbcd01f350ac071be1d944ba73e0d27e37d12acb549902b3f3d", "0xDA97F5697F7D8"); // 3845539075979224
ADD_CHECKPOINT2(150000, "c43281eb5b2a41ee77a4465735e4820c6d14d473b568df7987541afc48f18568", "0x12BDA9ED687AAF"); // 5275087110961839
ADD_CHECKPOINT2(225000, "7648405f7cfb24341d9580275b518bb3713c68e970f547faa0b3bcae450d9ec2", "0x18CD5F1B7BA43A"); // 6981207807730746
ADD_CHECKPOINT2(300000, "a18ca65464c1f2d876dd3a00643c9be265655d6bca364eccc5e3d628b9a5cd2c", "0x1F0C2A50FE631C"); // 8739100165038876
ADD_CHECKPOINT2(375000, "07f0c907cc5cb44cef88e5899a411adddd4b0a4419210906e0583efdcafb499f", "0x240C7F9742F265"); // 10146841299710565
}
return true;
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#if defined(__x86_64__)
#define DEBUG_BREAK() asm volatile("int $3")
#elif defined(__aarch64__)
#define DEBUG_BREAK() asm volatile("brk #0xf000")
#elif defined(__arm__)
#define DEBUG_BREAK() asm volatile(".inst 0xe7f001f0") // Encoding for bkpt #0xf01, common for ARM32
#else
#error "Unsupported architecture for DEBUG_BREAK"
#endif
+1
View File
@@ -71,6 +71,7 @@ monero_add_library(cncrypto
target_link_libraries(cncrypto
PUBLIC
epee
mx25519_static
randomx
${Boost_SYSTEM_LIBRARY}
${SODIUM_LIBRARY}
+4 -1
View File
@@ -887,4 +887,7 @@ const ge_p3 ge_p3_H = {
{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
};
};
// Precomputed sqrt(-486664) mod p as 32-byte little-endian array
const fe fe_sqrt_m486664 = {12222970, 8312128, 11511410, -9067497, 15300785, 241793, -25456130, -14121551, 12187136, -3972024};
+125 -2
View File
@@ -104,6 +104,21 @@ void fe_1(fe h) {
h[9] = 0;
}
int fe_equal(const fe a, const fe b)
{
fe t;
fe_sub(t, a, b);
return fe_isnonzero(t) == 0;
}
void ge_from_xy(ge_p3 *out, const fe x, const fe y)
{
fe_1(out->Z); // Z = 1
fe_copy(out->X, x); // X = x
fe_copy(out->Y, y); // Y = y
fe_mul(out->T, x, y); // T = x*y
}
/* From fe_add.c */
/*
@@ -365,7 +380,7 @@ int fe_isnegative(const fe f) {
/* From fe_isnonzero.c, modified */
static int fe_isnonzero(const fe f) {
int fe_isnonzero(const fe f) {
unsigned char s[32];
fe_tobytes(s, f);
return (((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] |
@@ -3967,6 +3982,114 @@ int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *
return 0;
}
// Precomputed sqrt(-1) from Monero (fe_sqrtm1 in crypto-ops-data.c)
extern const fe fe_sqrtm1;
extern const fe fe_sqrt_m486664;
// Function to recover v from u (returns 0 on success, -1 if not on curve)
int fe_sqrt_mont(fe v_out, const fe u_in) {
fe rhs;
fe t0, t1, t2;
fe candidate, c2, check;
// Compute rhs = u^3 + A u^2 + u, A=486662
fe A_fe;
fe_frombytes_vartime(A_fe, (const unsigned char[]){0x06, 0x6D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); // Correct little-endian 486662
fe_sq(t0, u_in); // t0 = u^2
fe_mul(t1, t0, u_in); // t1 = u^3
fe_mul(t2, t0, A_fe); // t2 = A u^2
fe_add(rhs, t1, t2); // u^3 + A u^2
fe_add(rhs, rhs, u_in); // + u
// The exponentiation chain for (p+3)/8
fe_1(t1);
fe_sq(t0, t1);
fe_mul(t0, t0, t1);
fe_sq(candidate, t0);
fe_mul(candidate, candidate, t1);
fe_mul(candidate, candidate, rhs);
fe_pow22523(candidate, candidate);
fe_mul(candidate, candidate, t0);
fe_mul(candidate, candidate, rhs);
// Check c^2 == rhs or -rhs
fe_sq(c2, candidate);
fe_sub(check, c2, rhs);
if (fe_isnonzero(check)) {
fe_add(check, c2, rhs);
if (fe_isnonzero(check)) {
return -1; // Not a quadratic residue
}
fe_mul(candidate, candidate, fe_sqrtm1); // Adjust with sqrt(-1)
}
// Output v (principal root; flip to -v if needed for your map)
fe_copy(v_out, candidate);
return 0;
}
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v) {
fe inv_v, temp;
fe t1, t2, inv_t2;
fe one;
fe_1(one);
fe_invert(inv_v, v); // 1/v
fe_mul(temp, u, inv_v); // u/v
fe_mul(x_out, fe_sqrt_m486664, temp); // sqrt(-486664) * (u/v)
fe_sub(t1, u, one); // u - 1
fe_add(t2, u, one); // u + 1
fe_invert(inv_t2, t2);
fe_mul(y_out, t1, inv_t2); // (u-1)/(u+1)
}
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y) {
fe t1, t2, inv_t2;
fe one;
fe_1(one);
fe_add(t1, one, y); // 1 + y
fe_sub(t2, one, y); // 1 - y
fe_invert(inv_t2, t2);
fe_mul(u_out, t1, inv_t2); // (1+y)/(1-y)
fe inv_x;
fe_invert(inv_x, x); // 1/x
fe_mul(t1, u_out, inv_x); // u / x
fe_mul(v_out, fe_sqrt_m486664, t1); // sqrt(-486664) * (u/x)
}
// Usage: Add two Montgomery points
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2) {
ge_p3 P_ed, Q_ed, sum_ed;
// Convert to Edwards
fe x1, y1, x2, y2;
mont_to_ed(x1, y1, u1, v1);
mont_to_ed(x2, y2, u2, v2);
// Load into ge_p3 (assume Z=1, T=x*y for affine)
fe_1(P_ed.Z); fe_mul(P_ed.T, x1, y1); fe_copy(P_ed.X, x1); fe_copy(P_ed.Y, y1);
fe_1(Q_ed.Z); fe_mul(Q_ed.T, x2, y2); fe_copy(Q_ed.X, x2); fe_copy(Q_ed.Y, y2);
// Add using ge_
ge_cached Q_cached;
ge_p3_to_cached(&Q_cached, &Q_ed);
ge_p1p1 sum_p1p1;
ge_add(&sum_p1p1, &P_ed, &Q_cached);
ge_p1p1_to_p3(&sum_ed, &sum_p1p1);
// Convert back (normalize to affine: divide by Z)
fe inv_z;
fe_invert(inv_z, sum_ed.Z);
fe x_out, y_out;
fe_mul(x_out, sum_ed.X, inv_z);
fe_mul(y_out, sum_ed.Y, inv_z);
ed_to_mont(u3, v3, x_out, y_out);
}
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
// https://eprint.iacr.org/2008/522
// X == T == 0 and Y/Z == 1
@@ -4063,4 +4186,4 @@ void fe_dbl(fe h, const fe f)
// Reduce the output for safety to ensure the result can be used as input to
// fe_add or fe_sub without an extra call to fe_reduce
fe_reduce(h, h_res);
}
}
+10 -1
View File
@@ -176,6 +176,11 @@ int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
int fe_sqrt_mont(fe v_out, const fe u_in);
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v);
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y);
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2);
// internal
uint64_t load_3(const unsigned char *in);
uint64_t load_4(const unsigned char *in);
@@ -192,10 +197,14 @@ void fe_sq(fe h, const fe f);
void fe_sub(fe h, const fe f, const fe g);
void fe_0(fe h);
void fe_1(fe h);
int fe_equal(const fe a, const fe b);
void ge_from_xy(ge_p3 *out, const fe x, const fe y);
int fe_isnonzero(const fe f);
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
void fe_ed_y_derivatives_to_wei_x(unsigned char *wei_x, const fe inv_one_minus_y, const fe one_plus_y);
void fe_reduce(fe reduced_f, const fe f);
void fe_dbl(fe h, const fe f);
void fe_dbl(fe h, const fe f);
+558 -1
View File
@@ -41,6 +41,7 @@
#include "common/varint.h"
#include "warnings.h"
#include "crypto.h"
#include "mx25519.h"
#include "hash.h"
#include "cryptonote_config.h"
@@ -90,6 +91,16 @@ namespace crypto {
return &reinterpret_cast<const unsigned char &>(scalar);
}
static const mx25519_impl* get_mx25519_impl()
{
static std::once_flag of;
static const mx25519_impl *impl;
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
if (impl == nullptr)
throw std::runtime_error("failed to obtain a mx25519 implementation");
return impl;
}
boost::mutex &get_random_lock()
{
static boost::mutex random_lock;
@@ -504,6 +515,391 @@ namespace crypto {
memwipe(&k, sizeof(k));
}
void crypto_ops::generate_carrot_tx_proof(
const hash &prefix_hash,
const public_key &R, // X25519 u-coordinate
const public_key &A, // Ed25519
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Check if we are sender or receiver
if (r != crypto::null_skey) {
// SENDER
generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
return;
}
// RECEIVER
// Load points (A and B and R) into ge_p3
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 R_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
throw std::runtime_error("recipient spend pubkey is invalid");
#if !defined(NDEBUG)
{
// Debug check D == a*R
mx25519_pubkey D_x25519;
mx25519_scmul_key(get_mx25519_impl(),
&D_x25519,
reinterpret_cast<const mx25519_privkey*>(&a),
reinterpret_cast<const mx25519_pubkey*>(&R));
public_key dbg_D;
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
#endif
//
// 1. Pick random nonce k
//
crypto::secret_key k;
random_scalar(k);
static const public_key zero = {{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 u-coord
buf.R = R; // X25519 u-coord
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
//
// 2. Compute X = ConvertPointE(k*G or k*B)
//
ge_p3 kB_or_kG_p3;
if (B)
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
else
ge_scalarmult_base(&kB_or_kG_p3, &k);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
//
// 3. Compute Y = k*R
//
mx25519_pubkey Y;
mx25519_scmul_key(get_mx25519_impl(),
&Y,
reinterpret_cast<const mx25519_privkey*>(&k),
reinterpret_cast<const mx25519_pubkey*>(&R));
memcpy(&buf.Y, &Y, sizeof(mx25519_pubkey));
// ---------- Extract and lift R ----------
fe u_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
fe v_R_cand;
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
throw std::runtime_error("R not on curve");
fe x1, y1, x2, y2, v_R_neg;
ge_p3 R_ed1, R_ed2;
// +v (principal)
mont_to_ed(x1, y1, u_R, v_R_cand);
ge_from_xy(&R_ed1, x1, y1);
// -v
fe_neg(v_R_neg, v_R_cand);
mont_to_ed(x2, y2, u_R, v_R_neg);
ge_from_xy(&R_ed2, x2, y2);
// Arbitrarily choose R_sign = true (principal v from fe_sqrt_mont)
bool R_sign = true;
ge_p3 R_ed_correct = R_ed1; // +v
// ---------- Extract and lift D (consistent with chosen R_sign) ----------
fe u_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
fe v_D_cand;
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
throw std::runtime_error("D not on curve");
fe x3, y3, x4, y4, v_D_neg;
// Compute D_ed_true = a * R_ed_correct
ge_p3 D_ed_true;
ge_scalarmult_p3(&D_ed_true, &a, &R_ed_correct);
// Normalize to affine for matching
fe inv_z;
fe_invert(inv_z, D_ed_true.Z);
fe xd_true, yd_true;
fe_mul(xd_true, D_ed_true.X, inv_z);
fe_mul(yd_true, D_ed_true.Y, inv_z);
// +v for D
mont_to_ed(x3, y3, u_D, v_D_cand);
bool D_match1 = fe_equal(x3, xd_true) && fe_equal(y3, yd_true); // Affine match (mont_to_ed gives affine x,y)
// -v for D
fe_neg(v_D_neg, v_D_cand);
mont_to_ed(x4, y4, u_D, v_D_neg);
bool D_match2 = fe_equal(x4, xd_true) && fe_equal(y4, yd_true);
bool D_sign = false;
if (D_match1)
D_sign = true;
else if (D_match2)
D_sign = false;
else
throw std::runtime_error("D lift mismatch with computed D_ed_true");
// Pack signs (MSB is set to [1] for outbound, [0] for inbound
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00);
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
//
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
//
// 8. Compute response z = k - c*a
//
sc_mulsub(&sig.r, &sig.c, &unwrap(a), &k);
memwipe(&k, sizeof(k));
#if !defined(NDEBUG)
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
assert(ok);
#endif
}
void crypto_ops::generate_carrot_tx_proof_as_sender(
const hash &prefix_hash,
const public_key &R, // X25519 u-coordinate
const public_key &A, // Ed25519
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Load only Ed points (A and B) into ge_p3
ge_p3 A_p3;
ge_p3 B_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
throw std::runtime_error("recipient spend pubkey is invalid");
#if !defined(NDEBUG)
{
assert(sc_check(&r) == 0);
// Debug check R == ConvertPointE(r*G or r*B)
public_key dbg_R;
ge_p3 dbg_R_p3;
if (B)
ge_scalarmult_p3(&dbg_R_p3, &r, &B_p3);
else
ge_scalarmult_base(&dbg_R_p3, &r);
mx25519_pubkey R_x25519;
ge_p3_to_x25519(R_x25519.data, &dbg_R_p3);
memcpy(&dbg_R, &R_x25519, sizeof(mx25519_pubkey));
assert(R == dbg_R);
// Debug check D == ConvertPointE(r*A)
public_key dbg_D;
ge_p3 dbg_D_p3;
ge_scalarmult_p3(&dbg_D_p3, &r, &A_p3);
mx25519_pubkey D_x25519;
ge_p3_to_x25519(D_x25519.data, &dbg_D_p3);
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
#endif
//
// 1. Pick random nonce k
//
ec_scalar k;
random_scalar(k);
static const ec_point zero = {{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 u-coord
buf.R = R; // X25519 u-coord
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
//
// 2. Compute X = ConvertPointE(k*G or k*B)
//
ge_p3 kB_or_kG_p3;
if (B)
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
else
ge_scalarmult_base(&kB_or_kG_p3, &k);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
//
// 3. Compute Y = ConvertPointE(k*A)
//
ge_p3 kA_p3;
ge_scalarmult_p3(&kA_p3, &k, &A_p3);
mx25519_pubkey Y_x25519;
ge_p3_to_x25519(Y_x25519.data, &kA_p3);
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
//
// 4. Compute true Ed points R_ed_true and D_ed_true
//
ge_p3 R_ed_true, D_ed_true;
if (B)
ge_scalarmult_p3(&R_ed_true, &r, &B_p3);
else
ge_scalarmult_base(&R_ed_true, &r);
ge_scalarmult_p3(&D_ed_true, &r, &A_p3);
//
// 5. Determine sign bits for R and D
//
// ---------- Extract and lift R ----------
fe u_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
fe v_R_cand;
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
throw std::runtime_error("R not on curve");
fe x1, y1, x2, y2, v_R_neg, v_D_neg;
mont_to_ed(x1, y1, u_R, v_R_cand);
fe_neg(v_R_neg, v_R_cand);
mont_to_ed(x2, y2, u_R, v_R_neg);
// Compute affine Edwards coords of R_ed_true
fe inv_z, xr_true, yr_true;
fe_invert(inv_z, R_ed_true.Z);
fe_mul(xr_true, R_ed_true.X, inv_z);
fe_mul(yr_true, R_ed_true.Y, inv_z);
bool R_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
bool R_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
if (!R_match1 && !R_match2)
throw std::runtime_error("R mapping mismatch");
bool R_sign = R_match1;
// ---------- Extract and lift D ----------
fe u_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
fe v_D_cand;
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
throw std::runtime_error("D not on curve");
mont_to_ed(x1, y1, u_D, v_D_cand);
fe_neg(v_D_neg, v_D_cand);
mont_to_ed(x2, y2, u_D, v_D_neg);
fe_invert(inv_z, D_ed_true.Z);
fe_mul(xr_true, D_ed_true.X, inv_z);
fe_mul(yr_true, D_ed_true.Y, inv_z);
bool D_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
bool D_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
if (!D_match1 && !D_match2)
throw std::runtime_error("D mapping mismatch");
bool D_sign = D_match1;
//
// 6. Pack sign bits into signature, include in challenge hash
//
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00) |
0x80;
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
//
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
//
// 8. Compute response z = k - c*r
//
sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
memwipe(&k, sizeof(k));
#if !defined(NDEBUG)
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
assert(ok);
#endif
}
// Verify a proof: either v1 (version == 1) or v2 (version == 2)
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
// sanity check
@@ -608,6 +1004,167 @@ namespace crypto {
return sc_isnonzero(&c2) == 0;
}
// R and D are provided in X25519 format (u-coordinate), A and B in Ed25519.
bool crypto_ops::check_carrot_tx_proof(
const hash &prefix_hash,
const public_key &R, // X25519 u
const public_key &A, // Ed25519 viewkey
const boost::optional<public_key> &B, // Ed25519 spendkey if any
const public_key &D, // X25519 u
const signature &sig)
{
ge_p3 A_p3, B_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
return false;
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
return false;
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
return false;
// Extract sign bits and direction flag
const bool R_sign = (sig.sign_mask & 0x01) != 0;
const bool D_sign = (sig.sign_mask & 0x02) != 0;
const bool outbound = (sig.sign_mask & 0x80) != 0;
//
// 1. Reconstruct R_ed and D_ed from X25519 u-coords + sign bits
//
// ----- R -----
fe u_R, v_R_candidate, v_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
if (fe_sqrt_mont(v_R_candidate, u_R) != 0)
return false;
if (R_sign) fe_copy(v_R, v_R_candidate);
else fe_neg(v_R, v_R_candidate);
fe x_R, y_R;
mont_to_ed(x_R, y_R, u_R, v_R);
ge_p3 R_ed;
ge_from_xy(&R_ed, x_R, y_R); // Z=1, T=X*Y
// ----- D -----
fe u_D, v_D_candidate, v_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
if (fe_sqrt_mont(v_D_candidate, u_D) != 0)
return false;
if (D_sign) fe_copy(v_D, v_D_candidate);
else fe_neg(v_D, v_D_candidate);
fe x_D, y_D;
mont_to_ed(x_D, y_D, u_D, v_D);
ge_p3 D_ed;
ge_from_xy(&D_ed, x_D, y_D);
//
// 2. Compute X'
// If inbound proof, X`= z*G + c*A (or z*B + c*A)
// If outbound proof, X`= z*G + c*R_ed (or z*B + c*R_ed)
//
ge_p3 c_p3;
if (outbound)
ge_scalarmult_p3(&c_p3, &sig.c, &R_ed);
else
ge_scalarmult_p3(&c_p3, &sig.c, &A_p3);
ge_p1p1 X_p1p1;
if (B)
{
// Subaddress: X' = c*A + z*B
ge_p3 rB_p3;
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
ge_cached rB_cached;
ge_p3_to_cached(&rB_cached, &rB_p3);
ge_add(&X_p1p1, &c_p3, &rB_cached);
}
else
{
// Main address: X' = c*R_ed + z*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_add(&X_p1p1, &c_p3, &rG_cached);
}
ge_p3 X_ed_p3;
ge_p1p1_to_p3(&X_ed_p3, &X_p1p1);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &X_ed_p3);
//
// 3. Compute Y'
// If inbound, Y' = c*D_ed + z*R
// If outbound, Y' = c*D_ed + z*A
//
ge_p3 cD_p3;
ge_scalarmult_p3(&cD_p3, &sig.c, &D_ed);
ge_p3 z_p3;
if (outbound)
ge_scalarmult_p3(&z_p3, &sig.r, &A_p3);
else
ge_scalarmult_p3(&z_p3, &sig.r, &R_ed);
ge_cached z_cached;
ge_p3_to_cached(&z_cached, &z_p3);
ge_p1p1 Y_p1p1;
ge_add(&Y_p1p1, &cD_p3, &z_cached);
ge_p3 Y_ed_p3;
ge_p1p1_to_p3(&Y_ed_p3, &Y_p1p1);
mx25519_pubkey Y_x25519;
ge_p3_to_x25519(Y_x25519.data, &Y_ed_p3);
//
// 4. Rebuild the hash transcript exactly as the prover did
//
static const ec_point zero = {{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 (same bytes as prover)
buf.R = R; // X25519
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 5. Recompute challenge and compare with sig.c
//
ec_scalar c2;
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), c2);
sc_sub(&c2, &c2, &sig.c);
return sc_isnonzero(&c2) == 0;
}
static void hash_to_ec(const public_key &key, ge_p3 &res) {
hash h;
ge_p2 point;
@@ -796,4 +1353,4 @@ POP_WARNINGS
ki.data[31] ^= 0x80;
}
}
}
}
+28 -3
View File
@@ -85,6 +85,7 @@ namespace crypto {
POD_CLASS signature {
ec_scalar c, r;
uint8_t sign_mask;
friend class crypto_ops;
};
@@ -99,7 +100,7 @@ namespace crypto {
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 &&
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(key_image_y) == 32 &&
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
sizeof(signature) == 65 && sizeof(view_tag) == 1, "Invalid structure size");
class crypto_ops {
crypto_ops();
@@ -131,8 +132,14 @@ namespace crypto {
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
static bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
friend bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
static void derive_key_image_generator(const public_key &, ec_point &);
friend void derive_key_image_generator(const public_key &, ec_point &);
static void generate_key_image(const public_key &, const secret_key &, key_image &);
@@ -260,10 +267,28 @@ namespace crypto {
inline void generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_tx_proof_v1(prefix_hash, R, A, B, D, r, sig);
}
/* Generation of a carrot tx proof; for carrot transactions, D is in X25519 domain (D = r*ConvertPointE(A))
* instead of Ed25519 domain (D = r*A). This version applies ConvertPointE transformation.
*/
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
inline void generate_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
}
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version);
}
/* Verification of a carrot tx proof; R and D should be in Ed25519 domain for verification,
*/
inline bool check_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
}
/*
inline bool check_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, sig);
}
*/
inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) {
crypto_ops::derive_key_image_generator(pub, ki_gen);
}
@@ -378,4 +403,4 @@ CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_HASHABLE(key_image_y)
CRYPTO_MAKE_COMPARABLE(signature)
CRYPTO_MAKE_COMPARABLE(view_tag)
CRYPTO_MAKE_COMPARABLE(view_tag)
+1 -6
View File
@@ -28,11 +28,7 @@
if(APPLE)
if(DEPENDS)
if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
else()
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
endif()
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
else()
find_library(IOKIT_LIBRARY IOKit)
mark_as_advanced(IOKIT_LIBRARY)
@@ -75,7 +71,6 @@ target_link_libraries(cryptonote_basic
checkpoints
cryptonote_format_utils_basic
device
polyseed_wrapper
oracle
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
+25 -25
View File
@@ -89,16 +89,24 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
{
// encrypt a large enough byte stream with chacha20
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.size());
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (8 + m_multisig_keys.size()));
const char *ptr = key_stream.data();
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_spend_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_view_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_polyseed.data[i] ^= *ptr++;
for (size_t i = 0; i < m_passphrase.size(); ++i)
m_passphrase.data()[i] ^= *ptr++;
s_master.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_prove_spend.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_view_balance.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_view_incoming.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_generate_image.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_generate_address.data[i] ^= *ptr++;
for (crypto::secret_key &k: m_multisig_keys)
{
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
@@ -120,11 +128,20 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
{
// encrypt a large enough byte stream with chacha20
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 8);
const char *ptr = key_stream.data();
ptr += sizeof(crypto::secret_key);
ptr += sizeof(crypto::secret_key); // Skip m_spend_secret_key
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_view_secret_key.data[i] ^= *ptr++;
ptr += (2 * sizeof(crypto::secret_key)); // Skip s_master, k_prove_spend
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_view_balance.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_view_incoming.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_generate_image.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_generate_address.data[i] ^= *ptr++;
}
//-----------------------------------------------------------------
void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
@@ -158,8 +175,6 @@ DISABLE_VS_WARNINGS(4244 4345)
m_keys.s_master = m_keys.m_spend_secret_key;
m_keys.k_prove_spend = m_keys.m_spend_secret_key;
m_keys.m_multisig_keys.clear();
m_keys.m_polyseed = crypto::secret_key();
m_keys.m_passphrase.wipe();
}
//-----------------------------------------------------------------
void account_base::set_spend_key(const crypto::secret_key& spend_secret_key)
@@ -266,21 +281,6 @@ DISABLE_VS_WARNINGS(4244 4345)
create_from_keys(address, fake, viewkey);
}
//-----------------------------------------------------------------
void account_base::create_from_polyseed(const polyseed::data& seed, const epee::wipeable_string &passphrase)
{
crypto::secret_key secret_key;
seed.keygen(&secret_key, sizeof(secret_key));
if (!passphrase.empty()) {
secret_key = cryptonote::decrypt_key(secret_key, passphrase);
}
generate(secret_key, true, false);
seed.save(m_keys.m_polyseed.data);
m_keys.m_passphrase = passphrase;
}
//-----------------------------------------------------------------
bool account_base::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)
{
m_keys.m_account_address.m_spend_public_key = spend_public_key;
@@ -304,9 +304,9 @@ DISABLE_VS_WARNINGS(4244 4345)
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};
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key, true};
// change this code into base 58
return get_account_address_as_str(nettype, false, addr, true);
return get_account_address_as_str(nettype, false, addr);
}
//-----------------------------------------------------------------
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
+5 -6
View File
@@ -33,7 +33,6 @@
#include "cryptonote_basic.h"
#include "crypto/crypto.h"
#include "serialization/keyvalue_serialization.h"
#include "polyseed/polyseed.hpp"
#include "carrot_core/account_secrets.h"
#include "carrot_core/address_utils.h"
@@ -53,8 +52,6 @@ namespace cryptonote
std::vector<crypto::secret_key> m_multisig_keys;
hw::device *m_device = &hw::get_device("default");
crypto::chacha_iv m_encryption_iv;
crypto::secret_key m_polyseed;
epee::wipeable_string m_passphrase; // Only used with polyseed
// carrot secret keys (minus k_v, which is shared with legacy k_v)
crypto::secret_key s_master;
@@ -78,8 +75,11 @@ namespace cryptonote
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed)
KV_SERIALIZE(m_passphrase)
if (m_account_address.m_spend_public_key == crypto::null_pkey) {
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(s_view_balance)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(k_view_incoming)
KV_SERIALIZE(m_carrot_account_address)
}
END_KV_SERIALIZE_MAP()
void encrypt(const crypto::chacha_key &key);
@@ -106,7 +106,6 @@ namespace cryptonote
void create_from_device(hw::device &hwdev);
void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey);
void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey);
void create_from_polyseed(const polyseed::data &polyseed, const epee::wipeable_string &passphrase);
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;
+209 -5
View File
@@ -44,6 +44,7 @@
#include "serialization/debug_archive.h"
#include "serialization/crypto.h"
#include "serialization/keyvalue_serialization.h" // eepe named serialization
#include "serialization/pair.h"
#include "serialization/string.h"
#include "carrot_core/core_types.h"
#include "carrot_impl/carrot_chain_serialization.h"
@@ -193,8 +194,6 @@ namespace cryptonote
VARINT_FIELD(amount)
FIELD(target)
END_SERIALIZE()
};
class protocol_tx_data_t {
@@ -214,6 +213,99 @@ namespace cryptonote
END_SERIALIZE()
};
struct erc_token_t
{
uint8_t version;
std::string contract_address;
std::string lockbox_address;
std::string ticker;
uint64_t erc20_asset_id; // NOTE: this is NOT the Salvium `asset_type_id`!!!
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(contract_address)
FIELD(lockbox_address)
FIELD(ticker)
VARINT_FIELD(erc20_asset_id)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(contract_address)
KV_SERIALIZE(lockbox_address)
KV_SERIALIZE(ticker)
KV_SERIALIZE(erc20_asset_id)
END_KV_SERIALIZE_MAP()
};
struct sal_token_t
{
uint8_t version;
uint64_t supply;
uint64_t size;
std::string name;
std::string url;
crypto::hash signature;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
VARINT_FIELD(supply)
VARINT_FIELD(size)
FIELD(name)
FIELD(url)
FIELD(signature)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(supply)
KV_SERIALIZE(size)
KV_SERIALIZE(name)
KV_SERIALIZE(url)
END_KV_SERIALIZE_MAP()
};
#define TOKEN_TYPE_UNSET 0
#define TOKEN_TYPE_ERC20 1
#define TOKEN_TYPE_SAL 2
typedef boost::variant<erc_token_t, sal_token_t> token_v;
struct token_metadata_t
{
uint8_t version;
std::string asset_type;
token_v token;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(asset_type)
FIELD(token)
END_SERIALIZE()
};
class layer2_rollup_tx_t {
public:
crypto::hash tx_prefix_hash;
crypto::key_image first_key_image;
uint64_t tx_fee;
BEGIN_SERIALIZE_OBJECT()
FIELD(tx_prefix_hash)
FIELD(first_key_image)
VARINT_FIELD(tx_fee)
END_SERIALIZE()
};
class layer2_rollup_data_t {
public:
uint8_t version;
std::vector<layer2_rollup_tx_t> txs;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(txs)
END_SERIALIZE()
};
class transaction_prefix
{
@@ -246,6 +338,10 @@ namespace cryptonote
protocol_tx_data_t protocol_tx_data;
carrot::rollup_binding_tag_t rollup_binding_tag;
token_metadata_t token_metadata;
layer2_rollup_data_t layer2_rollup_data;
BEGIN_SERIALIZE()
VARINT_FIELD(version)
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
@@ -263,9 +359,8 @@ namespace cryptonote
FIELD(return_address_list)
FIELD(return_address_change_mask)
} else {
if (type == cryptonote::transaction_type::STAKE &&
version >= TRANSACTION_VERSION_CARROT)
{
if ((type == cryptonote::transaction_type::STAKE || type == cryptonote::transaction_type::CREATE_TOKEN) &&
(version >= TRANSACTION_VERSION_CARROT)) {
FIELD(protocol_tx_data)
} else {
FIELD(return_address)
@@ -277,6 +372,17 @@ namespace cryptonote
VARINT_FIELD(amount_slippage_limit)
}
}
if (version < TRANSACTION_VERSION_ENABLE_TOKENS) {
return true;
}
if (type == cryptonote::transaction_type::CREATE_TOKEN) {
FIELD(token_metadata)
} else if (type == cryptonote::transaction_type::TRANSFER) {
FIELD(rollup_binding_tag)
} else if (type == cryptonote::transaction_type::ROLLUP) {
FIELD(rollup_binding_tag)
FIELD(layer2_rollup_data)
}
END_SERIALIZE()
public:
@@ -297,10 +403,14 @@ namespace cryptonote
protocol_tx_data.return_pubkey = crypto::null_pkey;
protocol_tx_data.return_view_tag = {};
protocol_tx_data.return_anchor_enc = {};
token_metadata.version = 0;
source_asset_type.clear();
destination_asset_type.clear();
amount_burnt = 0;
amount_slippage_limit = 0;
rollup_binding_tag = {0};
token_metadata = {};
layer2_rollup_data = {};
}
};
@@ -564,6 +674,91 @@ namespace cryptonote
END_SERIALIZE()
};
struct token_block_info {
uint64_t block_height;
std::map<uint32_t, uint64_t> token_info; // Replaces the vector
token_block_info() = default; // default ctor
explicit token_block_info(const uint64_t h)
: block_height(h) {}
void clear() {
block_height = 0;
token_info.clear();
}
std::vector<uint8_t> serialize() const noexcept {
constexpr uint8_t version = 1; // Bump if format changes
uint32_t sz = static_cast<uint32_t>(token_info.size());
size_t total_size = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
std::vector<uint8_t> buf(total_size);
uint8_t* ptr = buf.data();
std::memcpy(ptr, &version, sizeof(uint8_t));
ptr += sizeof(uint8_t);
std::memcpy(ptr, &block_height, sizeof(uint64_t));
ptr += sizeof(uint64_t);
std::memcpy(ptr, &sz, sizeof(uint32_t));
ptr += sizeof(uint32_t);
for (const auto& p : token_info) { // Iterates in sorted order
std::memcpy(ptr, &p.first, sizeof(uint32_t));
ptr += sizeof(uint32_t);
std::memcpy(ptr, &p.second, sizeof(uint64_t));
ptr += sizeof(uint64_t);
}
return buf;
}
void deserialize(const uint8_t* data, size_t len) {
if (len < sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
uint8_t version;
std::memcpy(&version, data, sizeof(uint8_t));
data += sizeof(uint8_t);
len -= sizeof(uint8_t);
if (version == 0) { // Legacy: No height/version (your current format)
// Handle old blobs: Caller must set block_height externally
block_height = 0; // Or throw if height is mandatory
} else if (version == 1) {
if (len < sizeof(uint64_t) + sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
std::memcpy(&block_height, data, sizeof(uint64_t));
data += sizeof(uint64_t);
len -= sizeof(uint64_t);
} else {
throw std::runtime_error("Unsupported version");
}
uint32_t sz;
std::memcpy(&sz, data, sizeof(uint32_t));
data += sizeof(uint32_t);
len -= sizeof(uint32_t);
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
throw std::runtime_error("Invalid serialized data length");
}
token_info.clear();
for (uint32_t i = 0; i < sz; ++i) {
uint32_t k;
uint64_t v;
std::memcpy(&k, data, sizeof(uint32_t));
data += sizeof(uint32_t);
std::memcpy(&v, data, sizeof(uint64_t));
data += sizeof(uint64_t);
token_info[k] = v; // Map handles sorting/uniquness
}
}
};
struct yield_block_info {
uint64_t block_height;
uint64_t slippage_total_this_block;
@@ -784,6 +979,9 @@ VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
VARIANT_TAG(binary_archive, cryptonote::txout_to_carrot_v1, 0x4);
VARIANT_TAG(binary_archive, cryptonote::protocol_tx_data_t, 0x0);
VARIANT_TAG(binary_archive, cryptonote::token_metadata_t, 0x0);
VARIANT_TAG(binary_archive, cryptonote::erc_token_t, 0x0);
VARIANT_TAG(binary_archive, cryptonote::sal_token_t, 0x1);
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
@@ -797,6 +995,9 @@ VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(json_archive, cryptonote::txout_to_tagged_key, "tagged_key");
VARIANT_TAG(json_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
VARIANT_TAG(json_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
VARIANT_TAG(json_archive, cryptonote::token_metadata_t, "token_metadata");
VARIANT_TAG(json_archive, cryptonote::erc_token_t, "erc_token");
VARIANT_TAG(json_archive, cryptonote::sal_token_t, "sal_token");
VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
VARIANT_TAG(json_archive, cryptonote::block, "block");
@@ -810,5 +1011,8 @@ VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(debug_archive, cryptonote::txout_to_tagged_key, "tagged_key");
VARIANT_TAG(debug_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
VARIANT_TAG(debug_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
VARIANT_TAG(debug_archive, cryptonote::token_metadata_t, "token_metadata");
VARIANT_TAG(debug_archive, cryptonote::erc_token_t, "erc_token");
VARIANT_TAG(debug_archive, cryptonote::sal_token_t, "sal_token");
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
VARIANT_TAG(debug_archive, cryptonote::block, "block");
@@ -155,10 +155,9 @@ namespace cryptonote {
network_type nettype
, bool subaddress
, account_public_address const & adr
, bool is_carrot
)
{
uint64_t address_prefix = is_carrot
uint64_t address_prefix = adr.m_is_carrot
? (subaddress ? get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX)
: (subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
@@ -169,10 +168,9 @@ namespace cryptonote {
network_type nettype
, account_public_address const & adr
, crypto::hash8 const & payment_id
, bool is_carrot
)
{
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;
uint64_t integrated_address_prefix = adr.m_is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
integrated_address iadr = {
adr, payment_id
@@ -88,14 +88,12 @@ 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(
@@ -45,6 +45,7 @@
#include "crypto/crypto.h"
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
#include "serialization/containers.h"
namespace boost
{
@@ -181,6 +182,50 @@ namespace boost
a & x.return_anchor_enc;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::erc_token_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.contract_address;
a & x.lockbox_address;
a & x.ticker;
a & x.erc20_asset_id;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::sal_token_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.supply;
a & x.size;
a & x.name;
a & x.url;
a & x.signature;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::token_metadata_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.asset_type;
a & x.token;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::layer2_rollup_tx_t &x, const boost::serialization::version_type ver)
{
a & x.tx_prefix_hash;
a & x.first_key_image;
a & x.tx_fee;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::layer2_rollup_data_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.txs;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
{
@@ -197,9 +242,8 @@ namespace boost
a & x.return_address_list;
a & x.return_address_change_mask;
} else {
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
(x.version >= TRANSACTION_VERSION_CARROT)) {
a & x.protocol_tx_data;
} else {
a & x.return_address;
@@ -211,6 +255,16 @@ namespace boost
a & x.amount_slippage_limit;
}
}
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
a & x.token_metadata;
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
a & x.rollup_binding_tag;
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
a & x.rollup_binding_tag;
a & x.layer2_rollup_data;
}
}
}
template <class Archive>
@@ -229,9 +283,8 @@ namespace boost
a & x.return_address_list;
a & x.return_address_change_mask;
} else {
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
(x.version >= TRANSACTION_VERSION_CARROT)) {
a & x.protocol_tx_data;
} else {
a & x.return_address;
@@ -242,6 +295,16 @@ namespace boost
a & x.destination_asset_type;
a & x.amount_slippage_limit;
}
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
a & x.token_metadata;
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
a & x.rollup_binding_tag;
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
a & x.rollup_binding_tag;
a & x.layer2_rollup_data;
}
}
}
if (x.version == 1)
{
+185 -28
View File
@@ -31,6 +31,7 @@
#include <atomic>
#include <csignal>
#include <map>
#include <boost/algorithm/string.hpp>
#include "wipeable_string.h"
#include "string_tools.h"
@@ -43,6 +44,7 @@
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
#include "oracle/asset_types.h"
#include "common/debugging.h"
using namespace epee;
@@ -1114,7 +1116,37 @@ namespace cryptonote
default:
break;
}
if (asset_type_id)
{
// Check to see if 1st byte is permitted
std::string s, s_prefix;
switch (asset_type_id >> 24) {
case 0x01:
// We have a user-generated token
s_prefix = "sal";
break;
case 0x02:
s_prefix = "erc";
break;
default:
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
}
// Break up the remaining 3 bytes into 4 chunks of base36/64
static const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
uint32_t asset_type_id_temp = asset_type_id & 0x00FFFFFF;
for (int i=0; i<4; ++i) {
uint8_t val = asset_type_id_temp & 0x0000003F;
if (val >= 36)
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
s.push_back(alphabet[val]);
asset_type_id_temp >>= 6;
}
std::reverse(s.begin(), s.end());
return s_prefix + s;
}
// Should probably throw() here
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
return "";
}
//---------------------------------------------------------------
@@ -1129,11 +1161,162 @@ namespace cryptonote
} else if (asset_type == "") {
return 0x00000000;
} else {
// Should probably throw() here
return static_cast<uint32_t>(-1);
if (asset_type.length() != 7) {
LOG_ERROR("Custom asset type '" << asset_type << "' has invalid length.");
return 0x00000000;
}
static const std::string alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Check the 4-char type
std::string s_type = asset_type.substr(3);
std::transform(s_type.begin(), s_type.end(), s_type.begin(),
[](unsigned char c){ return std::toupper(c); });
uint32_t asset_id = 0x00000000;
for (int i=0; i<s_type.length(); ++i) {
uint8_t idx = alphabet.find(s_type.at(i));
if (idx == std::string::npos || idx >= 36) {
LOG_ERROR("Custom asset type contains invalid char.");
return 0x00000000;
}
asset_id = (asset_id << 6) | (idx & 0x3F);
}
// Check the 3-char prefix
std::string s_prefix = asset_type.substr(0,3);
std::transform(s_prefix.begin(), s_prefix.end(), s_prefix.begin(),
[](unsigned char c){ return std::tolower(c); });
if (s_prefix == "sal") {
asset_id |= 0x01000000;
} else if (s_prefix == "erc") {
asset_id |= 0x02000000;
} else {
LOG_ERROR("Custom asset type has invalid prefix.");
return 0x00000000;
}
return asset_id;
}
}
//---------------------------------------------------------------
bool is_asset_type_token(const std::string& asset_type)
{
// SAL, SAL1, BURN are base asset types
// erc prefix is not included here
if (asset_type.length() >= 3) {
std::string prefix = asset_type.substr(0, 3);
return (prefix == "sal");
}
return false;
}
//---------------------------------------------------------------
bool is_valid_asset_type(const std::string& asset_type) {
// This method does NOT throw()
try {
uint32_t asset_id = asset_id_from_type(asset_type);
std::string asset_type_check = asset_type_from_id(asset_id);
return (asset_type_check == asset_type);
}
catch (std::exception e) {
return false;
}
}
//---------------------------------------------------------------
bool is_valid_custom_asset_type(const std::string& asset_type)
{
if (!is_valid_asset_type(asset_type))
return false;
// double check with reserved IDs
uint32_t id = asset_id_from_type(asset_type);
if (id == 0x53414C00 || id == 0x53414C31 || id == 0x4255524E || id == 0x00000000) {
return false;
}
return true;
}
//---------------------------------------------------------------
uint64_t get_token_creation_price(const std::string& ticker)
{
static const std::map<std::string, uint64_t> premium_tickers = {
// add more
{"USDT", 10000 * COIN},
{"USDC", 10000 * COIN},
{"WBTC", 10000 * COIN},
{"DOGE", 10000 * COIN},
{"SHIB", 10000 * COIN},
{"AVAX", 10000 * COIN},
{"ATOM", 10000 * COIN},
{"NEAR", 10000 * COIN},
{"TRON", 10000 * COIN},
{"HBAR", 10000 * COIN},
{"AAVE", 10000 * COIN},
{"FLOW", 10000 * COIN},
{"EGLD", 10000 * COIN},
{"KLAY", 10000 * COIN},
{"LUNA", 10000 * COIN},
{"DASH", 10000 * COIN},
{"NANO", 10000 * COIN},
{"CORE", 10000 * COIN},
{"BEAM", 10000 * COIN},
{"DYDX", 10000 * COIN},
{"COMP", 10000 * COIN},
{"SAND", 10000 * COIN},
{"MANA", 10000 * COIN},
{"RUNE", 10000 * COIN},
{"PYTH", 10000 * COIN},
{"ARKM", 10000 * COIN},
{"BLUR", 10000 * COIN},
{"STRK", 10000 * COIN},
{"PEPE", 10000 * COIN},
{"BONK", 10000 * COIN},
{"VIUM", 10000 * COIN},
{"GOLD", 10000 * COIN},
{"SILV", 10000 * COIN},
{"CASH", 10000 * COIN},
{"EURO", 10000 * COIN},
{"PESO", 10000 * COIN},
{"BOND", 10000 * COIN},
{"FUND", 10000 * COIN},
{"BANK", 10000 * COIN},
{"SWAP", 10000 * COIN},
{"LEND", 10000 * COIN},
{"LOAN", 10000 * COIN},
{"NOTE", 10000 * COIN},
{"HOLD", 10000 * COIN},
{"BULL", 10000 * COIN},
{"BEAR", 10000 * COIN},
{"TECH", 10000 * COIN},
{"DATA", 10000 * COIN},
{"HASH", 10000 * COIN},
{"NODE", 10000 * COIN},
{"BYTE", 10000 * COIN},
{"GRID", 10000 * COIN},
{"CODE", 10000 * COIN},
{"META", 10000 * COIN},
{"WEB3", 10000 * COIN},
{"NFTS", 10000 * COIN},
{"DEFI", 10000 * COIN},
{"LAND", 10000 * COIN},
{"REAL", 10000 * COIN},
{"RENT", 10000 * COIN},
{"FARM", 10000 * COIN},
{"OILX", 10000 * COIN},
{"ENRG", 10000 * COIN},
{"FUEL", 10000 * COIN},
{"VOTE", 10000 * COIN},
{"PASS", 10000 * COIN},
{"LOCK", 10000 * COIN},
};
// is it a premium ticker
auto it = premium_tickers.find(ticker);
if (it != premium_tickers.end()) {
return it->second;
}
// not premium
return 1000 * COIN;
}
//---------------------------------------------------------------
/**
* The various scenarios that are permitted for Salvium are more extensive than
* they are for Zepyhr / Havan. Specifically, we permit:
@@ -1406,32 +1589,6 @@ namespace cryptonote
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
<< "expected matching variant types in transaction");
}
// Verify the asset type
std::string asset_type;
CHECK_AND_ASSERT_MES(cryptonote::get_output_asset_type(o, asset_type), false, "failed to get asset type");
if (hf_version < HF_VERSION_SALVIUM_ONE_PROOFS) {
// Prior to the first audit, ONLY SAL was supported
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
} else {
if (tx.type == cryptonote::transaction_type::AUDIT) {
// HERE BE DRAGONS!!!
// SRCG: This will NOT always be the case - when we add an audit for SALx it'll need to support that as well
// The CHANGE for an AUDIT TX must be SAL (and 0 value, and unspendable, and to the origin wallet, and ...)
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
// LAND AHOY!!!
} else if (tx.type == cryptonote::transaction_type::PROTOCOL) {
if (hf_version < HF_VERSION_AUDIT1_PAUSE) {
// PROTOCOL TXs are responsible for paying out SAL and SAL1 during the first AUDIT
CHECK_AND_ASSERT_MES(asset_type == "SAL1" || asset_type == "SAL", false, "wrong output asset type:" << asset_type);
} else {
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
}
} else {
// All other TX types must only spend + create SAL1 (MINER, TRANSFER)
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
}
}
}
return true;
}
@@ -135,6 +135,10 @@ namespace cryptonote
uint64_t get_outs_money_amount(const transaction& tx);
std::string asset_type_from_id(const uint32_t asset_type_id);
uint32_t asset_id_from_type(const std::string asset_type);
bool is_valid_custom_asset_type(const std::string& asset_type);
bool is_valid_asset_type(const std::string& asset_type);
bool is_asset_type_token(const std::string& asset_type);
uint64_t get_token_creation_price(const std::string& ticker);
bool get_tx_asset_types(const transaction& tx, const crypto::hash &txid, std::string& source, std::string& destination, const bool is_miner_tx);
bool get_output_public_key(const cryptonote::tx_out& out, crypto::public_key& output_public_key);
boost::optional<crypto::view_tag> get_output_view_tag(const cryptonote::tx_out& out);
+6 -5
View File
@@ -46,7 +46,7 @@
#include "boost/logic/tribool.hpp"
#include <boost/filesystem.hpp>
#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
#ifdef __APPLE__
#include <sys/times.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/ps/IOPSKeys.h>
@@ -175,7 +175,8 @@ namespace cryptonote
uint64_t seed_height;
crypto::hash seed_hash;
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
crypto::public_key miner_reward_tx_key;
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash, miner_reward_tx_key))
{
LOG_ERROR("Failed to get_block_template(), stopping mining");
m_forced_stop = true;
@@ -902,7 +903,7 @@ namespace cryptonote
return true;
#elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
#elif defined(__APPLE__)
mach_msg_type_number_t count;
kern_return_t status;
@@ -968,7 +969,7 @@ namespace cryptonote
return true;
}
#elif (defined(__linux__) && defined(_SC_CLK_TCK)) || (defined(__APPLE__) && !defined(TARGET_OS_IPHONE)) || defined(__FreeBSD__)
#elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) || defined(__FreeBSD__)
struct tms tms;
if ( times(&tms) != (clock_t)-1 )
@@ -997,7 +998,7 @@ namespace cryptonote
return boost::logic::tribool(power_status.ACLineStatus != 1);
}
#elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
#elif defined(__APPLE__)
#if TARGET_OS_MAC && (!defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)
return boost::logic::tribool(IOPSGetTimeRemainingEstimate() != kIOPSTimeRemainingUnlimited);
+1 -1
View File
@@ -47,7 +47,7 @@ namespace cryptonote
struct i_miner_handler
{
virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) = 0;
protected:
~i_miner_handler(){};
};
+12 -1
View File
@@ -39,6 +39,7 @@
#define TX_EXTRA_NONCE 0x02
#define TX_EXTRA_MERGE_MINING_TAG 0x03
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
#define TX_EXTRA_TAG_TOKEN 0x80
#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
@@ -167,6 +168,15 @@ namespace cryptonote
END_SERIALIZE()
};
struct tx_extra_token
{
cryptonote::token_metadata_t token;
BEGIN_SERIALIZE()
FIELD(token)
END_SERIALIZE()
};
struct tx_extra_mysterious_minergate
{
std::string data;
@@ -180,7 +190,7 @@ namespace cryptonote
// varint tag;
// varint size;
// varint data[];
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate> tx_extra_field;
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate, tx_extra_token> tx_extra_field;
}
VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING);
@@ -189,3 +199,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_token, TX_EXTRA_TAG_TOKEN);
+20 -6
View File
@@ -44,10 +44,11 @@
#define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
#define CURRENT_TRANSACTION_VERSION 4
#define CURRENT_TRANSACTION_VERSION 5
#define TRANSACTION_VERSION_2_OUTS 2
#define TRANSACTION_VERSION_N_OUTS 3
#define TRANSACTION_VERSION_CARROT 4
#define TRANSACTION_VERSION_ENABLE_TOKENS 5
#define CURRENT_BLOCK_MAJOR_VERSION 1
#define CURRENT_BLOCK_MINOR_VERSION 1
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
@@ -91,6 +92,12 @@
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
#define CREATE_TOKEN_LOCK_PERIOD 10
// HF11 block reward split
#define BLOCK_REWARD_TREASURY_PCT 25 // to treasury, 21600-block unlock
#define BLOCK_REWARD_STAKER_PCT 20 // to stakers via yield
#define DIFFICULTY_TARGET_V2 120 // seconds
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
#define DIFFICULTY_WINDOW_V2 70 // blocks
@@ -244,13 +251,14 @@
#define HF_VERSION_AUDIT2 8
#define HF_VERSION_AUDIT2_PAUSE 9
#define HF_VERSION_CARROT 10
#define HF_VERSION_ENABLE_TOKENS 11
#define HF_VERSION_REQUIRE_VIEW_TAGS 255
#define HF_VERSION_ENABLE_CONVERT 255
#define HF_VERSION_ENABLE_ORACLE 255
#define HF_VERSION_SLIPPAGE_YIELD 255
#define TESTNET_VERSION 15
#define TESTNET_VERSION 18
#define STAGENET_VERSION 1
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
@@ -274,8 +282,6 @@
#define DNS_BLOCKLIST_LIFETIME (86400 * 8)
#define POLYSEED_COIN POLYSEED_MONERO
#define PRICING_RECORD_VALID_BLOCKS 10
#define PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK 120 // seconds
@@ -317,9 +323,10 @@ namespace config
const uint64_t STAKE_LOCK_PERIOD = 30*24*30;
const uint64_t TREASURY_SAL1_MINT_PERIOD = 30*24*30; // 1 month of blocks
std::string const TREASURY_ADDRESS_CARROT = "SC11ksHLFhy7H1yMk9bUZvADG1Z9ZkR1T5QMknm3RbGBbgdPkyanB2WBb5TER3MsiwJC5BnyoiYs2DBcvAfAm6JQ537iNKdtvm";
std::string const TREASURY_ADDRESS = "SaLvdZR6w1A21sf2Wh6jYEh1wzY4GSbT7RX6FjyPsnLsffWLrzFQeXUXJcmBLRWDzZC2YXeYe5t7qKsnrg9FpmxmEcxPHsEYfqA";
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
// 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 = {
{334750, {"1b2cd3ff56aa77c0cbab0473bfb96697ebdd0b25ad230136bfe41d5dc1ef265f","718cf02eabca157fd7ad7f8537db217624bfe1ca99dd09e758357e7000a5e57a","789cca3def51fb879eb7fbca271869b7","79bd0c"}},
{356350, {"b51acbf35265d09f3cfb83dcabde2746991ddf0d30b5a4ecc34043b349a77031","9dc0d2e9534cdccf83494687c55c67c8c1b29834acf97cce53124a08a9549231","588ebc2918d06c009a18a28a8ab76694","ab8c23"}},
@@ -421,9 +428,11 @@ namespace config
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
"-----END PUBLIC KEY-----\n";
std::string const TREASURY_ADDRESS = "SaLvTyLFta9BiAXeUfFkKvViBkFt4ay5nEUBpWyDKewYggtsoxBbtCUVqaBjtcCDyY1euun8Giv7LLEgvztuurLo5a6Km1zskZn36";
std::string const TREASURY_ADDRESS_CARROT = "SC1TouvX6e4HmkAqsU6AAXLRjgeZnnKHjSpVfMHepTXramNMT2P47AsDmteLH81wdPR2DwMg3cxKvgrhUBeDSUW6MhM3sQb92we";
std::string const TREASURY_ADDRESS = "SaLvTyL2pN2SCAPRxwDQ7qjhdg46VbhZrGZTp2wHKJ5sK434a8ivEH35eWp2FTcmyW6LY6ExfBb9chmQ9xAL1eJyZ5FQjtQGTis3v";
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
// check address type before future development
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
{1100, {"71336a480440ed24a53b2cfd5a5292d1e618c3e843637227883ea2cc42fb346f","a135f59a05ea9e33539e4502b187b4789cc7fba79616c6902a902cc6601f0359","7f1c6970232254a9b13f7f063df2a853","62073c"}},
{1120, {"2cc49b182addc0106b601c9876c01a4b06532c05f9dd9179b2c4f47e5c7c0d74","fd62e59324389f37d0bb628a39f413c11be34d572ec3a40f465e008b9dfd5e0c","fbf8584222e299bb748009eaf2177123","b76a8d"}},
@@ -465,6 +474,7 @@ namespace config
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
"-----END PUBLIC KEY-----\n";
std::string const TREASURY_ADDRESS_CARROT = ""; // TODO: generate stagenet Carrot treasury address ?
std::string const TREASURY_ADDRESS = "fuLMowH85abK8nz9BBMEem7MAfUbQu4aSHHUV9j5Z86o6Go9Lv2U5ZQiJCWPY9R9HA8p5idburazjAhCqDngLo7fYPCD9ciM9ee1A";
// treasury payout {tx-key, output-key, anchor_enc, view_tag} tuples
@@ -519,6 +529,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::string TREASURY_ADDRESS_CARROT;
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)
@@ -542,6 +553,7 @@ namespace cryptonote
::config::TREASURY_SAL1_MINT_PERIOD,
::config::AUDIT_HARD_FORKS,
::config::TREASURY_ADDRESS,
::config::TREASURY_ADDRESS_CARROT,
::config::TREASURY_SAL1_MINT_OUTPUT_DATA
};
static const config_t testnet = {
@@ -563,6 +575,7 @@ namespace cryptonote
::config::testnet::TREASURY_SAL1_MINT_PERIOD,
::config::testnet::AUDIT_HARD_FORKS,
::config::testnet::TREASURY_ADDRESS,
::config::testnet::TREASURY_ADDRESS_CARROT,
::config::testnet::TREASURY_SAL1_MINT_OUTPUT_DATA
};
static const config_t stagenet = {
@@ -584,6 +597,7 @@ namespace cryptonote
::config::stagenet::TREASURY_SAL1_MINT_PERIOD,
::config::stagenet::AUDIT_HARD_FORKS,
::config::stagenet::TREASURY_ADDRESS,
::config::stagenet::TREASURY_ADDRESS_CARROT,
::config::stagenet::TREASURY_SAL1_MINT_OUTPUT_DATA
};
switch (nettype)
+509 -79
View File
@@ -31,6 +31,7 @@
#include <algorithm>
#include <cstdio>
#include <limits>
#include <boost/asio/dispatch.hpp>
#include <boost/filesystem.hpp>
#include <boost/range/adaptor/reversed.hpp>
@@ -38,6 +39,7 @@
#include "include_base_utils.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "tx_pool.h"
#include "blockchain.h"
#include "blockchain_db/locked_txn.h"
@@ -56,6 +58,8 @@
#include "crypto/hash.h"
#include "cryptonote_core.h"
#include "ringct/rctSigs.h"
#include "carrot_core/payment_proposal.h"
#include "carrot_impl/format_utils.h"
#include "common/perf_timer.h"
#include "common/notify.h"
#include "common/varint.h"
@@ -66,6 +70,8 @@
#include "net/http_client.h"
#include "storages/http_abstract_invoke.h"
#include "common/debugging.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
@@ -877,7 +883,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
uint64_t height;
auto new_top_hash = get_tail_id(height); // get it again now that we have the lock
top_hash = get_tail_id(height); // get it again now that we have the lock
++height;
uint8_t version = get_current_hard_fork_version();
@@ -976,11 +982,11 @@ size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_heig
if (start_height_opt) {
start_height = *start_height_opt;
} else {
bool found = false;
// bool found;
for (size_t i=0; i<num_mainnet_hard_forks; ++i) {
if (version == mainnet_hard_forks[i].version) {
start_height = mainnet_hard_forks[i].height;
found = true;
//found = true;
break;
}
}
@@ -1348,7 +1354,10 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
CHECK_AND_ASSERT_MES(b.miner_tx.version > 1, false, "Invalid coinbase transaction version");
if (hf_version >= HF_VERSION_CARROT) {
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "miner transaction has wrong version");
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
} else if (hf_version >= HF_VERSION_CARROT) {
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_CARROT, false, "miner transaction has wrong version");
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
}
@@ -1407,7 +1416,10 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
uint8_t hf_version_submitted = get_ideal_hard_fork_version(height - stake_lock_period - 1);
if (hf_version >= HF_VERSION_CARROT) {
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "protocol transaction has wrong version");
hf_version_submitted = hf_version;
} else if (hf_version == HF_VERSION_CARROT) {
if (hf_version_submitted >= HF_VERSION_CARROT || b.protocol_tx.vout.size() == 0) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
} else {
@@ -1479,7 +1491,53 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
return {false, 0};
}
return {true, output - tx.vout.begin()};
// Burning-bug guards: verify view_tag, anchor_enc, and D_e from the hardcoded config
// so a miner cannot tamper with those fields while keeping K_o correct.
// Check view_tag
carrot::view_tag_t expected_view_tag{};
if (!epee::string_tools::hex_to_pod(viewtag, expected_view_tag)) {
MERROR_VER("treasury payout: failed to deserialize expected view_tag");
return {false, 0};
}
if (target.view_tag != expected_view_tag) {
MERROR_VER("treasury payout view_tag mismatch (burning bug: K_o correct but view_tag tampered)");
return {false, 0};
}
// Check anchor_enc
carrot::encrypted_janus_anchor_t expected_anchor_enc{};
if (!epee::string_tools::hex_to_pod(anchor_enc, expected_anchor_enc)) {
MERROR_VER("treasury payout: failed to deserialize expected anchor_enc");
return {false, 0};
}
if (0 != memcmp(&target.encrypted_janus_anchor, &expected_anchor_enc, sizeof(expected_anchor_enc))) {
MERROR_VER("treasury payout anchor_enc mismatch (burning bug: K_o correct but anchor tampered)");
return {false, 0};
}
// Check D_e (enote ephemeral pubkey)
mx25519_pubkey expected_tx_key_raw{};
if (!epee::string_tools::hex_to_pod(tx_key, expected_tx_key_raw)) {
MERROR_VER("treasury payout: failed to deserialize expected tx_key");
return {false, 0};
}
const crypto::public_key expected_De = carrot::raw_byte_convert<crypto::public_key>(expected_tx_key_raw);
const size_t output_idx = output - tx.vout.begin();
const crypto::public_key single_tx_pubkey = cryptonote::get_tx_pub_key_from_extra(tx);
const std::vector<crypto::public_key> additional_tx_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(tx);
crypto::public_key actual_De{};
if (!additional_tx_pubkeys.empty() && output_idx < additional_tx_pubkeys.size()) {
actual_De = additional_tx_pubkeys[output_idx];
} else if (single_tx_pubkey != crypto::null_pkey) {
actual_De = single_tx_pubkey;
}
if (actual_De != expected_De) {
MERROR_VER("treasury payout D_e mismatch (burning bug: K_o correct but ephemeral key tampered)");
return {false, 0};
}
return {true, output_idx};
}
//------------------------------------------------------------------
// This function validates the miner transaction reward
@@ -1502,41 +1560,23 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
treasury_index_in_tx_outputs = index_in_tx_outputs;
}
//validate reward
// Calculate reward being issued
uint64_t money_in_use = 0;
CHECK_AND_ASSERT_MES(b.miner_tx.amount_burnt > 0 || height == 0, false, "invalid tx.amount_burnt for miner_tx");
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt >= money_in_use, false, "miner transaction is overflowed by amount_burnt");
money_in_use += b.miner_tx.amount_burnt;
for(size_t i = 0; i < b.miner_tx.vout.size(); i++)
{
// skip the treasury output
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) {
continue;
}
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.vout[i].amount >= money_in_use, false, "miner transaction is overflowed by output amount");
money_in_use += b.miner_tx.vout[i].amount;
}
partial_block_reward = false;
switch (version) {
case HF_VERSION_BULLETPROOF_PLUS:
case HF_VERSION_ENABLE_N_OUTS:
case HF_VERSION_FULL_PROOFS:
case HF_VERSION_ENFORCE_FULL_PROOFS:
case HF_VERSION_SHUTDOWN_USER_TXS:
case HF_VERSION_SALVIUM_ONE_PROOFS:
case HF_VERSION_AUDIT1_PAUSE:
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
if (b.miner_tx.amount_burnt > 0) {
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
money_in_use += b.miner_tx.amount_burnt;
}
if (already_generated_coins != 0)
CHECK_AND_ASSERT_MES(money_in_use / 5 == b.miner_tx.amount_burnt, false, "miner_transaction has incorrect amount_burnt amount");
break;
default:
assert(false);
break;
}
// Make sure the TOTAL REWARD is correct
uint64_t median_weight = m_current_block_cumul_weight_median;
if (!get_block_reward(median_weight, cumulative_block_weight, already_generated_coins, base_reward, version))
{
@@ -1553,11 +1593,119 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
MDEBUG("coinbase transaction doesn't use full amount of block reward: spent: " << money_in_use << ", block reward " << base_reward + fee << "(" << base_reward << "+" << fee << ")");
return false;
}
// HF-specific additional checks
switch (version) {
case HF_VERSION_BULLETPROOF_PLUS:
case HF_VERSION_ENABLE_N_OUTS:
case HF_VERSION_FULL_PROOFS:
case HF_VERSION_ENFORCE_FULL_PROOFS:
case HF_VERSION_SHUTDOWN_USER_TXS:
case HF_VERSION_SALVIUM_ONE_PROOFS:
case HF_VERSION_AUDIT1_PAUSE:
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
if (already_generated_coins != 0) {
// HF1-10: block reward split is 80% miner + 20% staker (amount_burnt)
CHECK_AND_ASSERT_MES(money_in_use / 5 == b.miner_tx.amount_burnt, false, "miner_transaction has incorrect amount_burnt amount");
}
break;
case HF_VERSION_ENABLE_TOKENS:
// HF11: block reward split is 60% miner + 25% treasury + 15% staker (amount_burnt)
if (already_generated_coins != 0) {
// Validate treasury share: one output must equal block_reward * 25 / 100
uint64_t expected_treasury_block_reward = money_in_use * BLOCK_REWARD_TREASURY_PCT / 100;
// Validate staker share: amount_burnt == block_reward * 15 / 100
uint64_t expected_staker_block_reward = (money_in_use - expected_treasury_block_reward) * BLOCK_REWARD_STAKER_PCT / 100;
CHECK_AND_ASSERT_MES(expected_staker_block_reward == b.miner_tx.amount_burnt, false,
"miner_transaction has incorrect amount_burnt for HF11 (expected " << expected_staker_block_reward << ", got " << b.miner_tx.amount_burnt << ")");
uint64_t expected_miner_block_reward = money_in_use - b.miner_tx.amount_burnt - expected_treasury_block_reward;
// treasury_destination
address_parse_info treasury_addr_info;
bool addr_ok = cryptonote::get_account_address_from_str(treasury_addr_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS_CARROT);
CHECK_AND_ASSERT_MES(addr_ok, false, "Failed to parse treasury address for validation");
carrot::CarrotDestinationV1 treasury_destination;
carrot::make_carrot_main_address_v1(treasury_addr_info.address.m_spend_public_key,
treasury_addr_info.address.m_view_public_key,
treasury_destination);
// deterministic janus anchor
const carrot::janus_anchor_t treasury_anchor = get_deterministic_treasury_anchor_from_height(height);
const carrot::CarrotPaymentProposalV1 treasury_proposal{
.destination = treasury_destination,
.amount = expected_treasury_block_reward,
.asset_type = "SAL1",
.randomness = treasury_anchor
};
carrot::CarrotCoinbaseEnoteV1 expected_enote;
carrot::get_coinbase_output_proposal_v1(treasury_proposal, height, expected_enote);
// Get the ephemeral pubkeys from the TX
crypto::public_key tx_pubkey = cryptonote::get_tx_pub_key_from_extra(b.miner_tx.extra);
bool has_single = (tx_pubkey != crypto::null_pkey);
std::vector<crypto::public_key> additional_tx_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx.extra);
bool found_treasury_block_reward = false;
for (size_t i = 0; i < b.miner_tx.vout.size(); i++) {
// Get the output
CHECK_AND_ASSERT_MES(b.miner_tx.vout[i].target.type() == typeid(txout_to_carrot_v1), false, "Output of miner_tx is not txout_to_carrot_V1");
const auto &output = boost::get<txout_to_carrot_v1>(b.miner_tx.vout[i].target);
// Check the output type is SAL1
CHECK_AND_ASSERT_MES(output.asset_type == "SAL1", false, "Output of miner_tx is not SAL1");
// Skip the premine remint
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) continue;
// Could this be the treasury block reward?
if (b.miner_tx.vout[i].amount == expected_treasury_block_reward) {
/// Check Ko
if (output.key != expected_enote.onetime_address) continue;
// Check view_tag
CHECK_AND_ASSERT_MES(output.view_tag == expected_enote.view_tag, false,
"treasury output view_tag mismatch (burning bug: K_o correct but view_tag tampered)");
// Check anchor_enc
CHECK_AND_ASSERT_MES(0 == memcmp(&output.encrypted_janus_anchor, &expected_enote.anchor_enc, sizeof(expected_enote.anchor_enc)), false,
"treasury output anchor_enc mismatch (burning bug: K_o correct but anchor tampered)");
// Check D_e
const crypto::public_key expected_De = carrot::raw_byte_convert<crypto::public_key>(expected_enote.enote_ephemeral_pubkey);
crypto::public_key actual_De{};
if (!additional_tx_pubkeys.empty() && i < additional_tx_pubkeys.size()) {
actual_De = additional_tx_pubkeys[i];
} else if (has_single) {
actual_De = tx_pubkey;
}
CHECK_AND_ASSERT_MES(actual_De == expected_De, false,
"treasury output D_e mismatch"); //important
// Passed all checks
found_treasury_block_reward = true;
continue;
}
}
CHECK_AND_ASSERT_MES(found_treasury_block_reward, false, "miner_tx missing treasury output with expected amount " << expected_treasury_block_reward);
}
break;
default:
CHECK_AND_ASSERT_MES(false, false, "invalid HF detected in miner_tx : " << version);
break;
}
return true;
}
//------------------------------------------------------------------
// SRCG
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version)
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1633,9 +1781,20 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
}
}
// Count CREATE_TOKEN transactions if we're at the right hard fork version
size_t create_token_count = 0;
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !txs.empty()) {
for (const auto& tx_pair : txs) {
const transaction& tx = tx_pair.first;
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
create_token_count++;
}
}
}
// Check we have the correct number of entries
CHECK_AND_ASSERT_MES(
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size(),
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size() + create_token_count,
false, "Invalid number of outputs in protocol_tx - aborting"
);
@@ -1682,6 +1841,71 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
);
}
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
// Validate create_coin transaction outputs
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_data;
for (const auto& tx_pair : txs) {
const transaction& tx = tx_pair.first;
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
create_token_data.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
}
}
for (const auto& ct_data : create_token_data) {
// Verify the output key
crypto::public_key out_key;
cryptonote::get_output_public_key(b.protocol_tx.vout[output_idx], out_key);
CHECK_AND_ASSERT_MES(out_key == ct_data.first.return_address, false, "Incorrect CREATE_TOKEN output key detected in protocol_tx");
// Verify the return pubkey
if (b.protocol_tx.vout.size() > 1) {
const auto additional_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.protocol_tx.extra);
CHECK_AND_ASSERT_MES(additional_pubkeys.size() > output_idx, false, "Missing CREATE_TOKEN return pubkey detected in protocol_tx");
CHECK_AND_ASSERT_MES(additional_pubkeys[output_idx] == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN return pubkey detected in protocol_tx");
} else {
const auto main_pubkey = cryptonote::get_tx_pub_key_from_extra(b.protocol_tx.extra);
CHECK_AND_ASSERT_MES(main_pubkey == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN return pubkey detected in protocol_tx");
}
// Verify the correct metadata type is provided
CHECK_AND_ASSERT_MES(ct_data.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data.second.token);
// Verify the output amount
uint64_t hi, lo;
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
lo = mul128(token.supply, COIN, &hi);
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == lo, false, "Incorrect CREATE_TOKEN output amount detected in protocol_tx");
// Verify the output asset type (should be the new asset type from protocol_tx_data)
std::string expected_asset_type = "sal" + ct_data.second.asset_type;
std::string out_asset_type;
cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type);
CHECK_AND_ASSERT_MES(out_asset_type == expected_asset_type, false, "Incorrect CREATE_TOKEN output asset_type detected in protocol_tx");
// Validate custom asset type format
CHECK_AND_ASSERT_MES(cryptonote::is_valid_custom_asset_type(out_asset_type), false, "CREATE_TOKEN asset type is invalid");
// Verify the view tag
CHECK_AND_ASSERT_MES(
boost::get<cryptonote::txout_to_carrot_v1>(
b.protocol_tx.vout[output_idx].target
).view_tag == ct_data.first.return_view_tag, false, "Incorrect CREATE_TOKEN view tag detected in protocol_tx"
);
// Verify the anchor encrypted
CHECK_AND_ASSERT_MES(
boost::get<cryptonote::txout_to_carrot_v1>(
b.protocol_tx.vout[output_idx].target
).encrypted_janus_anchor == ct_data.first.return_anchor_enc, false, "Incorrect CREATE_TOKEN anchor detected in protocol_tx"
);
output_idx++;
}
}
return true;
}
@@ -1807,7 +2031,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
// in a lot of places. That flag is not referenced in any of the code
// nor any of the makefiles, howeve. Need to look into whether or not it's
// necessary at all.
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_weight;
@@ -1836,6 +2060,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
expected_reward = m_btc_expected_reward;
seed_height = m_btc_seed_height;
seed_hash = m_btc_seed_hash;
miner_reward_tx_key = m_btc_miner_reward_tx_key;
return true;
}
MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()) << ", from_block " << (!!from_block));
@@ -1974,19 +2199,46 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
std::vector<cryptonote::protocol_data_entry> protocol_entries;
size_t txs_weight;
uint64_t fee;
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_entries;
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, create_token_entries, b.major_version))
{
return false;
}
// create the protocol transaction entries for CREATE_TOKEN TXs
if (b.major_version >= HF_VERSION_ENABLE_TOKENS) {
for (const auto& ct_data_entry: create_token_entries) {
CHECK_AND_ASSERT_MES(ct_data_entry.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data_entry.second.token);
cryptonote::protocol_data_entry entry;
uint64_t hi, lo;
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
CHECK_AND_ASSERT_MES(token.supply > 0, false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
lo = mul128(token.supply, COIN, &hi);
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
entry.amount_burnt = cryptonote::get_token_creation_price(ct_data_entry.second.asset_type);
entry.amount_minted = lo;
entry.amount_slippage_limit = 0;
entry.source_asset = "SAL1";
entry.destination_asset = "sal" + ct_data_entry.second.asset_type;
entry.type = cryptonote::transaction_type::CREATE_TOKEN;
entry.return_address = ct_data_entry.first.return_address;
entry.return_pubkey = ct_data_entry.first.return_pubkey;
entry.return_view_tag = ct_data_entry.first.return_view_tag;
entry.return_anchor_enc = ct_data_entry.first.return_anchor_enc;
entry.is_carrot = true;
entry.origin_height = height;
protocol_entries.push_back(entry);
}
}
// Check to see if there are any matured YIELD TXs
uint64_t yield_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
uint64_t start_height = (height > yield_lock_period) ? height - yield_lock_period - 1 : 0;
std::vector<cryptonote::protocol_data_entry> protocol_entries;
cryptonote::yield_block_info ybi_matured;
bool ok = get_ybi_entry(start_height, ybi_matured);
if (ok && ybi_matured.locked_coins_this_block > 0) {
@@ -2123,10 +2375,6 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
*/
// Time to construct the protocol_tx
uint64_t protocol_fee = 0;
address_parse_info treasury_address_info;
ok = cryptonote::get_account_address_from_str(treasury_address_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS);
CHECK_AND_ASSERT_MES(ok, false, "Failed to obtain treasury address info");
ok = construct_protocol_tx(height, b.protocol_tx, protocol_entries, b.major_version);
CHECK_AND_ASSERT_MES(ok, false, "Failed to construct protocol tx");
@@ -2190,8 +2438,9 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
*/
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
uint8_t hf_version = b.major_version;
size_t max_outs = hf_version >= 4 ? 1 : 11;
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
size_t max_outs = hf_version >= HF_VERSION_ENABLE_TOKENS ? 3 : (hf_version >= 4 ? 1 : 11);
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
@@ -2200,7 +2449,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
#endif
for (size_t try_count = 0; try_count != 10; ++try_count)
{
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
size_t coinbase_weight = get_transaction_weight(b.miner_tx);
@@ -2245,16 +2494,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
#endif
if (!from_block)
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie);
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie, miner_reward_tx_key);
return true;
}
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
return false;
}
//------------------------------------------------------------------
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//------------------------------------------------------------------
bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
@@ -2954,7 +3203,7 @@ bool Blockchain::get_pricing_record(oracle::pricing_record &pr, std::map<std::st
bool r = false;
const uint64_t height = get_current_blockchain_height();
const uint8_t hf_version = m_hardfork->get_current_version();
// const uint8_t hf_version = m_hardfork->get_current_version();
epee::net_utils::http::http_simple_client http_client;
COMMAND_RPC_GET_PRICING_RECORD::request req = AUTO_VAL_INIT(req);
@@ -3728,7 +3977,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
if (hf_version >= HF_VERSION_CARROT) {
// from v10, force the new SalviumOne RCT data
if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT) {
if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::CREATE_TOKEN || tx.type == cryptonote::transaction_type::ROLLUP) {
if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) {
MERROR_VER("SalviumOne data required after v" + std::to_string(HF_VERSION_CARROT));
tvc.m_invalid_output = true;
@@ -3783,6 +4032,107 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
return true;
}
//------------------------------------------------------------------
bool Blockchain::check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
const uint8_t hf_version = m_hardfork->get_current_version();
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
// Get the valid tokens according to the DB
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
return true;
} else if (
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
tx.type == cryptonote::transaction_type::ROLLUP ||
tx.type == cryptonote::transaction_type::STAKE
) {
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
MERROR_VER("Invalid source/dest asset type for CREATE_TOKEN / ROLLUP / STAKE - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL1");
tvc.m_verifivation_failed = true;
return false;
}
} else if (tx.type == cryptonote::transaction_type::BURN) {
if ((tx.source_asset_type != "SAL" && tx.source_asset_type != "SAL1") || (tx.source_asset_type != tx.destination_asset_type)) {
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL/SAL1 for both");
tvc.m_verifivation_failed = true;
return false;
}
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
if (tx.source_asset_type != tx.destination_asset_type) {
MERROR_VER("Mismatched asset types for TRANSFER - provided source asset: " << tx.source_asset_type << ", provided destination asset: " << tx.destination_asset_type << ", expected them to match");
tvc.m_verifivation_failed = true;
return false;
} else if (!(tx.source_asset_type == "SAL1" || mapTokens.count(tx.source_asset_type))) {
MERROR_VER("Invalid source asset type for TRANSFER - provided source asset: " << tx.source_asset_type << ", expected valid token type or SAL1");
tvc.m_verifivation_failed = true;
return false;
} else if (tx.destination_asset_type != "SAL1" && !mapTokens.count(tx.destination_asset_type)) {
MERROR_VER("Invalid destination asset type for TRANSFER - provided destination asset: " << tx.destination_asset_type << ", expected valid token type or SAL1");
tvc.m_verifivation_failed = true;
return false;
}
} else if (tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::CONVERT) {
MERROR_VER("AUDIT and CONVERT transaction types are not allowed in this hardfork version:" << std::to_string(HF_VERSION_ENABLE_TOKENS));
} else {
MERROR_VER("Unknown transaction type: " << tx.type << ".");
tvc.m_verifivation_failed = true;
return false;
}
} else if (hf_version >= HF_VERSION_CARROT) {
if (tx.type == cryptonote::transaction_type::BURN) {
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "BURN") {
MERROR_VER("Invalid source/dest asset type for BURN - provided destination asset: " << tx.destination_asset_type << ", expected BURN" << ", provided source asset: " << tx.source_asset_type << ", expected SAL1");
tvc.m_verifivation_failed = true;
return false;
}
} else {
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL1" << ", provided source asset: " << tx.source_asset_type << ", expected SAL1");
tvc.m_verifivation_failed = true;
return false;
}
}
} else if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
// protocol and miner txs don't have source and destination asset types
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
return true;
} else if (tx.type == cryptonote::transaction_type::AUDIT) {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL" && tx.destination_asset_type == "SAL", false, "wrong source/destination asset type: provided source asset " << tx.source_asset_type << " expected SAL and provided destination asset " << tx.destination_asset_type << " expected SAL");
} else if (tx.type == cryptonote::transaction_type::BURN) {
if ((tx.source_asset_type != "SAL" && tx.source_asset_type != "SAL1") || tx.destination_asset_type != "BURN") {
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL/SAL1 and BURN respectively");
tvc.m_verifivation_failed = true;
return false;
}
} else {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1" && tx.destination_asset_type == "SAL1", false, "wrong source/destination asset type: provided source asset: " << tx.source_asset_type << " expected SAL1 and provided destination asset: " << tx.destination_asset_type << " expected SAL1");
}
} else {
if (tx.type == cryptonote::transaction_type::BURN) {
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "BURN") {
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL and BURN respectively");
tvc.m_verifivation_failed = true;
return false;
}
}
//only SAL existed before hf 6
else{
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "SAL") {
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL" << ", provided source asset: " << tx.source_asset_type << ", expected SAL");
tvc.m_verifivation_failed = true;
return false;}
}
}
return true;
}
//------------------------------------------------------------------
bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verification_context &tvc) const
{
@@ -3809,6 +4159,25 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
// Reverse the order of checking now, because we're using >=
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
if (tx.type == cryptonote::transaction_type::TRANSFER ||
tx.type == cryptonote::transaction_type::RETURN ||
tx.type == cryptonote::transaction_type::ROLLUP ||
tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
if (tx.version < TRANSACTION_VERSION_ENABLE_TOKENS) {
}
} else {
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
}
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
// Allowed multiple asset_types for `vout` entries
} else {
// No mixing of asset_types permitted - verify here
}
}
}
// After v2 allow N-out TXs for TRANSFER ONLY
if (hf_version >= HF_VERSION_ENABLE_N_OUTS && hf_version < HF_VERSION_CARROT) {
if (tx.version >= TRANSACTION_VERSION_N_OUTS && tx.type != cryptonote::transaction_type::TRANSFER) {
@@ -3818,7 +4187,15 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
if (hf_version >= HF_VERSION_CARROT) {
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_ENABLE_TOKENS));
tvc.m_version_mismatch = true;
return false;
}
}
if (hf_version == HF_VERSION_CARROT) {
if (tx.version != TRANSACTION_VERSION_CARROT) {
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_CARROT));
tvc.m_version_mismatch = true;
@@ -3835,6 +4212,19 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
// Make sure CREATE_TOKEN TXs are disabled until we are ready - belt and braces!
if (hf_version < HF_VERSION_ENABLE_TOKENS) {
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
MERROR("CREATE_TOKEN TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
tvc.m_version_mismatch = true;
return false;
}
if (tx.type == cryptonote::transaction_type::ROLLUP) {
MERROR("ROLLUP TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
tvc.m_version_mismatch = true;
return false;
}
}
if (tx.type == cryptonote::transaction_type::AUDIT) {
// Make sure we are supposed to accept AUDIT txs at this point
@@ -3842,24 +4232,40 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
CHECK_AND_ASSERT_MES(audit_hard_forks.find(hf_version) != audit_hard_forks.end(), false, "trying to audit outside an audit fork");
std::string expected_asset_type = audit_hard_forks.at(hf_version).second.first;
CHECK_AND_ASSERT_MES(tx.source_asset_type == expected_asset_type, false, "trying to spend " << tx.source_asset_type << " coins in an AUDIT TX");
} else {
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
} else {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
}
}
if (tx.type == cryptonote::transaction_type::BURN) {
CHECK_AND_ASSERT_MES(tx.destination_asset_type == "BURN", false, "incorrect burn tx destination type:" << tx.destination_asset_type);
} else {
if (tx.source_asset_type != tx.destination_asset_type) {
MERROR_VER("Tx " << get_transaction_hash(tx) << " has mismatched asset types: " << tx.source_asset_type << " != " << tx.destination_asset_type);
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
// Check that the ticker doesn't begin with the reserved chars `SAL`
CHECK_AND_ASSERT_MES(tx.token_metadata.asset_type.substr(0,3) != "SAL", false, "Invalid CREATE_TOKEN ticker - SAL* is reserved");
// Check that the specific asset_type being created isn't already in our list of tokens
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
std::string asset_type = "sal" + tx.token_metadata.asset_type;
if (mapTokens.count(asset_type)) {
MERROR("TX attempting to create '" + asset_type + "', which already exists");
tvc.m_verifivation_failed = true;
return false;
}
// Validate token metadata //! check */
CHECK_AND_ASSERT_MES(tx.token_metadata.token.type() == typeid(cryptonote::sal_token_t), false, "Invalid CREATE_TOKEN metadata type");
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(tx.token_metadata.token);
CHECK_AND_ASSERT_MES(token.supply > 0 && token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN");
// Validate the amount burnt matches the token creation price
CHECK_AND_ASSERT_MES(tx.amount_burnt == cryptonote::get_token_creation_price(tx.token_metadata.asset_type), false, "Invalid fee paid for CREATE_TOKEN");
}
if (tx.type == cryptonote::transaction_type::ROLLUP) {
CHECK_AND_ASSERT_MES(tx.layer2_rollup_data.version == 1, false, "Invalid ROLLUP data version");
CHECK_AND_ASSERT_MES(!tx.layer2_rollup_data.txs.empty(), false, "ROLLUP must include at least one paid TX entry");
uint64_t expected_amount_burnt = 0;
for (const auto &rollup_tx : tx.layer2_rollup_data.txs) {
CHECK_AND_ASSERT_MES(rollup_tx.tx_fee > 0, false, "ROLLUP contains a paid TX entry with zero fee");
CHECK_AND_ASSERT_MES(expected_amount_burnt <= std::numeric_limits<uint64_t>::max() - rollup_tx.tx_fee, false, "Numeric overflow in ROLLUP fee total");
expected_amount_burnt += rollup_tx.tx_fee;
}
CHECK_AND_ASSERT_MES(tx.amount_burnt == expected_amount_burnt, false, "Invalid amount_burnt for ROLLUP");
}
// Check for invalid TX types
if (tx.type == cryptonote::transaction_type::UNSET || tx.type > cryptonote::transaction_type::MAX) {
@@ -3895,7 +4301,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 || tx.version == 4, false, "Transaction version is not 2/3/4");
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3 || tx.version == 4 || tx.version == 5, false, "Transaction version is not 2/3/5");
rct::rctSig &rv = tx.rct_signatures;
@@ -4040,11 +4446,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
if (in_to_key.amount == 0)
{
// always consider rct inputs mixable. Even if there's not enough rct
// inputs on the chain to mix with, this is going to be the case for
// just a few blocks right after the fork at most
++n_mixable;
}
// don't always consider rct inputs mixable!
uint64_t n_outputs = m_db->get_num_outputs_of_asset_type(in_to_key.asset_type);
if (n_outputs <= min_mixin)
++n_unmixable;
else
++n_mixable;
}
else
{
uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
@@ -4518,8 +4926,7 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
//------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{
const uint8_t version = get_current_hard_fork_version();
const uint64_t db_height = m_db->height();
// const uint64_t db_height = m_db->height();
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
@@ -4670,7 +5077,7 @@ bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vecto
// Get the AUDIT TX information for matured staked coins
std::vector<cryptonote::yield_tx_info> audit_entries;
// We get the audit_tx_info from the block where they entered the chain
int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries);
m_db->get_audit_tx_info(start_height, audit_entries);
if (!audit_entries.size()) {
// Report error and abort
@@ -4723,7 +5130,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info_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);
m_db->get_carrot_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
@@ -4789,7 +5196,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info> yield_entries;
// We get the yield_tx_info from the block _before_ they started to accrue yield
int yield_tx_result = m_db->get_yield_tx_info(start_height, yield_entries);
m_db->get_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
@@ -4956,6 +5363,18 @@ bool Blockchain::get_ybi_entry(const uint64_t height, cryptonote::yield_block_in
return true;
}
//------------------------------------------------------------------
bool Blockchain::is_tx_paid_for(const cryptonote::transaction& tx)
{
// Is it a TRANSFER?
if (tx.type != cryptonote::transaction_type::TRANSFER)
return true;
// Is is a TOKEN?
if (!cryptonote::is_asset_type_token(tx.source_asset_type))
return true;
// Has the TRANSFER TOKEN been paid for?
return m_db->is_tx_paid_for(tx);
}
//------------------------------------------------------------------
//TODO: revisit, has changed a bit on upstream
bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const
{
@@ -5330,6 +5749,16 @@ leave:
}
}
#endif
// Check the TX type
tx_verification_context tvc;
if (!check_tx_type_and_version(tx, tvc)) {
MERROR("Block with id: " << id << " failed to pass transaction type and version check for transaction id: " << tx_id);
bvc.m_verifivation_failed = true;
return_tx_to_pool(txs);
goto leave;
}
TIME_MEASURE_FINISH(cc);
t_checktx += cc;
fee_summary += fee;
@@ -5362,7 +5791,7 @@ leave:
TIME_MEASURE_FINISH(vmt);
TIME_MEASURE_START(vpt);
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version()))
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version(), txs))
{
MERROR_VER("Block with id: " << id << " has incorrect protocol transaction");
bvc.m_verifivation_failed = true;
@@ -5406,7 +5835,7 @@ leave:
// Update the YBI cache data
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
// uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
if (new_height > yield_lock_period) {
if (m_yield_block_info_cache.count(new_height - yield_lock_period - 2) != 0) {
m_yield_block_info_cache.erase(new_height - yield_lock_period - 2);
@@ -5524,7 +5953,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
const uint64_t db_height = m_db->height();
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
const uint8_t hf_version = get_current_hard_fork_version();
// const uint8_t hf_version = get_current_hard_fork_version();
if (db_height < CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE)
return block_weight;
@@ -6171,7 +6600,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
} while(0); \
// generate sorted tables for all amounts and absolute offsets
size_t tx_index = 0, block_index = 0;
size_t tx_index = 0; //, block_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -6242,7 +6671,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
}
++block_index;
//++block_index;
}
// sort and remove duplicate absolute_offsets in offset_map
@@ -6514,7 +6943,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "1cf6e8892e0512c246cef62610ccf524f30f484e307ae01959a5a7dd166aa328";
static const char expected_block_hashes_hash[] = "f3bf12451890c9cbeb9f90b2762e4ee2756aaa726958e280e27b51ed9edf84b3";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
@@ -6656,7 +7085,7 @@ void Blockchain::invalidate_block_template_cache()
m_btc_valid = false;
}
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie)
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, crypto::public_key &miner_reward_tx_key)
{
MDEBUG("Setting block template cache");
m_btc = b;
@@ -6668,6 +7097,7 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
m_btc_seed_hash = seed_hash;
m_btc_seed_height = seed_height;
m_btc_pool_cookie = pool_cookie;
m_btc_miner_reward_tx_key = miner_reward_tx_key;
m_btc_valid = true;
}
+21 -5
View File
@@ -394,8 +394,8 @@ namespace cryptonote
*
* @return true if block template filled in successfully, else false
*/
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
/**
* @brief gets data required to create a block template and start mining on it
@@ -726,6 +726,19 @@ namespace cryptonote
*/
bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc) const;
/**
* @brief check a transaction's input/output/TX asset types conform to current standards
*
* This function checks, for example at the time of this writing, that
* the asset types are supported by the given TX for the current HF version on-chain.
*
* @param tx the transaction to check the asset types of
* @param tvc returned info about tx verification
*
* @return false if the TX version and/or type is unsupported, otherwise true
*/
bool check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const;
/**
* @brief check that a transaction's version & type conforms to current standards
*
@@ -1233,6 +1246,8 @@ namespace cryptonote
*/
bool validate_ybi_cache();
bool is_tx_paid_for(const cryptonote::transaction& tx);
#ifndef IN_UNIT_TESTS
private:
#endif
@@ -1318,7 +1333,7 @@ namespace cryptonote
crypto::hash m_btc_seed_hash;
uint64_t m_btc_seed_height;
bool m_btc_valid;
crypto::public_key m_btc_miner_reward_tx_key;
bool m_batch_success;
@@ -1564,10 +1579,11 @@ namespace cryptonote
* @param b the block containing the miner transaction to be validated
* @param height the blockchain's weight
* @param version hard fork version for that transaction
* @param txs create_coin transactions in the block
*
* @return false if anything is found wrong with the protocol transaction, otherwise true
*/
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version);
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs = std::vector<std::pair<transaction, blobdata>>());
/**
* @brief reverts the blockchain to its previous state following a failed switch
@@ -1743,7 +1759,7 @@ namespace cryptonote
*
* At some point, may be used to push an update to miners
*/
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, crypto::public_key &miner_reward_tx_key);
/**
* @brief sends new block notifications to ZMQ `miner_data` subscribers
+29 -9
View File
@@ -843,7 +843,8 @@ 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_CARROT ? TRANSACTION_VERSION_CARROT :
const size_t max_tx_version = hf_version >= HF_VERSION_ENABLE_TOKENS ? TRANSACTION_VERSION_ENABLE_TOKENS :
hf_version >= HF_VERSION_CARROT ? TRANSACTION_VERSION_CARROT :
hf_version >= HF_VERSION_ENABLE_N_OUTS ? TRANSACTION_VERSION_N_OUTS :
TRANSACTION_VERSION_2_OUTS;
if (tx.version == 0 || tx.version > max_tx_version)
@@ -904,6 +905,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block)
{
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
bool ret = true;
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
{
@@ -925,7 +927,6 @@ namespace cryptonote
if (tx_info[n].tx->version < 2)
continue;
const rct::rctSig &rv = tx_info[n].tx->rct_signatures;
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
if (hf_version >= HF_VERSION_CARROT) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) {
MERROR_VER("Invalid RCT type provided");
@@ -950,7 +951,11 @@ namespace cryptonote
tx_info[n].result = false;
return false;
}
}
}
bool need_rollup = false;
if (hf_version >= HF_VERSION_CARROT) {
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
}
switch (rv.type) {
case rct::RCTTypeNull:
// coinbase should not come here, so we reject for all other types
@@ -965,7 +970,8 @@ namespace cryptonote
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
0
0,
need_rollup
))
{
MERROR_VER("rct signature semantics check failed");
@@ -1030,12 +1036,19 @@ namespace cryptonote
continue;
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus && tx_info[n].tx->rct_signatures.type != rct::RCTTypeFullProofs && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumZero && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumOne)
continue;
bool need_rollup = false;
if (hf_version >= HF_VERSION_CARROT) {
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
}
if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures,
tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
0
tx_info[n].tx->type == cryptonote::transaction_type::CREATE_TOKEN ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::ROLLUP ? tx_info[n].tx->amount_burnt :
0,
need_rollup
))
{
set_semantics_failed(tx_info[n].tx_hash);
@@ -1249,6 +1262,13 @@ namespace cryptonote
return false;
}
/*
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !m_blockchain_storage.is_tx_paid_for(tx))
{
MERROR_VER("tx has not been paid for by ROLLUP");
return false;
}
*/
return true;
}
//-----------------------------------------------------------------------------------------------
@@ -1522,14 +1542,14 @@ namespace cryptonote
notify_txpool_event(tx_blobs, epee::to_span(tx_hashes), epee::to_span(txs), just_broadcasted);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//-----------------------------------------------------------------------------------------------
bool core::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
+2 -2
View File
@@ -231,8 +231,8 @@ namespace cryptonote
*
* @note see Blockchain::create_block_template
*/
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) override;
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) override;
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
/**
* @copydoc Blockchain::get_miner_data
+85 -41
View File
@@ -346,7 +346,7 @@ namespace cryptonote
// Clear the TX contents
tx.set_null();
tx.version = 2;
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? 3 : 2;
bool carrot_found = false;
bool noncarrot_found = false;
tx.type = cryptonote::transaction_type::PROTOCOL;
@@ -356,7 +356,7 @@ namespace cryptonote
if (entry.is_carrot) carrot_found = true;
else noncarrot_found = true;
}
if (carrot_found && noncarrot_found) {
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
return false;
@@ -365,7 +365,7 @@ namespace cryptonote
LOG_ERROR("Carrot outputs found in CryptoNote protocol transaction");
return false;
}
if (carrot_found || (!noncarrot_found && hard_fork_version >= HF_VERSION_CARROT))
{
// Ensure the TX version is correct
@@ -381,7 +381,8 @@ namespace cryptonote
// Build the proposal
carrot::CarrotCoinbaseEnoteV1 e;
e.onetime_address = entry.return_address;
e.amount = entry.amount_burnt;
// amount_minted for CREATE_TOKEN, amount_burnt for STAKE/AUDIT
e.amount = (entry.type == cryptonote::transaction_type::CREATE_TOKEN) ? entry.amount_minted : entry.amount_burnt;
e.asset_type = entry.destination_asset;
e.view_tag = entry.return_view_tag;
e.anchor_enc = entry.return_anchor_enc;
@@ -391,6 +392,7 @@ namespace cryptonote
}
tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL, height);
tx.amount_burnt = 0;
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
tx.invalidate_hashes();
}
catch (const std::exception &e)
@@ -457,7 +459,17 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
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, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
carrot::janus_anchor_t get_deterministic_treasury_anchor_from_height(uint64_t height)
{
carrot::janus_anchor_t treasury_anchor{};
for (int i = 0; i < 8; ++i)
treasury_anchor.bytes[i] = (height >> (8 * i)) & 0xff;
for (int i = 8; i < 16; ++i)
treasury_anchor.bytes[i] = 0;
return treasury_anchor;
}
//---------------------------------------------------------------
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, crypto::public_key& miner_reward_tx_key, transaction& tx, network_type nettype, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
// Clear the TX contents
tx.set_null();
@@ -485,33 +497,70 @@ namespace cryptonote
{
try
{
// Build the miner payout
carrot::CarrotDestinationV1 destination;
// miner destination
carrot::CarrotDestinationV1 miner_destination;
carrot::make_carrot_main_address_v1(miner_address.m_spend_public_key,
miner_address.m_view_public_key,
destination);
miner_destination);
CHECK_AND_ASSERT_THROW_MES(!miner_destination.is_subaddress,
"construct_miner_tx: subaddresses are not allowed in miner transactions");
CHECK_AND_ASSERT_THROW_MES(miner_destination.payment_id == carrot::null_payment_id,
"construct_miner_tx: integrated addresses are not allowed in miner transactions");
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");
const bool do_new_split = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS);
uint64_t treasury_reward = do_new_split ? (block_reward * BLOCK_REWARD_TREASURY_PCT / 100) : 0;
uint64_t stake_reward = (block_reward - treasury_reward) * BLOCK_REWARD_STAKER_PCT / 100;
uint64_t miner_reward = (block_reward - treasury_reward - stake_reward);
uint64_t stake_reward = block_reward / 5;
const carrot::CarrotPaymentProposalV1 payment_proposal{
.destination = destination,
.amount = block_reward - stake_reward,
//miner enote
const carrot::CarrotPaymentProposalV1 miner_proposal{
.destination = miner_destination,
.amount = miner_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());
// Determine number of enotes: miner + optional treasury_reward + optional treasury_mint
size_t num_enotes = 1;
if (do_new_split) num_enotes++; // treasury reward output
if (treasury_payout_exists) num_enotes++;
std::vector<carrot::CarrotCoinbaseEnoteV1> enotes(num_enotes);
carrot::get_coinbase_output_proposal_v1(miner_proposal, height, enotes[0]);
// Check to see if there needs to be a treasury payout
// STORE THE MINER TX_PUB_KEY NOW
miner_reward_tx_key = carrot::raw_byte_convert<crypto::public_key>(enotes[0].enote_ephemeral_pubkey);
size_t enote_idx = 1;
// Add the treasury reward enote (25% of block reward) for HF11+
if (do_new_split) {
//treasury address for HF11+ block reward split
address_parse_info treasury_addr_info;
bool treasury_ok = cryptonote::get_account_address_from_str(treasury_addr_info, nettype, get_config(nettype).TREASURY_ADDRESS_CARROT);
CHECK_AND_ASSERT_MES(treasury_ok, false, "Failed to parse treasury address for block reward split"); // maybe more check can be added here, but it's enough for now (bcs validation)
carrot::CarrotDestinationV1 treasury_destination;
carrot::make_carrot_main_address_v1(treasury_addr_info.address.m_spend_public_key,
treasury_addr_info.address.m_view_public_key,
treasury_destination);
// Derive a deterministic janus anchor from height so validators can independently recompute the expected treasury K_o
const carrot::janus_anchor_t treasury_anchor = get_deterministic_treasury_anchor_from_height(height);
const carrot::CarrotPaymentProposalV1 treasury_proposal{
.destination = treasury_destination,
.amount = treasury_reward,
.asset_type = "SAL1",
.randomness = treasury_anchor
};
carrot::get_coinbase_output_proposal_v1(treasury_proposal, height, enotes[enote_idx]);
enote_idx++;
}
//
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");
@@ -522,8 +571,8 @@ namespace cryptonote
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();
//
carrot::CarrotCoinbaseEnoteV1 &treasury_enote = enotes[enote_idx];
treasury_enote.onetime_address = onetime_address;
treasury_enote.amount = TREASURY_SAL1_MINT_AMOUNT;
treasury_enote.asset_type = "SAL1";
@@ -531,15 +580,15 @@ namespace cryptonote
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);
// Sort enotes by K_o
std::sort(enotes.begin(), enotes.end(), [](const carrot::CarrotCoinbaseEnoteV1 &a, const carrot::CarrotCoinbaseEnoteV1 &b) {
return a.onetime_address < b.onetime_address;
});
tx = carrot::store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER, height);
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
tx.amount_burnt = stake_reward;
tx.invalidate_hashes();
}
@@ -554,6 +603,8 @@ namespace cryptonote
keypair txkey = keypair::generate(hw::get_device("default"));
add_tx_pub_key_to_extra(tx, txkey.pub);
// STORE THE MINER TX_PUB_KEY NOW
miner_reward_tx_key = txkey.pub;
if(!extra_nonce.empty())
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
return false;
@@ -597,6 +648,7 @@ namespace cryptonote
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
case HF_VERSION_ENABLE_TOKENS:
// SRCG: subtract 20% that will be rewarded to staking users
CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero");
tx.amount_burnt = amount / 5;
@@ -1440,24 +1492,16 @@ namespace cryptonote
bool get_block_longhash(const Blockchain *pbc, const blobdata& bd, crypto::hash& res, const uint64_t height, const int major_version, const crypto::hash *seed_hash, const int miners)
{
crypto::hash hash;
if (pbc != NULL)
{
crypto::hash hash;
const uint64_t seed_height = rx_seedheight(height);
hash = seed_hash ? *seed_hash : pbc->get_pending_block_id_by_height(seed_height);
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
} else
{
// only happens when generating genesis block
// Hardcoded genesis for ios compat
const char* hex = "4ade63d5ccb8cfae075e8b882514c471f35da95f85dd1b20fdcd6f3a95caabc5";
char bytes[32];
for (int i = 0; i < 32; i++) {
char byte_str[3] = { hex[i * 2], hex[i * 2 + 1], '\0' };
bytes[i] = (char)strtol(byte_str, NULL, 16);
}
memcpy(res.data, bytes, sizeof(bytes));
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
}
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
return true;
}
+3 -1
View File
@@ -77,7 +77,9 @@ 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);
carrot::janus_anchor_t get_deterministic_treasury_anchor_from_height(uint64_t height);
//---------------------------------------------------------------
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, crypto::public_key& miner_reward_tx_key, 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);
struct tx_source_entry
{
+62 -2
View File
@@ -47,6 +47,7 @@
#include "common/perf_timer.h"
#include "crypto/hash.h"
#include "crypto/duration.h"
#include "common/debugging.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "txpool"
@@ -266,6 +267,20 @@ namespace cryptonote
tvc.m_verifivation_failed = true;
return false;
}
// Check the TX asset types
if (!m_blockchain.check_tx_asset_types(tx, tvc)) {
LOG_PRINT_L1("Transaction with id= "<< id << " has invalid asset type(s)");
tvc.m_verifivation_failed = true;
return false;
}
// HERE BE DRAGONS!!!
// Check that CREATE_TOKEN txs are unique in the pool
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
// TODO: ...scan the existing entries - requires either a registry of CREATE_TOKEN TXs, or to interatively process the pool
}
// LAND AHOY!!!
// assume failure during verification steps until success is certain
tvc.m_verifivation_failed = true;
@@ -1505,6 +1520,10 @@ namespace cryptonote
return false;
}
// Check that `transfer token` TXs have been paid for before accepting them into a block template
if (!m_blockchain.is_tx_paid_for(lazy_tx()))
return false;
//transaction is ok.
return true;
}
@@ -1614,7 +1633,7 @@ namespace cryptonote
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -1630,7 +1649,6 @@ namespace cryptonote
return false;
}
size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5;
@@ -1638,6 +1656,15 @@ namespace cryptonote
LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
// Store list of tokens being created
std::set<std::string> tokens;
// Get the list of already-created tokens that are in the DB
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_blockchain.get_db().get_tokens();
for (const auto &entry: mapTokens) {
tokens.insert(entry.second.asset_type);
}
LockedTXN lock(m_blockchain.get_db());
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
@@ -1760,6 +1787,39 @@ namespace cryptonote
continue;
}
// check for ROLLUP TXs
if (tx.type == cryptonote::transaction_type::ROLLUP)
{
if (version < HF_VERSION_ENABLE_TOKENS) {
LOG_PRINT_L2(" is a ROLLUP transaction before they are permitted - cannot be mined");
continue;
}
}
// check and include create token entries
if (tx.type == transaction_type::CREATE_TOKEN)
{
if (version < HF_VERSION_ENABLE_TOKENS) {
LOG_PRINT_L2(" is a CREATE_TOKEN transaction before they are permitted - cannot be mined");
continue;
}
std::string asset_type = "sal" + tx.token_metadata.asset_type;
if (!cryptonote::is_valid_asset_type(asset_type)) {
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an invalid asset_type - cannot be mined");
continue;
}
// Check to see if token is already being created
if (tokens.find(tx.token_metadata.asset_type) != tokens.end()) {
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an asset_type already in the block template - cannot be mined");
continue;
}
tokens.insert(tx.token_metadata.asset_type);
create_token_entries.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
}
bl.tx_hashes.push_back(sorted_it->second);
total_weight += meta.weight;
fee += meta.fee;
+2 -1
View File
@@ -226,11 +226,12 @@ namespace cryptonote
* @param total_weight return-by-reference the total weight of the new block
* @param fee return-by-reference the total of fees from the included transactions
* @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
* @param create_token_entries list of create coin entries to include in protocol tx
* @param version hard fork version to use for consensus rules
*
* @return true
*/
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version);
/**
* @brief get a list of all transactions in the pool
@@ -187,7 +187,7 @@ bool ver_rct_non_semantics_simple_cached
// mixring. Future versions of the protocol may differ in this regard, but if this assumptions
// holds true in the future, enable the verification hash by modifying the `untested_tx`
// condition below.
const bool untested_tx = tx.version > 4 || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
const bool untested_tx = tx.version > TRANSACTION_VERSION_ENABLE_TOKENS || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here.");
// Don't cache older (or newer) rctSig types
+3 -1
View File
@@ -54,6 +54,8 @@ namespace cryptonote
STAKE = 6,
RETURN = 7,
AUDIT = 8,
MAX = 8
CREATE_TOKEN = 9,
ROLLUP = 10,
MAX = 10
};
}
+2 -1
View File
@@ -149,7 +149,8 @@ void print_genesis_tx_hex(const cryptonote::network_type nettype) {
//Prepare genesis_tx
cryptonote::transaction tx_genesis;
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
crypto::public_key miner_reward_tx_key = crypto::null_pkey;
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, miner_reward_tx_key, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
std::cout << "Object:" << std::endl;
std::cout << obj_to_json_str(tx_genesis) << std::endl << std::endl;
+1 -1
View File
@@ -1355,7 +1355,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
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, is_carrot);
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address);
req.threads_count = num_threads;
req.do_background_mining = do_background_mining;
req.ignore_battery = ignore_battery;
+2 -4
View File
@@ -29,11 +29,10 @@
set(device_sources
device.cpp
device_default.cpp
device_io_dummy.cpp
log.cpp
)
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
if(HIDAPI_FOUND)
set(device_sources
${device_sources}
device_ledger.cpp
@@ -46,11 +45,10 @@ set(device_headers
device_io.hpp
device_default.hpp
device_cold.hpp
device_io_dummy.hpp
log.hpp
)
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
if(HIDAPI_FOUND)
set(device_headers
${device_headers}
device_ledger.hpp
+4 -6
View File
@@ -29,7 +29,7 @@
#include "device.hpp"
#include "device_default.hpp"
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
#ifdef WITH_DEVICE_LEDGER
#include "device_ledger.hpp"
#endif
#include "misc_log_ex.h"
@@ -57,7 +57,7 @@ namespace hw {
device_registry::device_registry(){
hw::core::register_all(registry);
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
#ifdef WITH_DEVICE_LEDGER
hw::ledger::register_all(registry);
#endif
atexit(clear_device_registry);
@@ -83,13 +83,11 @@ namespace hw {
auto device = registry.find(device_descriptor_lookup);
if (device == registry.end()) {
std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: ");
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n");
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: ");
for( const auto& sm_pair : registry ) {
ss << "\n- " + sm_pair.first;
MERROR(" - " << sm_pair.first);
}
throw std::runtime_error("device not found: " + device_descriptor + "\n" + ss.str());
throw std::runtime_error("device not found: " + device_descriptor);
}
return *device->second;
}
+15 -1
View File
@@ -34,7 +34,17 @@
#include "ringct/rctTypes.h"
#include "cryptonote_config.h"
#if defined(HAVE_HIDAPI) || defined(HIDAPI_DUMMY)
#ifndef USE_DEVICE_LEDGER
#define USE_DEVICE_LEDGER 1
#endif
#if !defined(HAVE_HIDAPI)
#undef USE_DEVICE_LEDGER
#define USE_DEVICE_LEDGER 0
#endif
#if USE_DEVICE_LEDGER
#define WITH_DEVICE_LEDGER
#endif
@@ -192,6 +202,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) = 0;
virtual void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) = 0;
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0;
+6
View File
@@ -282,6 +282,12 @@ namespace hw {
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
void device_default::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) {
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_default::open_tx(crypto::secret_key &tx_key) {
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
tx_key = txkey.sec;
+4
View File
@@ -112,6 +112,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
-161
View File
@@ -1,161 +0,0 @@
// Copyright (c) 2017-2022, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// device_io_dummy
// Main goal of device_io_dummy is to emulate a hw::io::device_io without the need to actually
// connect a device.
// Many operating systems do not support giving raw USB access to a process (android), or don't
// support that at all (hi iOS), therefore other means of connection can be used, either USB
// abstraction provided by the OS (monerujo), or BLE (also monerujo).
// Monerujo implementation is written in Java, which makes it a nice fit for iOS, but makes the
// code extremely unportable, so for this reason the code in here is written in CPP.
// Data transport is made available in wallet2_api.h, so wallet developers can easily plug their
// own USB/BLE/other transport layer.
#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)
#include <boost/scope_exit.hpp>
#include "log.hpp"
#include "device_io_dummy.hpp"
#include "device_ledger.hpp"
bool hw::io::device_io_dummy::stateIsConnected = false;
unsigned char* hw::io::device_io_dummy::sendToDevice = {};
size_t hw::io::device_io_dummy::sendToDeviceLength = 0;
unsigned char* hw::io::device_io_dummy::receivedFromDevice = {};
size_t hw::io::device_io_dummy::receivedFromDeviceLength = 0;
bool hw::io::device_io_dummy::waitsForDeviceSend = false;
bool hw::io::device_io_dummy::waitsForDeviceReceive = false;
void (*hw::io::device_io_dummy::sendToLedgerDeviceCallback)(unsigned char *command, unsigned int cmd_len) = nullptr;
std::mutex hw::io::device_io_dummy::mutex;
std::condition_variable hw::io::device_io_dummy::cv_send;
std::condition_variable hw::io::device_io_dummy::cv_receive;
namespace hw {
namespace io {
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "device.io_dummy"
device_io_dummy::device_io_dummy(int a, int b, int c, int d) {
MDEBUG("device_io_dummy(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<")");
}
void device_io_dummy::init() {
MDEBUG("init()");
}
void device_io_dummy::connect(void *params) {
MDEBUG("connect(" << params << ")");
stateIsConnected = true;
}
void device_io_dummy::connect(const std::vector<hw::io::hid_conn_params>& known_devices) {
MDEBUG("connect([");
for (const auto &item: known_devices) {
MDEBUG("{ interface_number: " << item.interface_number);
MDEBUG(" pid : " << item.pid);
MDEBUG(" usage_page : " << item.usage_page);
MDEBUG(" vid : " << item.vid << " },");
}
MDEBUG("])");
stateIsConnected = true;
}
bool device_io_dummy::connected() const {
MDEBUG("connected()");
return stateIsConnected;
}
int device_io_dummy::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) {
MDEBUG("exchange(): locking mutex");
std::unique_lock<std::mutex> lock(mutex);
sendToDevice = command;
sendToDeviceLength = cmd_len;
waitsForDeviceSend = true;
waitsForDeviceReceive = true;
// Call the callback if it's set
if (sendToLedgerDeviceCallback != nullptr) {
MDEBUG("exchange(): calling sendToLedgerDeviceCallback");
sendToLedgerDeviceCallback(command, cmd_len);
}
MDEBUG("exchange(): waitsForDeviceSend");
// Wait for the send flag to be cleared by external code
while (waitsForDeviceSend) {
cv_send.wait(lock);
MDEBUG("exchange(): waitsForDeviceSend notified");
}
MDEBUG("exchange(): waitsForDeviceReceive");
// Wait for the receive flag to be cleared by external code
while (waitsForDeviceReceive) {
cv_receive.wait(lock);
MDEBUG("exchange(): waitsForDeviceReceive notified");
}
if (receivedFromDeviceLength > max_resp_len) {
MDEBUG("exchange(): receivedFromDeviceLength ("<<receivedFromDeviceLength<<") is larger than max_resp_len ("<<max_resp_len<<")");
return 1;
}
memset(response, 0, max_resp_len);
memcpy(response, receivedFromDevice, receivedFromDeviceLength);
return receivedFromDeviceLength;
}
void device_io_dummy::disconnect() {
MDEBUG("disconnect()");
}
void device_io_dummy::release() {
MDEBUG("release()");
}
void device_io_dummy::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)) {
MDEBUG("setLedgerCallback()");
sendToLedgerDeviceCallback = sendToLedgerDevice;
}
void device_io_dummy::setDeviceReceivedData(unsigned char* data, size_t len) {
MDEBUG("setDeviceReceivedData(len: " << len << ")");
std::unique_lock<std::mutex> lock(mutex);
receivedFromDevice = static_cast<unsigned char *>(malloc(len));
receivedFromDeviceLength = len;
memset(receivedFromDevice, 0, len);
memcpy(receivedFromDevice, data, len);
waitsForDeviceReceive = false;
waitsForDeviceSend = false;
cv_send.notify_all();
cv_receive.notify_all();
}
}
}
#endif // HAVE_HIDAPI
-82
View File
@@ -1,82 +0,0 @@
// Copyright (c) 2017-2022, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifdef HIDAPI_DUMMY
#pragma once
#include "device_io.hpp"
#include "device_io_hid.hpp"
#include <mutex>
#include <condition_variable>
namespace hw {
namespace io {
struct hid_conn_params {
unsigned int vid;
unsigned int pid;
int interface_number;
unsigned short usage_page;
};
class device_io_dummy : device_io {
private:
static std::mutex mutex;
public:
static std::condition_variable cv_send;
static std::condition_variable cv_receive;
static bool stateIsConnected;
static unsigned char* sendToDevice;
static size_t sendToDeviceLength;
static unsigned char* receivedFromDevice;
static size_t receivedFromDeviceLength;
static bool waitsForDeviceSend;
static bool waitsForDeviceReceive;
static void (*sendToLedgerDeviceCallback)(unsigned char *command, unsigned int cmd_len);
device_io_dummy() = default;
device_io_dummy(int a, int b, int c, int d);
~device_io_dummy() = default;
void init();
void release();
void connect(void *parms);
void connect(const std::vector<hw::io::hid_conn_params>& known_devices);
void disconnect();
bool connected() const;
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input);
static void setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len));
static void setDeviceReceivedData(unsigned char* data, size_t len);
};
};
};
#endif // HAVE_HIDAPI
+10 -4
View File
@@ -38,7 +38,7 @@ namespace hw {
namespace ledger {
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
#ifdef WITH_DEVICE_LEDGER
namespace {
bool apdu_verbose =true;
@@ -300,7 +300,7 @@ namespace hw {
device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) {
this->id = device_id++;
this->reset_buffer();
this->reset_buffer();
this->mode = NONE;
this->has_view_key = false;
this->tx_in_progress = false;
@@ -533,9 +533,7 @@ namespace hw {
bool device_ledger::connect(void) {
this->disconnect();
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
hw_device.connect(known_devices);
#endif
this->reset();
#ifdef DEBUG_HWDEVICE
cryptonote::account_public_address pubkey;
@@ -1435,6 +1433,14 @@ namespace hw {
#endif
}
void device_ledger::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) {
// to-do: For now, carrot tx proofs are not supported
AUTO_LOCK_CMD();
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
AUTO_LOCK_CMD();
this->lock();
+5 -6
View File
@@ -35,7 +35,6 @@
#include "device.hpp"
#include "log.hpp"
#include "device_io_hid.hpp"
#include "device_io_dummy.hpp"
#include <mutex>
namespace hw {
@@ -56,7 +55,7 @@ namespace hw {
void register_all(std::map<std::string, std::unique_ptr<device>> &registry);
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
#ifdef WITH_DEVICE_LEDGER
// Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h
#define SW_OK 0x9000
@@ -144,11 +143,7 @@ namespace hw {
mutable std::mutex command_locker;
//IO
#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)
hw::io::device_io_dummy hw_device;
#else
hw::io::device_io_hid hw_device;
#endif
unsigned int length_send;
unsigned char buffer_send[BUFFER_SEND_SIZE];
unsigned int length_recv;
@@ -258,6 +253,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
+6
View File
@@ -61,6 +61,9 @@ const hardfork_t mainnet_hard_forks[] = {
// version 10 Carrot - including treasury mint - starts from block 334750, which is on or around the 13th of October, 2025. Fork time finalised on 2025-09-29. No fork voting occurs for the v10 fork.
{10, 334750, 0, 1759142500 },
// version 11 Two Milestone 1 - starts from block 465000, which is on or around the 13th of April, 2026. Fork time finalised on 2026-03-20. No fork voting occurs for the v11 fork.
{11, 465000, 0, 1774000000 },
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
@@ -95,6 +98,9 @@ const hardfork_t testnet_hard_forks[] = {
// version 10 Carrot - including treasury mint - starts from block 1100
{10, 1100, 0, 1739780005 },
// version 11
{11, 1200, 0, 1739880005 },
};
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);
+120
View File
@@ -26,6 +26,8 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <cstdint> // For uint8_t, etc.
#include <cstring> // For memcpy
#include <string>
#include <vector>
@@ -74,4 +76,122 @@ namespace oracle {
}
}
};
class asset_type_counts_v2
{
public:
// Fields
std::map<uint32_t, uint64_t> asset_counts;
asset_type_counts_v2() noexcept
: asset_counts{}
{
}
uint64_t operator[](const uint32_t asset_type_id) const noexcept
{
if (asset_counts.count(asset_type_id) == 0)
return 0;
else
return asset_counts.at(asset_type_id);
}
bool add(const uint32_t asset_type_id, const uint64_t amount)
{
if (asset_counts.count(asset_type_id) == 0)
return false;
asset_counts[asset_type_id] += amount;
return true;
}
bool add_asset_type(const uint32_t asset_type_id)
{
if (asset_counts.count(asset_type_id) != 0)
return false;
asset_counts[asset_type_id] = 0;
return true;
}
std::map<uint32_t, uint64_t> get()
{
return asset_counts;
}
std::vector<uint32_t> get_asset_types() const noexcept
{
std::vector<uint32_t> asset_type_ids;
for (const auto& entry: asset_counts) {
asset_type_ids.push_back(entry.first);
}
return asset_type_ids;
}
bool has_asset_type(const uint32_t asset_type_id) const noexcept
{
return asset_counts.count(asset_type_id) == 1;
}
std::vector<uint8_t> serialize() const noexcept {
constexpr uint8_t version = 1; // Bump if format changes
uint32_t sz = static_cast<uint32_t>(asset_counts.size());
size_t total_size = sizeof(uint8_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
std::vector<uint8_t> buf(total_size);
uint8_t* ptr = buf.data();
std::memcpy(ptr, &version, sizeof(uint8_t));
ptr += sizeof(uint8_t);
std::memcpy(ptr, &sz, sizeof(uint32_t));
ptr += sizeof(uint32_t);
for (const auto& p : asset_counts) { // Iterates in sorted order
std::memcpy(ptr, &p.first, sizeof(uint32_t));
ptr += sizeof(uint32_t);
std::memcpy(ptr, &p.second, sizeof(uint64_t));
ptr += sizeof(uint64_t);
}
return buf;
}
void deserialize(const uint8_t* data, size_t len) {
if (len < sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
uint8_t version;
std::memcpy(&version, data, sizeof(uint8_t));
data += sizeof(uint8_t);
len -= sizeof(uint8_t);
if (version == 1) {
if (len < sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
} else {
throw std::runtime_error("Unsupported version");
}
uint32_t sz;
std::memcpy(&sz, data, sizeof(uint32_t));
data += sizeof(uint32_t);
len -= sizeof(uint32_t);
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
throw std::runtime_error("Invalid serialized data length");
}
asset_counts.clear();
for (uint32_t i = 0; i < sz; ++i) {
uint32_t k;
uint64_t v;
std::memcpy(&k, data, sizeof(uint32_t));
data += sizeof(uint32_t);
std::memcpy(&v, data, sizeof(uint64_t));
data += sizeof(uint64_t);
asset_counts[k] = v; // Map handles sorting/uniquness
}
}
};
}
+1 -19
View File
@@ -881,12 +881,6 @@ namespace nodetool
if (m_nettype == cryptonote::MAINNET)
{
return {
"xwvz3ekocr3dkyxfkmgm2hvbpzx2ysqmaxgter7znnqrhoicygkfswid.onion:18083",
"4pixvbejrvihnkxmduo2agsnmc3rrulrqc7s3cbwwrep6h6hrzsibeqd.onion:18083",
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
};
}
return {};
@@ -894,10 +888,6 @@ namespace nodetool
if (m_nettype == cryptonote::MAINNET)
{
return {
"s3l6ke4ed3df466khuebb4poienoingwof7oxtbo6j4n56sghe3a.b32.i2p:18080",
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080",
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p:18080",
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p:18080",
};
}
return {};
@@ -2054,20 +2044,13 @@ namespace nodetool
return true;
static const std::vector<std::string> dns_urls = {
"blocklist.moneropulse.se"
, "blocklist.moneropulse.org"
, "blocklist.moneropulse.net"
, "blocklist.moneropulse.no"
, "blocklist.moneropulse.fr"
, "blocklist.moneropulse.de"
, "blocklist.moneropulse.ch"
};
std::vector<std::string> records;
if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
return true;
unsigned good = 0, bad = 0;
unsigned good = 0;
for (const auto& record : records)
{
std::vector<std::string> ips;
@@ -2091,7 +2074,6 @@ namespace nodetool
continue;
}
MWARNING("Invalid IP address or subnet from DNS blocklist: " << ip << " - " << parsed_addr.error());
++bad;
}
}
if (good > 0)
-25
View File
@@ -1,25 +0,0 @@
set(polyseed_sources
pbkdf2.c
polyseed.cpp
)
monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}")
monero_private_headers(polyseed_wrapper
${polyseed_private_headers}
)
monero_add_library(polyseed_wrapper
${polyseed_sources}
${polyseed_headers}
${polyseed_private_headers}
)
target_link_libraries(polyseed_wrapper
PUBLIC
polyseed
utf8proc
${SODIUM_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES}
)
-85
View File
@@ -1,85 +0,0 @@
// Copyright (c) 2023, The Monero Project
// Copyright (c) 2021, tevador <tevador@gmail.com>
// Copyright (c) 2005,2007,2009 Colin Percival
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
#include <string.h>
#include <sodium/crypto_auth_hmacsha256.h>
#include <sodium/utils.h>
static inline void
store32_be(uint8_t dst[4], uint32_t w)
{
dst[3] = (uint8_t) w; w >>= 8;
dst[2] = (uint8_t) w; w >>= 8;
dst[1] = (uint8_t) w; w >>= 8;
dst[0] = (uint8_t) w;
}
void
crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
const uint8_t* salt, size_t saltlen, uint64_t c,
uint8_t* buf, size_t dkLen)
{
crypto_auth_hmacsha256_state Phctx, PShctx, hctx;
size_t i;
uint8_t ivec[4];
uint8_t U[32];
uint8_t T[32];
uint64_t j;
int k;
size_t clen;
crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen);
PShctx = Phctx;
crypto_auth_hmacsha256_update(&PShctx, salt, saltlen);
for (i = 0; i * 32 < dkLen; i++) {
store32_be(ivec, (uint32_t)(i + 1));
hctx = PShctx;
crypto_auth_hmacsha256_update(&hctx, ivec, 4);
crypto_auth_hmacsha256_final(&hctx, U);
memcpy(T, U, 32);
for (j = 2; j <= c; j++) {
hctx = Phctx;
crypto_auth_hmacsha256_update(&hctx, U, 32);
crypto_auth_hmacsha256_final(&hctx, U);
for (k = 0; k < 32; k++) {
T[k] ^= U[k];
}
}
clen = dkLen - i * 32;
if (clen > 32) {
clen = 32;
}
memcpy(&buf[i * 32], T, clen);
}
sodium_memzero((void*)&Phctx, sizeof Phctx);
sodium_memzero((void*)&PShctx, sizeof PShctx);
}

Some files were not shown because too many files have changed in this diff Show More