Compare commits

..

19 Commits

Author SHA1 Message Date
Codex Bot 42ec3826a0 Add store tx info and return addresses API
ci/gh-actions/depends / Cross-Mac aarch64 (push) Has been cancelled
ci/gh-actions/depends / ARM v8 (push) Has been cancelled
ci/gh-actions/depends / ARM v7 (push) Has been cancelled
ci/gh-actions/depends / i686 Linux (push) Has been cancelled
ci/gh-actions/depends / i686 Win (push) Has been cancelled
ci/gh-actions/depends / RISCV 64bit (push) Has been cancelled
ci/gh-actions/depends / Cross-Mac x86_64 (push) Has been cancelled
ci/gh-actions/depends / x86_64 Freebsd (push) Has been cancelled
ci/gh-actions/depends / x86_64 Linux (push) Has been cancelled
ci/gh-actions/depends / Win64 (push) Has been cancelled
2026-04-06 18:29:54 +02:00
Czarek Nakamoto c388ca652f add .patch-applied 2026-04-06 14:43:37 +02:00
Czarek Nakamoto c5edb53e57 serialize cache to JSON 2026-04-06 14:43:30 +02:00
julian e446baa47c ios no randomx JIT 2026-04-06 14:43:30 +02:00
Czarek Nakamoto 71d7754572 remove trivially_copyable assert 2026-04-06 14:43:30 +02:00
Czarek Nakamoto 691400c0cd change earliest fork height message 2026-04-06 14:43:29 +02:00
Czarek Nakamoto 2319090437 include locale only when targeting WIN32 2026-04-06 14:43:29 +02:00
Czarek Nakamoto d684c281c5 fix iOS depends build 2026-04-06 14:43:29 +02:00
cyan abc72ce9a1 add monero submodule support 2026-04-06 14:43:29 +02:00
Konstantin Ullrich 7ab8d9499c Add recoverDeterministicWalletFromSpendKey
This function is used by Cake Wallet to enable polyseed (dart implementation)
support.

Sourced from the following commit:
https://github.com/cake-tech/monero/commit/cb6fb5ab218878702ed151c0e3d5d68eb2732788

Co-authored-by: Godwin Asuquo <godilite@gmail.com>
2026-04-06 14:43:29 +02:00
M 25610c4c4b Add hex encoding and tx key getter for PendingTransction in wallet api. 2026-04-06 14:43:29 +02:00
tobtoht c1f36b676d coin control 2026-04-06 14:43:29 +02:00
tobtoht b9e87450eb polyseed
Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
2026-04-06 14:43:29 +02:00
Czarek Nakamoto c366058ba7 add dummy device for ledger 2026-04-06 14:43:29 +02:00
tobtoht ef8c2f00b9 UR functions
This commit adds UR functions for UR tasks,
I believe that the right place to get
UR strings is the wallet code itself,
especially because it allows us to
skip the part when we have to store
things to file to encode them later.
Now we are fully in memory

Things broken in the commit
- ledger support.
  AUTO_LOCK_CMD macro causes compile time
  issues with this patch. I don't know why
  just yet, this is a issue that I'll fix
  later. However (considering the purpose
  of this patch) it is not a dealbreaker.
2026-04-06 14:43:29 +02:00
Czarek Nakamoto 74811f7eb3 use proper error handling in get_seed 2026-04-06 14:43:29 +02:00
Czarek Nakamoto f699ca21c5 uint64_t missing definition fix 2026-04-06 14:43:29 +02:00
Czarek Nakamoto 20014ae7c2 store crash fix
Monero wallet crashes (sometimes) when it is syncing,
while the proper solution (that can be seen in feather)
is to not store wallet while it is being synced, this is not
acceptable for mobile wallets where OS can just come
and kill the wallet because it felt like it.

This patch depends on the background-sync patch, but
to use it as a standalone fix grabbing the definition for the
LOCK_REFRESH macro should be enough.

tobtoht suggested:
_say you want to store every 15 minutes during background sync. you stop the refresh every 15 minutes. then do something like this in the callback:_

```
// Make sure this doesn't run in the refresh thread
onRefreshed() {
  if (hasItBeen15MinutesSinceWeStored()) {
    store();
  }

  if (shouldWeContinueRefreshing()) {
    startRefresh();
  }
}
```

