Compare commits
125 Commits
salvium_one
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1998d79c3e | |||
| ea4d5a9132 | |||
| 585f7b9d05 | |||
| b7c707bd4e | |||
| b57d2c338c | |||
| 28069fc84b | |||
| d7ec62cdbe | |||
| 244fc7acaa | |||
| 3716330ac7 | |||
| c4e7f6e0f0 | |||
| c0862ea66e | |||
| 67b41240d2 | |||
| 16110603a1 | |||
| 83934efaad | |||
| 6fb167308c | |||
| d6f95156bc | |||
| 35804afc90 | |||
| bc59547714 | |||
| f2b6068d8b | |||
| c820967cba | |||
| eadaa21e83 | |||
| 465b384195 | |||
| 768ca26a8d | |||
| 6fb131a52b | |||
| 3f18c388f6 | |||
| 2f646cdc10 | |||
| a5523be459 | |||
| 81d322b2ce | |||
| 3b1a939417 | |||
| a4e7ebc591 | |||
| 25dccd5423 | |||
| d05ac7f44e | |||
| 35b1a03b3d | |||
| 7acf8068ea | |||
| 383f3d36e6 | |||
| 7d06436d08 | |||
| 3bee380b18 | |||
| 58c70115f2 | |||
| 6be4081332 | |||
| 538e4a5d1f | |||
| 3e49572539 | |||
| 05c7152ad5 | |||
| 8d31fa2842 | |||
| 2abe39f178 | |||
| a6f47a9f92 | |||
| ac13287c78 | |||
| 305b92909e | |||
| 248667016a | |||
| 10b58aac73 | |||
| 0221fe8a34 | |||
| 1ff480e64d | |||
| fd121aae19 | |||
| 0deb19c53c | |||
| 7f3e389d92 | |||
| 87da2d4661 | |||
| f6075ae9ec | |||
| c424e84f4b | |||
| 3b4efe9636 | |||
| dfa27e78c6 | |||
| 9b57fe3eae | |||
| 8f60758a3c | |||
| 3a7ec4db32 | |||
| 679bc9f0d7 | |||
| 362eb38ff8 | |||
| 6243e992cf | |||
| e872414d57 | |||
| fcaf640bcb | |||
| f5237ceaf5 | |||
| 1503ec6629 | |||
| 0f744520ad | |||
| caf52cca20 | |||
| 1c4309c400 | |||
| 9725b921a5 | |||
| 38d2515dc5 | |||
| 13efd79f88 | |||
| 0c273d3571 | |||
| d22389b37a | |||
| f9b060e552 | |||
| 7d13f90e4a | |||
| f7a75a4fdc | |||
| b9fa97daad | |||
| 6f0f5b5e83 | |||
| 6fbd3184b1 | |||
| d5fee31ec6 | |||
| 2a93e04180 | |||
| b902ec9406 | |||
| e3ba570fb1 | |||
| fac65e5093 | |||
| eebc1f1d26 | |||
| 502ece5ba3 | |||
| 62967db201 | |||
| 4b7f863c71 | |||
| 55a6f17c91 | |||
| 8a32d7f73b | |||
| dfb6a705ea | |||
| 5246138398 | |||
| cc3e6a0822 | |||
| 8e68f58eff | |||
| 0a79a4d9fd | |||
| 48fb95bdc1 | |||
| bb4d3768b2 | |||
| cea3f0f341 | |||
| 35fc88c0ec | |||
| 312413aeb0 | |||
| 6ba060e116 | |||
| 88d5e1f50e | |||
| 644a8d3b4d | |||
| e80d135c15 | |||
| a40026f941 | |||
| fb25a36bf2 | |||
| 6f3f7c8e9a | |||
| c6c35d5639 | |||
| 63433cc58f | |||
| 940c0e03f7 | |||
| 445e498fbf | |||
| 09ad75f0cc | |||
| f8d9f9335e | |||
| cd31dafa97 | |||
| 6aa32701b8 | |||
| f84a622bfa | |||
| 4bfb5f51bf | |||
| 260bc3721b | |||
| 3e0457de09 | |||
| 24f9916287 | |||
| cd22e55296 |
@@ -22,7 +22,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build-cross:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
strategy:
|
||||
@@ -52,10 +52,10 @@ jobs:
|
||||
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
- name: "Cross-Mac x86_64"
|
||||
host: "x86_64-apple-darwin"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
|
||||
- name: "Cross-Mac aarch64"
|
||||
host: "aarch64-apple-darwin"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
|
||||
- name: "x86_64 Freebsd"
|
||||
host: "x86_64-unknown-freebsd"
|
||||
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
${{env.CCACHE_SETTINGS}}
|
||||
make depends target=${{ matrix.toolchain.host }} -j2
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
|
||||
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' || matrix.toolchain.host == 'aarch64-linux-gnu' }}
|
||||
with:
|
||||
name: ${{ matrix.toolchain.name }}
|
||||
path: |
|
||||
|
||||
+1
-12
@@ -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
|
||||
|
||||
+15
-20
@@ -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
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "memwipe.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -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)};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
-3
@@ -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)
|
||||
|
||||
Vendored
-1
Submodule external/bc-ur deleted from d82e7c753e
Vendored
-1
Submodule external/polyseed deleted from bd79f5014c
Vendored
+1
-1
Submodule external/randomx updated: ce72c9bb9c...102f8acf90
Vendored
-1
Submodule external/utf8proc deleted from 3de4596fbe
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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.
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -122,6 +122,11 @@ bool operator==(const view_tag_t &a, const view_tag_t &b)
|
||||
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(rollup_binding_tag_t)) == 0;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
janus_anchor_t gen_janus_anchor()
|
||||
{
|
||||
return crypto::rand<janus_anchor_t>();
|
||||
|
||||
@@ -106,6 +106,13 @@ struct encrypted_return_pubkey_t final
|
||||
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_BYTES];
|
||||
};
|
||||
|
||||
/// Salvium rollup binding tag
|
||||
constexpr std::size_t ROLLUP_BINDING_TAG_BYTES{8};
|
||||
struct rollup_binding_tag_t final
|
||||
{
|
||||
unsigned char bytes[ROLLUP_BINDING_TAG_BYTES];
|
||||
};
|
||||
|
||||
/// overloaded operators: address tag
|
||||
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b);
|
||||
static inline bool operator!=(const janus_anchor_t &a, const janus_anchor_t &b) { return !(a == b); }
|
||||
@@ -139,6 +146,10 @@ bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubke
|
||||
static inline bool operator!=(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b) { return !(a == b); }
|
||||
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
|
||||
|
||||
/// overloaded operators: rollup binding tag
|
||||
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b);
|
||||
static inline bool operator!=(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b) { return !(a == b); }
|
||||
|
||||
/// generate a random janus anchor
|
||||
janus_anchor_t gen_janus_anchor();
|
||||
/// generate a random (non-null) payment ID
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
|
||||
|
||||
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
|
||||
class carrot_logic_error: public std::logic_error { using std::logic_error::logic_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
|
||||
@@ -55,7 +55,7 @@ CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error)
|
||||
|
||||
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
class carrot_runtime_error: public std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
|
||||
|
||||
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
|
||||
}
|
||||
else if (!need_change_output)
|
||||
{
|
||||
return AdditionalOutputType::DUMMY;
|
||||
return AdditionalOutputType::CHANGE_UNIQUE;
|
||||
}
|
||||
else // num_selfsend == 1 && need_change_output
|
||||
{
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 -
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define DEBUG_BREAK() asm volatile("int $3")
|
||||
#elif defined(__aarch64__)
|
||||
#define DEBUG_BREAK() asm volatile("brk #0xf000")
|
||||
#elif defined(__arm__)
|
||||
#define DEBUG_BREAK() asm volatile(".inst 0xe7f001f0") // Encoding for bkpt #0xf01, common for ARM32
|
||||
#else
|
||||
#error "Unsupported architecture for DEBUG_BREAK"
|
||||
#endif
|
||||
@@ -71,6 +71,7 @@ monero_add_library(cncrypto
|
||||
target_link_libraries(cncrypto
|
||||
PUBLIC
|
||||
epee
|
||||
mx25519_static
|
||||
randomx
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${SODIUM_LIBRARY}
|
||||
|
||||
@@ -887,4 +887,7 @@ const ge_p3 ge_p3_H = {
|
||||
{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},
|
||||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
|
||||
};
|
||||
};
|
||||
|
||||
// Precomputed sqrt(-486664) mod p as 32-byte little-endian array
|
||||
const fe fe_sqrt_m486664 = {12222970, 8312128, 11511410, -9067497, 15300785, 241793, -25456130, -14121551, 12187136, -3972024};
|
||||
|
||||
+125
-2
@@ -104,6 +104,21 @@ void fe_1(fe h) {
|
||||
h[9] = 0;
|
||||
}
|
||||
|
||||
int fe_equal(const fe a, const fe b)
|
||||
{
|
||||
fe t;
|
||||
fe_sub(t, a, b);
|
||||
return fe_isnonzero(t) == 0;
|
||||
}
|
||||
|
||||
void ge_from_xy(ge_p3 *out, const fe x, const fe y)
|
||||
{
|
||||
fe_1(out->Z); // Z = 1
|
||||
fe_copy(out->X, x); // X = x
|
||||
fe_copy(out->Y, y); // Y = y
|
||||
fe_mul(out->T, x, y); // T = x*y
|
||||
}
|
||||
|
||||
/* From fe_add.c */
|
||||
|
||||
/*
|
||||
@@ -365,7 +380,7 @@ int fe_isnegative(const fe f) {
|
||||
|
||||
/* From fe_isnonzero.c, modified */
|
||||
|
||||
static int fe_isnonzero(const fe f) {
|
||||
int fe_isnonzero(const fe f) {
|
||||
unsigned char s[32];
|
||||
fe_tobytes(s, f);
|
||||
return (((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] |
|
||||
@@ -3967,6 +3982,114 @@ int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Precomputed sqrt(-1) from Monero (fe_sqrtm1 in crypto-ops-data.c)
|
||||
extern const fe fe_sqrtm1;
|
||||
extern const fe fe_sqrt_m486664;
|
||||
|
||||
// Function to recover v from u (returns 0 on success, -1 if not on curve)
|
||||
int fe_sqrt_mont(fe v_out, const fe u_in) {
|
||||
fe rhs;
|
||||
fe t0, t1, t2;
|
||||
fe candidate, c2, check;
|
||||
|
||||
// Compute rhs = u^3 + A u^2 + u, A=486662
|
||||
fe A_fe;
|
||||
fe_frombytes_vartime(A_fe, (const unsigned char[]){0x06, 0x6D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); // Correct little-endian 486662
|
||||
|
||||
fe_sq(t0, u_in); // t0 = u^2
|
||||
fe_mul(t1, t0, u_in); // t1 = u^3
|
||||
fe_mul(t2, t0, A_fe); // t2 = A u^2
|
||||
fe_add(rhs, t1, t2); // u^3 + A u^2
|
||||
fe_add(rhs, rhs, u_in); // + u
|
||||
|
||||
// The exponentiation chain for (p+3)/8
|
||||
fe_1(t1);
|
||||
fe_sq(t0, t1);
|
||||
fe_mul(t0, t0, t1);
|
||||
fe_sq(candidate, t0);
|
||||
fe_mul(candidate, candidate, t1);
|
||||
fe_mul(candidate, candidate, rhs);
|
||||
fe_pow22523(candidate, candidate);
|
||||
fe_mul(candidate, candidate, t0);
|
||||
fe_mul(candidate, candidate, rhs);
|
||||
|
||||
// Check c^2 == rhs or -rhs
|
||||
fe_sq(c2, candidate);
|
||||
fe_sub(check, c2, rhs);
|
||||
if (fe_isnonzero(check)) {
|
||||
fe_add(check, c2, rhs);
|
||||
if (fe_isnonzero(check)) {
|
||||
return -1; // Not a quadratic residue
|
||||
}
|
||||
fe_mul(candidate, candidate, fe_sqrtm1); // Adjust with sqrt(-1)
|
||||
}
|
||||
|
||||
// Output v (principal root; flip to -v if needed for your map)
|
||||
fe_copy(v_out, candidate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v) {
|
||||
fe inv_v, temp;
|
||||
fe t1, t2, inv_t2;
|
||||
fe one;
|
||||
fe_1(one);
|
||||
|
||||
fe_invert(inv_v, v); // 1/v
|
||||
fe_mul(temp, u, inv_v); // u/v
|
||||
fe_mul(x_out, fe_sqrt_m486664, temp); // sqrt(-486664) * (u/v)
|
||||
|
||||
fe_sub(t1, u, one); // u - 1
|
||||
fe_add(t2, u, one); // u + 1
|
||||
fe_invert(inv_t2, t2);
|
||||
fe_mul(y_out, t1, inv_t2); // (u-1)/(u+1)
|
||||
}
|
||||
|
||||
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y) {
|
||||
fe t1, t2, inv_t2;
|
||||
fe one;
|
||||
fe_1(one);
|
||||
|
||||
fe_add(t1, one, y); // 1 + y
|
||||
fe_sub(t2, one, y); // 1 - y
|
||||
fe_invert(inv_t2, t2);
|
||||
fe_mul(u_out, t1, inv_t2); // (1+y)/(1-y)
|
||||
|
||||
fe inv_x;
|
||||
fe_invert(inv_x, x); // 1/x
|
||||
fe_mul(t1, u_out, inv_x); // u / x
|
||||
fe_mul(v_out, fe_sqrt_m486664, t1); // sqrt(-486664) * (u/x)
|
||||
}
|
||||
|
||||
// Usage: Add two Montgomery points
|
||||
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2) {
|
||||
ge_p3 P_ed, Q_ed, sum_ed;
|
||||
|
||||
// Convert to Edwards
|
||||
fe x1, y1, x2, y2;
|
||||
mont_to_ed(x1, y1, u1, v1);
|
||||
mont_to_ed(x2, y2, u2, v2);
|
||||
|
||||
// Load into ge_p3 (assume Z=1, T=x*y for affine)
|
||||
fe_1(P_ed.Z); fe_mul(P_ed.T, x1, y1); fe_copy(P_ed.X, x1); fe_copy(P_ed.Y, y1);
|
||||
fe_1(Q_ed.Z); fe_mul(Q_ed.T, x2, y2); fe_copy(Q_ed.X, x2); fe_copy(Q_ed.Y, y2);
|
||||
|
||||
// Add using ge_
|
||||
ge_cached Q_cached;
|
||||
ge_p3_to_cached(&Q_cached, &Q_ed);
|
||||
ge_p1p1 sum_p1p1;
|
||||
ge_add(&sum_p1p1, &P_ed, &Q_cached);
|
||||
ge_p1p1_to_p3(&sum_ed, &sum_p1p1);
|
||||
|
||||
// Convert back (normalize to affine: divide by Z)
|
||||
fe inv_z;
|
||||
fe_invert(inv_z, sum_ed.Z);
|
||||
fe x_out, y_out;
|
||||
fe_mul(x_out, sum_ed.X, inv_z);
|
||||
fe_mul(y_out, sum_ed.Y, inv_z);
|
||||
ed_to_mont(u3, v3, x_out, y_out);
|
||||
}
|
||||
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
|
||||
// https://eprint.iacr.org/2008/522
|
||||
// X == T == 0 and Y/Z == 1
|
||||
@@ -4063,4 +4186,4 @@ void fe_dbl(fe h, const fe f)
|
||||
// Reduce the output for safety to ensure the result can be used as input to
|
||||
// fe_add or fe_sub without an extra call to fe_reduce
|
||||
fe_reduce(h, h_res);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -176,6 +176,11 @@ int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
|
||||
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
|
||||
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
|
||||
|
||||
int fe_sqrt_mont(fe v_out, const fe u_in);
|
||||
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v);
|
||||
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y);
|
||||
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2);
|
||||
|
||||
// internal
|
||||
uint64_t load_3(const unsigned char *in);
|
||||
uint64_t load_4(const unsigned char *in);
|
||||
@@ -192,10 +197,14 @@ void fe_sq(fe h, const fe f);
|
||||
void fe_sub(fe h, const fe f, const fe g);
|
||||
void fe_0(fe h);
|
||||
void fe_1(fe h);
|
||||
int fe_equal(const fe a, const fe b);
|
||||
void ge_from_xy(ge_p3 *out, const fe x, const fe y);
|
||||
|
||||
int fe_isnonzero(const fe f);
|
||||
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
|
||||
|
||||
void fe_ed_y_derivatives_to_wei_x(unsigned char *wei_x, const fe inv_one_minus_y, const fe one_plus_y);
|
||||
|
||||
void fe_reduce(fe reduced_f, const fe f);
|
||||
void fe_dbl(fe h, const fe f);
|
||||
void fe_dbl(fe h, const fe f);
|
||||
|
||||
+558
-1
@@ -41,6 +41,7 @@
|
||||
#include "common/varint.h"
|
||||
#include "warnings.h"
|
||||
#include "crypto.h"
|
||||
#include "mx25519.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
@@ -90,6 +91,16 @@ namespace crypto {
|
||||
return &reinterpret_cast<const unsigned char &>(scalar);
|
||||
}
|
||||
|
||||
static const mx25519_impl* get_mx25519_impl()
|
||||
{
|
||||
static std::once_flag of;
|
||||
static const mx25519_impl *impl;
|
||||
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
|
||||
if (impl == nullptr)
|
||||
throw std::runtime_error("failed to obtain a mx25519 implementation");
|
||||
return impl;
|
||||
}
|
||||
|
||||
boost::mutex &get_random_lock()
|
||||
{
|
||||
static boost::mutex random_lock;
|
||||
@@ -504,6 +515,391 @@ namespace crypto {
|
||||
memwipe(&k, sizeof(k));
|
||||
}
|
||||
|
||||
void crypto_ops::generate_carrot_tx_proof(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u-coordinate
|
||||
const public_key &A, // Ed25519
|
||||
const boost::optional<public_key> &B, // Ed if present
|
||||
const public_key &D, // X25519 u-coordinate
|
||||
const secret_key &r,
|
||||
const secret_key &a,
|
||||
signature &sig)
|
||||
{
|
||||
// Check if we are sender or receiver
|
||||
if (r != crypto::null_skey) {
|
||||
// SENDER
|
||||
generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
|
||||
return;
|
||||
}
|
||||
|
||||
// RECEIVER
|
||||
|
||||
// Load points (A and B and R) into ge_p3
|
||||
ge_p3 A_p3;
|
||||
ge_p3 B_p3;
|
||||
ge_p3 R_p3;
|
||||
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
throw std::runtime_error("recipient view pubkey is invalid");
|
||||
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
throw std::runtime_error("recipient spend pubkey is invalid");
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
{
|
||||
// Debug check D == a*R
|
||||
mx25519_pubkey D_x25519;
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&D_x25519,
|
||||
reinterpret_cast<const mx25519_privkey*>(&a),
|
||||
reinterpret_cast<const mx25519_pubkey*>(&R));
|
||||
public_key dbg_D;
|
||||
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
|
||||
assert(D == dbg_D);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// 1. Pick random nonce k
|
||||
//
|
||||
crypto::secret_key k;
|
||||
random_scalar(k);
|
||||
|
||||
static const public_key zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 u-coord
|
||||
buf.R = R; // X25519 u-coord
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
//
|
||||
// 2. Compute X = ConvertPointE(k*G or k*B)
|
||||
//
|
||||
ge_p3 kB_or_kG_p3;
|
||||
if (B)
|
||||
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&kB_or_kG_p3, &k);
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 3. Compute Y = k*R
|
||||
//
|
||||
mx25519_pubkey Y;
|
||||
mx25519_scmul_key(get_mx25519_impl(),
|
||||
&Y,
|
||||
reinterpret_cast<const mx25519_privkey*>(&k),
|
||||
reinterpret_cast<const mx25519_pubkey*>(&R));
|
||||
memcpy(&buf.Y, &Y, sizeof(mx25519_pubkey));
|
||||
|
||||
// ---------- Extract and lift R ----------
|
||||
fe u_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
|
||||
fe v_R_cand;
|
||||
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
|
||||
throw std::runtime_error("R not on curve");
|
||||
|
||||
fe x1, y1, x2, y2, v_R_neg;
|
||||
ge_p3 R_ed1, R_ed2;
|
||||
|
||||
// +v (principal)
|
||||
mont_to_ed(x1, y1, u_R, v_R_cand);
|
||||
ge_from_xy(&R_ed1, x1, y1);
|
||||
|
||||
// -v
|
||||
fe_neg(v_R_neg, v_R_cand);
|
||||
mont_to_ed(x2, y2, u_R, v_R_neg);
|
||||
ge_from_xy(&R_ed2, x2, y2);
|
||||
|
||||
// Arbitrarily choose R_sign = true (principal v from fe_sqrt_mont)
|
||||
bool R_sign = true;
|
||||
ge_p3 R_ed_correct = R_ed1; // +v
|
||||
|
||||
// ---------- Extract and lift D (consistent with chosen R_sign) ----------
|
||||
fe u_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
|
||||
fe v_D_cand;
|
||||
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
|
||||
throw std::runtime_error("D not on curve");
|
||||
|
||||
fe x3, y3, x4, y4, v_D_neg;
|
||||
|
||||
// Compute D_ed_true = a * R_ed_correct
|
||||
ge_p3 D_ed_true;
|
||||
ge_scalarmult_p3(&D_ed_true, &a, &R_ed_correct);
|
||||
|
||||
// Normalize to affine for matching
|
||||
fe inv_z;
|
||||
fe_invert(inv_z, D_ed_true.Z);
|
||||
fe xd_true, yd_true;
|
||||
fe_mul(xd_true, D_ed_true.X, inv_z);
|
||||
fe_mul(yd_true, D_ed_true.Y, inv_z);
|
||||
|
||||
// +v for D
|
||||
mont_to_ed(x3, y3, u_D, v_D_cand);
|
||||
bool D_match1 = fe_equal(x3, xd_true) && fe_equal(y3, yd_true); // Affine match (mont_to_ed gives affine x,y)
|
||||
|
||||
// -v for D
|
||||
fe_neg(v_D_neg, v_D_cand);
|
||||
mont_to_ed(x4, y4, u_D, v_D_neg);
|
||||
bool D_match2 = fe_equal(x4, xd_true) && fe_equal(y4, yd_true);
|
||||
|
||||
bool D_sign = false;
|
||||
if (D_match1)
|
||||
D_sign = true;
|
||||
else if (D_match2)
|
||||
D_sign = false;
|
||||
else
|
||||
throw std::runtime_error("D lift mismatch with computed D_ed_true");
|
||||
|
||||
// Pack signs (MSB is set to [1] for outbound, [0] for inbound
|
||||
sig.sign_mask =
|
||||
(R_sign ? 0x01 : 0x00) |
|
||||
(D_sign ? 0x02 : 0x00);
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
|
||||
//
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
|
||||
|
||||
//
|
||||
// 8. Compute response z = k - c*a
|
||||
//
|
||||
sc_mulsub(&sig.r, &sig.c, &unwrap(a), &k);
|
||||
|
||||
memwipe(&k, sizeof(k));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
assert(ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
void crypto_ops::generate_carrot_tx_proof_as_sender(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u-coordinate
|
||||
const public_key &A, // Ed25519
|
||||
const boost::optional<public_key> &B, // Ed if present
|
||||
const public_key &D, // X25519 u-coordinate
|
||||
const secret_key &r,
|
||||
const secret_key &a,
|
||||
signature &sig)
|
||||
{
|
||||
// Load only Ed points (A and B) into ge_p3
|
||||
ge_p3 A_p3;
|
||||
ge_p3 B_p3;
|
||||
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
throw std::runtime_error("recipient view pubkey is invalid");
|
||||
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
throw std::runtime_error("recipient spend pubkey is invalid");
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
{
|
||||
assert(sc_check(&r) == 0);
|
||||
|
||||
// Debug check R == ConvertPointE(r*G or r*B)
|
||||
public_key dbg_R;
|
||||
ge_p3 dbg_R_p3;
|
||||
|
||||
if (B)
|
||||
ge_scalarmult_p3(&dbg_R_p3, &r, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&dbg_R_p3, &r);
|
||||
|
||||
mx25519_pubkey R_x25519;
|
||||
ge_p3_to_x25519(R_x25519.data, &dbg_R_p3);
|
||||
|
||||
memcpy(&dbg_R, &R_x25519, sizeof(mx25519_pubkey));
|
||||
assert(R == dbg_R);
|
||||
|
||||
// Debug check D == ConvertPointE(r*A)
|
||||
public_key dbg_D;
|
||||
ge_p3 dbg_D_p3;
|
||||
ge_scalarmult_p3(&dbg_D_p3, &r, &A_p3);
|
||||
|
||||
mx25519_pubkey D_x25519;
|
||||
ge_p3_to_x25519(D_x25519.data, &dbg_D_p3);
|
||||
|
||||
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
|
||||
assert(D == dbg_D);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// 1. Pick random nonce k
|
||||
//
|
||||
ec_scalar k;
|
||||
random_scalar(k);
|
||||
|
||||
static const ec_point zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 u-coord
|
||||
buf.R = R; // X25519 u-coord
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
//
|
||||
// 2. Compute X = ConvertPointE(k*G or k*B)
|
||||
//
|
||||
ge_p3 kB_or_kG_p3;
|
||||
if (B)
|
||||
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&kB_or_kG_p3, &k);
|
||||
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 3. Compute Y = ConvertPointE(k*A)
|
||||
//
|
||||
ge_p3 kA_p3;
|
||||
ge_scalarmult_p3(&kA_p3, &k, &A_p3);
|
||||
|
||||
mx25519_pubkey Y_x25519;
|
||||
ge_p3_to_x25519(Y_x25519.data, &kA_p3);
|
||||
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
//
|
||||
// 4. Compute true Ed points R_ed_true and D_ed_true
|
||||
//
|
||||
ge_p3 R_ed_true, D_ed_true;
|
||||
|
||||
if (B)
|
||||
ge_scalarmult_p3(&R_ed_true, &r, &B_p3);
|
||||
else
|
||||
ge_scalarmult_base(&R_ed_true, &r);
|
||||
|
||||
ge_scalarmult_p3(&D_ed_true, &r, &A_p3);
|
||||
|
||||
//
|
||||
// 5. Determine sign bits for R and D
|
||||
//
|
||||
|
||||
// ---------- Extract and lift R ----------
|
||||
fe u_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
|
||||
fe v_R_cand;
|
||||
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
|
||||
throw std::runtime_error("R not on curve");
|
||||
|
||||
fe x1, y1, x2, y2, v_R_neg, v_D_neg;
|
||||
mont_to_ed(x1, y1, u_R, v_R_cand);
|
||||
|
||||
fe_neg(v_R_neg, v_R_cand);
|
||||
mont_to_ed(x2, y2, u_R, v_R_neg);
|
||||
|
||||
// Compute affine Edwards coords of R_ed_true
|
||||
fe inv_z, xr_true, yr_true;
|
||||
fe_invert(inv_z, R_ed_true.Z);
|
||||
fe_mul(xr_true, R_ed_true.X, inv_z);
|
||||
fe_mul(yr_true, R_ed_true.Y, inv_z);
|
||||
|
||||
bool R_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
|
||||
bool R_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
|
||||
|
||||
if (!R_match1 && !R_match2)
|
||||
throw std::runtime_error("R mapping mismatch");
|
||||
|
||||
bool R_sign = R_match1;
|
||||
|
||||
// ---------- Extract and lift D ----------
|
||||
fe u_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
|
||||
fe v_D_cand;
|
||||
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
|
||||
throw std::runtime_error("D not on curve");
|
||||
|
||||
mont_to_ed(x1, y1, u_D, v_D_cand);
|
||||
|
||||
fe_neg(v_D_neg, v_D_cand);
|
||||
mont_to_ed(x2, y2, u_D, v_D_neg);
|
||||
|
||||
fe_invert(inv_z, D_ed_true.Z);
|
||||
fe_mul(xr_true, D_ed_true.X, inv_z);
|
||||
fe_mul(yr_true, D_ed_true.Y, inv_z);
|
||||
|
||||
bool D_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
|
||||
bool D_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
|
||||
|
||||
if (!D_match1 && !D_match2)
|
||||
throw std::runtime_error("D mapping mismatch");
|
||||
|
||||
bool D_sign = D_match1;
|
||||
|
||||
//
|
||||
// 6. Pack sign bits into signature, include in challenge hash
|
||||
//
|
||||
sig.sign_mask =
|
||||
(R_sign ? 0x01 : 0x00) |
|
||||
(D_sign ? 0x02 : 0x00) |
|
||||
0x80;
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
|
||||
//
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
|
||||
|
||||
//
|
||||
// 8. Compute response z = k - c*r
|
||||
//
|
||||
sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
|
||||
|
||||
memwipe(&k, sizeof(k));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
assert(ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Verify a proof: either v1 (version == 1) or v2 (version == 2)
|
||||
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
|
||||
// sanity check
|
||||
@@ -608,6 +1004,167 @@ namespace crypto {
|
||||
return sc_isnonzero(&c2) == 0;
|
||||
}
|
||||
|
||||
// R and D are provided in X25519 format (u-coordinate), A and B in Ed25519.
|
||||
bool crypto_ops::check_carrot_tx_proof(
|
||||
const hash &prefix_hash,
|
||||
const public_key &R, // X25519 u
|
||||
const public_key &A, // Ed25519 viewkey
|
||||
const boost::optional<public_key> &B, // Ed25519 spendkey if any
|
||||
const public_key &D, // X25519 u
|
||||
const signature &sig)
|
||||
{
|
||||
ge_p3 A_p3, B_p3;
|
||||
if (ge_frombytes_vartime(&A_p3, &A) != 0)
|
||||
return false;
|
||||
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
|
||||
return false;
|
||||
|
||||
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
|
||||
return false;
|
||||
|
||||
// Extract sign bits and direction flag
|
||||
const bool R_sign = (sig.sign_mask & 0x01) != 0;
|
||||
const bool D_sign = (sig.sign_mask & 0x02) != 0;
|
||||
const bool outbound = (sig.sign_mask & 0x80) != 0;
|
||||
|
||||
//
|
||||
// 1. Reconstruct R_ed and D_ed from X25519 u-coords + sign bits
|
||||
//
|
||||
|
||||
// ----- R -----
|
||||
fe u_R, v_R_candidate, v_R;
|
||||
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
|
||||
if (fe_sqrt_mont(v_R_candidate, u_R) != 0)
|
||||
return false;
|
||||
if (R_sign) fe_copy(v_R, v_R_candidate);
|
||||
else fe_neg(v_R, v_R_candidate);
|
||||
|
||||
fe x_R, y_R;
|
||||
mont_to_ed(x_R, y_R, u_R, v_R);
|
||||
ge_p3 R_ed;
|
||||
ge_from_xy(&R_ed, x_R, y_R); // Z=1, T=X*Y
|
||||
|
||||
// ----- D -----
|
||||
fe u_D, v_D_candidate, v_D;
|
||||
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
|
||||
if (fe_sqrt_mont(v_D_candidate, u_D) != 0)
|
||||
return false;
|
||||
if (D_sign) fe_copy(v_D, v_D_candidate);
|
||||
else fe_neg(v_D, v_D_candidate);
|
||||
|
||||
fe x_D, y_D;
|
||||
mont_to_ed(x_D, y_D, u_D, v_D);
|
||||
ge_p3 D_ed;
|
||||
ge_from_xy(&D_ed, x_D, y_D);
|
||||
|
||||
//
|
||||
// 2. Compute X'
|
||||
// If inbound proof, X`= z*G + c*A (or z*B + c*A)
|
||||
// If outbound proof, X`= z*G + c*R_ed (or z*B + c*R_ed)
|
||||
//
|
||||
|
||||
ge_p3 c_p3;
|
||||
if (outbound)
|
||||
ge_scalarmult_p3(&c_p3, &sig.c, &R_ed);
|
||||
else
|
||||
ge_scalarmult_p3(&c_p3, &sig.c, &A_p3);
|
||||
|
||||
ge_p1p1 X_p1p1;
|
||||
if (B)
|
||||
{
|
||||
// Subaddress: X' = c*A + z*B
|
||||
ge_p3 rB_p3;
|
||||
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
|
||||
ge_cached rB_cached;
|
||||
ge_p3_to_cached(&rB_cached, &rB_p3);
|
||||
ge_add(&X_p1p1, &c_p3, &rB_cached);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Main address: X' = c*R_ed + z*G
|
||||
ge_p3 rG_p3;
|
||||
ge_scalarmult_base(&rG_p3, &sig.r);
|
||||
ge_cached rG_cached;
|
||||
ge_p3_to_cached(&rG_cached, &rG_p3);
|
||||
ge_add(&X_p1p1, &c_p3, &rG_cached);
|
||||
}
|
||||
|
||||
ge_p3 X_ed_p3;
|
||||
ge_p1p1_to_p3(&X_ed_p3, &X_p1p1);
|
||||
|
||||
mx25519_pubkey X_x25519;
|
||||
ge_p3_to_x25519(X_x25519.data, &X_ed_p3);
|
||||
|
||||
//
|
||||
// 3. Compute Y'
|
||||
// If inbound, Y' = c*D_ed + z*R
|
||||
// If outbound, Y' = c*D_ed + z*A
|
||||
//
|
||||
|
||||
ge_p3 cD_p3;
|
||||
ge_scalarmult_p3(&cD_p3, &sig.c, &D_ed);
|
||||
|
||||
ge_p3 z_p3;
|
||||
if (outbound)
|
||||
ge_scalarmult_p3(&z_p3, &sig.r, &A_p3);
|
||||
else
|
||||
ge_scalarmult_p3(&z_p3, &sig.r, &R_ed);
|
||||
|
||||
ge_cached z_cached;
|
||||
ge_p3_to_cached(&z_cached, &z_p3);
|
||||
|
||||
ge_p1p1 Y_p1p1;
|
||||
ge_add(&Y_p1p1, &cD_p3, &z_cached);
|
||||
|
||||
ge_p3 Y_ed_p3;
|
||||
ge_p1p1_to_p3(&Y_ed_p3, &Y_p1p1);
|
||||
|
||||
mx25519_pubkey Y_x25519;
|
||||
ge_p3_to_x25519(Y_x25519.data, &Y_ed_p3);
|
||||
|
||||
//
|
||||
// 4. Rebuild the hash transcript exactly as the prover did
|
||||
//
|
||||
|
||||
static const ec_point zero = {{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
}};
|
||||
|
||||
s_comm_2 buf;
|
||||
buf.msg = prefix_hash;
|
||||
buf.D = D; // X25519 (same bytes as prover)
|
||||
buf.R = R; // X25519
|
||||
buf.A = A; // Ed25519
|
||||
buf.B = B ? *B : zero;
|
||||
|
||||
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
|
||||
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
|
||||
buf.sep);
|
||||
|
||||
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
|
||||
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
|
||||
|
||||
struct {
|
||||
s_comm_2 buf;
|
||||
uint8_t sign_mask;
|
||||
} challenge_hash;
|
||||
|
||||
challenge_hash.buf = buf;
|
||||
challenge_hash.sign_mask = sig.sign_mask;
|
||||
|
||||
//
|
||||
// 5. Recompute challenge and compare with sig.c
|
||||
//
|
||||
|
||||
ec_scalar c2;
|
||||
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), c2);
|
||||
sc_sub(&c2, &c2, &sig.c);
|
||||
return sc_isnonzero(&c2) == 0;
|
||||
}
|
||||
|
||||
static void hash_to_ec(const public_key &key, ge_p3 &res) {
|
||||
hash h;
|
||||
ge_p2 point;
|
||||
@@ -796,4 +1353,4 @@ POP_WARNINGS
|
||||
ki.data[31] ^= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-3
@@ -85,6 +85,7 @@ namespace crypto {
|
||||
|
||||
POD_CLASS signature {
|
||||
ec_scalar c, r;
|
||||
uint8_t sign_mask;
|
||||
friend class crypto_ops;
|
||||
};
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace crypto {
|
||||
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
|
||||
sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 &&
|
||||
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(key_image_y) == 32 &&
|
||||
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
sizeof(signature) == 65 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
|
||||
class crypto_ops {
|
||||
crypto_ops();
|
||||
@@ -131,8 +132,14 @@ namespace crypto {
|
||||
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
|
||||
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
static void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
friend void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
|
||||
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
|
||||
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
|
||||
static bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
|
||||
friend bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
|
||||
static void derive_key_image_generator(const public_key &, ec_point &);
|
||||
friend void derive_key_image_generator(const public_key &, ec_point &);
|
||||
static void generate_key_image(const public_key &, const secret_key &, key_image &);
|
||||
@@ -260,10 +267,28 @@ namespace crypto {
|
||||
inline void generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
|
||||
crypto_ops::generate_tx_proof_v1(prefix_hash, R, A, B, D, r, sig);
|
||||
}
|
||||
/* Generation of a carrot tx proof; for carrot transactions, D is in X25519 domain (D = r*ConvertPointE(A))
|
||||
* instead of Ed25519 domain (D = r*A). This version applies ConvertPointE transformation.
|
||||
*/
|
||||
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
|
||||
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
inline void generate_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
|
||||
crypto_ops::generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
|
||||
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version);
|
||||
}
|
||||
|
||||
/* Verification of a carrot tx proof; R and D should be in Ed25519 domain for verification,
|
||||
*/
|
||||
inline bool check_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
|
||||
return crypto_ops::check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
|
||||
}
|
||||
/*
|
||||
inline bool check_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
|
||||
return crypto_ops::check_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, sig);
|
||||
}
|
||||
*/
|
||||
inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) {
|
||||
crypto_ops::derive_key_image_generator(pub, ki_gen);
|
||||
}
|
||||
@@ -378,4 +403,4 @@ CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe)
|
||||
CRYPTO_MAKE_HASHABLE(key_image)
|
||||
CRYPTO_MAKE_HASHABLE(key_image_y)
|
||||
CRYPTO_MAKE_COMPARABLE(signature)
|
||||
CRYPTO_MAKE_COMPARABLE(view_tag)
|
||||
CRYPTO_MAKE_COMPARABLE(view_tag)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(){};
|
||||
};
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace cryptonote
|
||||
STAKE = 6,
|
||||
RETURN = 7,
|
||||
AUDIT = 8,
|
||||
MAX = 8
|
||||
CREATE_TOKEN = 9,
|
||||
ROLLUP = 10,
|
||||
MAX = 10
|
||||
};
|
||||
}
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -282,6 +282,12 @@ namespace hw {
|
||||
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
|
||||
}
|
||||
|
||||
void device_default::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) {
|
||||
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
|
||||
}
|
||||
|
||||
bool device_default::open_tx(crypto::secret_key &tx_key) {
|
||||
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
|
||||
tx_key = txkey.sec;
|
||||
|
||||
@@ -112,6 +112,10 @@ namespace hw {
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
crypto::signature &sig) override;
|
||||
|
||||
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
|
||||
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
|
||||
const crypto::secret_key &a, crypto::signature &sig) override;
|
||||
|
||||
bool open_tx(crypto::secret_key &tx_key) override;
|
||||
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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>> ®istry);
|
||||
|
||||
#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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
@@ -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
Reference in New Issue
Block a user