Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42ec3826a0 | |||
| c388ca652f | |||
| c5edb53e57 | |||
| e446baa47c | |||
| 71d7754572 | |||
| 691400c0cd | |||
| 2319090437 | |||
| d684c281c5 | |||
| abc72ce9a1 | |||
| 7ab8d9499c | |||
| 25610c4c4b | |||
| c1f36b676d | |||
| b9e87450eb | |||
| c366058ba7 | |||
| ef8c2f00b9 | |||
| 74811f7eb3 | |||
| f699ca21c5 | |||
| 20014ae7c2 | |||
| 60713cc8e5 |
@@ -22,7 +22,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build-cross:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.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 python3-dev python3-setuptools-git"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
- name: "Cross-Mac aarch64"
|
||||
host: "aarch64-apple-darwin"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
|
||||
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
|
||||
- name: "x86_64 Freebsd"
|
||||
host: "x86_64-unknown-freebsd"
|
||||
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
@@ -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' || matrix.toolchain.host == 'aarch64-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' }}
|
||||
with:
|
||||
name: ${{ matrix.toolchain.name }}
|
||||
path: |
|
||||
|
||||
+12
-1
@@ -6,11 +6,22 @@
|
||||
url = https://github.com/trezor/trezor-common.git
|
||||
[submodule "external/randomx"]
|
||||
path = external/randomx
|
||||
url = https://github.com/tevador/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
|
||||
[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
|
||||
|
||||
+20
-15
@@ -45,10 +45,6 @@ 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)
|
||||
@@ -100,6 +96,7 @@ 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)
|
||||
|
||||
@@ -225,9 +222,9 @@ function(forbid_undefined_symbols)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(test)
|
||||
option(EXPECT_SUCCESS "" ON)
|
||||
file(WRITE "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }")
|
||||
file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }")
|
||||
if (EXPECT_SUCCESS)
|
||||
file(APPEND "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ")
|
||||
file(APPEND "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ")
|
||||
endif()
|
||||
add_library(l0 SHARED incorrect_source.cpp)
|
||||
add_library(l1 MODULE incorrect_source.cpp)
|
||||
@@ -367,12 +364,15 @@ 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_SOURCE_DIR}/cmake")
|
||||
"${CMAKE_CURRENT_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)
|
||||
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include external/polyseed/include external/utf8proc)
|
||||
|
||||
if(APPLE)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
@@ -692,16 +692,21 @@ include_directories(${LMDB_INCLUDE})
|
||||
include_directories(${LIBUNWIND_INCLUDE})
|
||||
link_directories(${LIBUNWIND_LIBRARY_DIRS})
|
||||
|
||||
# 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})
|
||||
if (HIDAPI_DUMMY)
|
||||
add_definitions(-DHIDAPI_DUMMY)
|
||||
else()
|
||||
message(STATUS "Could not find HIDAPI")
|
||||
# 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()
|
||||
endif()
|
||||
|
||||
|
||||
# Trezor support check
|
||||
include(CheckTrezor)
|
||||
|
||||
|
||||
+2
-4
@@ -3,16 +3,14 @@ FROM ubuntu:24.04 AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install build dependencies including cross-compilers
|
||||
# Install build dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential cmake pkg-config git imagemagick libcap-dev librsvg2-bin libz-dev \
|
||||
g++-mingw-w64-x86-64 clang gcc-arm-none-eabi binutils-x86-64-linux-gnu libtiff-tools \
|
||||
g++-x86-64-linux-gnu gcc-x86-64-linux-gnu binutils-aarch64-linux-gnu \
|
||||
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu crossbuild-essential-amd64 \
|
||||
libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev \
|
||||
libreadline-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \
|
||||
libprotobuf-dev protobuf-compiler libudev-dev libboost-all-dev python3 ccache doxygen graphviz \
|
||||
ca-certificates curl zip libtool gperf automake autoconf \
|
||||
ca-certificates curl zip libtool gperf \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Clone the develop branch
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Salvium One v1.0.7
|
||||
# Salvium One v1.0.0
|
||||
|
||||
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.7
|
||||
git checkout v1.0.0
|
||||
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.7
|
||||
git checkout v1.0.0
|
||||
```
|
||||
|
||||
* 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.7'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.0'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v1.0.7
|
||||
git checkout v1.0.0
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE)
|
||||
message(STATUS "Looking for ${flag} linker flag")
|
||||
endif()
|
||||
|
||||
set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
set(_cle_source ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
|
||||
set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
|
||||
set(CMAKE_C_FLAGS "${flag}")
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "memwipe.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -162,7 +162,6 @@ 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,6 +34,7 @@
|
||||
#include <string>
|
||||
#include "memwipe.h"
|
||||
#include "fnv1.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
@@ -75,6 +76,12 @@ 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,4 +261,14 @@ 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,5 +69,8 @@ endif()
|
||||
add_subdirectory(db_drivers)
|
||||
add_subdirectory(easylogging++)
|
||||
add_subdirectory(qrcodegen)
|
||||
add_subdirectory(polyseed EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(utf8proc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(bc-ur)
|
||||
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(mx25519)
|
||||
|
||||
+1
Submodule external/bc-ur added at d82e7c753e
+1
Submodule external/polyseed added at bd79f5014c
Vendored
+1
-1
Submodule external/randomx updated: 102f8acf90...ce72c9bb9c
+1
Submodule external/utf8proc added at 3de4596fbe
@@ -98,6 +98,7 @@ 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)
|
||||
|
||||
@@ -214,9 +214,11 @@ 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 currsz = 0;
|
||||
// uint64_t currtxs = 0;
|
||||
// uint64_t currblks = 0;
|
||||
uint64_t prevsz = 0, currsz = 0;
|
||||
uint64_t prevtxs = 0, currtxs = 0;
|
||||
uint64_t currblks = 0;
|
||||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
|
||||
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
|
||||
|
||||
@@ -244,7 +246,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
prevtm = currtm;
|
||||
}
|
||||
skip:
|
||||
// currsz += bd.size();
|
||||
currsz += bd.size();
|
||||
uint64_t coinbase_amount;
|
||||
uint64_t tx_fee_amount = 0;
|
||||
std::set<std::string> used_assets, miner_tx_assets, protocol_tx_assets;
|
||||
std::map<size_t, std::vector<std::string>> used_tx_versions;
|
||||
used_assets.insert("SAL");
|
||||
@@ -321,11 +325,10 @@ 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++;
|
||||
db->get_prunable_tx_blob(tx_id, bd);
|
||||
currsz += bd.size();
|
||||
if (db->get_prunable_tx_blob(tx_id, bd))
|
||||
currsz += bd.size();
|
||||
currtxs++;
|
||||
|
||||
if (tx.type != cryptonote::transaction_type::TRANSFER &&
|
||||
tx.type != cryptonote::transaction_type::BURN &&
|
||||
@@ -371,7 +374,7 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
// currblks++;
|
||||
currblks++;
|
||||
|
||||
if (stop_requested)
|
||||
break;
|
||||
|
||||
@@ -211,15 +211,8 @@ int main(int argc, char* argv[])
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
||||
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();
|
||||
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
|
||||
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -282,62 +282,13 @@ crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::publ
|
||||
return L;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
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)
|
||||
void carrot_and_legacy_account::generate_subaddress_map()
|
||||
{
|
||||
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
|
||||
|
||||
for (uint32_t major_index = 0; major_index <= lookahead_size.first; ++major_index)
|
||||
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
|
||||
{
|
||||
for (uint32_t minor_index = 0; minor_index <= lookahead_size.second; ++minor_index)
|
||||
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
|
||||
{
|
||||
for (const AddressDeriveType derive_type : derive_types)
|
||||
{
|
||||
@@ -386,7 +337,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);
|
||||
@@ -408,18 +359,9 @@ 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)
|
||||
@@ -451,19 +393,13 @@ 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}});
|
||||
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});
|
||||
}
|
||||
}
|
||||
for (const auto &p : subaddress_map_cn)
|
||||
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
|
||||
|
||||
+21
-61
@@ -48,59 +48,12 @@ 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_retired_t() {
|
||||
return_output_info_t() {
|
||||
// Default constructor for serialization
|
||||
input_context = input_context_t();
|
||||
K_o = crypto::public_key();
|
||||
K_change = crypto::public_key();
|
||||
@@ -109,6 +62,20 @@ 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)
|
||||
@@ -177,12 +144,7 @@ namespace carrot
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
crypto::key_image derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
|
||||
const crypto::secret_key &sender_extension_g,
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
void generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size);
|
||||
void generate_subaddress_map();
|
||||
|
||||
crypto::secret_key generate(
|
||||
const crypto::secret_key& recovery_key = crypto::secret_key(),
|
||||
@@ -221,10 +183,9 @@ 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.sum_g = crypto::secret_key();
|
||||
x.sender_extension_t = crypto::secret_key();
|
||||
x.x = crypto::secret_key();
|
||||
x.y = crypto::secret_key();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
@@ -233,10 +194,9 @@ 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.sum_g;
|
||||
a & x.sender_extension_t;
|
||||
a & x.x;
|
||||
a & x.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
|
||||
|
||||
class carrot_logic_error: public std::logic_error { using std::logic_error::logic_error; };
|
||||
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
|
||||
@@ -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: public std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
|
||||
|
||||
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
|
||||
}
|
||||
else if (!need_change_output)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_UNIQUE;
|
||||
return AdditionalOutputType::DUMMY;
|
||||
}
|
||||
else // num_selfsend == 1 && need_change_output
|
||||
{
|
||||
|
||||
@@ -219,7 +219,6 @@ 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;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -229,7 +228,6 @@ 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));
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -82,8 +82,6 @@ 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
|
||||
|
||||
+19
-19
@@ -387,11 +387,6 @@ 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;
|
||||
@@ -400,7 +395,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,
|
||||
main_address_spend_pubkeys,
|
||||
{&destination.address_spend_pubkey, 1},
|
||||
sender_extension_g_out,
|
||||
sender_extension_t_out,
|
||||
recovered_address_spend_pubkey,
|
||||
@@ -475,9 +470,6 @@ 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);
|
||||
|
||||
@@ -496,6 +488,7 @@ 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,
|
||||
@@ -525,17 +518,24 @@ 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_view_only(address_spend_pubkey_out,
|
||||
sum_g,
|
||||
sender_extension_t_out,
|
||||
K_r
|
||||
);
|
||||
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
|
||||
);
|
||||
|
||||
// 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!!!
|
||||
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}}});
|
||||
}
|
||||
|
||||
// janus protection checks are not needed for internal scans
|
||||
|
||||
@@ -275,8 +275,7 @@ 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,
|
||||
.asset_type = "SAL1"
|
||||
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE
|
||||
},
|
||||
.subaddr_index = change_address_index
|
||||
});
|
||||
|
||||
@@ -28,7 +28,11 @@
|
||||
|
||||
if(APPLE)
|
||||
if(DEPENDS)
|
||||
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
|
||||
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()
|
||||
else()
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
mark_as_advanced(IOKIT_LIBRARY)
|
||||
|
||||
@@ -183,33 +183,28 @@ 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(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
|
||||
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
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ monero_add_library(cncrypto
|
||||
target_link_libraries(cncrypto
|
||||
PUBLIC
|
||||
epee
|
||||
mx25519_static
|
||||
randomx
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${SODIUM_LIBRARY}
|
||||
|
||||
@@ -887,7 +887,4 @@ 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};
|
||||
};
|
||||
+2
-125
@@ -104,21 +104,6 @@ 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 */
|
||||
|
||||
/*
|
||||
@@ -380,7 +365,7 @@ int fe_isnegative(const fe f) {
|
||||
|
||||
/* From fe_isnonzero.c, modified */
|
||||
|
||||
int fe_isnonzero(const fe f) {
|
||||
static 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] |
|
||||
@@ -3982,114 +3967,6 @@ 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
|
||||
@@ -4186,4 +4063,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);
|
||||
}
|
||||
}
|
||||
+1
-10
@@ -176,11 +176,6 @@ 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);
|
||||
@@ -197,14 +192,10 @@ 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);
|
||||
+1
-558
@@ -41,7 +41,6 @@
|
||||
#include "common/varint.h"
|
||||
#include "warnings.h"
|
||||
#include "crypto.h"
|
||||
#include "mx25519.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
@@ -91,16 +90,6 @@ 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;
|
||||
@@ -515,391 +504,6 @@ 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
|
||||
@@ -1004,167 +608,6 @@ 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;
|
||||
@@ -1353,4 +796,4 @@ POP_WARNINGS
|
||||
ki.data[31] ^= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-28
@@ -85,7 +85,6 @@ namespace crypto {
|
||||
|
||||
POD_CLASS signature {
|
||||
ec_scalar c, r;
|
||||
uint8_t sign_mask;
|
||||
friend class crypto_ops;
|
||||
};
|
||||
|
||||
@@ -100,7 +99,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) == 65 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
|
||||
class crypto_ops {
|
||||
crypto_ops();
|
||||
@@ -132,14 +131,8 @@ 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 &);
|
||||
@@ -267,28 +260,10 @@ 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);
|
||||
}
|
||||
@@ -403,4 +378,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,7 +28,11 @@
|
||||
|
||||
if(APPLE)
|
||||
if(DEPENDS)
|
||||
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
|
||||
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()
|
||||
else()
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
mark_as_advanced(IOKIT_LIBRARY)
|
||||
@@ -71,6 +75,7 @@ target_link_libraries(cryptonote_basic
|
||||
checkpoints
|
||||
cryptonote_format_utils_basic
|
||||
device
|
||||
polyseed_wrapper
|
||||
oracle
|
||||
${Boost_DATE_TIME_LIBRARY}
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
|
||||
@@ -89,24 +89,16 @@ 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) * (8 + m_multisig_keys.size()));
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.size());
|
||||
const char *ptr = key_stream.data();
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_spend_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_master.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_prove_spend.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_view_balance.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_view_incoming.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k_generate_image.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
s_generate_address.data[i] ^= *ptr++;
|
||||
m_polyseed.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < m_passphrase.size(); ++i)
|
||||
m_passphrase.data()[i] ^= *ptr++;
|
||||
for (crypto::secret_key &k: m_multisig_keys)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
@@ -128,20 +120,11 @@ 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) * 8);
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
|
||||
const char *ptr = key_stream.data();
|
||||
ptr += sizeof(crypto::secret_key); // Skip m_spend_secret_key
|
||||
ptr += sizeof(crypto::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)
|
||||
@@ -175,6 +158,8 @@ 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)
|
||||
@@ -281,6 +266,21 @@ 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, true};
|
||||
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key};
|
||||
// change this code into base 58
|
||||
return get_account_address_as_str(nettype, false, addr);
|
||||
return get_account_address_as_str(nettype, false, addr, true);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#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"
|
||||
@@ -52,6 +53,8 @@ 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;
|
||||
@@ -75,11 +78,8 @@ namespace cryptonote
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
|
||||
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
|
||||
if (m_account_address.m_spend_public_key == crypto::null_pkey) {
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(s_view_balance)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(k_view_incoming)
|
||||
KV_SERIALIZE(m_carrot_account_address)
|
||||
}
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed)
|
||||
KV_SERIALIZE(m_passphrase)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
void encrypt(const crypto::chacha_key &key);
|
||||
@@ -106,6 +106,7 @@ 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;
|
||||
|
||||
@@ -155,9 +155,10 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, bool subaddress
|
||||
, account_public_address const & adr
|
||||
, bool is_carrot
|
||||
)
|
||||
{
|
||||
uint64_t address_prefix = adr.m_is_carrot
|
||||
uint64_t address_prefix = is_carrot
|
||||
? (subaddress ? get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX)
|
||||
: (subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
|
||||
|
||||
@@ -168,9 +169,10 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, account_public_address const & adr
|
||||
, crypto::hash8 const & payment_id
|
||||
, bool is_carrot
|
||||
)
|
||||
{
|
||||
uint64_t integrated_address_prefix = adr.m_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 = 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,12 +88,14 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, bool subaddress
|
||||
, const account_public_address& adr
|
||||
, bool is_carrot = false
|
||||
);
|
||||
|
||||
std::string get_account_integrated_address_as_str(
|
||||
network_type nettype
|
||||
, const account_public_address& adr
|
||||
, const crypto::hash8& payment_id
|
||||
, bool is_carrot = false
|
||||
);
|
||||
|
||||
bool get_account_address_from_str(
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#include "boost/logic/tribool.hpp"
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
|
||||
#include <sys/times.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/ps/IOPSKeys.h>
|
||||
@@ -902,7 +902,7 @@ namespace cryptonote
|
||||
|
||||
return true;
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
|
||||
|
||||
mach_msg_type_number_t count;
|
||||
kern_return_t status;
|
||||
@@ -968,7 +968,7 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#elif (defined(__linux__) && defined(_SC_CLK_TCK)) || (defined(__APPLE__) && !defined(TARGET_OS_IPHONE)) || defined(__FreeBSD__)
|
||||
|
||||
struct tms tms;
|
||||
if ( times(&tms) != (clock_t)-1 )
|
||||
@@ -997,7 +997,7 @@ namespace cryptonote
|
||||
return boost::logic::tribool(power_status.ACLineStatus != 1);
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
|
||||
|
||||
#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);
|
||||
|
||||
@@ -274,6 +274,8 @@
|
||||
|
||||
#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
|
||||
|
||||
|
||||
@@ -877,7 +877,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> difficulties;
|
||||
uint64_t height;
|
||||
top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||
auto new_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 +976,11 @@ size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_heig
|
||||
if (start_height_opt) {
|
||||
start_height = *start_height_opt;
|
||||
} else {
|
||||
// bool found;
|
||||
bool found = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -2123,6 +2123,7 @@ 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");
|
||||
@@ -2953,7 +2954,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);
|
||||
@@ -4517,7 +4518,8 @@ 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 uint64_t db_height = m_db->height();
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const uint64_t db_height = m_db->height();
|
||||
|
||||
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
|
||||
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
|
||||
@@ -4668,7 +4670,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
|
||||
m_db->get_audit_tx_info(start_height, audit_entries);
|
||||
int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries);
|
||||
if (!audit_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -4721,7 +4723,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
|
||||
m_db->get_carrot_yield_tx_info(start_height, yield_entries);
|
||||
int yield_tx_result = m_db->get_carrot_yield_tx_info(start_height, yield_entries);
|
||||
if (!yield_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -4787,7 +4789,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
|
||||
m_db->get_yield_tx_info(start_height, yield_entries);
|
||||
int yield_tx_result = m_db->get_yield_tx_info(start_height, yield_entries);
|
||||
if (!yield_entries.size()) {
|
||||
|
||||
// Report error and abort
|
||||
@@ -5404,7 +5406,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);
|
||||
@@ -5522,7 +5524,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;
|
||||
@@ -6169,7 +6171,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)
|
||||
@@ -6240,7 +6242,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
|
||||
@@ -6512,7 +6514,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "fb30b46662cb1cfca74d443d66be5860936f9fd904ba5c6f8daecb926ce545f0";
|
||||
static const char expected_block_hashes_hash[] = "1cf6e8892e0512c246cef62610ccf524f30f484e307ae01959a5a7dd166aa328";
|
||||
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
|
||||
{
|
||||
if (get_checkpoints == nullptr || !m_fast_sync)
|
||||
|
||||
@@ -1440,16 +1440,24 @@ 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
|
||||
{
|
||||
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
|
||||
// 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));
|
||||
}
|
||||
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address, is_carrot);
|
||||
req.threads_count = num_threads;
|
||||
req.do_background_mining = do_background_mining;
|
||||
req.ignore_battery = ignore_battery;
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
set(device_sources
|
||||
device.cpp
|
||||
device_default.cpp
|
||||
device_io_dummy.cpp
|
||||
log.cpp
|
||||
)
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
|
||||
set(device_sources
|
||||
${device_sources}
|
||||
device_ledger.cpp
|
||||
@@ -45,10 +46,11 @@ set(device_headers
|
||||
device_io.hpp
|
||||
device_default.hpp
|
||||
device_cold.hpp
|
||||
device_io_dummy.hpp
|
||||
log.hpp
|
||||
)
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
|
||||
set(device_headers
|
||||
${device_headers}
|
||||
device_ledger.hpp
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "device.hpp"
|
||||
#include "device_default.hpp"
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
#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);
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
hw::ledger::register_all(registry);
|
||||
#endif
|
||||
atexit(clear_device_registry);
|
||||
@@ -83,11 +83,13 @@ namespace hw {
|
||||
|
||||
auto device = registry.find(device_descriptor_lookup);
|
||||
if (device == registry.end()) {
|
||||
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: ");
|
||||
std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: ");
|
||||
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n");
|
||||
for( const auto& sm_pair : registry ) {
|
||||
ss << "\n- " + sm_pair.first;
|
||||
MERROR(" - " << sm_pair.first);
|
||||
}
|
||||
throw std::runtime_error("device not found: " + device_descriptor);
|
||||
throw std::runtime_error("device not found: " + device_descriptor + "\n" + ss.str());
|
||||
}
|
||||
return *device->second;
|
||||
}
|
||||
|
||||
+1
-15
@@ -34,17 +34,7 @@
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "cryptonote_config.h"
|
||||
|
||||
|
||||
#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
|
||||
#if defined(HAVE_HIDAPI) || defined(HIDAPI_DUMMY)
|
||||
#define WITH_DEVICE_LEDGER
|
||||
#endif
|
||||
|
||||
@@ -202,10 +192,6 @@ 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,12 +282,6 @@ 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,10 +112,6 @@ 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;
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
// 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
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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 {
|
||||
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
|
||||
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,7 +533,9 @@ 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;
|
||||
@@ -1433,14 +1435,6 @@ 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,6 +35,7 @@
|
||||
#include "device.hpp"
|
||||
#include "log.hpp"
|
||||
#include "device_io_hid.hpp"
|
||||
#include "device_io_dummy.hpp"
|
||||
#include <mutex>
|
||||
|
||||
namespace hw {
|
||||
@@ -55,7 +56,7 @@ namespace hw {
|
||||
|
||||
void register_all(std::map<std::string, std::unique_ptr<device>> ®istry);
|
||||
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
|
||||
// Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h
|
||||
#define SW_OK 0x9000
|
||||
@@ -143,7 +144,11 @@ 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;
|
||||
@@ -253,10 +258,6 @@ 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;
|
||||
|
||||
+19
-1
@@ -881,6 +881,12 @@ 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 {};
|
||||
@@ -888,6 +894,10 @@ 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 {};
|
||||
@@ -2044,13 +2054,20 @@ 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;
|
||||
unsigned good = 0, bad = 0;
|
||||
for (const auto& record : records)
|
||||
{
|
||||
std::vector<std::string> ips;
|
||||
@@ -2074,6 +2091,7 @@ namespace nodetool
|
||||
continue;
|
||||
}
|
||||
MWARNING("Invalid IP address or subnet from DNS blocklist: " << ip << " - " << parsed_addr.error());
|
||||
++bad;
|
||||
}
|
||||
}
|
||||
if (good > 0)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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}
|
||||
)
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef PBKDF2_H
|
||||
#define PBKDF2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// 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 "polyseed.hpp"
|
||||
#include "pbkdf2.h"
|
||||
|
||||
#include <sodium/core.h>
|
||||
#include <sodium/utils.h>
|
||||
#include <sodium/randombytes.h>
|
||||
#include <utf8proc.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace polyseed {
|
||||
|
||||
inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) {
|
||||
utf8proc_int32_t buffer[POLYSEED_STR_SIZE];
|
||||
utf8proc_ssize_t result;
|
||||
|
||||
result = utf8proc_decompose(reinterpret_cast<const uint8_t*>(str), 0, buffer, POLYSEED_STR_SIZE, options);
|
||||
if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) {
|
||||
throw std::runtime_error("Unicode normalization failed");
|
||||
}
|
||||
|
||||
result = utf8proc_reencode(buffer, result, options);
|
||||
if (result < 0 || result > POLYSEED_STR_SIZE) {
|
||||
throw std::runtime_error("Unicode normalization failed");
|
||||
}
|
||||
|
||||
strcpy(norm, reinterpret_cast<const char*>(buffer));
|
||||
sodium_memzero(buffer, POLYSEED_STR_SIZE);
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t utf8_nfc(const char* str, polyseed_str norm) {
|
||||
// Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases
|
||||
// to allow wallets to split on ' '.
|
||||
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA));
|
||||
}
|
||||
|
||||
static size_t utf8_nfkd(const char* str, polyseed_str norm) {
|
||||
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA));
|
||||
}
|
||||
|
||||
struct dependency {
|
||||
dependency();
|
||||
std::vector<language> languages;
|
||||
};
|
||||
|
||||
static dependency deps;
|
||||
|
||||
dependency::dependency() {
|
||||
if (sodium_init() == -1) {
|
||||
throw std::runtime_error("sodium_init failed");
|
||||
}
|
||||
|
||||
polyseed_dependency pd;
|
||||
pd.randbytes = &randombytes_buf;
|
||||
pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256;
|
||||
pd.memzero = &sodium_memzero;
|
||||
pd.u8_nfc = &utf8_nfc;
|
||||
pd.u8_nfkd = &utf8_nfkd;
|
||||
pd.time = nullptr;
|
||||
pd.alloc = nullptr;
|
||||
pd.free = nullptr;
|
||||
|
||||
polyseed_inject(&pd);
|
||||
|
||||
for (int i = 0; i < polyseed_get_num_langs(); ++i) {
|
||||
languages.push_back(language(polyseed_get_lang(i)));
|
||||
}
|
||||
}
|
||||
|
||||
static language invalid_lang;
|
||||
|
||||
const std::vector<language>& get_langs() {
|
||||
return deps.languages;
|
||||
}
|
||||
|
||||
const language& get_lang_by_name(const std::string& name) {
|
||||
for (auto& lang : deps.languages) {
|
||||
if (name == lang.name_en()) {
|
||||
return lang;
|
||||
}
|
||||
if (name == lang.name()) {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
return invalid_lang;
|
||||
}
|
||||
|
||||
inline void data::check_init() const {
|
||||
if (valid()) {
|
||||
throw std::runtime_error("already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
static std::array<const char*, 8> error_desc = {
|
||||
"Success",
|
||||
"Wrong number of words in the phrase",
|
||||
"Unknown language or unsupported words",
|
||||
"Checksum mismatch",
|
||||
"Unsupported seed features",
|
||||
"Invalid seed format",
|
||||
"Memory allocation failure",
|
||||
"Unicode normalization failed"
|
||||
};
|
||||
|
||||
static error get_error(polyseed_status status) {
|
||||
if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) {
|
||||
return error(error_desc[(int)status], status);
|
||||
}
|
||||
return error("Unknown error", status);
|
||||
}
|
||||
|
||||
void data::create(feature_type features) {
|
||||
check_init();
|
||||
auto status = polyseed_create(features, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
void data::split(const language& lang, polyseed_phrase& words) {
|
||||
check_init();
|
||||
if (!lang.valid()) {
|
||||
throw std::runtime_error("invalid language");
|
||||
}
|
||||
}
|
||||
|
||||
void data::load(polyseed_storage storage) {
|
||||
check_init();
|
||||
auto status = polyseed_load(storage, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
void data::load(const crypto::secret_key &key) {
|
||||
polyseed_storage d;
|
||||
memcpy(&d, &key.data, 32);
|
||||
auto status = polyseed_load(d, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
language data::decode(const char* phrase) {
|
||||
check_init();
|
||||
const polyseed_lang* lang;
|
||||
auto status = polyseed_decode(phrase, m_coin, &lang, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
return language(lang);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef POLYSEED_HPP
|
||||
#define POLYSEED_HPP
|
||||
|
||||
#include <polyseed/include/polyseed.h>
|
||||
#include <polyseed/src/lang.h>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
namespace polyseed {
|
||||
|
||||
class data;
|
||||
|
||||
class language {
|
||||
public:
|
||||
language() : m_lang(nullptr) {}
|
||||
language(const language&) = default;
|
||||
language(const polyseed_lang* lang) : m_lang(lang) {}
|
||||
const char* name() const {
|
||||
return polyseed_get_lang_name(m_lang);
|
||||
}
|
||||
const char* name_en() const {
|
||||
return polyseed_get_lang_name_en(m_lang);
|
||||
}
|
||||
const char* separator() const {
|
||||
return m_lang->separator;
|
||||
}
|
||||
bool valid() const {
|
||||
return m_lang != nullptr;
|
||||
}
|
||||
|
||||
const polyseed_lang* m_lang;
|
||||
private:
|
||||
|
||||
friend class data;
|
||||
};
|
||||
|
||||
const std::vector<language>& get_langs();
|
||||
const language& get_lang_by_name(const std::string& name);
|
||||
|
||||
class error : public std::runtime_error {
|
||||
public:
|
||||
error(const char* msg, polyseed_status status)
|
||||
: std::runtime_error(msg), m_status(status)
|
||||
{
|
||||
}
|
||||
polyseed_status status() const {
|
||||
return m_status;
|
||||
}
|
||||
private:
|
||||
polyseed_status m_status;
|
||||
};
|
||||
|
||||
using feature_type = unsigned int;
|
||||
|
||||
inline int enable_features(feature_type features) {
|
||||
return polyseed_enable_features(features);
|
||||
}
|
||||
|
||||
class data {
|
||||
public:
|
||||
data(const data&) = delete;
|
||||
data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {}
|
||||
~data() {
|
||||
polyseed_free(m_data);
|
||||
}
|
||||
|
||||
void create(feature_type features);
|
||||
|
||||
void load(polyseed_storage storage);
|
||||
|
||||
void load(const crypto::secret_key &key);
|
||||
|
||||
language decode(const char* phrase);
|
||||
|
||||
template<class str_type>
|
||||
void encode(const language& lang, str_type& str) const {
|
||||
check_valid();
|
||||
if (!lang.valid()) {
|
||||
throw std::runtime_error("invalid language");
|
||||
}
|
||||
str.resize(POLYSEED_STR_SIZE);
|
||||
auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]);
|
||||
str.resize(size);
|
||||
}
|
||||
|
||||
void split(const language& lang, polyseed_phrase& words);
|
||||
|
||||
void save(polyseed_storage storage) const {
|
||||
check_valid();
|
||||
polyseed_store(m_data, storage);
|
||||
}
|
||||
|
||||
void save(void *storage) const {
|
||||
check_valid();
|
||||
polyseed_store(m_data, (uint8_t*)storage);
|
||||
}
|
||||
|
||||
void crypt(const char* password) {
|
||||
check_valid();
|
||||
polyseed_crypt(m_data, password);
|
||||
}
|
||||
|
||||
void keygen(void* ptr, size_t key_size) const {
|
||||
check_valid();
|
||||
polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr);
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
bool encrypted() const {
|
||||
check_valid();
|
||||
return polyseed_is_encrypted(m_data);
|
||||
}
|
||||
|
||||
uint64_t birthday() const {
|
||||
check_valid();
|
||||
return polyseed_get_birthday(m_data);
|
||||
}
|
||||
|
||||
bool has_feature(feature_type feature) const {
|
||||
check_valid();
|
||||
return polyseed_get_feature(m_data, feature) != 0;
|
||||
}
|
||||
private:
|
||||
void check_valid() const {
|
||||
if (m_data == nullptr) {
|
||||
throw std::runtime_error("invalid object");
|
||||
}
|
||||
}
|
||||
void check_init() const;
|
||||
|
||||
polyseed_data* m_data;
|
||||
polyseed_coin m_coin;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //POLYSEED_HPP
|
||||
@@ -1879,29 +1879,12 @@ namespace cryptonote
|
||||
}
|
||||
blobdata block_blob = t_serializable_object_to_blob(b);
|
||||
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
|
||||
const std::vector<crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx);
|
||||
if(tx_pub_key == crypto::null_pkey)
|
||||
{
|
||||
// Check for Carrot treasury payout
|
||||
const uint8_t hf_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
|
||||
if (hf_version >= HF_VERSION_CARROT && b.miner_tx.vout.size() == 2) {
|
||||
|
||||
const auto treasury_payout_data = get_config(nettype()).TREASURY_SAL1_MINT_OUTPUT_DATA;
|
||||
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
|
||||
if (!treasury_payout_exists) {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template (missing treasury payout)";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra (missing treasury payout)");
|
||||
return false;
|
||||
}
|
||||
tx_pub_key = additional_tx_pub_keys.back();
|
||||
|
||||
} else {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra");
|
||||
return false;
|
||||
}
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t next_height;
|
||||
@@ -3125,6 +3108,7 @@ namespace cryptonote
|
||||
|
||||
CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE);
|
||||
|
||||
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
|
||||
m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.grace_blocks, res.fees);
|
||||
res.fee = res.fees[0];
|
||||
res.quantization_mask = Blockchain::get_fee_quantization_mask();
|
||||
|
||||
@@ -237,7 +237,6 @@ namespace
|
||||
const char* USAGE_SIGN_TRANSFER("sign_transfer [export_raw] [<filename>]");
|
||||
const char* USAGE_SET_LOG("set_log <level>|{+,-,}<categories>");
|
||||
const char* USAGE_ACCOUNT("account\n"
|
||||
" account all\n"
|
||||
" account new <label text with white spaces allowed>\n"
|
||||
" account switch <index> \n"
|
||||
" account label <index> <label text with white spaces allowed>\n"
|
||||
@@ -3636,8 +3635,7 @@ simple_wallet::simple_wallet()
|
||||
m_cmd_binder.set_handler("account",
|
||||
boost::bind(&simple_wallet::on_command, this, &simple_wallet::account, _1),
|
||||
tr(USAGE_ACCOUNT),
|
||||
tr("If no arguments are specified, the wallet shows all the existing accounts (that have balances) along with their balances.\n"
|
||||
"If the \"all\" argument is specified, the wallet shows all the existing accounts, even if they have zero balances.\n"
|
||||
tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n"
|
||||
"If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n"
|
||||
"If the \"switch\" argument is specified, the wallet switches to the account specified by <index>.\n"
|
||||
"If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text.\n"
|
||||
@@ -4612,7 +4610,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
{
|
||||
m_wallet_file = m_generate_from_svb_key;
|
||||
// parse address
|
||||
std::string address_string = input_line("Carrot wallet address");
|
||||
std::string address_string = input_line("Standard address");
|
||||
if (std::cin.eof())
|
||||
return false;
|
||||
if (address_string.empty()) {
|
||||
@@ -4637,7 +4635,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
}
|
||||
|
||||
// parse view secret key
|
||||
epee::wipeable_string viewkey_string = input_secure_line("View-balance secret");
|
||||
epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
|
||||
if (std::cin.eof())
|
||||
return false;
|
||||
if (viewkey_string.empty()) {
|
||||
@@ -5337,7 +5335,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||
.m_is_carrot = true
|
||||
};
|
||||
message_writer(console_color_white, true) << tr("Generated new Carrot wallet: ")
|
||||
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address);
|
||||
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address, true);
|
||||
PAUSE_READLINE();
|
||||
std::cout << tr("View key: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||
@@ -5617,12 +5615,6 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p
|
||||
m_wallet->rewrite(m_wallet_file, password);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_wallet->force_rescan()) {
|
||||
m_wallet->force_rescan(false);
|
||||
refresh_main(m_wallet->get_refresh_from_block_height(), ResetSoft);
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -6458,10 +6450,6 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||
bool simple_wallet::refresh(const std::vector<std::string>& args)
|
||||
{
|
||||
uint64_t start_height = 0;
|
||||
if (m_wallet->force_rescan()) {
|
||||
m_wallet->force_rescan(false);
|
||||
return refresh_main(start_height, ResetSoft);
|
||||
}
|
||||
if(!args.empty()){
|
||||
try
|
||||
{
|
||||
@@ -7349,25 +7337,25 @@ bool simple_wallet::transfer_main(
|
||||
switch (transfer_type) {
|
||||
case Burn:
|
||||
unlock_block = 0;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::BURN, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::BURN, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
||||
break;
|
||||
case Convert:
|
||||
unlock_block = CONVERT_LOCK_PERIOD;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::CONVERT, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::CONVERT, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
||||
break;
|
||||
case Stake:
|
||||
unlock_block = get_config(m_wallet->nettype()).STAKE_LOCK_PERIOD;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::STAKE, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::STAKE, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
||||
break;
|
||||
case TransferLocked:
|
||||
unlock_block = locked_blocks;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown transfer method, using default");
|
||||
/* FALLTHRU */
|
||||
case Transfer:
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -9443,7 +9431,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
|
||||
try
|
||||
{
|
||||
std::string sig_str = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, args.size() == 3 ? args[2] : "");
|
||||
const std::string filename = "salvium_tx_proof";
|
||||
const std::string filename = "monero_tx_proof";
|
||||
if (m_wallet->save_to_file(filename, sig_str, true))
|
||||
success_msg_writer() << tr("signature file saved to: ") << filename;
|
||||
else
|
||||
@@ -10128,9 +10116,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
||||
transfer.type == "burnt" ? console_color_yellow :
|
||||
transfer.type == "stake" ? console_color_cyan :
|
||||
transfer.type == "yield" ? console_color_magenta :
|
||||
transfer.confirmed ?
|
||||
((transfer.direction == "in" || transfer.direction == "block") ?
|
||||
(transfer.asset_type == "SAL" ? console_color_green : console_color_blue) : console_color_white) : console_color_default;
|
||||
transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_white) : console_color_default;
|
||||
|
||||
std::string destinations = "-";
|
||||
if (!transfer.outputs.empty())
|
||||
@@ -10710,21 +10696,12 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
|
||||
{
|
||||
// print all the existing accounts
|
||||
LOCK_IDLE_SCOPE();
|
||||
print_accounts(false);
|
||||
print_accounts();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> local_args = args;
|
||||
std::string command = local_args[0];
|
||||
|
||||
if (command == "all")
|
||||
{
|
||||
// print all accounts including zero balance
|
||||
LOCK_IDLE_SCOPE();
|
||||
print_accounts(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
local_args.erase(local_args.begin());
|
||||
if (command == "new")
|
||||
{
|
||||
@@ -10856,20 +10833,20 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::print_accounts(bool show_all)
|
||||
void simple_wallet::print_accounts()
|
||||
{
|
||||
const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
|
||||
size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
|
||||
for (const std::pair<const std::string, std::string>& p : account_tags.first)
|
||||
{
|
||||
const std::string& tag = p.first;
|
||||
print_accounts(tag, show_all);
|
||||
print_accounts(tag);
|
||||
num_untagged_accounts -= std::count(account_tags.second.begin(), account_tags.second.end(), tag);
|
||||
success_msg_writer() << "";
|
||||
}
|
||||
|
||||
if (num_untagged_accounts > 0)
|
||||
print_accounts("", show_all);
|
||||
print_accounts("");
|
||||
|
||||
if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts()) {
|
||||
std::map<std::string, uint64_t> balances = m_wallet->balance_all(false);
|
||||
@@ -10881,7 +10858,7 @@ void simple_wallet::print_accounts(bool show_all)
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::print_accounts(const std::string& tag, bool show_all)
|
||||
void simple_wallet::print_accounts(const std::string& tag)
|
||||
{
|
||||
const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
|
||||
if (tag.empty())
|
||||
@@ -10910,7 +10887,7 @@ void simple_wallet::print_accounts(const std::string& tag, bool show_all)
|
||||
|
||||
auto balance = m_wallet->balance(account_index, asset, false);
|
||||
auto unlocked_balance = m_wallet->unlocked_balance(account_index, asset, false);
|
||||
if (!show_all && balance == 0)
|
||||
if (balance == 0)
|
||||
continue;
|
||||
success_msg_writer() << boost::format(tr(" %c%8u %8s %21s %21s %6s %21s"))
|
||||
% (m_current_subaddress_account == account_index ? '*' : ' ')
|
||||
@@ -11254,9 +11231,9 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
|
||||
success_msg_writer() << tr("Index: ") << i;
|
||||
std::string address;
|
||||
if (row.m_has_payment_id)
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id);
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id, row.m_is_carrot);
|
||||
else
|
||||
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
|
||||
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address, row.m_is_carrot);
|
||||
success_msg_writer() << tr("Address: ") << address;
|
||||
success_msg_writer() << tr("Description: ") << row.m_description << "\n";
|
||||
}
|
||||
|
||||
@@ -205,8 +205,8 @@ namespace cryptonote
|
||||
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
|
||||
);
|
||||
bool account(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
void print_accounts(bool show_all = false);
|
||||
void print_accounts(const std::string& tag, bool show_all = false);
|
||||
void print_accounts();
|
||||
void print_accounts(const std::string& tag);
|
||||
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION "1.0.7"
|
||||
#define DEF_SALVIUM_VERSION "1.0.0"
|
||||
#define DEF_MONERO_VERSION_TAG "release"
|
||||
#define DEF_MONERO_VERSION "0.18.3.4"
|
||||
#define DEF_MONERO_RELEASE_NAME "One"
|
||||
|
||||
@@ -38,6 +38,7 @@ set(wallet_sources
|
||||
message_store.cpp
|
||||
message_transporter.cpp
|
||||
wallet_rpc_payments.cpp
|
||||
wallet_cache_to_json.cpp
|
||||
tx_builder.cpp
|
||||
)
|
||||
|
||||
@@ -51,6 +52,7 @@ monero_add_library(wallet
|
||||
target_link_libraries(wallet
|
||||
PUBLIC
|
||||
rpc_base
|
||||
bc-ur
|
||||
multisig
|
||||
carrot_impl
|
||||
common
|
||||
|
||||
@@ -41,7 +41,9 @@ set(wallet_api_sources
|
||||
address_book.cpp
|
||||
subaddress.cpp
|
||||
subaddress_account.cpp
|
||||
unsigned_transaction.cpp)
|
||||
unsigned_transaction.cpp
|
||||
coins.cpp
|
||||
coins_info.cpp)
|
||||
|
||||
set(wallet_api_headers
|
||||
wallet2_api.h)
|
||||
@@ -57,7 +59,9 @@ set(wallet_api_private_headers
|
||||
address_book.h
|
||||
subaddress.h
|
||||
subaddress_account.h
|
||||
unsigned_transaction.h)
|
||||
unsigned_transaction.h
|
||||
coins.h
|
||||
coins_info.h)
|
||||
|
||||
monero_private_headers(wallet_api
|
||||
${wallet_api_private_headers})
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "coins.h"
|
||||
#include "coins_info.h"
|
||||
#include "wallet.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "wallet/wallet2.h"
|
||||
#include "common_defines.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace epee;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
Coins::~Coins() = default;
|
||||
|
||||
CoinsImpl::CoinsImpl(WalletImpl *wallet)
|
||||
: m_wallet(wallet) {}
|
||||
|
||||
CoinsImpl::~CoinsImpl()
|
||||
{
|
||||
for (auto t : m_rows)
|
||||
delete t;
|
||||
}
|
||||
|
||||
int CoinsImpl::count() const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
int result = m_rows.size();
|
||||
return result;
|
||||
}
|
||||
|
||||
CoinsInfo *CoinsImpl::coin(int index) const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
// sanity check
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
auto index_ = static_cast<unsigned>(index);
|
||||
return index_ < m_rows.size() ? m_rows[index_] : nullptr;
|
||||
}
|
||||
|
||||
std::vector<CoinsInfo *> CoinsImpl::getAll() const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
return m_rows;
|
||||
}
|
||||
|
||||
|
||||
void CoinsImpl::refresh()
|
||||
{
|
||||
LOG_PRINT_L2("Refreshing coins");
|
||||
|
||||
boost::unique_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_wallet->m_transfers_mutex);
|
||||
|
||||
// delete old outputs;
|
||||
for (auto t : m_rows)
|
||||
delete t;
|
||||
m_rows.clear();
|
||||
|
||||
for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i)
|
||||
{
|
||||
const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i);
|
||||
|
||||
// Make a subaddress_index_extended from td.m_subaddr_index
|
||||
carrot::subaddress_index_extended csub{
|
||||
{td.m_subaddr_index.major, td.m_subaddr_index.minor},
|
||||
td.is_carrot() ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot,
|
||||
false};
|
||||
|
||||
auto ci = new CoinsInfoImpl();
|
||||
ci->m_blockHeight = td.m_block_height;
|
||||
ci->m_hash = string_tools::pod_to_hex(td.m_txid);
|
||||
ci->m_internalOutputIndex = td.m_internal_output_index;
|
||||
ci->m_globalOutputIndex = td.m_global_output_index;
|
||||
ci->m_spent = td.m_spent;
|
||||
ci->m_frozen = td.m_frozen;
|
||||
ci->m_spentHeight = td.m_spent_height;
|
||||
ci->m_amount = td.m_amount;
|
||||
ci->m_rct = td.m_rct;
|
||||
ci->m_keyImageKnown = td.m_key_image_known;
|
||||
ci->m_pkIndex = td.m_pk_index;
|
||||
ci->m_subaddrIndex = td.m_subaddr_index.minor;
|
||||
ci->m_subaddrAccount = td.m_subaddr_index.major;
|
||||
ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(csub); // todo: this is expensive, cache maybe?
|
||||
ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index);
|
||||
ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image);
|
||||
ci->m_unlockTime = td.m_tx.unlock_time;
|
||||
ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td);
|
||||
ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key());
|
||||
ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
|
||||
ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid);
|
||||
ci->m_asset = td.asset_type;
|
||||
ci->m_type = td.m_tx.type;
|
||||
|
||||
m_rows.push_back(ci);
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::setFrozen(std::string public_key)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
if (!epee::string_tools::hex_to_pod(public_key, pk))
|
||||
{
|
||||
LOG_ERROR("Invalid public key: " << public_key);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->m_wallet->freeze(pk);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("setFrozen: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::setFrozen(int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
LOG_ERROR("Freezing coin: " << index);
|
||||
m_wallet->m_wallet->freeze(index);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("setLabel: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::thaw(std::string public_key)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
if (!epee::string_tools::hex_to_pod(public_key, pk))
|
||||
{
|
||||
LOG_ERROR("Invalid public key: " << public_key);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->m_wallet->thaw(pk);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("thaw: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::thaw(int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_wallet->m_wallet->thaw(index);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("thaw: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) {
|
||||
return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight);
|
||||
}
|
||||
|
||||
void CoinsImpl::setDescription(const std::string &public_key, const std::string &description)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
if (!epee::string_tools::hex_to_pod(public_key, pk))
|
||||
{
|
||||
LOG_ERROR("Invalid public key: " << public_key);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const size_t index = m_wallet->m_wallet->get_transfer_details(pk);
|
||||
const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index);
|
||||
m_wallet->m_wallet->set_tx_note(td.m_txid, description);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("setDescription: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef FEATHER_COINS_H
|
||||
#define FEATHER_COINS_H
|
||||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
namespace Monero {
|
||||
|
||||
class WalletImpl;
|
||||
|
||||
class CoinsImpl : public Coins
|
||||
{
|
||||
public:
|
||||
explicit CoinsImpl(WalletImpl * wallet);
|
||||
~CoinsImpl() override;
|
||||
int count() const override;
|
||||
CoinsInfo * coin(int index) const override;
|
||||
std::vector<CoinsInfo*> getAll() const override;
|
||||
void refresh() override;
|
||||
|
||||
void setFrozen(std::string public_key) override;
|
||||
void setFrozen(int index) override;
|
||||
void thaw(std::string public_key) override;
|
||||
void thaw(int index) override;
|
||||
|
||||
bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override;
|
||||
|
||||
void setDescription(const std::string &public_key, const std::string &description) override;
|
||||
|
||||
private:
|
||||
WalletImpl *m_wallet;
|
||||
std::vector<CoinsInfo*> m_rows;
|
||||
mutable boost::shared_mutex m_rowsMutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Bitmonero = Monero;
|
||||
|
||||
#endif //FEATHER_COINS_H
|
||||
@@ -0,0 +1,132 @@
|
||||
#include "coins_info.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
CoinsInfo::~CoinsInfo() = default;
|
||||
|
||||
CoinsInfoImpl::CoinsInfoImpl()
|
||||
: m_blockHeight(0)
|
||||
, m_internalOutputIndex(0)
|
||||
, m_globalOutputIndex(0)
|
||||
, m_spent(false)
|
||||
, m_frozen(false)
|
||||
, m_spentHeight(0)
|
||||
, m_amount(0)
|
||||
, m_rct(false)
|
||||
, m_keyImageKnown(false)
|
||||
, m_pkIndex(0)
|
||||
, m_subaddrAccount(0)
|
||||
, m_subaddrIndex(0)
|
||||
, m_unlockTime(0)
|
||||
, m_unlocked(false)
|
||||
, m_type(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CoinsInfoImpl::~CoinsInfoImpl() = default;
|
||||
|
||||
uint64_t CoinsInfoImpl::blockHeight() const
|
||||
{
|
||||
return m_blockHeight;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::hash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
size_t CoinsInfoImpl::internalOutputIndex() const {
|
||||
return m_internalOutputIndex;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::globalOutputIndex() const
|
||||
{
|
||||
return m_globalOutputIndex;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::spent() const
|
||||
{
|
||||
return m_spent;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::frozen() const
|
||||
{
|
||||
return m_frozen;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::spentHeight() const
|
||||
{
|
||||
return m_spentHeight;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::amount() const
|
||||
{
|
||||
return m_amount;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::rct() const {
|
||||
return m_rct;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::keyImageKnown() const {
|
||||
return m_keyImageKnown;
|
||||
}
|
||||
|
||||
size_t CoinsInfoImpl::pkIndex() const {
|
||||
return m_pkIndex;
|
||||
}
|
||||
|
||||
uint32_t CoinsInfoImpl::subaddrIndex() const {
|
||||
return m_subaddrIndex;
|
||||
}
|
||||
|
||||
uint32_t CoinsInfoImpl::subaddrAccount() const {
|
||||
return m_subaddrAccount;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::address() const {
|
||||
return m_address;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::addressLabel() const {
|
||||
return m_addressLabel;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::keyImage() const {
|
||||
return m_keyImage;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::unlockTime() const {
|
||||
return m_unlockTime;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::unlocked() const {
|
||||
return m_unlocked;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::pubKey() const {
|
||||
return m_pubKey;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::coinbase() const {
|
||||
return m_coinbase;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::description() const {
|
||||
return m_description;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::asset() const {
|
||||
return m_asset;
|
||||
}
|
||||
|
||||
uint8_t CoinsInfoImpl::type() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Bitmonero = Monero;
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef FEATHER_COINS_INFO_H
|
||||
#define FEATHER_COINS_INFO_H
|
||||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
namespace Monero {
|
||||
|
||||
class CoinsImpl;
|
||||
|
||||
class CoinsInfoImpl : public CoinsInfo
|
||||
{
|
||||
public:
|
||||
CoinsInfoImpl();
|
||||
~CoinsInfoImpl();
|
||||
|
||||
virtual uint64_t blockHeight() const override;
|
||||
virtual std::string hash() const override;
|
||||
virtual size_t internalOutputIndex() const override;
|
||||
virtual uint64_t globalOutputIndex() const override;
|
||||
virtual bool spent() const override;
|
||||
virtual bool frozen() const override;
|
||||
virtual uint64_t spentHeight() const override;
|
||||
virtual uint64_t amount() const override;
|
||||
virtual bool rct() const override;
|
||||
virtual bool keyImageKnown() const override;
|
||||
virtual size_t pkIndex() const override;
|
||||
virtual uint32_t subaddrIndex() const override;
|
||||
virtual uint32_t subaddrAccount() const override;
|
||||
virtual std::string address() const override;
|
||||
virtual std::string addressLabel() const override;
|
||||
virtual std::string keyImage() const override;
|
||||
virtual uint64_t unlockTime() const override;
|
||||
virtual bool unlocked() const override;
|
||||
virtual std::string pubKey() const override;
|
||||
virtual bool coinbase() const override;
|
||||
virtual std::string description() const override;
|
||||
virtual std::string asset() const override;
|
||||
virtual uint8_t type() const override;
|
||||
|
||||
private:
|
||||
uint64_t m_blockHeight;
|
||||
std::string m_hash;
|
||||
size_t m_internalOutputIndex;
|
||||
uint64_t m_globalOutputIndex;
|
||||
bool m_spent;
|
||||
bool m_frozen;
|
||||
uint64_t m_spentHeight;
|
||||
uint64_t m_amount;
|
||||
bool m_rct;
|
||||
bool m_keyImageKnown;
|
||||
size_t m_pkIndex;
|
||||
uint32_t m_subaddrIndex;
|
||||
uint32_t m_subaddrAccount;
|
||||
std::string m_address;
|
||||
std::string m_addressLabel;
|
||||
std::string m_keyImage;
|
||||
uint64_t m_unlockTime;
|
||||
bool m_unlocked;
|
||||
std::string m_pubKey;
|
||||
bool m_coinbase;
|
||||
std::string m_description;
|
||||
std::string m_asset;
|
||||
uint8_t m_type;
|
||||
|
||||
friend class CoinsImpl;
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Bitmonero = Monero;
|
||||
|
||||
#endif //FEATHER_COINS_INFO_H
|
||||
@@ -42,6 +42,8 @@
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "bc-ur/src/bc-ur.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
@@ -78,6 +80,22 @@ std::vector<std::string> PendingTransactionImpl::txid() const
|
||||
return txid;
|
||||
}
|
||||
|
||||
std::vector<std::string> PendingTransactionImpl::hex() const
|
||||
{
|
||||
std::vector<std::string> hexs;
|
||||
for (const auto &pt: m_pending_tx)
|
||||
hexs.push_back(epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(pt.tx)));
|
||||
return hexs;
|
||||
}
|
||||
|
||||
std::vector<std::string> PendingTransactionImpl::txKey() const
|
||||
{
|
||||
std::vector<std::string> keys;
|
||||
for (const auto& pt: m_pending_tx)
|
||||
keys.push_back(epee::string_tools::pod_to_hex(pt.tx_key));
|
||||
return keys;
|
||||
}
|
||||
|
||||
bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
|
||||
{
|
||||
|
||||
@@ -162,6 +180,37 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
|
||||
return m_status == Status_Ok;
|
||||
}
|
||||
|
||||
std::string PendingTransactionImpl::commitUR(int max_fragment_length) {
|
||||
|
||||
LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size());
|
||||
|
||||
try {
|
||||
std::string ptx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx);
|
||||
m_status = Status_Ok;
|
||||
auto urMessage = ur::string_to_bytes(ptx);
|
||||
ur::ByteVector cbor;
|
||||
ur::CborLite::encodeBytes(cbor, urMessage);
|
||||
std::string type;
|
||||
if (m_wallet.watchOnly()) {
|
||||
type = "xmr-txunsigned";
|
||||
} else {
|
||||
type = "xmr-txsigned";
|
||||
}
|
||||
ur::UR urData = ur::UR(type, cbor);
|
||||
auto encoder = ur::UREncoder(urData, max_fragment_length);
|
||||
std::string output;
|
||||
for(size_t i = 0; i < encoder.seq_len(); i++) {
|
||||
output.append("\n"+encoder.next_part());
|
||||
}
|
||||
return output;
|
||||
} catch (const std::exception &e) {
|
||||
m_errorString = string(tr("Unknown exception: ")) + e.what();
|
||||
m_status = Status_Error;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t PendingTransactionImpl::amount() const
|
||||
{
|
||||
uint64_t result = 0;
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
int status() const override;
|
||||
std::string errorString() const override;
|
||||
bool commit(const std::string &filename = "", bool overwrite = false) override;
|
||||
std::string commitUR(int max_fragment_length = 130) override;
|
||||
uint64_t amount() const override;
|
||||
uint64_t dust() const override;
|
||||
uint64_t fee() const override;
|
||||
@@ -58,6 +59,8 @@ public:
|
||||
std::string multisigSignData() override;
|
||||
void signMultisigTx() override;
|
||||
std::vector<std::string> signersKeys() const override;
|
||||
std::vector<std::string> hex() const override;
|
||||
std::vector<std::string> txKey() const override;
|
||||
|
||||
private:
|
||||
friend class WalletImpl;
|
||||
|
||||
@@ -34,16 +34,60 @@
|
||||
#include "wallet.h"
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace epee;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string to_hex_or_empty(const crypto::public_key &key)
|
||||
{
|
||||
if (key == crypto::null_pkey)
|
||||
return {};
|
||||
return string_tools::pod_to_hex(key);
|
||||
}
|
||||
|
||||
std::vector<std::string> extract_return_addresses(const cryptonote::transaction_prefix &tx)
|
||||
{
|
||||
std::vector<std::string> addresses;
|
||||
if (tx.type == cryptonote::transaction_type::STAKE)
|
||||
{
|
||||
auto hex = to_hex_or_empty(tx.protocol_tx_data.return_address);
|
||||
if (hex.empty())
|
||||
hex = to_hex_or_empty(tx.return_address);
|
||||
if (!hex.empty())
|
||||
addresses.push_back(hex);
|
||||
return addresses;
|
||||
}
|
||||
if (tx.type != cryptonote::transaction_type::PROTOCOL)
|
||||
return addresses;
|
||||
|
||||
std::unordered_set<std::string> seen;
|
||||
for (const auto &out : tx.vout)
|
||||
{
|
||||
crypto::public_key output_key = crypto::null_pkey;
|
||||
if (!cryptonote::get_output_public_key(out, output_key))
|
||||
continue;
|
||||
auto hex = to_hex_or_empty(output_key);
|
||||
if (hex.empty())
|
||||
continue;
|
||||
if (seen.emplace(hex).second)
|
||||
addresses.push_back(hex);
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TransactionHistory::~TransactionHistory() {}
|
||||
|
||||
|
||||
@@ -130,6 +174,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> in_payments;
|
||||
m_wallet->m_wallet->get_payments(in_payments, min_height, max_height);
|
||||
std::unordered_map<crypto::hash, TransactionInfoImpl*> protocol_by_hash;
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = in_payments.begin(); i != in_payments.end(); ++i) {
|
||||
const tools::wallet2::payment_details &pd = i->second;
|
||||
std::string payment_id = string_tools::pod_to_hex(i->first);
|
||||
@@ -152,6 +197,8 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_unlock_time = pd.m_unlock_time;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
|
||||
ti->m_asset = pd.m_asset_type;
|
||||
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
|
||||
protocol_by_hash.emplace(pd.m_tx_hash, ti);
|
||||
m_history.push_back(ti);
|
||||
|
||||
}
|
||||
@@ -175,13 +222,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
|
||||
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
|
||||
uint64_t amount = pd.m_amount_in - change - fee;
|
||||
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
|
||||
amount = pd.m_tx.amount_burnt;
|
||||
if (fee > amount) fee -= amount;
|
||||
}
|
||||
|
||||
|
||||
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
|
||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||
@@ -190,7 +231,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
TransactionInfoImpl * ti = new TransactionInfoImpl();
|
||||
ti->m_paymentid = payment_id;
|
||||
ti->m_amount = amount;
|
||||
ti->m_amount = pd.m_amount_in - change - fee;
|
||||
ti->m_fee = fee;
|
||||
ti->m_direction = TransactionInfo::Direction_Out;
|
||||
ti->m_hash = string_tools::pod_to_hex(hash);
|
||||
@@ -203,6 +244,7 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx.type));
|
||||
ti->m_asset = pd.m_tx.source_asset_type;
|
||||
ti->m_return_addresses = extract_return_addresses(pd.m_tx);
|
||||
|
||||
// single output transaction might contain multiple transfers
|
||||
for (const auto &d: pd.m_dests) {
|
||||
@@ -218,16 +260,8 @@ void TransactionHistoryImpl::refresh()
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments_out.begin(); i != upayments_out.end(); ++i) {
|
||||
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
|
||||
const crypto::hash &hash = i->first;
|
||||
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
|
||||
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
|
||||
uint64_t amount = pd.m_amount_in - change - fee;
|
||||
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
|
||||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
|
||||
amount = pd.m_tx.amount_burnt;
|
||||
if (fee > amount) fee -= amount;
|
||||
}
|
||||
|
||||
uint64_t amount = pd.m_amount_in;
|
||||
uint64_t fee = amount - pd.m_amount_out;
|
||||
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
|
||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||
payment_id = payment_id.substr(0,16);
|
||||
@@ -235,7 +269,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
TransactionInfoImpl * ti = new TransactionInfoImpl();
|
||||
ti->m_paymentid = payment_id;
|
||||
ti->m_amount = amount;
|
||||
ti->m_amount = amount - pd.m_change - fee;
|
||||
ti->m_fee = fee;
|
||||
ti->m_direction = TransactionInfo::Direction_Out;
|
||||
ti->m_failed = is_failed;
|
||||
@@ -249,6 +283,7 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_confirmations = 0;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx.type));
|
||||
ti->m_asset = pd.m_tx.source_asset_type;
|
||||
ti->m_return_addresses = extract_return_addresses(pd.m_tx);
|
||||
for (const auto &d : pd.m_dests)
|
||||
{
|
||||
ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id), d.asset_type});
|
||||
@@ -280,11 +315,23 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_confirmations = 0;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
|
||||
ti->m_asset = pd.m_asset_type;
|
||||
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
|
||||
protocol_by_hash.emplace(pd.m_tx_hash, ti);
|
||||
m_history.push_back(ti);
|
||||
|
||||
LOG_PRINT_L1(__FUNCTION__ << ": Unconfirmed payment found " << pd.m_amount);
|
||||
}
|
||||
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_protocol_payments;
|
||||
m_wallet->m_wallet->get_payments_out(confirmed_protocol_payments, min_height, max_height);
|
||||
for (const auto &entry : confirmed_protocol_payments)
|
||||
{
|
||||
const auto it = protocol_by_hash.find(entry.first);
|
||||
if (it == protocol_by_hash.end())
|
||||
continue;
|
||||
it->second->m_return_addresses = extract_return_addresses(entry.second.m_tx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -134,6 +134,11 @@ string TransactionInfoImpl::paymentId() const
|
||||
return m_paymentid;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &TransactionInfoImpl::returnAddresses() const
|
||||
{
|
||||
return m_return_addresses;
|
||||
}
|
||||
|
||||
const std::vector<TransactionInfo::Transfer> &TransactionInfoImpl::transfers() const
|
||||
{
|
||||
return m_transfers;
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
virtual std::string hash() const override;
|
||||
virtual std::time_t timestamp() const override;
|
||||
virtual std::string paymentId() const override;
|
||||
virtual const std::vector<std::string> &returnAddresses() const override;
|
||||
virtual const std::vector<Transfer> &transfers() const override;
|
||||
virtual uint64_t confirmations() const override;
|
||||
virtual uint64_t unlockTime() const override;
|
||||
@@ -81,6 +82,7 @@ private:
|
||||
std::string m_hash;
|
||||
std::time_t m_timestamp;
|
||||
std::string m_paymentid;
|
||||
std::vector<std::string> m_return_addresses;
|
||||
std::vector<Transfer> m_transfers;
|
||||
uint64_t m_confirmations;
|
||||
uint64_t m_unlock_time;
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <sstream>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "bc-ur/src/bc-ur.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
@@ -96,6 +98,46 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string UnsignedTransactionImpl::signUR(int max_fragment_length)
|
||||
{
|
||||
if(m_wallet.watchOnly())
|
||||
{
|
||||
m_errorString = tr("This is a watch only wallet");
|
||||
m_status = Status_Error;
|
||||
return "";
|
||||
}
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
try
|
||||
{
|
||||
tools::wallet2::signed_tx_set signed_txes;
|
||||
std::string signedTx = m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes);
|
||||
if (signedTx.empty())
|
||||
{
|
||||
m_errorString = tr("Failed to sign transaction");
|
||||
m_status = Status_Error;
|
||||
return "";
|
||||
}
|
||||
auto urMessage = ur::string_to_bytes(signedTx);
|
||||
ur::ByteVector cbor;
|
||||
ur::CborLite::encodeBytes(cbor, urMessage);
|
||||
std::string type = "xmr-txsigned";
|
||||
ur::UR urData = ur::UR(type, cbor);
|
||||
auto encoder = ur::UREncoder(urData, max_fragment_length);
|
||||
std::string output;
|
||||
for(size_t i = 0; i < encoder.seq_len(); i++) {
|
||||
output.append("\n"+encoder.next_part());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
m_errorString = string(tr("Failed to sign transaction")) + e.what();
|
||||
m_status = Status_Error;
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message)
|
||||
{
|
||||
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
uint64_t txCount() const override;
|
||||
// sign txs and save to file
|
||||
bool sign(const std::string &signedFileName) override;
|
||||
std::string signUR(int max_fragment_length = 130) override;
|
||||
std::string confirmationMessage() const override {return m_confirmationMessage;}
|
||||
uint64_t minMixinCount() const override;
|
||||
|
||||
|
||||
+695
-69
@@ -36,6 +36,7 @@
|
||||
#include "transaction_history.h"
|
||||
#include "address_book.h"
|
||||
#include "subaddress.h"
|
||||
#include "coins.h"
|
||||
#include "subaddress_account.h"
|
||||
#include "common_defines.h"
|
||||
#include "common/util.h"
|
||||
@@ -46,8 +47,14 @@
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <boost/locale.hpp>
|
||||
#endif
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "bc-ur/src/bc-ur.hpp"
|
||||
#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)
|
||||
#include "device/device_io_dummy.hpp"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace cryptonote;
|
||||
@@ -56,8 +63,8 @@ using namespace cryptonote;
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI"
|
||||
|
||||
#define LOCK_REFRESH() \
|
||||
bool refresh_enabled = m_refreshEnabled; \
|
||||
m_refreshEnabled = false; \
|
||||
bool refresh_enabled = m_wallet->get_refresh_enabled(); \
|
||||
m_wallet->set_refresh_enabled(false); \
|
||||
m_wallet->stop(); \
|
||||
m_refreshCV.notify_one(); \
|
||||
boost::mutex::scoped_lock lock(m_refreshMutex); \
|
||||
@@ -438,9 +445,10 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
|
||||
m_wallet2Callback.reset(new Wallet2CallbackImpl(this));
|
||||
m_wallet->callback(m_wallet2Callback.get());
|
||||
m_refreshThreadDone = false;
|
||||
m_refreshEnabled = false;
|
||||
m_wallet->set_refresh_enabled(false);
|
||||
m_addressBook.reset(new AddressBookImpl(this));
|
||||
m_subaddress.reset(new SubaddressImpl(this));
|
||||
m_coins.reset(new CoinsImpl(this));
|
||||
m_subaddressAccount.reset(new SubaddressAccountImpl(this));
|
||||
|
||||
|
||||
@@ -696,6 +704,28 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletImpl::createFromPolyseed(const std::string &path, const std::string &password, const std::string &seed,
|
||||
const std::string &passphrase, bool newWallet, uint64_t restoreHeight)
|
||||
{
|
||||
clearStatus();
|
||||
m_recoveringFromSeed = !newWallet;
|
||||
m_recoveringFromDevice = false;
|
||||
|
||||
polyseed::data polyseed(POLYSEED_COIN);
|
||||
|
||||
try {
|
||||
auto lang = polyseed.decode(seed.data());
|
||||
m_wallet->set_seed_language(lang.name());
|
||||
m_wallet->generate(path, password, polyseed, passphrase, !newWallet);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
setStatusError(e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Wallet::Device WalletImpl::getDeviceType() const
|
||||
{
|
||||
return static_cast<Wallet::Device>(m_wallet->get_device_type());
|
||||
@@ -768,6 +798,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
|
||||
return status() == Status_Ok;
|
||||
}
|
||||
|
||||
bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string)
|
||||
{
|
||||
clearStatus();
|
||||
m_errorString.clear();
|
||||
|
||||
m_recoveringFromSeed = true;
|
||||
m_recoveringFromDevice = false;
|
||||
|
||||
// parse spend key
|
||||
crypto::secret_key spendkey;
|
||||
if (!spendkey_string.empty()) {
|
||||
cryptonote::blobdata spendkey_data;
|
||||
if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
|
||||
{
|
||||
setStatusError(tr("failed to parse secret spend key"));
|
||||
return false;
|
||||
}
|
||||
spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
|
||||
}
|
||||
|
||||
try {
|
||||
m_wallet->generate(path, password, spendkey, true, false);
|
||||
setSeedLanguage(language);
|
||||
} catch (const std::exception &e) {
|
||||
setStatusCritical(e.what());
|
||||
}
|
||||
return status() == Status_Ok;
|
||||
}
|
||||
|
||||
bool WalletImpl::close(bool store)
|
||||
{
|
||||
|
||||
@@ -798,14 +857,79 @@ bool WalletImpl::close(bool store)
|
||||
|
||||
std::string WalletImpl::seed(const std::string& seed_offset) const
|
||||
{
|
||||
if (checkBackgroundSync("cannot get seed"))
|
||||
if (checkBackgroundSync("cannot get seed")) {
|
||||
setStatusError("cannot get seed");
|
||||
return std::string();
|
||||
epee::wipeable_string seed;
|
||||
if (m_wallet)
|
||||
m_wallet->get_seed(seed, seed_offset);
|
||||
return std::string(seed.data(), seed.size()); // TODO
|
||||
}
|
||||
try {
|
||||
epee::wipeable_string seed;
|
||||
if (m_wallet)
|
||||
m_wallet->get_seed(seed, seed_offset);
|
||||
return std::string(seed.data(), seed.size()); // TODO
|
||||
} catch (const std::exception &e) {
|
||||
setStatusError(e.what());
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
void WalletImpl::setStoreTxInfo(bool store)
|
||||
{
|
||||
m_wallet->store_tx_info(store);
|
||||
}
|
||||
|
||||
bool WalletImpl::storeTxInfo() const
|
||||
{
|
||||
return m_wallet->store_tx_info();
|
||||
}
|
||||
|
||||
bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const
|
||||
{
|
||||
epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size());
|
||||
epee::wipeable_string passphrase_epee(passphrase.c_str(), passphrase.size());
|
||||
clearStatus();
|
||||
|
||||
if (!m_wallet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = m_wallet->get_polyseed(seed_words_epee, passphrase_epee);
|
||||
|
||||
seed_words.assign(seed_words_epee.data(), seed_words_epee.size());
|
||||
passphrase.assign(passphrase_epee.data(), passphrase_epee.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> Wallet::getPolyseedLanguages()
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> languages;
|
||||
|
||||
auto langs = polyseed::get_langs();
|
||||
for (const auto &lang : langs) {
|
||||
languages.emplace_back(std::pair<std::string, std::string>(lang.name_en(), lang.name()));
|
||||
}
|
||||
|
||||
return languages;
|
||||
}
|
||||
|
||||
bool Wallet::createPolyseed(std::string &seed_words, std::string &err, const std::string &language)
|
||||
{
|
||||
epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size());
|
||||
|
||||
try {
|
||||
polyseed::data polyseed(POLYSEED_COIN);
|
||||
polyseed.create(0);
|
||||
polyseed.encode(polyseed::get_lang_by_name(language), seed_words_epee);
|
||||
|
||||
seed_words.assign(seed_words_epee.data(), seed_words_epee.size());
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
err = e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
std::string WalletImpl::getSeedLanguage() const
|
||||
{
|
||||
return m_wallet->get_seed_language();
|
||||
@@ -926,12 +1050,12 @@ std::string WalletImpl::publicSpendKey() const
|
||||
std::vector<std::string> WalletImpl::carrotKeys() const
|
||||
{
|
||||
return {
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_master))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image))),
|
||||
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)))
|
||||
epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().s_master),
|
||||
epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().k_prove_spend),
|
||||
epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().s_view_balance),
|
||||
epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().k_view_incoming),
|
||||
epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().k_generate_image),
|
||||
epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().s_generate_address)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -947,22 +1071,22 @@ std::string WalletImpl::publicMultisigSignerKey() const
|
||||
|
||||
std::string WalletImpl::secretViewBalance() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance)));
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().s_view_balance);
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretProveSpend() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend)));
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().k_prove_spend);
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretGenerateAddress() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)));
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().s_generate_address);
|
||||
}
|
||||
|
||||
std::string WalletImpl::secretGenerateImage() const
|
||||
{
|
||||
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image)));
|
||||
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().k_generate_image);
|
||||
}
|
||||
|
||||
std::string WalletImpl::path() const
|
||||
@@ -978,6 +1102,7 @@ void WalletImpl::stop()
|
||||
bool WalletImpl::store(const std::string &path)
|
||||
{
|
||||
clearStatus();
|
||||
LOCK_REFRESH();
|
||||
try {
|
||||
if (path.empty()) {
|
||||
m_wallet->store();
|
||||
@@ -1043,6 +1168,24 @@ uint64_t WalletImpl::unlockedBalance(const std::string& asset, uint32_t accountI
|
||||
return m_wallet->unlocked_balance(accountIndex, asset, false);
|
||||
}
|
||||
|
||||
uint64_t WalletImpl::viewOnlyBalance(uint32_t accountIndex, const std::vector<std::string> &key_images, const std::string& asset) const
|
||||
{
|
||||
clearStatus();
|
||||
|
||||
std::vector<crypto::key_image> kis;
|
||||
for (const auto &key_image : key_images) {
|
||||
crypto::key_image ki;
|
||||
if (!epee::string_tools::hex_to_pod(key_image, ki))
|
||||
{
|
||||
setStatusError(tr("failed to parse key image"));
|
||||
return 0;
|
||||
}
|
||||
kis.push_back(ki);
|
||||
}
|
||||
|
||||
return m_wallet->view_only_balance(accountIndex, kis, asset);
|
||||
}
|
||||
|
||||
uint64_t WalletImpl::blockChainHeight() const
|
||||
{
|
||||
return m_wallet->get_blockchain_current_height();
|
||||
@@ -1176,6 +1319,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
|
||||
return transaction;
|
||||
}
|
||||
|
||||
|
||||
UnsignedTransaction *WalletImpl::loadUnsignedTxUR(const std::string &input) {
|
||||
clearStatus();
|
||||
UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this);
|
||||
auto decoder = ur::URDecoder();
|
||||
|
||||
std::string delimiter = "\n";
|
||||
std::string inp = input;
|
||||
size_t pos = 0;
|
||||
std::string token;
|
||||
while ((pos = inp.find(delimiter)) != std::string::npos) {
|
||||
token = inp.substr(0, pos);
|
||||
decoder.receive_part(token);
|
||||
inp.erase(0, pos + delimiter.length());
|
||||
}
|
||||
decoder.receive_part(inp);
|
||||
|
||||
if (decoder.is_failure()) {
|
||||
setStatusError(decoder.result_error().what());
|
||||
transaction->m_status = UnsignedTransaction::Status::Status_Error;
|
||||
transaction->m_errorString = errorString();
|
||||
return transaction;
|
||||
}
|
||||
|
||||
if (!decoder.is_complete()) {
|
||||
setStatusError("file ended but ur didn't complete");
|
||||
transaction->m_status = UnsignedTransaction::Status::Status_Error;
|
||||
transaction->m_errorString = errorString();
|
||||
return transaction;
|
||||
}
|
||||
|
||||
std::string data;
|
||||
auto cbor = decoder.result_ur().cbor();
|
||||
auto i = cbor.begin();
|
||||
auto end = cbor.end();
|
||||
ur::CborLite::decodeBytes(i, end, data);
|
||||
|
||||
if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){
|
||||
setStatusError(tr("Failed to load unsigned transactions"));
|
||||
transaction->m_status = UnsignedTransaction::Status::Status_Error;
|
||||
transaction->m_errorString = errorString();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
// Check tx data and construct confirmation message
|
||||
std::string extra_message;
|
||||
if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty())
|
||||
extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str();
|
||||
transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message);
|
||||
setStatus(transaction->status(), transaction->errorString());
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
bool WalletImpl::submitTransaction(const string &fileName) {
|
||||
clearStatus();
|
||||
if (checkBackgroundSync("cannot submit tx"))
|
||||
@@ -1196,6 +1394,61 @@ bool WalletImpl::submitTransaction(const string &fileName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WalletImpl::submitTransactionUR(const string &input) {
|
||||
clearStatus();
|
||||
auto decoder = ur::URDecoder();
|
||||
|
||||
std::string delimiter = "\n";
|
||||
std::string inp = input;
|
||||
size_t pos = 0;
|
||||
std::string token;
|
||||
while ((pos = inp.find(delimiter)) != std::string::npos) {
|
||||
token = inp.substr(0, pos);
|
||||
decoder.receive_part(token);
|
||||
inp.erase(0, pos + delimiter.length());
|
||||
}
|
||||
decoder.receive_part(inp);
|
||||
|
||||
if (decoder.is_failure()) {
|
||||
setStatusError(decoder.result_error().what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!decoder.is_complete()) {
|
||||
setStatusError("file ended but ur didn't complete");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string data;
|
||||
auto cbor = decoder.result_ur().cbor();
|
||||
auto i = cbor.begin();
|
||||
auto end = cbor.end();
|
||||
ur::CborLite::decodeBytes(i, end, data);
|
||||
if (checkBackgroundSync("cannot submit tx"))
|
||||
return false;
|
||||
std::unique_ptr<PendingTransactionImpl> transaction(new PendingTransactionImpl(*this));
|
||||
|
||||
bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL);
|
||||
if (!r) {
|
||||
setStatus(Status_Ok, tr("Failed to load transaction from file"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!transaction->commit()) {
|
||||
setStatusError(transaction->m_errorString);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WalletImpl::hasUnknownKeyImages() const
|
||||
{
|
||||
return m_wallet->has_unknown_key_images();
|
||||
}
|
||||
|
||||
bool WalletImpl::exportKeyImages(const string &filename, bool all)
|
||||
{
|
||||
if (m_wallet->watch_only())
|
||||
@@ -1223,6 +1476,39 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string WalletImpl::exportKeyImagesUR(size_t max_fragment_length, bool all)
|
||||
{
|
||||
if (m_wallet->watch_only())
|
||||
{
|
||||
setStatusError(tr("Wallet is view only"));
|
||||
return "";
|
||||
}
|
||||
if (checkBackgroundSync("cannot export key images"))
|
||||
return "";
|
||||
|
||||
try
|
||||
{
|
||||
std::string keyImages = m_wallet->export_key_images_str(all);
|
||||
auto urMessage = ur::string_to_bytes(keyImages);
|
||||
ur::ByteVector cbor;
|
||||
ur::CborLite::encodeBytes(cbor, urMessage);
|
||||
ur::UR urData = ur::UR("xmr-keyimage", cbor);
|
||||
auto encoder = ur::UREncoder(urData, max_fragment_length);
|
||||
std::string output;
|
||||
for(size_t i = 0; i < encoder.seq_len(); i++) {
|
||||
output.append("\n"+encoder.next_part());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR("Error exporting key images: " << e.what());
|
||||
setStatusError(e.what());
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool WalletImpl::importKeyImages(const string &filename)
|
||||
{
|
||||
if (checkBackgroundSync("cannot import key images"))
|
||||
@@ -1248,6 +1534,62 @@ bool WalletImpl::importKeyImages(const string &filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WalletImpl::importKeyImagesUR(const string &input)
|
||||
{
|
||||
if (checkBackgroundSync("cannot import key images"))
|
||||
return false;
|
||||
if (!trustedDaemon()) {
|
||||
setStatusError(tr("Key images can only be imported with a trusted daemon"));
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
auto decoder = ur::URDecoder();
|
||||
std::string delimiter = "\n";
|
||||
std::string inp = input;
|
||||
size_t pos = 0;
|
||||
std::string token;
|
||||
while ((pos = inp.find(delimiter)) != std::string::npos) {
|
||||
token = inp.substr(0, pos);
|
||||
decoder.receive_part(token);
|
||||
inp.erase(0, pos + delimiter.length());
|
||||
}
|
||||
decoder.receive_part(inp);
|
||||
|
||||
if (decoder.is_failure()) {
|
||||
setStatusError(decoder.result_error().what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!decoder.is_complete()) {
|
||||
setStatusError("file ended but ur didn't complete");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string data;
|
||||
auto cbor = decoder.result_ur().cbor();
|
||||
auto i = cbor.begin();
|
||||
auto end = cbor.end();
|
||||
ur::CborLite::decodeBytes(i, end, data);
|
||||
|
||||
uint64_t spent = 0, unspent = 0;
|
||||
|
||||
uint64_t height = m_wallet->import_key_images_str(data, spent, unspent);
|
||||
LOG_PRINT_L2("Signed key images imported to height " << height << ", "
|
||||
<< print_money(spent) << " spent, " << print_money(unspent) << " unspent");
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR("Error exporting key images: " << e.what());
|
||||
setStatusError(string(tr("Failed to import key images: ")) + e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WalletImpl::exportOutputs(const string &filename, bool all)
|
||||
{
|
||||
if (checkBackgroundSync("cannot export outputs"))
|
||||
@@ -1280,6 +1622,40 @@ bool WalletImpl::exportOutputs(const string &filename, bool all)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string WalletImpl::exportOutputsUR(size_t max_fragment_length, bool all)
|
||||
{
|
||||
|
||||
if (checkBackgroundSync("cannot export outputs"))
|
||||
return "";
|
||||
if (m_wallet->key_on_device())
|
||||
{
|
||||
setStatusError(string(tr("Not supported on HW wallets.")));
|
||||
return "";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::string data = m_wallet->export_outputs_to_str(all);
|
||||
auto urMessage = ur::string_to_bytes(data);
|
||||
ur::ByteVector cbor;
|
||||
ur::CborLite::encodeBytes(cbor, urMessage);
|
||||
ur::UR urData = ur::UR("xmr-output", cbor);
|
||||
auto encoder = ur::UREncoder(urData, max_fragment_length);
|
||||
std::string output;
|
||||
for(size_t i = 0; i < encoder.seq_len(); i++) {
|
||||
output.append("\n"+encoder.next_part());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR("Error exporting outputs: " << e.what());
|
||||
setStatusError(string(tr("Error exporting outputs: ")) + e.what());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WalletImpl::importOutputs(const string &filename)
|
||||
{
|
||||
if (checkBackgroundSync("cannot import outputs"))
|
||||
@@ -1314,6 +1690,61 @@ bool WalletImpl::importOutputs(const string &filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WalletImpl::importOutputsUR(const string &input)
|
||||
{
|
||||
if (checkBackgroundSync("cannot import outputs"))
|
||||
return false;
|
||||
if (m_wallet->key_on_device())
|
||||
{
|
||||
setStatusError(string(tr("Not supported on HW wallets.")));
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto decoder = ur::URDecoder();
|
||||
|
||||
std::string delimiter = "\n";
|
||||
std::string inp = input;
|
||||
size_t pos = 0;
|
||||
std::string token;
|
||||
while ((pos = inp.find(delimiter)) != std::string::npos) {
|
||||
token = inp.substr(0, pos);
|
||||
decoder.receive_part(token);
|
||||
inp.erase(0, pos + delimiter.length());
|
||||
}
|
||||
decoder.receive_part(inp);
|
||||
|
||||
if (decoder.is_failure()) {
|
||||
setStatusError(decoder.result_error().what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!decoder.is_complete()) {
|
||||
setStatusError("file ended but ur didn't complete");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string data;
|
||||
auto cbor = decoder.result_ur().cbor();
|
||||
auto i = cbor.begin();
|
||||
auto end = cbor.end();
|
||||
ur::CborLite::decodeBytes(i, end, data);
|
||||
size_t n_outputs = m_wallet->import_outputs_from_str(std::string(data));
|
||||
LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported");
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR("Failed to import outputs: " << e.what());
|
||||
setStatusError(string(tr("Failed to import outputs: ")) + e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WalletImpl::scanTransactions(const std::vector<std::string> &txids)
|
||||
{
|
||||
if (checkBackgroundSync("cannot scan transactions"))
|
||||
@@ -1682,7 +2113,7 @@ PendingTransaction *WalletImpl::createAuditTransaction(
|
||||
// - unconfirmed_transfer_details;
|
||||
// - confirmed_transfer_details)
|
||||
|
||||
PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transaction_type &tx_type, const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
||||
PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transaction_type &tx_type, const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
|
||||
|
||||
{
|
||||
clearStatus();
|
||||
@@ -1721,55 +2152,114 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool error = false;
|
||||
for (size_t i = 0; i < dst_addr.size() && !error; i++) {
|
||||
if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr[i])) {
|
||||
// TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
|
||||
setStatusError(tr("Invalid destination address"));
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
if (info.has_payment_id) {
|
||||
if (!extra_nonce.empty()) {
|
||||
setStatusError(tr("a single transaction cannot use more than one payment id"));
|
||||
uint64_t max_coin_control_input = 0;
|
||||
uint64_t max_frozen_input = 0;
|
||||
try {
|
||||
bool error = false;
|
||||
uint64_t amountSum = 0;
|
||||
for (size_t i = 0; i < dst_addr.size() && !error; i++) {
|
||||
if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr[i])) {
|
||||
// TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
|
||||
setStatusError(tr("Invalid destination address"));
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
||||
}
|
||||
if (info.has_payment_id) {
|
||||
if (!extra_nonce.empty()) {
|
||||
setStatusError(tr("a single transaction cannot use more than one payment id"));
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
|
||||
}
|
||||
|
||||
if (amount) {
|
||||
cryptonote::tx_destination_entry de;
|
||||
de.original = dst_addr[i];
|
||||
de.addr = info.address;
|
||||
de.amount = (*amount)[i];
|
||||
de.asset_type = asset_type;
|
||||
de.is_subaddress = info.is_subaddress;
|
||||
de.is_integrated = info.has_payment_id;
|
||||
de.is_return = is_return;
|
||||
dsts.push_back(de);
|
||||
} else {
|
||||
if (subaddr_indices.empty()) {
|
||||
for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index)
|
||||
subaddr_indices.insert(index);
|
||||
if (amount) {
|
||||
cryptonote::tx_destination_entry de;
|
||||
de.original = dst_addr[i];
|
||||
de.addr = info.address;
|
||||
de.amount = (*amount)[i];
|
||||
de.asset_type = asset_type;
|
||||
amountSum += (*amount)[i];
|
||||
de.is_subaddress = info.is_subaddress;
|
||||
de.is_integrated = info.has_payment_id;
|
||||
de.is_return = is_return;
|
||||
dsts.push_back(de);
|
||||
} else {
|
||||
if (subaddr_indices.empty()) {
|
||||
for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index)
|
||||
subaddr_indices.insert(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
|
||||
setStatusError(tr("failed to set up payment id, though it was decoded correctly"));
|
||||
break;
|
||||
}
|
||||
try {
|
||||
|
||||
// uint64_t maxAllowedSpend = m_wallet->unlocked_balance(subaddr_account, true);
|
||||
// if (maxAllowedSpend < amountSum) {
|
||||
// error = true;
|
||||
// setStatusError(tr("Amount you are trying to spend is larger than unlocked amount"));
|
||||
// break;
|
||||
// }
|
||||
std::vector<crypto::key_image> preferred_input_list;
|
||||
if (!preferred_inputs.empty()) {
|
||||
LOG_ERROR("not empty");
|
||||
|
||||
for (const auto &public_key : preferred_inputs) {
|
||||
crypto::key_image keyImage;
|
||||
bool r = epee::string_tools::hex_to_pod(public_key, keyImage);
|
||||
if (!r) {
|
||||
error = true;
|
||||
setStatusError(tr("failed to parse key image"));
|
||||
break;
|
||||
}
|
||||
if (m_wallet->frozen(keyImage)) {
|
||||
error = true;
|
||||
setStatusError(tr("refusing to spend frozen coin"));
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i) {
|
||||
const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
|
||||
if (td.m_key_image == keyImage) {
|
||||
max_coin_control_input += td.amount();
|
||||
}
|
||||
if (td.m_frozen) {
|
||||
max_frozen_input += td.amount();
|
||||
}
|
||||
}
|
||||
|
||||
preferred_input_list.push_back(keyImage);
|
||||
}
|
||||
LOG_ERROR("not empty");
|
||||
|
||||
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_transfers_mutex);
|
||||
for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i) {
|
||||
const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
|
||||
LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<<td.m_spent << ";" << td.m_frozen << ";" << m_wallet->frozen(td));
|
||||
if (td.m_spent) continue;
|
||||
LOG_ERROR("is frozen");
|
||||
if (!td.m_frozen) {
|
||||
LOG_ERROR("isn't:");
|
||||
LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount());
|
||||
preferred_input_list.push_back(td.m_key_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &de : preferred_input_list) {
|
||||
LOG_ERROR("preferred input: " << de);
|
||||
}
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
|
||||
setStatusError(tr("failed to set up payment id, though it was decoded correctly"));
|
||||
break;
|
||||
}
|
||||
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
|
||||
fake_outs_count = m_wallet->adjust_mixin(mixin_count);
|
||||
|
||||
if (amount) {
|
||||
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, asset_type, asset_type, converted_tx_type, fake_outs_count, 0 /* unlock_time */,
|
||||
adjusted_priority,
|
||||
extra, subaddr_account, subaddr_indices);
|
||||
extra, subaddr_account, subaddr_indices, preferred_input_list);
|
||||
} else {
|
||||
std::vector<tools::wallet2::pending_tx> m_pending_txs;
|
||||
for (const auto subaddr_index : subaddr_indices) {
|
||||
@@ -1791,7 +2281,8 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
||||
adjusted_priority,
|
||||
extra,
|
||||
subaddr_account,
|
||||
std::set<uint32_t> {subaddr_index}
|
||||
std::set<uint32_t> {subaddr_index},
|
||||
preferred_input_list
|
||||
);
|
||||
m_pending_txs.insert(m_pending_txs.end(), result.begin(), result.end());
|
||||
|
||||
@@ -1829,6 +2320,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
||||
writer << boost::format(tr("not enough money to transfer, available only %s, sent amount %s")) %
|
||||
print_money(e.available()) %
|
||||
print_money(e.tx_amount());
|
||||
if (max_coin_control_input != 0 &&
|
||||
max_coin_control_input != e.available()) {
|
||||
writer << std::endl << boost::format(tr("In addition, coin control was enabled for this transaction, limiting available balance to %s. Make sure that you have enough outputs selected in coin control")) %
|
||||
print_money(max_coin_control_input);
|
||||
}
|
||||
if (max_frozen_input != 0 &&
|
||||
max_frozen_input != e.available()) {
|
||||
writer << std::endl << boost::format(tr("In addition, some a total of %s is frozen. Make sure that you have enough outputs unforzen outputs in coin control")) %
|
||||
print_money(max_frozen_input);
|
||||
}
|
||||
setStatusError(writer.str());
|
||||
} catch (const tools::error::not_enough_money& e) {
|
||||
std::ostringstream writer;
|
||||
@@ -1836,6 +2337,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
||||
writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) %
|
||||
print_money(e.available()) %
|
||||
print_money(e.tx_amount());
|
||||
if (max_coin_control_input != 0 &&
|
||||
max_coin_control_input != e.available()) {
|
||||
writer << std::endl << boost::format(tr("In addition, coin control was enabled for this transaction, limiting available balance to %s. Make sure that you have enough outputs selected in coin control")) %
|
||||
print_money(max_coin_control_input);
|
||||
}
|
||||
if (max_frozen_input != 0 &&
|
||||
max_frozen_input != e.available()) {
|
||||
writer << std::endl << boost::format(tr("In addition, some a total of %s is frozen. Make sure that you have enough outputs unforzen outputs in coin control")) %
|
||||
print_money(max_frozen_input);
|
||||
}
|
||||
setStatusError(writer.str());
|
||||
} catch (const tools::error::tx_not_possible& e) {
|
||||
std::ostringstream writer;
|
||||
@@ -1845,6 +2356,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
||||
print_money(e.tx_amount() + e.fee()) %
|
||||
print_money(e.tx_amount()) %
|
||||
print_money(e.fee());
|
||||
if (max_coin_control_input != 0 &&
|
||||
max_coin_control_input != e.available()) {
|
||||
writer << std::endl << boost::format(tr("In addition, coin control was enabled for this transaction, limiting available balance to %s. Make sure that you have enough outputs selected in coin control")) %
|
||||
print_money(max_coin_control_input);
|
||||
}
|
||||
if (max_frozen_input != 0 &&
|
||||
max_frozen_input != e.available()) {
|
||||
writer << std::endl << boost::format(tr("In addition, some a total of %s is frozen. Make sure that you have enough outputs unforzen outputs in coin control")) %
|
||||
print_money(max_frozen_input);
|
||||
}
|
||||
setStatusError(writer.str());
|
||||
} catch (const tools::error::not_enough_outs_to_mix& e) {
|
||||
std::ostringstream writer;
|
||||
@@ -1886,10 +2407,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
|
||||
}
|
||||
|
||||
PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count,
|
||||
const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
|
||||
const std::string &asset_type, const bool is_return, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
|
||||
|
||||
{
|
||||
return createTransactionMultDest(Monero::transaction_type::TRANSFER, std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, asset_type, is_return, priority, subaddr_account, subaddr_indices);
|
||||
return createTransactionMultDest(Monero::transaction_type::TRANSFER, std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, asset_type, is_return, priority, subaddr_account, subaddr_indices, preferred_inputs);
|
||||
}
|
||||
|
||||
PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
|
||||
@@ -2014,6 +2535,11 @@ AddressBook *WalletImpl::addressBook()
|
||||
return m_addressBook.get();
|
||||
}
|
||||
|
||||
Coins *WalletImpl::coins()
|
||||
{
|
||||
return m_coins.get();
|
||||
}
|
||||
|
||||
Subaddress *WalletImpl::subaddress()
|
||||
{
|
||||
return m_subaddress.get();
|
||||
@@ -2508,10 +3034,10 @@ void WalletImpl::refreshThreadFunc()
|
||||
}
|
||||
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired...");
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled);
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_wallet->get_refresh_enabled());
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status());
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan);
|
||||
if (m_refreshEnabled) {
|
||||
if (m_wallet->get_refresh_enabled()) {
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": refreshing...");
|
||||
doRefresh();
|
||||
}
|
||||
@@ -2541,12 +3067,12 @@ void WalletImpl::doRefresh()
|
||||
}
|
||||
m_wallet->find_and_save_rings(false);
|
||||
} else {
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced");
|
||||
LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced");
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
setStatusError(e.what());
|
||||
break;
|
||||
}while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested
|
||||
}while(m_wallet->get_refresh_enabled() && !rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested
|
||||
|
||||
if (m_wallet2Callback->getListener()) {
|
||||
m_wallet2Callback->getListener()->refreshed();
|
||||
@@ -2556,9 +3082,9 @@ void WalletImpl::doRefresh()
|
||||
|
||||
void WalletImpl::startRefresh()
|
||||
{
|
||||
if (!m_refreshEnabled) {
|
||||
if (!m_wallet->get_refresh_enabled()) {
|
||||
LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed...");
|
||||
m_refreshEnabled = true;
|
||||
m_wallet->set_refresh_enabled(true);
|
||||
m_refreshCV.notify_one();
|
||||
}
|
||||
}
|
||||
@@ -2568,7 +3094,7 @@ void WalletImpl::startRefresh()
|
||||
void WalletImpl::stopRefresh()
|
||||
{
|
||||
if (!m_refreshThreadDone) {
|
||||
m_refreshEnabled = false;
|
||||
m_wallet->set_refresh_enabled(false);
|
||||
m_refreshThreadDone = true;
|
||||
m_refreshCV.notify_one();
|
||||
m_refreshThread.join();
|
||||
@@ -2579,9 +3105,7 @@ void WalletImpl::pauseRefresh()
|
||||
{
|
||||
LOG_PRINT_L2(__FUNCTION__ << ": refresh paused...");
|
||||
// TODO synchronize access
|
||||
if (!m_refreshThreadDone) {
|
||||
m_refreshEnabled = false;
|
||||
}
|
||||
m_wallet->set_refresh_enabled(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -2925,6 +3449,108 @@ uint64_t WalletImpl::getBytesSent()
|
||||
return m_wallet->get_bytes_sent();
|
||||
}
|
||||
|
||||
|
||||
// HIDAPI_DUMMY
|
||||
bool Wallet::getStateIsConnected() {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return false;
|
||||
#else
|
||||
return hw::io::device_io_dummy::stateIsConnected;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned char* Wallet::getSendToDevice() {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return {};
|
||||
#else
|
||||
return hw::io::device_io_dummy::sendToDevice;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t Wallet::getSendToDeviceLength() {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return -1;
|
||||
#else
|
||||
return hw::io::device_io_dummy::sendToDeviceLength;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned char* Wallet::getReceivedFromDevice() {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return {};
|
||||
#else
|
||||
return hw::io::device_io_dummy::receivedFromDevice;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t Wallet::getReceivedFromDeviceLength() {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return -1;
|
||||
#else
|
||||
return hw::io::device_io_dummy::receivedFromDeviceLength;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Wallet::getWaitsForDeviceSend() {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return false;
|
||||
#else
|
||||
return hw::io::device_io_dummy::waitsForDeviceSend;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Wallet::getWaitsForDeviceReceive() {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return false;
|
||||
#else
|
||||
return hw::io::device_io_dummy::waitsForDeviceReceive;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Wallet::setDeviceReceivedData(unsigned char* data, size_t len) {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return;
|
||||
#else
|
||||
hw::io::device_io_dummy::setDeviceReceivedData(data, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Wallet::setDeviceSendData(unsigned char* data, size_t len) {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return;
|
||||
#else
|
||||
hw::io::device_io_dummy::sendToDevice = static_cast<unsigned char *>(malloc(len));
|
||||
hw::io::device_io_dummy::sendToDeviceLength = len;
|
||||
memset(hw::io::device_io_dummy::sendToDevice, 0, len);
|
||||
memcpy(hw::io::device_io_dummy::sendToDevice, data, len);
|
||||
hw::io::device_io_dummy::waitsForDeviceSend = false;
|
||||
hw::io::device_io_dummy::cv_send.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Wallet::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)) {
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))");
|
||||
return;
|
||||
#else
|
||||
hw::io::device_io_dummy::setLedgerCallback(sendToLedgerDevice);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string WalletImpl::serializeCacheToJson() const
|
||||
{
|
||||
return std::string(m_wallet->serialize_cache_to_json());
|
||||
}
|
||||
|
||||
YieldInfo * WalletImpl::getYieldInfo()
|
||||
{
|
||||
auto yi = new YieldInfoImpl(*this);
|
||||
|
||||
+48
-3
@@ -47,6 +47,7 @@ class PendingTransactionImpl;
|
||||
class UnsignedTransactionImpl;
|
||||
class AddressBookImpl;
|
||||
class SubaddressImpl;
|
||||
class CoinsImpl;
|
||||
class SubaddressAccountImpl;
|
||||
struct Wallet2CallbackImpl;
|
||||
|
||||
@@ -77,12 +78,28 @@ public:
|
||||
const std::string &address_string,
|
||||
const std::string &viewkey_string,
|
||||
const std::string &spendkey_string = "");
|
||||
bool recoverDeterministicWalletFromSpendKey(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
const std::string &spendkey_string);
|
||||
bool recoverFromDevice(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &device_name);
|
||||
|
||||
bool createFromPolyseed(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &seed,
|
||||
const std::string &passphrase = "",
|
||||
bool newWallet = true,
|
||||
uint64_t restoreHeight = 0);
|
||||
|
||||
Device getDeviceType() const override;
|
||||
bool close(bool store = true);
|
||||
std::string seed(const std::string& seed_offset = "") const override;
|
||||
bool getPolyseed(std::string &seed_words, std::string &passphrase) const override;
|
||||
void setStoreTxInfo(bool store) override;
|
||||
bool storeTxInfo() const override;
|
||||
|
||||
std::string getSeedLanguage() const override;
|
||||
void setSeedLanguage(const std::string &arg) override;
|
||||
// void setListener(Listener *) {}
|
||||
@@ -118,6 +135,7 @@ public:
|
||||
bool setProxy(const std::string &address) override;
|
||||
uint64_t balance(const std::string& asset, uint32_t accountIndex = 0) const override;
|
||||
uint64_t unlockedBalance(const std::string& asset, uint32_t accountIndex = 0) const override;
|
||||
uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector<std::string> &key_images, const std::string& asset = "SAL1") const override;
|
||||
uint64_t blockChainHeight() const override;
|
||||
uint64_t approximateBlockChainHeight() const override;
|
||||
uint64_t estimateBlockChainHeight() const override;
|
||||
@@ -173,20 +191,29 @@ public:
|
||||
const std::string &asset_type, const bool is_return,
|
||||
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) override;
|
||||
std::set<uint32_t> subaddr_indices = {},
|
||||
const std::set<std::string> &preferred_inputs = {}) override;
|
||||
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
|
||||
optional<uint64_t> amount, uint32_t mixin_count,
|
||||
const std::string &asset_type, const bool is_return,
|
||||
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) override;
|
||||
std::set<uint32_t> subaddr_indices = {},
|
||||
const std::set<std::string> &preferred_inputs = {}) override;
|
||||
virtual PendingTransaction * createSweepUnmixableTransaction() override;
|
||||
bool submitTransaction(const std::string &fileName) override;
|
||||
bool submitTransactionUR(const std::string &input) override;
|
||||
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override;
|
||||
virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override;
|
||||
bool hasUnknownKeyImages() const override;
|
||||
bool exportKeyImages(const std::string &filename, bool all = false) override;
|
||||
std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) override;
|
||||
bool importKeyImages(const std::string &filename) override;
|
||||
bool importKeyImagesUR(const std::string &input) override;
|
||||
bool exportOutputs(const std::string &filename, bool all = false) override;
|
||||
std::string exportOutputsUR(size_t max_fragment_length, bool all) override;
|
||||
bool importOutputs(const std::string &filename) override;
|
||||
bool importOutputsUR(const std::string &filename) override;
|
||||
bool scanTransactions(const std::vector<std::string> &txids) override;
|
||||
|
||||
bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional<std::string> &background_cache_password = optional<std::string>()) override;
|
||||
@@ -201,6 +228,7 @@ public:
|
||||
PendingTransaction::Priority priority) const override;
|
||||
virtual TransactionHistory * history() override;
|
||||
virtual AddressBook * addressBook() override;
|
||||
virtual Coins * coins() override;
|
||||
virtual Subaddress * subaddress() override;
|
||||
virtual SubaddressAccount * subaddressAccount() override;
|
||||
virtual void setListener(WalletListener * l) override;
|
||||
@@ -273,6 +301,7 @@ private:
|
||||
friend class TransactionHistoryImpl;
|
||||
friend struct Wallet2CallbackImpl;
|
||||
friend class AddressBookImpl;
|
||||
friend class CoinsImpl;
|
||||
friend class SubaddressImpl;
|
||||
friend class SubaddressAccountImpl;
|
||||
|
||||
@@ -289,10 +318,10 @@ private:
|
||||
std::unique_ptr<Wallet2CallbackImpl> m_wallet2Callback;
|
||||
std::unique_ptr<AddressBookImpl> m_addressBook;
|
||||
std::unique_ptr<SubaddressImpl> m_subaddress;
|
||||
std::unique_ptr<CoinsImpl> m_coins;
|
||||
std::unique_ptr<SubaddressAccountImpl> m_subaddressAccount;
|
||||
|
||||
// multi-threaded refresh stuff
|
||||
std::atomic<bool> m_refreshEnabled;
|
||||
std::atomic<bool> m_refreshThreadDone;
|
||||
std::atomic<int> m_refreshIntervalMillis;
|
||||
std::atomic<bool> m_refreshShouldRescan;
|
||||
@@ -313,6 +342,22 @@ private:
|
||||
// cache connection status to avoid unnecessary RPC calls
|
||||
mutable std::atomic<bool> m_is_connected;
|
||||
boost::optional<epee::net_utils::http::login> m_daemon_login{};
|
||||
|
||||
bool getStateIsConnected();
|
||||
|
||||
unsigned char *getSendToDevice();
|
||||
|
||||
size_t getSendToDeviceLength();
|
||||
|
||||
unsigned char *getReceivedFromDevice();
|
||||
|
||||
size_t getReceivedFromDeviceLength();
|
||||
|
||||
bool getWaitsForDeviceSend();
|
||||
|
||||
bool getWaitsForDeviceReceive();
|
||||
|
||||
virtual std::string serializeCacheToJson() const override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ struct PendingTransaction
|
||||
virtual std::string errorString() const = 0;
|
||||
// commit transaction or save to file if filename is provided.
|
||||
virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0;
|
||||
virtual std::string commitUR(int max_fragment_length = 130) = 0;
|
||||
virtual uint64_t amount() const = 0;
|
||||
virtual uint64_t dust() const = 0;
|
||||
virtual uint64_t fee() const = 0;
|
||||
@@ -161,6 +162,8 @@ struct PendingTransaction
|
||||
* @return vector of base58-encoded signers' public keys
|
||||
*/
|
||||
virtual std::vector<std::string> signersKeys() const = 0;
|
||||
virtual std::vector<std::string> hex() const = 0;
|
||||
virtual std::vector<std::string> txKey() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -195,7 +198,8 @@ struct UnsignedTransaction
|
||||
* @param signedFileName
|
||||
* return - true on success
|
||||
*/
|
||||
virtual bool sign(const std::string &signedFileName) = 0;
|
||||
virtual bool sign(const std::string &signedFileName) = 0;
|
||||
virtual std::string signUR(int max_fragment_length = 130) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -234,6 +238,7 @@ struct TransactionInfo
|
||||
virtual std::string hash() const = 0;
|
||||
virtual std::time_t timestamp() const = 0;
|
||||
virtual std::string paymentId() const = 0;
|
||||
virtual const std::vector<std::string> & returnAddresses() const = 0;
|
||||
//! only applicable for output transactions
|
||||
virtual const std::vector<Transfer> & transfers() const = 0;
|
||||
virtual Monero::transaction_type type() const = 0;
|
||||
@@ -299,6 +304,53 @@ struct AddressBook
|
||||
virtual int lookupPaymentID(const std::string &payment_id) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The CoinsInfo - interface for displaying coins information
|
||||
*/
|
||||
struct CoinsInfo
|
||||
{
|
||||
virtual ~CoinsInfo() = 0;
|
||||
|
||||
virtual uint64_t blockHeight() const = 0;
|
||||
virtual std::string hash() const = 0;
|
||||
virtual size_t internalOutputIndex() const = 0;
|
||||
virtual uint64_t globalOutputIndex() const = 0;
|
||||
virtual bool spent() const = 0;
|
||||
virtual bool frozen() const = 0;
|
||||
virtual uint64_t spentHeight() const = 0;
|
||||
virtual uint64_t amount() const = 0;
|
||||
virtual bool rct() const = 0;
|
||||
virtual bool keyImageKnown() const = 0;
|
||||
virtual size_t pkIndex() const = 0;
|
||||
virtual uint32_t subaddrIndex() const = 0;
|
||||
virtual uint32_t subaddrAccount() const = 0;
|
||||
virtual std::string address() const = 0;
|
||||
virtual std::string addressLabel() const = 0;
|
||||
virtual std::string keyImage() const = 0;
|
||||
virtual uint64_t unlockTime() const = 0;
|
||||
virtual bool unlocked() const = 0;
|
||||
virtual std::string pubKey() const = 0;
|
||||
virtual bool coinbase() const = 0;
|
||||
virtual std::string description() const = 0;
|
||||
virtual std::string asset() const = 0;
|
||||
virtual uint8_t type() const = 0;
|
||||
};
|
||||
|
||||
struct Coins
|
||||
{
|
||||
virtual ~Coins() = 0;
|
||||
virtual int count() const = 0;
|
||||
virtual CoinsInfo * coin(int index) const = 0;
|
||||
virtual std::vector<CoinsInfo*> getAll() const = 0;
|
||||
virtual void refresh() = 0;
|
||||
virtual void setFrozen(std::string public_key) = 0;
|
||||
virtual void setFrozen(int index) = 0;
|
||||
virtual void thaw(std::string public_key) = 0;
|
||||
virtual void thaw(int index) = 0;
|
||||
virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0;
|
||||
virtual void setDescription(const std::string &public_key, const std::string &description) = 0;
|
||||
};
|
||||
|
||||
struct SubaddressRow {
|
||||
public:
|
||||
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
|
||||
@@ -501,6 +553,8 @@ struct Wallet
|
||||
|
||||
virtual ~Wallet() = 0;
|
||||
virtual std::string seed(const std::string& seed_offset = "") const = 0;
|
||||
virtual void setStoreTxInfo(bool store) = 0;
|
||||
virtual bool storeTxInfo() const = 0;
|
||||
virtual std::string getSeedLanguage() const = 0;
|
||||
virtual void setSeedLanguage(const std::string &arg) = 0;
|
||||
//! returns wallet status (Status_Ok | Status_Error)
|
||||
@@ -710,6 +764,7 @@ struct Wallet
|
||||
result += unlockedBalance(asset, i);
|
||||
return result;
|
||||
}
|
||||
virtual uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector<std::string> &key_images = {}, const std::string& asset = "SAL1") const = 0;
|
||||
|
||||
/**
|
||||
* @brief watchOnly - checks if wallet is watch only
|
||||
@@ -790,6 +845,10 @@ struct Wallet
|
||||
static void warning(const std::string &category, const std::string &str);
|
||||
static void error(const std::string &category, const std::string &str);
|
||||
|
||||
virtual bool getPolyseed(std::string &seed, std::string &passphrase) const = 0;
|
||||
static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English");
|
||||
static std::vector<std::pair<std::string, std::string>> getPolyseedLanguages();
|
||||
|
||||
/**
|
||||
* @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds)
|
||||
*/
|
||||
@@ -969,7 +1028,8 @@ struct Wallet
|
||||
const std::string &asset_type, const bool is_return,
|
||||
PendingTransaction::Priority = PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
std::set<uint32_t> subaddr_indices = {},
|
||||
const std::set<std::string> &preferred_inputs = {}) = 0;
|
||||
|
||||
/*!
|
||||
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
|
||||
@@ -991,7 +1051,8 @@ struct Wallet
|
||||
const std::string &asset_type, const bool is_return,
|
||||
PendingTransaction::Priority = PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
std::set<uint32_t> subaddr_indices = {},
|
||||
const std::set<std::string> &preferred_inputs = {}) = 0;
|
||||
|
||||
/*!
|
||||
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
|
||||
@@ -1007,13 +1068,15 @@ struct Wallet
|
||||
* after object returned
|
||||
*/
|
||||
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0;
|
||||
|
||||
/*!
|
||||
virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) = 0;
|
||||
|
||||
/*!
|
||||
* \brief submitTransaction - submits transaction in signed tx file
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool submitTransaction(const std::string &fileName) = 0;
|
||||
|
||||
virtual bool submitTransactionUR(const std::string &input) = 0;
|
||||
|
||||
|
||||
/*!
|
||||
* \brief disposeTransaction - destroys transaction object
|
||||
@@ -1029,6 +1092,8 @@ struct Wallet
|
||||
virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
|
||||
PendingTransaction::Priority priority) const = 0;
|
||||
|
||||
virtual bool hasUnknownKeyImages() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief exportKeyImages - exports key images to file
|
||||
* \param filename
|
||||
@@ -1036,20 +1101,22 @@ struct Wallet
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0;
|
||||
|
||||
virtual std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) = 0;
|
||||
/*!
|
||||
* \brief importKeyImages - imports key images from file
|
||||
* \param filename
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool importKeyImages(const std::string &filename) = 0;
|
||||
virtual bool importKeyImagesUR(const std::string &input) = 0;
|
||||
|
||||
/*!
|
||||
* \brief importOutputs - exports outputs to file
|
||||
* \brief exportOutputs - exports outputs to file
|
||||
* \param filename
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool exportOutputs(const std::string &filename, bool all = false) = 0;
|
||||
virtual std::string exportOutputsUR(size_t max_fragment_length, bool all = false) = 0;
|
||||
|
||||
/*!
|
||||
* \brief importOutputs - imports outputs from file
|
||||
@@ -1057,6 +1124,7 @@ struct Wallet
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool importOutputs(const std::string &filename) = 0;
|
||||
virtual bool importOutputsUR(const std::string &filename) = 0;
|
||||
|
||||
/*!
|
||||
* \brief scanTransactions - scan a list of transaction ids, this operation may reveal the txids to the remote node and affect your privacy
|
||||
@@ -1103,6 +1171,7 @@ struct Wallet
|
||||
|
||||
virtual TransactionHistory * history() = 0;
|
||||
virtual AddressBook * addressBook() = 0;
|
||||
virtual Coins * coins() = 0;
|
||||
virtual Subaddress * subaddress() = 0;
|
||||
virtual SubaddressAccount * subaddressAccount() = 0;
|
||||
virtual void setListener(WalletListener *) = 0;
|
||||
@@ -1258,6 +1327,22 @@ struct Wallet
|
||||
//! get bytes sent
|
||||
virtual uint64_t getBytesSent() = 0;
|
||||
|
||||
// HIDAPI_DUMMY
|
||||
static bool getStateIsConnected();
|
||||
static unsigned char* getSendToDevice();
|
||||
static size_t getSendToDeviceLength();
|
||||
static unsigned char* getReceivedFromDevice();
|
||||
static size_t getReceivedFromDeviceLength();
|
||||
static bool getWaitsForDeviceSend();
|
||||
static bool getWaitsForDeviceReceive();
|
||||
|
||||
static void setDeviceReceivedData(unsigned char* data, size_t len);
|
||||
static void setDeviceSendData(unsigned char* data, size_t len);
|
||||
static void setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len));
|
||||
|
||||
//! serialize wallet cache to JSON
|
||||
virtual std::string serializeCacheToJson() const = 0;
|
||||
|
||||
//! get yield information
|
||||
virtual YieldInfo * getYieldInfo() = 0;
|
||||
};
|
||||
@@ -1367,6 +1452,25 @@ struct WalletManager
|
||||
return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief recover deterministic wallet from spend key.
|
||||
* \param path Name of wallet file to be created
|
||||
* \param password Password of wallet file
|
||||
* \param language language
|
||||
* \param nettype Network type
|
||||
* \param restoreHeight restore from start height
|
||||
* \param spendKeyString spend key
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
|
||||
*/
|
||||
virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
NetworkType nettype,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &spendKeyString,
|
||||
uint64_t kdf_rounds = 1) = 0;
|
||||
|
||||
/*!
|
||||
* \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead
|
||||
* \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted
|
||||
@@ -1418,6 +1522,27 @@ struct WalletManager
|
||||
uint64_t kdf_rounds = 1,
|
||||
WalletListener * listener = nullptr) = 0;
|
||||
|
||||
/*!
|
||||
* \brief creates a wallet from a polyseed mnemonic phrase
|
||||
* \param path Name of the wallet file to be created
|
||||
* \param password Password of wallet file
|
||||
* \param nettype Network type
|
||||
* \param mnemonic Polyseed mnemonic
|
||||
* \param passphrase Optional seed offset passphrase
|
||||
* \param newWallet Whether it is a new wallet
|
||||
* \param restoreHeight Override the embedded restore height
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* @return
|
||||
*/
|
||||
virtual Wallet * createWalletFromPolyseed(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
const std::string &mnemonic,
|
||||
const std::string &passphrase = "",
|
||||
bool newWallet = true,
|
||||
uint64_t restore_height = 0,
|
||||
uint64_t kdf_rounds = 1) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
|
||||
* \param wallet previously opened / created wallet instance
|
||||
|
||||
@@ -127,6 +127,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
NetworkType nettype,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &spendkey_string,
|
||||
uint64_t kdf_rounds)
|
||||
{
|
||||
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
|
||||
if(restoreHeight > 0){
|
||||
wallet->setRefreshFromBlockHeight(restoreHeight);
|
||||
}
|
||||
wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
@@ -156,6 +172,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype,
|
||||
const std::string &mnemonic, const std::string &passphrase,
|
||||
bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds)
|
||||
{
|
||||
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
|
||||
wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
|
||||
{
|
||||
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
|
||||
@@ -188,10 +213,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name,
|
||||
|
||||
bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const
|
||||
{
|
||||
hw::device::device_type type;
|
||||
bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds);
|
||||
device_type = static_cast<Wallet::Device>(type);
|
||||
return r;
|
||||
try {
|
||||
hw::device::device_type type;
|
||||
bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds);
|
||||
device_type = static_cast<Wallet::Device>(type);
|
||||
return r;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path)
|
||||
|
||||
@@ -67,6 +67,13 @@ public:
|
||||
const std::string &addressString,
|
||||
const std::string &viewKeyString,
|
||||
const std::string &spendKeyString = "") override;
|
||||
virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
NetworkType nettype,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &spendkey_string,
|
||||
uint64_t kdf_rounds) override;
|
||||
virtual Wallet * createWalletFromDevice(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
@@ -75,6 +82,16 @@ public:
|
||||
const std::string &subaddressLookahead = "",
|
||||
uint64_t kdf_rounds = 1,
|
||||
WalletListener * listener = nullptr) override;
|
||||
|
||||
virtual Wallet * createWalletFromPolyseed(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
const std::string &mnemonic,
|
||||
const std::string &passphrase,
|
||||
bool newWallet = true,
|
||||
uint64_t restore_height = 0,
|
||||
uint64_t kdf_rounds = 1) override;
|
||||
|
||||
virtual bool closeWallet(Wallet *wallet, bool store = true) override;
|
||||
bool walletExists(const std::string &path) override;
|
||||
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override;
|
||||
|
||||
@@ -973,14 +973,7 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
|
||||
// x = k_s + k^j_subext + k^g_o
|
||||
rct::key x;
|
||||
if (enote_scan_info.is_carrot) {
|
||||
// if we don't have the s_master key, derive view-only key image
|
||||
if (acc.get_keys().s_master == crypto::null_skey) {
|
||||
return acc.derive_key_image_view_only(enote_scan_info.address_spend_pubkey,
|
||||
enote_scan_info.sender_extension_g,
|
||||
enote_scan_info.sender_extension_t,
|
||||
rct::rct2pk(onetime_address));
|
||||
}
|
||||
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
|
||||
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
|
||||
enote_scan_info.sender_extension_g,
|
||||
enote_scan_info.sender_extension_t,
|
||||
rct::rct2pk(onetime_address));
|
||||
|
||||
+19
-33
@@ -91,7 +91,7 @@ static bool is_transfer_usable_for_input_selection(const wallet2::transfer_detai
|
||||
&& td.m_key_image_known
|
||||
&& !td.m_key_image_partial
|
||||
&& !td.m_frozen
|
||||
&& (top_block_index +1 >= td.m_block_height + blocks_locked_for)
|
||||
&& (top_block_index >= td.m_block_height + blocks_locked_for)
|
||||
// && last_locked_block_index <= top_block_index
|
||||
&& td.m_subaddr_index.major == from_account
|
||||
&& (from_subaddresses.empty() || from_subaddresses.count(td.m_subaddr_index.minor) == 1)
|
||||
@@ -148,10 +148,9 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
|
||||
const carrot::CarrotPaymentProposalV1 &payment_proposal)
|
||||
{
|
||||
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.amount,
|
||||
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey, /*m_is_carrot*/true},
|
||||
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey},
|
||||
payment_proposal.destination.is_subaddress);
|
||||
dest.is_integrated = payment_proposal.destination.payment_id != carrot::null_payment_id;
|
||||
dest.asset_type = payment_proposal.asset_type;
|
||||
return dest;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -166,11 +165,9 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
|
||||
address_view_pubkey),
|
||||
"make_tx_destination_entry: view-key multiplication failed");
|
||||
|
||||
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
|
||||
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey, /*m_is_carrot*/true},
|
||||
payment_proposal.subaddr_index.index.is_subaddress());
|
||||
dest.asset_type = payment_proposal.proposal.asset_type;
|
||||
return dest;
|
||||
return cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
|
||||
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey},
|
||||
payment_proposal.subaddr_index.index.is_subaddress());
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -349,7 +346,7 @@ std::vector<cryptonote::tx_source_entry> get_sources(
|
||||
w.get_outs(outs, selected_transfers, fake_outputs_count, true, valid_public_keys_cache); // may throw
|
||||
|
||||
LOG_PRINT_L2("preparing outputs");
|
||||
size_t out_index = 0;
|
||||
size_t i = 0, out_index = 0;
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
for(size_t idx: selected_transfers)
|
||||
{
|
||||
@@ -390,6 +387,7 @@ std::vector<cryptonote::tx_source_entry> get_sources(
|
||||
oe.second.mask = std::get<2>(outs[out_index][n]);
|
||||
src.outputs.push_back(oe);
|
||||
}
|
||||
++i;
|
||||
|
||||
//paste real transaction to the random index
|
||||
auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
|
||||
@@ -817,14 +815,8 @@ bool get_address_openings_x_y(
|
||||
if (return_output_map.find(rct::rct2pk(src.outputs[src.real_output].second.dest)) != return_output_map.end())
|
||||
{
|
||||
const auto &return_output = return_output_map.at(rct::rct2pk(src.outputs[src.real_output].second.dest));
|
||||
bool r = w.get_account().try_searching_for_opening_for_onetime_address(
|
||||
return_output.K_spend_pubkey,
|
||||
return_output.sum_g,
|
||||
return_output.sender_extension_t,
|
||||
x_out,
|
||||
y_out
|
||||
);
|
||||
CHECK_AND_ASSERT_THROW_MES(r, "Failed to obtain openings for onetime address (return_payment)");
|
||||
x_out = return_output.x;
|
||||
y_out = return_output.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1096,14 +1088,14 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
|
||||
const auto &sources = tx_proposal.sources;
|
||||
|
||||
// inputs
|
||||
// uint64_t amount_in = 0;
|
||||
uint64_t amount_in = 0;
|
||||
rct::carrot_ctkeyV inSk;
|
||||
inSk.reserve(sources.size());
|
||||
std::vector<uint64_t> inamounts;
|
||||
std::vector<unsigned int> index;
|
||||
for (const auto& src: sources)
|
||||
{
|
||||
// amount_in += src.amount;
|
||||
amount_in += src.amount;
|
||||
inamounts.push_back(src.amount);
|
||||
index.push_back(src.real_output);
|
||||
|
||||
@@ -1152,7 +1144,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
|
||||
}
|
||||
|
||||
// outputs
|
||||
// uint64_t amount_out = 0;
|
||||
uint64_t amount_out = 0;
|
||||
std::vector<uint64_t> outamounts;
|
||||
rct::keyV destinations;
|
||||
std::vector<std::string> destination_asset_types;
|
||||
@@ -1162,7 +1154,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
|
||||
destinations.push_back(rct::pk2rct(oep.enote.onetime_address));
|
||||
destination_asset_types.push_back(oep.enote.asset_type);
|
||||
outamounts.push_back(oep.amount);
|
||||
// amount_out += oep.amount;
|
||||
amount_out += oep.amount;
|
||||
|
||||
rct::ctkey key;
|
||||
key.mask = rct::sk2rct(oep.amount_blinding_factor);
|
||||
@@ -1296,8 +1288,7 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
|
||||
carrot::encrypted_payment_id_t encrypted_payment_id;
|
||||
std::vector<std::pair<bool, std::size_t>> sorted_payment_proposal_indices;
|
||||
carrot::get_output_enote_proposals_from_proposal_v1(tx_proposal,
|
||||
&account.s_view_balance_dev,
|
||||
///*s_view_balance_dev=*/nullptr,
|
||||
/*s_view_balance_dev=*/nullptr,
|
||||
&account.k_view_incoming_dev,
|
||||
output_enote_proposals,
|
||||
encrypted_payment_id,
|
||||
@@ -1369,16 +1360,11 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
|
||||
ptx.change_dts = change_dts;
|
||||
ptx.selected_transfers = std::move(selected_transfers);
|
||||
ptx.key_images = key_images_string.str();
|
||||
if (ephemeral_privkeys.size() == 1) {
|
||||
ptx.tx_key = ephemeral_privkeys.at(0);
|
||||
ptx.additional_tx_keys.clear();
|
||||
} else if (ephemeral_privkeys.size() == 2 && shared_ephemeral_pubkey) {
|
||||
ptx.tx_key = (ephemeral_privkeys.at(0) == crypto::null_skey) ? ephemeral_privkeys.at(1) : ephemeral_privkeys.at(0);
|
||||
ptx.additional_tx_keys.clear();
|
||||
} else {
|
||||
ptx.tx_key = crypto::null_skey;
|
||||
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
|
||||
}
|
||||
ptx.tx_key = shared_ephemeral_pubkey ? ephemeral_privkeys.at(0) : crypto::null_skey;
|
||||
if (shared_ephemeral_pubkey)
|
||||
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
|
||||
else
|
||||
ptx.additional_tx_keys.clear();
|
||||
ptx.dests = std::move(dests);
|
||||
ptx.multisig_sigs = {};
|
||||
ptx.multisig_tx_key_entropy = {};
|
||||
|
||||
+380
-517
File diff suppressed because it is too large
Load Diff
+49
-17
@@ -78,6 +78,7 @@
|
||||
#include "message_store.h"
|
||||
#include "wallet_light_rpc.h"
|
||||
#include "wallet_rpc_helpers.h"
|
||||
#include "polyseed/polyseed.hpp"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
|
||||
@@ -993,6 +994,20 @@ private:
|
||||
void generate(const std::string& wallet_, const epee::wipeable_string& password,
|
||||
const epee::wipeable_string& multisig_data, bool create_address_file = false);
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet from a polyseed.
|
||||
* @param wallet_ Name of wallet file
|
||||
* @param password Password of wallet file
|
||||
* @param seed Polyseed data
|
||||
* @param passphrase Optional seed offset passphrase
|
||||
* @param recover Whether it is a restore
|
||||
* @param restoreHeight Override the embedded restore height
|
||||
* @param create_address_file Whether to create an address file
|
||||
*/
|
||||
void generate(const std::string& wallet_, const epee::wipeable_string& password,
|
||||
const polyseed::data &seed, const epee::wipeable_string& passphrase = "",
|
||||
bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false);
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \param wallet_ Name of wallet file
|
||||
@@ -1171,6 +1186,8 @@ private:
|
||||
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
|
||||
const std::string &proxy = "");
|
||||
bool set_proxy(const std::string &address);
|
||||
bool get_refresh_enabled();
|
||||
void set_refresh_enabled(bool val);
|
||||
|
||||
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
|
||||
|
||||
@@ -1186,6 +1203,15 @@ private:
|
||||
bool is_deterministic() const;
|
||||
bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
|
||||
|
||||
/*!
|
||||
* \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet.
|
||||
* @param seed Polyseed mnemonic phrase
|
||||
* @param passphrase Seed offset passphrase that was used to restore the wallet
|
||||
* @return Returns true if the wallet has a polyseed.
|
||||
* Note: both the mnemonic phrase and the passphrase are needed to recover the wallet
|
||||
*/
|
||||
bool get_polyseed(epee::wipeable_string& seed, epee::wipeable_string &passphrase) const;
|
||||
|
||||
/*!
|
||||
* \brief Gets the seed language
|
||||
*/
|
||||
@@ -1246,6 +1272,7 @@ private:
|
||||
// locked & unlocked balance of given or current subaddress account
|
||||
uint64_t balance(uint32_t subaddr_index_major, const std::string& asset_type, bool strict) const;
|
||||
uint64_t unlocked_balance(uint32_t subaddr_index_major, const std::string& asset_type, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL);
|
||||
uint64_t view_only_balance(uint32_t index_major, const std::vector<crypto::key_image>& selected_inputs = {}, const std::string& asset_type = "SAL1");
|
||||
// locked & unlocked balance per subaddress of given or current subaddress account
|
||||
std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major, const std::string& asset_type, bool strict) const;
|
||||
std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, const std::string& asset_type, bool strict);
|
||||
@@ -1294,8 +1321,8 @@ private:
|
||||
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
|
||||
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
||||
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {});
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
std::vector<wallet2::pending_tx> create_transactions_return(std::vector<size_t> transfers_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, const std::string& asset_type, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
@@ -1477,7 +1504,7 @@ private:
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
MAGIC_FIELD("monero wallet cache")
|
||||
VERSION_FIELD(3)
|
||||
VERSION_FIELD(2)
|
||||
FIELD(m_blockchain)
|
||||
FIELD(m_transfers)
|
||||
FIELD(m_transfers_indices)
|
||||
@@ -1521,17 +1548,15 @@ private:
|
||||
}
|
||||
FIELD(m_background_sync_data)
|
||||
FIELD(m_subaddresses_extended)
|
||||
if (version == 2)
|
||||
{
|
||||
serializable_unordered_map<crypto::public_key, carrot::return_output_info_retired_t> old_roi;
|
||||
FIELD(old_roi)
|
||||
m_return_output_info = {};
|
||||
m_force_rescan = true;
|
||||
return true;
|
||||
}
|
||||
FIELD(m_return_output_info)
|
||||
END_SERIALIZE()
|
||||
|
||||
/*!
|
||||
* \brief Serialize wallet cache fields to JSON
|
||||
* \return const char* pointing to JSON string containing all cache fields
|
||||
*/
|
||||
const char* serialize_cache_to_json() const;
|
||||
|
||||
/*!
|
||||
* \brief Check if wallet keys and bin files exist
|
||||
* \param file_path Wallet file path
|
||||
@@ -1563,8 +1588,6 @@ private:
|
||||
void set_default_priority(uint32_t p) { m_default_priority = p; }
|
||||
bool auto_refresh() const { return m_auto_refresh; }
|
||||
void auto_refresh(bool r) { m_auto_refresh = r; }
|
||||
bool force_rescan() const { return m_force_rescan; }
|
||||
void force_rescan(bool r) { m_force_rescan = r; }
|
||||
AskPasswordType ask_password() const { return m_ask_password; }
|
||||
void ask_password(AskPasswordType ask) { m_ask_password = ask; }
|
||||
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
|
||||
@@ -1677,6 +1700,7 @@ private:
|
||||
uint64_t get_num_rct_outputs();
|
||||
size_t get_num_transfer_details() const { return m_transfers.size(); }
|
||||
const transfer_details &get_transfer_details(size_t idx) const;
|
||||
size_t get_transfer_details(const crypto::public_key &pk) const;
|
||||
|
||||
uint8_t estimate_current_hard_fork(const uint64_t height = 0) const;
|
||||
uint8_t get_current_hard_fork();
|
||||
@@ -1695,8 +1719,8 @@ private:
|
||||
/*!
|
||||
* \brief Calculates the approximate blockchain height from current date/time.
|
||||
*/
|
||||
uint64_t get_approximate_blockchain_height() const;
|
||||
uint64_t estimate_blockchain_height();
|
||||
uint64_t get_approximate_blockchain_height(uint64_t time = 0) const;
|
||||
uint64_t estimate_blockchain_height(uint64_t time = 0);
|
||||
std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct);
|
||||
std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
|
||||
std::vector<size_t> select_available_unmixable_outputs();
|
||||
@@ -1765,9 +1789,11 @@ private:
|
||||
std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const;
|
||||
void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc);
|
||||
bool export_key_images(const std::string &filename, bool all = false) const;
|
||||
std::string export_key_images_str(bool all) const;
|
||||
std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images(bool all = false) const;
|
||||
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
|
||||
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
|
||||
uint64_t import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent);
|
||||
bool import_key_images(std::vector<crypto::key_image> key_images, size_t offset=0, boost::optional<std::unordered_set<size_t>> selected_transfers=boost::none);
|
||||
bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false);
|
||||
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
|
||||
@@ -1788,6 +1814,7 @@ private:
|
||||
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
|
||||
|
||||
uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31
|
||||
uint64_t get_blockchain_height_by_timestamp(uint64_t timestamp);
|
||||
|
||||
bool is_synced();
|
||||
|
||||
@@ -1888,7 +1915,9 @@ private:
|
||||
void freeze(size_t idx);
|
||||
void thaw(size_t idx);
|
||||
bool frozen(size_t idx) const;
|
||||
void freeze(const crypto::public_key &pk);
|
||||
void freeze(const crypto::key_image &ki);
|
||||
void thaw(const crypto::public_key &pk);
|
||||
void thaw(const crypto::key_image &ki);
|
||||
bool frozen(const crypto::key_image &ki) const;
|
||||
bool frozen(const transfer_details &td) const;
|
||||
@@ -1929,6 +1958,8 @@ private:
|
||||
|
||||
static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
|
||||
|
||||
boost::shared_mutex m_transfers_mutex;
|
||||
|
||||
bool get_pricing_record(oracle::pricing_record& pr, const uint64_t height);
|
||||
bool get_circulating_supply(std::vector<std::pair<std::string, std::string>> &amounts);
|
||||
bool get_yield_info(std::vector<cryptonote::yield_block_info>& ybi_data);
|
||||
@@ -2076,7 +2107,7 @@ private:
|
||||
std::vector<uint64_t> get_unspent_amounts_vector(bool strict);
|
||||
uint64_t get_dynamic_base_fee_estimate();
|
||||
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
|
||||
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::string& asset_type);
|
||||
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::string& asset_type, const std::vector<crypto::key_image>& preferred_input_list);
|
||||
void set_spent(size_t idx, uint64_t height);
|
||||
void set_spent(const crypto::key_image &ki, const uint64_t height);
|
||||
void set_unspent(size_t idx);
|
||||
@@ -2189,6 +2220,7 @@ private:
|
||||
|
||||
boost::recursive_mutex m_daemon_rpc_mutex;
|
||||
|
||||
bool m_refreshEnabled;
|
||||
bool m_trusted_daemon;
|
||||
i_wallet2_callback* m_callback;
|
||||
hw::device::device_type m_key_device_type;
|
||||
@@ -2197,6 +2229,7 @@ private:
|
||||
std::string seed_language; /*!< Language of the mnemonics (seed). */
|
||||
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */
|
||||
bool m_watch_only; /*!< no spend key */
|
||||
bool m_polyseed;
|
||||
bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */
|
||||
uint32_t m_multisig_threshold;
|
||||
std::vector<crypto::public_key> m_multisig_signers;
|
||||
@@ -2210,7 +2243,6 @@ private:
|
||||
uint32_t m_default_priority;
|
||||
RefreshType m_refresh_type;
|
||||
bool m_auto_refresh;
|
||||
bool m_force_rescan = false;
|
||||
bool m_first_refresh_done;
|
||||
uint64_t m_refresh_from_block_height;
|
||||
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
#include "wallet2.h"
|
||||
#include "serialization/binary_archive.h"
|
||||
#include "serialization/json_archive.h"
|
||||
#include "serialization/serialization.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
static void write_escaped_json_string(std::ostream& os, const std::string& str)
|
||||
{
|
||||
for (char c : str) {
|
||||
switch (c) {
|
||||
case '"': os << "\\\""; break;
|
||||
case '\\': os << "\\\\"; break;
|
||||
case '\n': os << "\\n"; break;
|
||||
case '\r': os << "\\r"; break;
|
||||
case '\t': os << "\\t"; break;
|
||||
case '\b': os << "\\b"; break;
|
||||
case '\f': os << "\\f"; break;
|
||||
default: os << c; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void post_process_json(std::string& json)
|
||||
{
|
||||
// ": ," --> ": null,"
|
||||
size_t pos = 0;
|
||||
while ((pos = json.find(": ,", pos)) != std::string::npos) {
|
||||
json.replace(pos, 3, ": null,");
|
||||
pos += 7;
|
||||
}
|
||||
|
||||
// ": }" --> ": null}"
|
||||
pos = 0;
|
||||
while ((pos = json.find(": }", pos)) != std::string::npos) {
|
||||
json.replace(pos, 3, ": null}");
|
||||
pos += 7;
|
||||
}
|
||||
|
||||
// ": ]" --> ": null]"
|
||||
pos = 0;
|
||||
while ((pos = json.find(": ]", pos)) != std::string::npos) {
|
||||
json.replace(pos, 3, ": null]");
|
||||
pos += 7;
|
||||
}
|
||||
|
||||
// "key": number"hexstring" --> "key": "numberhexstring"
|
||||
pos = 0;
|
||||
while (pos < json.length()) {
|
||||
size_t colon_pos = json.find(": ", pos);
|
||||
if (colon_pos == std::string::npos) break;
|
||||
|
||||
size_t value_start = colon_pos + 2;
|
||||
if (value_start >= json.length()) break;
|
||||
|
||||
if (std::isdigit(json[value_start])) {
|
||||
size_t quote_pos = json.find('"', value_start);
|
||||
if (quote_pos != std::string::npos && quote_pos < json.find_first_of(",}]", value_start)) {
|
||||
size_t closing_quote = json.find('"', quote_pos + 1);
|
||||
if (closing_quote != std::string::npos && closing_quote < json.find_first_of(",}]", value_start)) {
|
||||
std::string digits;
|
||||
size_t digit_end = value_start;
|
||||
while (digit_end < quote_pos && std::isdigit(json[digit_end])) {
|
||||
digits += json[digit_end];
|
||||
digit_end++;
|
||||
}
|
||||
|
||||
if (digit_end == quote_pos && !digits.empty()) {
|
||||
std::string hex_part = json.substr(quote_pos + 1, closing_quote - quote_pos - 1);
|
||||
|
||||
std::string replacement = "\"" + digits + hex_part + "\"";
|
||||
json.replace(value_start, closing_quote - value_start + 1, replacement);
|
||||
pos = value_start + replacement.length();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pos = colon_pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
const char* wallet2::serialize_cache_to_json() const
|
||||
{
|
||||
static std::string json_result;
|
||||
|
||||
try
|
||||
{
|
||||
std::stringstream oss;
|
||||
json_archive<true> ar(oss, true); // true for pretty printing
|
||||
|
||||
ar.begin_object();
|
||||
|
||||
// MAGIC_FIELD("monero wallet cache")
|
||||
std::string magic = "monero wallet cache";
|
||||
ar.tag("magic");
|
||||
ar.serialize_blob((void*)magic.data(), magic.size());
|
||||
if (!ar.good()) {
|
||||
json_result = "{\"error\":\"Failed to serialize magic field\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// VERSION_FIELD(2)
|
||||
uint32_t version = 2;
|
||||
ar.tag("version");
|
||||
ar.serialize_varint(version);
|
||||
if (!ar.good()) {
|
||||
json_result = "{\"error\":\"Failed to serialize version field\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_blockchain) - hashchain type, has serialization support
|
||||
ar.tag("m_blockchain");
|
||||
if (!::serialization::serialize(ar, const_cast<hashchain&>(m_blockchain))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_blockchain\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_transfers) - transfer_container (std::vector<transfer_details>)
|
||||
ar.tag("m_transfers");
|
||||
if (!::serialization::serialize(ar, const_cast<transfer_container&>(m_transfers))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_transfers\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_account_public_address) - cryptonote::account_public_address
|
||||
ar.tag("m_account_public_address");
|
||||
if (!::serialization::serialize(ar, const_cast<cryptonote::account_public_address&>(m_account_public_address))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_account_public_address\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_key_images) - serializable_unordered_map<crypto::key_image, size_t>
|
||||
ar.tag("m_key_images");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::key_image, size_t>&>(m_key_images))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_key_images\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_unconfirmed_txs) - serializable_unordered_map<crypto::hash, unconfirmed_transfer_details>
|
||||
ar.tag("m_unconfirmed_txs");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, unconfirmed_transfer_details>&>(m_unconfirmed_txs))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_unconfirmed_txs\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_payments) - payment_container (serializable_unordered_multimap<crypto::hash, payment_details>)
|
||||
ar.tag("m_payments");
|
||||
if (!::serialization::serialize(ar, const_cast<payment_container&>(m_payments))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_payments\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_tx_keys) - serializable_unordered_map<crypto::hash, crypto::secret_key>
|
||||
ar.tag("m_tx_keys");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, crypto::secret_key>&>(m_tx_keys))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_tx_keys\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_confirmed_txs) - serializable_unordered_map<crypto::hash, confirmed_transfer_details>
|
||||
ar.tag("m_confirmed_txs");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, confirmed_transfer_details>&>(m_confirmed_txs))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_confirmed_txs\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_tx_notes) - serializable_unordered_map<crypto::hash, std::string>
|
||||
ar.tag("m_tx_notes");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, std::string>&>(m_tx_notes))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_tx_notes\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_unconfirmed_payments) - serializable_unordered_multimap<crypto::hash, pool_payment_details>
|
||||
ar.tag("m_unconfirmed_payments");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_multimap<crypto::hash, pool_payment_details>&>(m_unconfirmed_payments))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_unconfirmed_payments\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_pub_keys) - serializable_unordered_map<crypto::public_key, size_t>
|
||||
ar.tag("m_pub_keys");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::public_key, size_t>&>(m_pub_keys))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_pub_keys\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_address_book) - std::vector<tools::wallet2::address_book_row>
|
||||
ar.tag("m_address_book");
|
||||
if (!::serialization::serialize(ar, const_cast<std::vector<tools::wallet2::address_book_row>&>(m_address_book))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_address_book\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_scanned_pool_txs[0]) - std::unordered_set<crypto::hash>
|
||||
ar.tag("m_scanned_pool_txs_0");
|
||||
if (!::serialization::serialize(ar, const_cast<std::unordered_set<crypto::hash>&>(m_scanned_pool_txs[0]))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_scanned_pool_txs[0]\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_scanned_pool_txs[1]) - std::unordered_set<crypto::hash>
|
||||
ar.tag("m_scanned_pool_txs_1");
|
||||
if (!::serialization::serialize(ar, const_cast<std::unordered_set<crypto::hash>&>(m_scanned_pool_txs[1]))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_scanned_pool_txs[1]\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_subaddresses) - serializable_unordered_map<crypto::public_key, cryptonote::subaddress_index>
|
||||
ar.tag("m_subaddresses");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::public_key, cryptonote::subaddress_index>&>(m_subaddresses))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_subaddresses\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_subaddress_labels) - std::vector<std::vector<std::string>> - manual JSON serialization
|
||||
oss << ", \n \"m_subaddress_labels\": [";
|
||||
for (size_t i = 0; i < m_subaddress_labels.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << "\n [";
|
||||
for (size_t j = 0; j < m_subaddress_labels[i].size(); ++j) {
|
||||
if (j > 0) oss << ", ";
|
||||
oss << "\"";
|
||||
write_escaped_json_string(oss, m_subaddress_labels[i][j]);
|
||||
oss << "\"";
|
||||
}
|
||||
oss << "]";
|
||||
}
|
||||
oss << "\n ]";
|
||||
|
||||
// FIELD(m_additional_tx_keys) - serializable_unordered_map<crypto::hash, std::vector<crypto::secret_key>>
|
||||
ar.tag("m_additional_tx_keys");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, std::vector<crypto::secret_key>>&>(m_additional_tx_keys))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_additional_tx_keys\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_attributes) - serializable_unordered_map<std::string, std::string> - manual JSON serialization
|
||||
oss << ", \n \"m_attributes\": {";
|
||||
bool first_attr = true;
|
||||
for (const auto& attr : m_attributes) {
|
||||
if (!first_attr) oss << ", ";
|
||||
first_attr = false;
|
||||
oss << "\n \"";
|
||||
write_escaped_json_string(oss, attr.first);
|
||||
oss << "\": \"";
|
||||
write_escaped_json_string(oss, attr.second);
|
||||
oss << "\"";
|
||||
}
|
||||
oss << "\n }";
|
||||
|
||||
// FIELD(m_account_tags) - std::pair<serializable_map<std::string, std::string>, std::vector<std::string>> - manual JSON serialization
|
||||
oss << ", \n \"m_account_tags\": {";
|
||||
oss << "\n \"tags_map\": {";
|
||||
bool first_tag = true;
|
||||
for (const auto& tag : m_account_tags.first) {
|
||||
if (!first_tag) oss << ", ";
|
||||
first_tag = false;
|
||||
oss << "\n \"";
|
||||
write_escaped_json_string(oss, tag.first);
|
||||
oss << "\": \"";
|
||||
write_escaped_json_string(oss, tag.second);
|
||||
oss << "\"";
|
||||
}
|
||||
oss << "\n },";
|
||||
oss << "\n \"account_list\": [";
|
||||
for (size_t i = 0; i < m_account_tags.second.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << "\n \"";
|
||||
write_escaped_json_string(oss, m_account_tags.second[i]);
|
||||
oss << "\"";
|
||||
}
|
||||
oss << "\n ]";
|
||||
oss << "\n }";
|
||||
|
||||
// FIELD(m_ring_history_saved) - bool
|
||||
// ar.tag("m_ring_history_saved");
|
||||
// ar.serialize_blob(&m_ring_history_saved, sizeof(m_ring_history_saved));
|
||||
// if (!ar.good()) {
|
||||
// json_result = "{\"error\":\"Failed to serialize m_ring_history_saved\"}";
|
||||
// return json_result.c_str();
|
||||
// }
|
||||
|
||||
// FIELD(m_last_block_reward) - uint64_t
|
||||
ar.tag("m_last_block_reward");
|
||||
ar.serialize_int(m_last_block_reward);
|
||||
if (!ar.good()) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_last_block_reward\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_tx_device) - serializable_unordered_map<crypto::hash, std::string>
|
||||
ar.tag("m_tx_device");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::hash, std::string>&>(m_tx_device))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_tx_device\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_device_last_key_image_sync) - uint64_t
|
||||
ar.tag("m_device_last_key_image_sync");
|
||||
ar.serialize_int(m_device_last_key_image_sync);
|
||||
if (!ar.good()) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_device_last_key_image_sync\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_cold_key_images) - serializable_unordered_map<crypto::public_key, crypto::key_image>
|
||||
ar.tag("m_cold_key_images");
|
||||
if (!::serialization::serialize(ar, const_cast<serializable_unordered_map<crypto::public_key, crypto::key_image>&>(m_cold_key_images))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_cold_key_images\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
// FIELD(m_rpc_client_secret_key) - crypto::secret_key
|
||||
// ar.tag("m_rpc_client_secret_key");
|
||||
// ar.serialize_blob(&m_rpc_client_secret_key, sizeof(m_rpc_client_secret_key));
|
||||
// if (!ar.good()) {
|
||||
// json_result = "{\"error\":\"Failed to serialize m_rpc_client_secret_key\"}";
|
||||
// return json_result.c_str();
|
||||
// }
|
||||
|
||||
// Version-dependent fields
|
||||
if (version >= 1) {
|
||||
// FIELD(m_has_ever_refreshed_from_node) - bool
|
||||
// ar.tag("m_has_ever_refreshed_from_node");
|
||||
// ar.serialize_blob(&m_has_ever_refreshed_from_node, sizeof(m_has_ever_refreshed_from_node));
|
||||
// if (!ar.good()) {
|
||||
// json_result = "{\"error\":\"Failed to serialize m_has_ever_refreshed_from_node\"}";
|
||||
// return json_result.c_str();
|
||||
// }
|
||||
}
|
||||
|
||||
if (version >= 2) {
|
||||
// FIELD(m_background_sync_data) - background_sync_data_t
|
||||
ar.tag("m_background_sync_data");
|
||||
if (!::serialization::serialize(ar, const_cast<background_sync_data_t&>(m_background_sync_data))) {
|
||||
json_result = "{\"error\":\"Failed to serialize m_background_sync_data\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
ar.end_object();
|
||||
|
||||
if (!ar.good()) {
|
||||
json_result = "{\"error\":\"Failed to finalize JSON serialization\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
|
||||
json_result = oss.str();
|
||||
|
||||
// Post-process to fix malformed JSON
|
||||
post_process_json(json_result);
|
||||
|
||||
return json_result.c_str();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
json_result = "{\"error\":\"Failed to serialize wallet cache: " + std::string(e.what()) + "\"}";
|
||||
return json_result.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
@@ -152,7 +152,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads
|
||||
{
|
||||
tpool.submit(&waiter, [&, i] {
|
||||
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i);
|
||||
// const uint8_t major_version = hashing_blob[0];
|
||||
const uint8_t major_version = hashing_blob[0];
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1302,7 +1302,7 @@ namespace tools
|
||||
{
|
||||
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
|
||||
uint32_t priority = m_wallet->adjust_priority(req.priority);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, type, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, type, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, {}, req.subtract_fee_from_outputs);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -3354,9 +3354,9 @@ namespace tools
|
||||
const auto &entry = ab[idx];
|
||||
std::string address;
|
||||
if (entry.m_has_payment_id)
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id);
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id, entry.m_is_carrot);
|
||||
else
|
||||
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
|
||||
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address, entry.m_is_carrot);
|
||||
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ set(unit_tests_sources
|
||||
carrot_mock_helpers.cpp
|
||||
carrot_sparc.cpp
|
||||
carrot_transcript_fixed.cpp
|
||||
carrot_tx_proof.cpp
|
||||
chacha.cpp
|
||||
checkpoints.cpp
|
||||
command_line.cpp
|
||||
|
||||
@@ -1,445 +0,0 @@
|
||||
// Copyright (c) 2025, Salvium (authors: SRCG, auruya)
|
||||
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
extern "C" {
|
||||
#include "crypto/crypto-ops.h"
|
||||
}
|
||||
#include "crypto/hash.h"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "mx25519.h"
|
||||
|
||||
#include "carrot_core/account.h"
|
||||
#include "carrot_impl/format_utils.h"
|
||||
#include "string_tools.h"
|
||||
|
||||
using namespace carrot;
|
||||
|
||||
static inline void random_carrot_keys(crypto::secret_key& a,
|
||||
crypto::public_key& A,
|
||||
crypto::secret_key& b,
|
||||
crypto::public_key& B,
|
||||
const bool create_subaddress)
|
||||
{
|
||||
// Generate a new CN address
|
||||
carrot::carrot_and_legacy_account alice;
|
||||
alice.generate();
|
||||
|
||||
const auto& keys = alice.get_keys();
|
||||
|
||||
// Check for subaddress
|
||||
if (create_subaddress) {
|
||||
|
||||
// Create subaddress
|
||||
carrot::subaddress_index_extended sie{{0,1}, AddressDeriveType::Carrot, false};
|
||||
carrot::CarrotDestinationV1 subaddr = alice.subaddress(sie);
|
||||
|
||||
a = keys.k_view_incoming;
|
||||
A = subaddr.address_view_pubkey;
|
||||
b = keys.k_prove_spend;
|
||||
B = subaddr.address_spend_pubkey;
|
||||
|
||||
} else {
|
||||
|
||||
a = keys.k_view_incoming;
|
||||
A = keys.m_carrot_account_address.m_view_public_key;
|
||||
b = keys.k_prove_spend;
|
||||
B = keys.m_carrot_account_address.m_spend_public_key;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(carrot_tx_proofs, fuzz_stability)
|
||||
{
|
||||
static const size_t ITER = 5000; // increase to 50k if needed
|
||||
|
||||
for (size_t i = 0; i < ITER; ++i)
|
||||
{
|
||||
// 1. Generate random Carrot recipient view & spend keys A/B/a/b
|
||||
bool use_subaddress = ((i & 1) == 1); // alternate: main/sub
|
||||
crypto::secret_key a;
|
||||
crypto::public_key A;
|
||||
crypto::secret_key b;
|
||||
crypto::public_key B;
|
||||
random_carrot_keys(a, A, b, B, use_subaddress);
|
||||
|
||||
// 2. Generate random tx private key r
|
||||
crypto::secret_key r;
|
||||
crypto::random32_unbiased((unsigned char*)unwrap(r).data);
|
||||
|
||||
// 3. Recipient can be main address (no B) or subaddress
|
||||
//const crypto::public_key *B_ptr = use_subaddress ? &B : nullptr;
|
||||
|
||||
// 4. Compute R = ConvertPointE(r * (G or B))
|
||||
crypto::public_key R_pk;
|
||||
mx25519_pubkey enote_ephemeral_pubkey_out;
|
||||
carrot::make_carrot_enote_ephemeral_pubkey(r,
|
||||
B,
|
||||
use_subaddress,
|
||||
enote_ephemeral_pubkey_out);
|
||||
R_pk = carrot::raw_byte_convert<crypto::public_key>(enote_ephemeral_pubkey_out);
|
||||
|
||||
// 5. Compute D = ConvertPointE(r * A)
|
||||
mx25519_pubkey s_sr;
|
||||
bool success = carrot::make_carrot_uncontextualized_shared_key_sender(r, A, s_sr);
|
||||
ASSERT_TRUE(success) << "failure to compute shared secret";
|
||||
crypto::public_key D_pk = carrot::raw_byte_convert<crypto::public_key>(s_sr);
|
||||
|
||||
// 6. Random prefix hash
|
||||
crypto::hash prefix_hash;
|
||||
for (int j = 0; j < 32; j++) prefix_hash.data[j] = rand() & 0xFF;
|
||||
|
||||
// 7. Prove
|
||||
crypto::signature sig;
|
||||
crypto::generate_carrot_tx_proof(
|
||||
prefix_hash,
|
||||
R_pk, A,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_pk, r, a, sig
|
||||
);
|
||||
|
||||
// 8. Verify
|
||||
bool ok = crypto::check_carrot_tx_proof(
|
||||
prefix_hash,
|
||||
R_pk, A,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_pk, sig
|
||||
);
|
||||
|
||||
ASSERT_TRUE(ok) << "failure at iteration " << i;
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 9. NEGATIVE TESTS
|
||||
// ---------------------------------------------------------
|
||||
|
||||
// 9a. Flip a bit in R
|
||||
{
|
||||
crypto::public_key R_bad = R_pk;
|
||||
R_bad.data[5] ^= 0x20;
|
||||
bool ok2 = crypto::check_carrot_tx_proof(
|
||||
prefix_hash, R_bad, A,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_pk, sig
|
||||
);
|
||||
ASSERT_FALSE(ok2);
|
||||
}
|
||||
|
||||
// 9b. Flip a bit in D
|
||||
{
|
||||
crypto::public_key D_bad = D_pk;
|
||||
D_bad.data[7] ^= 0x10;
|
||||
bool ok2 = crypto::check_carrot_tx_proof(
|
||||
prefix_hash, R_pk, A,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_bad, sig
|
||||
);
|
||||
ASSERT_FALSE(ok2);
|
||||
}
|
||||
|
||||
// 9c. Flip a bit in sig.c
|
||||
{
|
||||
crypto::signature sig_bad = sig;
|
||||
sig_bad.c.data[3] ^= 0x80;
|
||||
bool ok2 = crypto::check_carrot_tx_proof(
|
||||
prefix_hash, R_pk, A,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_pk, sig_bad
|
||||
);
|
||||
ASSERT_FALSE(ok2);
|
||||
}
|
||||
|
||||
// 9d. Flip a bit in sign_mask
|
||||
{
|
||||
crypto::signature sig_bad = sig;
|
||||
sig_bad.sign_mask ^= 0x01; // flip R_sign
|
||||
bool ok2 = crypto::check_carrot_tx_proof(
|
||||
prefix_hash, R_pk, A,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_pk, sig_bad
|
||||
);
|
||||
ASSERT_FALSE(ok2);
|
||||
}
|
||||
|
||||
// 9e. Flip a bit in sig.r
|
||||
{
|
||||
crypto::signature sig_bad = sig;
|
||||
sig_bad.r.data[0] ^= 0x40;
|
||||
bool ok2 = crypto::check_carrot_tx_proof(
|
||||
prefix_hash, R_pk, A,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_pk, sig_bad
|
||||
);
|
||||
ASSERT_FALSE(ok2);
|
||||
}
|
||||
|
||||
// 9f. Flip a bit in A
|
||||
{
|
||||
crypto::public_key A_bad = A;
|
||||
A_bad.data[12] ^= 0x08;
|
||||
bool ok2 = crypto::check_carrot_tx_proof(
|
||||
prefix_hash, R_pk, A_bad,
|
||||
use_subaddress ? boost::make_optional(B) : boost::none,
|
||||
D_pk, sig
|
||||
);
|
||||
ASSERT_FALSE(ok2);
|
||||
}
|
||||
|
||||
// 9g. Flip a bit in B (when subaddress)
|
||||
if (use_subaddress)
|
||||
{
|
||||
crypto::public_key B_bad = B;
|
||||
B_bad.data[9] ^= 0x40;
|
||||
bool ok2 = crypto::check_carrot_tx_proof(
|
||||
prefix_hash, R_pk, A,
|
||||
boost::make_optional(B_bad),
|
||||
D_pk, sig
|
||||
);
|
||||
ASSERT_FALSE(ok2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(carrot_tx_proofs, known_values_mutation_rejection_main_address)
|
||||
{
|
||||
// Real Salvium Carrot addresses for reproducible test (testnet) (main address)
|
||||
// Sender: SC1ToqKSXRw3rE3rNQzjUA1nntvHhM6id3coWry25y4jUvHDRKDRGFv1vJRCTMHWUyVXct2aedmvzUfd3CofjTpKEhHmpnftqZk
|
||||
// Recipient: SC1ToumwqT5GeDcn2JjrHoFPUMPMcNu73STkP3Sono94iZpizheMJ3ADpGGE92Wcb7b3gDCxKFT5NEp94ueQQMbu8VBYyAGHEy7
|
||||
// Tx ID: e8729399d2af3dede8c110e370b3505c1669f4fba593fd740a16c1e4f425a728
|
||||
|
||||
// Tx priv key
|
||||
crypto::secret_key r;
|
||||
const char* r_hex = "748b8f3131661fd8ee0f06ab3de53649381522185ea6e8148c8daf395ded010d";
|
||||
ASSERT_TRUE(epee::string_tools::hex_to_pod(r_hex, r));
|
||||
|
||||
// Recipient's actual Carrot keys from Salvium network
|
||||
crypto::secret_key a, b; // view-incoming and prove-spend priv keys
|
||||
crypto::public_key A, B; // view and spend pub keys
|
||||
|
||||
// recipient view-incoming key
|
||||
const char* a_hex = "88b6442966238bfd349eb412fe55a717b7b363175b48ae88c34a252dd8868e05";
|
||||
ASSERT_TRUE(epee::string_tools::hex_to_pod(a_hex, a));
|
||||
|
||||
// recipient prove-spend key
|
||||
const char* b_hex = "94607b25bceb408bbb5393d25729777e92686b9447f9c989f2e735e878950d0b";
|
||||
ASSERT_TRUE(epee::string_tools::hex_to_pod(b_hex, b));
|
||||
|
||||
// Generate pub keys from priv keys
|
||||
ASSERT_TRUE(crypto::secret_key_to_public_key(a, A));
|
||||
ASSERT_TRUE(crypto::secret_key_to_public_key(b, B));
|
||||
|
||||
// Compute R = rG (main address case)
|
||||
mx25519_pubkey enote_ephemeral_pubkey_out;
|
||||
carrot::make_carrot_enote_ephemeral_pubkey(r, B, false, enote_ephemeral_pubkey_out);
|
||||
crypto::public_key R_G = carrot::raw_byte_convert<crypto::public_key>(enote_ephemeral_pubkey_out);
|
||||
|
||||
// Compute D = rA
|
||||
mx25519_pubkey s_sr;
|
||||
ASSERT_TRUE(carrot::make_carrot_uncontextualized_shared_key_sender(r, A, s_sr));
|
||||
crypto::public_key D = carrot::raw_byte_convert<crypto::public_key>(s_sr);
|
||||
|
||||
// Fixed message hash
|
||||
crypto::hash prefix_hash;
|
||||
memset(&prefix_hash, 0, 32);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
prefix_hash.data[i] = i; // Sequential bytes for reproducibility
|
||||
}
|
||||
|
||||
// Generate proof with known values
|
||||
crypto::signature sig;
|
||||
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, r, a, sig);
|
||||
|
||||
// Verify original proof works
|
||||
ASSERT_TRUE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig));
|
||||
|
||||
// Test mutations are rejected
|
||||
|
||||
// 1. Mutate R by flipping one bit
|
||||
{
|
||||
crypto::public_key R_mutated = R_G;
|
||||
R_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_mutated, A, boost::none, D, sig));
|
||||
}
|
||||
|
||||
// 2. Mutate D by flipping one bit
|
||||
{
|
||||
crypto::public_key D_mutated = D;
|
||||
D_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D_mutated, sig));
|
||||
}
|
||||
|
||||
// 3. Mutate A by flipping one bit
|
||||
{
|
||||
crypto::public_key A_mutated = A;
|
||||
A_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A_mutated, boost::none, D, sig));
|
||||
}
|
||||
|
||||
// 4. Mutate prefix_hash by flipping one bit
|
||||
{
|
||||
crypto::hash hash_mutated = prefix_hash;
|
||||
hash_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(hash_mutated, R_G, A, boost::none, D, sig));
|
||||
}
|
||||
|
||||
// 5. Mutate signature.c by flipping one bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.c.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
|
||||
}
|
||||
|
||||
// 6. Mutate signature.r by flipping one bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.r.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
|
||||
}
|
||||
|
||||
// 7. Mutate signature.sign_mask by flipping R_sign bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.sign_mask ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
|
||||
}
|
||||
|
||||
// 8. Mutate signature.sign_mask by flipping D_sign bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.sign_mask ^= 0x02;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(carrot_tx_proofs, known_values_mutation_rejection_subaddress)
|
||||
{
|
||||
// Real Salvium Carrot addresses for reproducible test (testnet) (subaddress)
|
||||
// Sender: SC1ToqKSXRw3rE3rNQzjUA1nntvHhM6id3coWry25y4jUvHDRKDRGFv1vJRCTMHWUyVXct2aedmvzUfd3CofjTpKEhHmpnftqZk
|
||||
// Recipient: SC1TsCevdYfZRZCRb83i5caRDJDb45UoqBeynNciVW8LAihKchQ4MfmW7PmPJquaXDZyntRcJCfduPVtdFUb5nsQLokFM434usw
|
||||
// Tx ID: 01f5e1e56df714e3af919ab443b1acc4b1bebffed03198a9aaf3d22449809453
|
||||
|
||||
// Tx priv key
|
||||
crypto::secret_key r, a;
|
||||
const char* r_hex = "4eccc86c26ac250132d141d1b447e1fe25d0d1e4f3f2d7f3aca10a2633b52808";
|
||||
ASSERT_TRUE(epee::string_tools::hex_to_pod(r_hex, r));
|
||||
|
||||
// view and spend pub keys
|
||||
crypto::public_key A, B;
|
||||
|
||||
cryptonote::address_parse_info info;
|
||||
std::string recipent_address_str = "SC1TsCevdYfZRZCRb83i5caRDJDb45UoqBeynNciVW8LAihKchQ4MfmW7PmPJquaXDZyntRcJCfduPVtdFUb5nsQLokFM434usw";
|
||||
ASSERT_TRUE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::TESTNET, recipent_address_str));
|
||||
A = info.address.m_view_public_key;
|
||||
B = info.address.m_spend_public_key;
|
||||
a = crypto::null_skey;
|
||||
|
||||
// Compute R = rG (subaddress case)
|
||||
mx25519_pubkey enote_ephemeral_pubkey_out;
|
||||
carrot::make_carrot_enote_ephemeral_pubkey(r, B, true, enote_ephemeral_pubkey_out);
|
||||
crypto::public_key R_G = carrot::raw_byte_convert<crypto::public_key>(enote_ephemeral_pubkey_out);
|
||||
|
||||
// Compute D = rA
|
||||
mx25519_pubkey s_sr;
|
||||
ASSERT_TRUE(carrot::make_carrot_uncontextualized_shared_key_sender(r, A, s_sr));
|
||||
crypto::public_key D = carrot::raw_byte_convert<crypto::public_key>(s_sr);
|
||||
|
||||
// Fixed message hash
|
||||
crypto::hash prefix_hash;
|
||||
memset(&prefix_hash, 0, 32);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
prefix_hash.data[i] = i; // Sequential bytes for reproducibility
|
||||
}
|
||||
|
||||
// Generate proof with known values
|
||||
crypto::signature sig;
|
||||
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, B, D, r, a, sig);
|
||||
|
||||
// Verify original proof works
|
||||
ASSERT_TRUE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig));
|
||||
|
||||
// Test mutations are rejected
|
||||
|
||||
// 1. Mutate R by flipping one bit
|
||||
{
|
||||
crypto::public_key R_mutated = R_G;
|
||||
R_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_mutated, A, B, D, sig));
|
||||
}
|
||||
|
||||
// 2. Mutate D by flipping one bit
|
||||
{
|
||||
crypto::public_key D_mutated = D;
|
||||
D_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D_mutated, sig));
|
||||
}
|
||||
|
||||
// 3. Mutate A by flipping one bit
|
||||
{
|
||||
crypto::public_key A_mutated = A;
|
||||
A_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A_mutated, B, D, sig));
|
||||
}
|
||||
|
||||
// 4. Mutate prefix_hash by flipping one bit
|
||||
{
|
||||
crypto::hash hash_mutated = prefix_hash;
|
||||
hash_mutated.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(hash_mutated, R_G, A, B, D, sig));
|
||||
}
|
||||
|
||||
// 5. Mutate signature.c by flipping one bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.c.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
|
||||
}
|
||||
|
||||
// 6. Mutate signature.r by flipping one bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.r.data[0] ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig_mutated));
|
||||
}
|
||||
|
||||
// 7. Mutate signature.sign_mask by flipping R_sign bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.sign_mask ^= 0x01;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig_mutated));
|
||||
}
|
||||
|
||||
// 8. Mutate signature.sign_mask by flipping D_sign bit
|
||||
{
|
||||
crypto::signature sig_mutated = sig;
|
||||
sig_mutated.sign_mask ^= 0x02;
|
||||
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig_mutated));
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ TEST(Crypto, Ostream)
|
||||
EXPECT_TRUE(is_formatted<crypto::hash8>());
|
||||
EXPECT_TRUE(is_formatted<crypto::hash>());
|
||||
EXPECT_TRUE(is_formatted<crypto::public_key>());
|
||||
//EXPECT_TRUE(is_formatted<crypto::signature>());
|
||||
EXPECT_TRUE(is_formatted<crypto::signature>());
|
||||
EXPECT_TRUE(is_formatted<crypto::key_derivation>());
|
||||
EXPECT_TRUE(is_formatted<crypto::key_image>());
|
||||
EXPECT_TRUE(is_formatted<rct::key>());
|
||||
|
||||
+3375
-3637
File diff suppressed because it is too large
Load Diff
+3572
-3833
File diff suppressed because one or more lines are too long
+3571
-3832
File diff suppressed because it is too large
Load Diff
+3712
-3973
File diff suppressed because one or more lines are too long
+3571
-3832
File diff suppressed because it is too large
Load Diff
+3571
-3832
File diff suppressed because one or more lines are too long
+3571
-3832
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user