which works for crashes after the wallet is initially synced
but doesn't solve the issue for wallet that are syncing (it
would just wait for it to finish before actually storing).

Also imo store() functin should store the wallet, no matter
the current state.
2026-04-06 14:43:29 +02:00
Czarek Nakamoto 60713cc8e5 fix missing ___clear_cache when targetting iOS 2026-04-06 14:43:29 +02:00
104 changed files with 42658 additions and 44367 deletions
+4 -4
View File
@@ -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
View File
@@ -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
View File
+20 -15
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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:
+1 -1
View File
@@ -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}")
+1 -1
View File
@@ -28,7 +28,7 @@
#pragma once
#include "memwipe.h"
#include <stdint.h>
#include <boost/utility/string_ref.hpp>
#include <string>
-1
View File
@@ -162,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)};
}
+7
View File
@@ -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);
+10
View File
@@ -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];
}
}
+3
View File
@@ -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)
Vendored Submodule
+1
Submodule external/bc-ur added at d82e7c753e
Vendored Submodule
+1
Submodule external/polyseed added at bd79f5014c
Vendored Submodule
+1
Submodule external/utf8proc added at 3de4596fbe
+1
View File
@@ -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)
+13 -10
View File
@@ -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.
+9 -73
View File
@@ -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
View File
@@ -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;
}
}
}
+3 -3
View File
@@ -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)
+1 -1
View File
@@ -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
{
-2
View File
@@ -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));
}
//-------------------------------------------------------------------------------------------------------------------
-2
View File
@@ -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
View File
@@ -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
+1 -2
View File
@@ -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
});
+5 -1
View File
@@ -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)
+22 -27
View File
@@ -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;
}
-1
View File
@@ -71,7 +71,6 @@ monero_add_library(cncrypto
target_link_libraries(cncrypto
PUBLIC
epee
mx25519_static
randomx
${Boost_SYSTEM_LIBRARY}
${SODIUM_LIBRARY}
+1 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+6 -1
View File
@@ -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}
+25 -25
View File
@@ -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
+6 -5
View File
@@ -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(
+4 -4
View File
@@ -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);
+2
View File
@@ -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
+15 -13
View File
@@ -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)
+11 -3
View File
@@ -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;
}
+1 -1
View File
@@ -1355,7 +1355,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining, bool ignore_battery, bool is_carrot) {
cryptonote::COMMAND_RPC_START_MINING::request req;
cryptonote::COMMAND_RPC_START_MINING::response res;
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address);
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;
+4 -2
View File
@@ -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
+6 -4
View File
@@ -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
View File
@@ -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;
-6
View File
@@ -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;
-4
View File
@@ -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;
+161
View File
@@ -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
+82
View File
@@ -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
+4 -10
View File
@@ -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();
+6 -5
View File
@@ -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>> &registry);
#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
View File
@@ -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)
+25
View File
@@ -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}
)
+85
View File
@@ -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);
}
+46
View File
@@ -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
+182
View File
@@ -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);
}
}
+167
View File
@@ -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
+5 -21
View File
@@ -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();
+19 -42
View File
@@ -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";
}
+2 -2
View File
@@ -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
View File
@@ -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"
+2
View File
@@ -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
+6 -2
View File
@@ -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})
+194
View File
@@ -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
+40
View File
@@ -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
+132
View File
@@ -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;
+75
View File
@@ -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
+49
View File
@@ -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;
+3
View File
@@ -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;
+67 -20
View File
@@ -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
+5
View File
@@ -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;
+2
View File
@@ -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;
+42
View File
@@ -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)
{
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+133 -8
View File
@@ -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
+33 -4
View File
@@ -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)
+17
View File
@@ -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;
+1 -8
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+49 -17
View File
@@ -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
+368
View File
@@ -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
+1 -1
View File
@@ -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);
});
}
+3 -3
View File
@@ -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});
}
}
-1
View File
@@ -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
-445
View File
@@ -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));
}
}
+1 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+3572 -3833
View File
File diff suppressed because one or more lines are too long
+3571 -3832
View File
File diff suppressed because it is too large Load Diff
+3712 -3973
View File
File diff suppressed because one or more lines are too long
+3571 -3832
View File
File diff suppressed because it is too large Load Diff
+3571 -3832
View File
File diff suppressed because one or more lines are too long
+3571 -3832
View File
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