Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42ec3826a0 | |||
| c388ca652f | |||
| c5edb53e57 | |||
| e446baa47c | |||
| 71d7754572 | |||
| 691400c0cd | |||
| 2319090437 | |||
| d684c281c5 | |||
| abc72ce9a1 | |||
| 7ab8d9499c | |||
| 25610c4c4b | |||
| c1f36b676d | |||
| b9e87450eb | |||
| c366058ba7 | |||
| ef8c2f00b9 | |||
| 74811f7eb3 | |||
| f699ca21c5 | |||
| 20014ae7c2 | |||
| 60713cc8e5 | |||
| f60b7209f8 | |||
| 3b00a41fff | |||
| 119a7fab57 | |||
| 6d0a4a4d7b | |||
| 45404ecc71 | |||
| e5bfc2f6ad | |||
| c5be51053a | |||
| d7a9facee9 | |||
| ce6f6e0593 | |||
| f1efb700d2 | |||
| dd246296b9 | |||
| 22334d7c6c | |||
| c07698fcc8 | |||
| ce69272638 | |||
| 2b95d100b3 | |||
| b7706d4def | |||
| 845d46d7b3 | |||
| e4f8929f2c | |||
| c03402d525 | |||
| d8b18b17f6 | |||
| 5347266ed9 | |||
| 419c26adbf | |||
| 7727bdb51e | |||
| 37a58646fd | |||
| 0e747e3f15 | |||
| caf8a0a962 |
+12
-1
@@ -6,11 +6,22 @@
|
||||
url = https://github.com/trezor/trezor-common.git
|
||||
[submodule "external/randomx"]
|
||||
path = external/randomx
|
||||
url = https://github.com/tevador/RandomX
|
||||
url = https://github.com/MrCyjaneK/RandomX
|
||||
branch = cyjan-fix-ios
|
||||
[submodule "external/utf8proc"]
|
||||
path = external/utf8proc
|
||||
url = https://github.com/JuliaStrings/utf8proc.git
|
||||
[submodule "external/polyseed"]
|
||||
path = external/polyseed
|
||||
url = https://github.com/tevador/polyseed.git
|
||||
[submodule "external/supercop"]
|
||||
path = external/supercop
|
||||
url = https://github.com/monero-project/supercop
|
||||
branch = monero
|
||||
[submodule "external/bc-ur"]
|
||||
path = external/bc-ur
|
||||
url = https://github.com/MrCyjaneK/bc-ur
|
||||
branch = misc
|
||||
[submodule "external/miniupnp"]
|
||||
path = external/miniupnp
|
||||
url = https://github.com/miniupnp/miniupnp
|
||||
|
||||
+20
-15
@@ -45,10 +45,6 @@ if (POLICY CMP0148)
|
||||
endif()
|
||||
include(FindPythonInterp)
|
||||
|
||||
if (IOS)
|
||||
INCLUDE(CmakeLists_IOS.txt)
|
||||
endif()
|
||||
|
||||
project(salvium)
|
||||
|
||||
option (USE_CCACHE "Use ccache if a usable instance is found" ON)
|
||||
@@ -100,6 +96,7 @@ set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES) # boost: no template named 'unary_function' in namespace 'std'; did you mean '__unary_function'?
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
@@ -225,9 +222,9 @@ function(forbid_undefined_symbols)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(test)
|
||||
option(EXPECT_SUCCESS "" ON)
|
||||
file(WRITE "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }")
|
||||
file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }")
|
||||
if (EXPECT_SUCCESS)
|
||||
file(APPEND "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ")
|
||||
file(APPEND "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ")
|
||||
endif()
|
||||
add_library(l0 SHARED incorrect_source.cpp)
|
||||
add_library(l1 MODULE incorrect_source.cpp)
|
||||
@@ -367,12 +364,15 @@ if(NOT MANUAL_SUBMODULES)
|
||||
endfunction ()
|
||||
|
||||
message(STATUS "Checking submodules")
|
||||
# check_submodule(external/bc-ur)
|
||||
check_submodule(external/miniupnp)
|
||||
check_submodule(external/rapidjson)
|
||||
check_submodule(external/trezor-common)
|
||||
check_submodule(external/randomx)
|
||||
check_submodule(external/supercop)
|
||||
check_submodule(external/mx25519)
|
||||
check_submodule(external/polyseed)
|
||||
check_submodule(external/utf8proc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -390,7 +390,7 @@ else()
|
||||
endif()
|
||||
|
||||
list(INSERT CMAKE_MODULE_PATH 0
|
||||
"${CMAKE_SOURCE_DIR}/cmake")
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
if (NOT DEFINED ENV{DEVELOPER_LOCAL_TOOLS})
|
||||
message(STATUS "Could not find DEVELOPER_LOCAL_TOOLS in env (not required)")
|
||||
@@ -459,7 +459,7 @@ elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
|
||||
set(BSDI TRUE)
|
||||
endif()
|
||||
|
||||
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include)
|
||||
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include external/polyseed/include external/utf8proc)
|
||||
|
||||
if(APPLE)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
@@ -692,16 +692,21 @@ include_directories(${LMDB_INCLUDE})
|
||||
include_directories(${LIBUNWIND_INCLUDE})
|
||||
link_directories(${LIBUNWIND_LIBRARY_DIRS})
|
||||
|
||||
# Final setup for hid
|
||||
if (HIDAPI_FOUND)
|
||||
message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}")
|
||||
add_definitions(-DHAVE_HIDAPI)
|
||||
include_directories(${HIDAPI_INCLUDE_DIR})
|
||||
link_directories(${LIBHIDAPI_LIBRARY_DIRS})
|
||||
if (HIDAPI_DUMMY)
|
||||
add_definitions(-DHIDAPI_DUMMY)
|
||||
else()
|
||||
message(STATUS "Could not find HIDAPI")
|
||||
# Final setup for hid
|
||||
if (HIDAPI_FOUND)
|
||||
message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}")
|
||||
add_definitions(-DHAVE_HIDAPI)
|
||||
include_directories(${HIDAPI_INCLUDE_DIR})
|
||||
link_directories(${LIBHIDAPI_LIBRARY_DIRS})
|
||||
else()
|
||||
message(STATUS "Could not find HIDAPI")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Trezor support check
|
||||
include(CheckTrezor)
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Use Ubuntu 24.04 as base
|
||||
FROM ubuntu:24.04 AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# 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 \
|
||||
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 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Clone the develop branch
|
||||
WORKDIR /opt
|
||||
RUN git clone --recursive -b develop https://github.com/salvium/salvium.git
|
||||
WORKDIR /opt/salvium
|
||||
|
||||
# make the script runnable
|
||||
RUN chmod +x make_releases.sh
|
||||
|
||||
# Make sure the output folder exists
|
||||
RUN mkdir ~/releases
|
||||
|
||||
# Expose the releases directory for copying zip files to the host
|
||||
VOLUME ["~/releases"]
|
||||
|
||||
ENTRYPOINT ["/opt/salvium/make_releases.sh"]
|
||||
|
||||
|
||||
# To access the generated zip files on your host, run the container with:
|
||||
# docker run -v ~/releases:/root/releases <image>
|
||||
# This will copy the zip files to your host's ~/salvium-releases directory.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Salvium One v1.0.0
|
||||
|
||||
Copyright (c) 2023-2024, Salvium
|
||||
Copyright (c) 2023-2025, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
Portions Copyright (c) 2012-2013 The Cryptonote developers.
|
||||
|
||||
@@ -47,7 +47,7 @@ As with many development projects, the repository on GitHub is considered to be
|
||||
|
||||
## Supporting the project
|
||||
|
||||
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate/](https://salvium.io/donate/) for more information.
|
||||
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate](https://salvium.io/donate) for more information.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE)
|
||||
message(STATUS "Looking for ${flag} linker flag")
|
||||
endif()
|
||||
|
||||
set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
set(_cle_source ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
|
||||
set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
|
||||
set(CMAKE_C_FLAGS "${flag}")
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "memwipe.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -162,7 +162,6 @@ namespace epee
|
||||
{
|
||||
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
|
||||
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
|
||||
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
|
||||
static_assert(alignof(T) == 1, "type may have padding");
|
||||
return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <string>
|
||||
#include "memwipe.h"
|
||||
#include "fnv1.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
@@ -75,6 +76,12 @@ namespace epee
|
||||
bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; }
|
||||
wipeable_string &operator=(wipeable_string &&other);
|
||||
wipeable_string &operator=(const wipeable_string &other);
|
||||
char& operator[](size_t idx);
|
||||
const char& operator[](size_t idx) const;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
private:
|
||||
void grow(size_t sz, size_t reserved = 0);
|
||||
|
||||
@@ -261,4 +261,14 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
char& wipeable_string::operator[](size_t idx) {
|
||||
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
|
||||
return buffer[idx];
|
||||
}
|
||||
|
||||
const char& wipeable_string::operator[](size_t idx) const {
|
||||
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
|
||||
return buffer[idx];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+3
@@ -69,5 +69,8 @@ endif()
|
||||
add_subdirectory(db_drivers)
|
||||
add_subdirectory(easylogging++)
|
||||
add_subdirectory(qrcodegen)
|
||||
add_subdirectory(polyseed EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(utf8proc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(bc-ur)
|
||||
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(mx25519)
|
||||
|
||||
+1
Submodule external/bc-ur added at d82e7c753e
+1
Submodule external/polyseed added at bd79f5014c
Vendored
+1
-1
Submodule external/randomx updated: 102f8acf90...ce72c9bb9c
+1
Submodule external/utf8proc added at 3de4596fbe
@@ -98,6 +98,7 @@ add_subdirectory(net)
|
||||
add_subdirectory(hardforks)
|
||||
add_subdirectory(blockchain_db)
|
||||
add_subdirectory(mnemonics)
|
||||
add_subdirectory(polyseed)
|
||||
add_subdirectory(rpc)
|
||||
add_subdirectory(seraphis_crypto)
|
||||
if(NOT IOS)
|
||||
|
||||
Binary file not shown.
@@ -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)
|
||||
|
||||
@@ -28,7 +28,11 @@
|
||||
|
||||
if(APPLE)
|
||||
if(DEPENDS)
|
||||
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
|
||||
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
|
||||
else()
|
||||
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
|
||||
endif()
|
||||
else()
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
mark_as_advanced(IOKIT_LIBRARY)
|
||||
@@ -71,6 +75,7 @@ target_link_libraries(cryptonote_basic
|
||||
checkpoints
|
||||
cryptonote_format_utils_basic
|
||||
device
|
||||
polyseed_wrapper
|
||||
oracle
|
||||
${Boost_DATE_TIME_LIBRARY}
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
|
||||
@@ -89,12 +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) * (2 + 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)
|
||||
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)
|
||||
@@ -154,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)
|
||||
@@ -260,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;
|
||||
|
||||
@@ -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,6 +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)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed)
|
||||
KV_SERIALIZE(m_passphrase)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
void encrypt(const crypto::chacha_key &key);
|
||||
@@ -101,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;
|
||||
|
||||
@@ -1381,12 +1381,7 @@ namespace cryptonote
|
||||
|
||||
for (const auto &o: tx.vout)
|
||||
{
|
||||
if (hf_version >= HF_VERSION_ENFORCE_CARROT)
|
||||
{
|
||||
// from v10, require outputs be carrot outputs
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
|
||||
} else if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (tx.type != cryptonote::transaction_type::PROTOCOL) {
|
||||
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
|
||||
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
|
||||
|
||||
@@ -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);
|
||||
|
||||
+20
-35
@@ -88,8 +88,8 @@
|
||||
#define PREMINE_AMOUNT_UPFRONT ((uint64_t)650000000000000ull) // 3.4% of MONEY_SUPPLY
|
||||
#define PREMINE_AMOUNT_MONTHLY ((uint64_t)65000000000000ull) // 8.6%/24 of MONEY_SUPPLY
|
||||
|
||||
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)65000000000000ull) // 650K
|
||||
#define TREASURY_SAL1_MINT_COUNT 16 // 16 times
|
||||
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
|
||||
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
|
||||
|
||||
#define DIFFICULTY_TARGET_V2 120 // seconds
|
||||
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
|
||||
@@ -244,7 +244,6 @@
|
||||
#define HF_VERSION_AUDIT2 8
|
||||
#define HF_VERSION_AUDIT2_PAUSE 9
|
||||
#define HF_VERSION_CARROT 10
|
||||
#define HF_VERSION_ENFORCE_CARROT 11
|
||||
|
||||
#define HF_VERSION_REQUIRE_VIEW_TAGS 255
|
||||
#define HF_VERSION_ENABLE_CONVERT 255
|
||||
@@ -275,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
|
||||
|
||||
@@ -320,22 +321,14 @@ namespace config
|
||||
|
||||
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
|
||||
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
|
||||
{1000000ULL, {"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8", "00", "00"}},
|
||||
{1020000ULL, {"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c", "00", "00"}},
|
||||
{1040000ULL, {"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6", "00", "00"}},
|
||||
{1060000ULL, {"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88", "00", "00"}},
|
||||
{1080000ULL, {"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb", "00", "00"}},
|
||||
{1100000ULL, {"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b", "00", "00"}},
|
||||
{1120000ULL, {"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c", "00", "00"}},
|
||||
{1140000ULL, {"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8", "00", "00"}},
|
||||
{1160000ULL, {"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454", "00", "00"}},
|
||||
{1180000ULL, {"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42", "00", "00"}},
|
||||
{1200000ULL, {"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890", "00", "00"}},
|
||||
{1220000ULL, {"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0", "00", "00"}},
|
||||
{1240000ULL, {"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e", "00", "00"}},
|
||||
{1260000ULL, {"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c", "00", "00"}},
|
||||
{1280000ULL, {"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91", "00", "00"}},
|
||||
{1300000ULL, {"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007", "00", "00"}},
|
||||
{334750, {"1b2cd3ff56aa77c0cbab0473bfb96697ebdd0b25ad230136bfe41d5dc1ef265f","718cf02eabca157fd7ad7f8537db217624bfe1ca99dd09e758357e7000a5e57a","789cca3def51fb879eb7fbca271869b7","79bd0c"}},
|
||||
{356350, {"b51acbf35265d09f3cfb83dcabde2746991ddf0d30b5a4ecc34043b349a77031","9dc0d2e9534cdccf83494687c55c67c8c1b29834acf97cce53124a08a9549231","588ebc2918d06c009a18a28a8ab76694","ab8c23"}},
|
||||
{377950, {"771c6865980493846cbb049b34f72b937878cf799ae1126775df35064cf53526","60d340613c7721aeb03f2de1b56e2916d6cb023cc668acaca00e1606e0bb7f64","198bb0e5880c8abf8108158284ca7637","9b93f2"}},
|
||||
{399550, {"d9159f9bb654230382a69bb7a739c14f9d506c21dba5be9f4cdf66126ed1b24e","c3b34e7f9977f31f659abd9306b650416a674ed7bcfb8c75b257040a1a06b8b9","6f0de181716e0a9e100228d58f11d42c","4c87fd"}},
|
||||
{421150, {"1e2ab1ae204e7a9375f5b57fed2524e2d1a4702c4bbc6d420eb65d92af44c60c","826ecca0e2837860a6d2be89a410ff6aab22c5f5a66bd0a318de3abe646aa26a","bad663aa8253c40d00de0a2d1dfca60f","4a04d7"}},
|
||||
{442750, {"18267940a2b37c82850f3fb83d935281ee5cc436b0797ec10ca26c9cc3c0ef01","77bdec7bbc4cabd84cc8cf0666e1fdfe299480b9b2a4cf52cc959d728fa899ab","a8807121e33471cf18aa82af4826c9a7","d35d35"}},
|
||||
{464350, {"9ab50223bfecec2508e8233540dc13c3fc7e15a8d77ecb90939afee96859724d","675a1b3f2a2cd2bd0e7ec073c426125d95e12ad13e541bf019925c481c2bca15","591d2f2331832c347654ab255ad97e2d","d87c9c"}},
|
||||
{485950, {"c0d7299fe0b873687e7319f1a0d1f67712ef3ccf6a579d96c5f99743add6f029","dd3d17b5145b56377e023c169ae858cdf48bd6c23e8b2a77e02765ce316e3ca7","6a9e74df01762e2b32db1dc4d69dc3e9","75debe"}}
|
||||
};
|
||||
|
||||
// Hash domain separators
|
||||
@@ -432,22 +425,14 @@ namespace config
|
||||
|
||||
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
|
||||
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
|
||||
{1100, {"0d82afb3ae9c5de25a06da49e24e64d37ba05d6144f9f6153cea325c61407106","81440913c1484891d25010986806a62d1fae1b2fe045e3ac1396413638d594f1","40864bbf80397c05516368a1397eb5c3","220bda"}},
|
||||
{1120, {"a032f2de4d3b5d880d3a636d9786a4a2f586bee360c323bc7521f987a2ada51b","cf8e5eeb5d37996d86c9c4c88fabaabc1ec77bab9cd5ce610a61b49f1e079ce2","9877a989e0216f76a8cdf0a568e57d45","017549"}},
|
||||
{1140, {"51da7a4c34aa20a800ada615a931fbba5fd1b9f7df78f0f911097ad9d99dc230","dd784a5bf1adf9c01cb44946ca094ed4d38f8d9cccf8edfdab397db3b7631dbf","97314be452f184b54f16a558ee8a7dbe","357ac3"}},
|
||||
{1160, {"a0cf49118c62cea834fabae83e12f26939ce170f308adadacf7275226e5cdf6d","7ec9cb2dfac8dfa45bab1bab2b1e31a4291a9e3d939e9c193740cc191d1b720d","4e950acc71ffd4f3835ec79202dc46fd","e7da3a"}},
|
||||
{1180, {"5193e35f6cd3ff8408a442be5eaf4aa1871419ecb0c485546e9d995c4c9c2730","39fb9792b88290b3377036a150a6681334ebe5cb201e2005d2c1d7b212b3df4d","cb458163806a277c7b3513df0d5b0028","1939b5"}},
|
||||
{1200, {"a2c71a7381bc48576f4cb4313cb6264d2a6e82cd2ddcbbd5460bce1db8d5021e","d0c46f29f4bbdd5538d64a438c0ab93cb0586e67fbfc7d1671f0c9809d6a7fac","af38cc2ddccb9229728ef704e360e15a","8a8dc1"}},
|
||||
{1220, {"8358105081510e907c98da59636eef777460513d827aba400efbb81d82aead4c","b2b8e2f9bb13e7527d0ee1bc3ebf0ca5057139ee4f9302251455b9f27dca3b16","dc226161830e952b2533fe34b1dd3c48","fb7c67"}},
|
||||
{1240, {"90b0bc153b97a93eefd59af9b47d26c0c91aadda6203de91a15f2f76fc949074","06e6a11b8742ee78beafe89f894fb9078a514ee6dd4e099182ac982ead37eacc","4fbfd0f3c2b8d514f2c675b37b9f0b6f","3308ad"}},
|
||||
{1260, {"cd10148b3175804b17e3c68a6a5556364d585dee89953904b32f005a5f6d8f14","d9d6f64119776e9c97fe2fb540cdb5f40bea0c40127fda0f1f2b831cb7fa7d2b","9c21b83d29324e2d91774a623219ae28","1d647c"}},
|
||||
{1280, {"5963697782de7db5111d12c853f3fec0dd53f597d80cc376c7945f1d4afa224b","cc5e5b1273c10016dc855ec1d9bfa0bcf4736a748125f5341717ad9d080fbba0","19dd1f9a0cac118564e2ccb9f36be53a","7ab2c2"}},
|
||||
{1300, {"cf1d4d2d21b7b3f1a54889e0688a4654687e28a302a2ac3ad046ac2ad323f97a","5b5926da5e9199f190e86073ebd09ccb867faf1e9887c9e52a47c194915b2575","5775fa47af3c7226f2ec3017470410c9","2b03b3"}},
|
||||
{1320, {"b565ec8842f3e6c22c7a23eb23f0e5684b462d2261681dff7b2ce71ab02ff170","4f94cf4d811574c9fcc27888cc972ebc6c01ae97146b9882d397b77c9232d1fd","9d142686627c918a26b6c6853b911143","685b18"}},
|
||||
{1340, {"50d1b1bcacf36ba14b23b3827e36a02a7610dd3199a9f1b9d0a23ebcbd1c2d54","38818e41cf0ce7d54391978801c58087ddd5fe517dadb5e61d840f27a7a81a52","55328f4bff5120d44c5d76fc5a9dfdbd","d17be2"}},
|
||||
{1360, {"7657bf97a9be9beac5f999a1e2c1226613849c41c12df5d7185d4d6613a27328","63d96a47f29fab0e727d13905cc843df40ccb76b14dc48857d0dd9ec0873e8c5","ab4f7ffa6431452f0fac7eec33e89e3e","5acd14"}},
|
||||
{1380, {"845e4cbe9b9d2462fae2225da698d6a4e18340f06b312545ee9cfda8127abd41","cc65f1f22f863b31f3c4a53ac3987186c2a699f387a2bf08d8dfed54037912c6","bda3aa3bdd2b42e8183c1013e4350c98","d4a418"}},
|
||||
{1400, {"4b11413fcba49df61537427ff35fdd47ee12a8467751236ff22681bbea343d2c","32d22bba59559489f422298ae653ef55ec8c87e8705daf4c89da7187c8574cbd","8868e1c1d788ce4560beb4afbe77aa5a","5a35d9"}}
|
||||
{1100, {"71336a480440ed24a53b2cfd5a5292d1e618c3e843637227883ea2cc42fb346f","a135f59a05ea9e33539e4502b187b4789cc7fba79616c6902a902cc6601f0359","7f1c6970232254a9b13f7f063df2a853","62073c"}},
|
||||
{1120, {"2cc49b182addc0106b601c9876c01a4b06532c05f9dd9179b2c4f47e5c7c0d74","fd62e59324389f37d0bb628a39f413c11be34d572ec3a40f465e008b9dfd5e0c","fbf8584222e299bb748009eaf2177123","b76a8d"}},
|
||||
{1140, {"50f1dd5244bb6fdc62b5a18b868a4dc9e9ad15f4a7a54e5a98084a3a5213c846","942de90a3347df3e962d9b0a2745f8aedb265a8caf73aabef9aa713f4ec3e493","933702e29d680bf21ad559a2d3243621","dae32b"}},
|
||||
{1160, {"114a1fef3a03c81b3b6308fb4e51db42d86945d8f71ee8798749a4aa2d8bfe57","fd77f0289b7e25ae68c86f4cd2c72ac0211a4d0e7a31c84b8d58a33bbd3c511b","d8048bb76b9313768a4c69100cba6d43","46b0a3"}},
|
||||
{1180, {"cf7fe9d97a7a4cb881bff1d37bb6981bcb0691649c465d660522a43f14b32849","b76908d56108e7111374f60993e9411def7bb2899118f20f49e67a801936a1ad","f203a5acd48f93f1451d32d70716e585","d72097"}},
|
||||
{1200, {"415e8fa4fb6bed8bc59591883f5e098dde6179e980eccfc4cbb3a64c4168cf47","5426c588d7c5750c76b526ef268b45f7414e2ddea40218ee92d123564c5114c4","fb1926bbef8c70ebd796ae77dffca07d","351edf"}},
|
||||
{1220, {"1f47cce00e8e1c77cb3ff007491d261dae75de5bbfdbe0f4913d74a8503aaa5e","4d8aac299306989aa169982d89457852a1bab858c668c9b740d96b682bb1961b","d8c0be7926ce704290cfd8d5e0500f33","750003"}},
|
||||
{1240, {"3afd93c684638d54e68f5d8d417863035f12482251feea5cf7515725a2bb0f4f","7895e6baded473b093b57993ae3497a47b0ea78cf35b37d1387f226f445d9018","76a0c37e6f3d9d508c4bce1d7162514e","09e146"}}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1403,14 +1403,19 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version");
|
||||
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT || 2, false, "protocol transaction has wrong version");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
|
||||
}
|
||||
// Work out what the HF version _was_ when the STAKE outputs were created
|
||||
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
|
||||
uint8_t hf_version_submitted = get_ideal_hard_fork_version(height - stake_lock_period - 1);
|
||||
|
||||
if (hf_version >= HF_VERSION_ENFORCE_CARROT) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
|
||||
if (hf_version >= HF_VERSION_CARROT) {
|
||||
if (hf_version_submitted >= HF_VERSION_CARROT || b.protocol_tx.vout.size() == 0) {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
|
||||
} else {
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version == 2, false, "protocol transaction has wrong version");
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
|
||||
} else {
|
||||
hf_version_submitted = hf_version;
|
||||
}
|
||||
|
||||
// for v2 txes (ringct), we only accept empty rct signatures for protocol transactions,
|
||||
@@ -1433,8 +1438,7 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version_submitted), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@@ -1521,7 +1525,6 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENFORCE_CARROT:
|
||||
if (b.miner_tx.amount_burnt > 0) {
|
||||
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
|
||||
money_in_use += b.miner_tx.amount_burnt;
|
||||
@@ -2093,6 +2096,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
entry.P_change = audit_entry.first.P_change;
|
||||
entry.return_pubkey = audit_entry.first.return_pubkey;
|
||||
entry.origin_height = matured_audit_height;
|
||||
entry.is_carrot = false;
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
@@ -6510,7 +6514,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
|
||||
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)
|
||||
|
||||
@@ -361,10 +361,13 @@ namespace cryptonote
|
||||
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
|
||||
return false;
|
||||
}
|
||||
if (carrot_found && hard_fork_version < HF_VERSION_CARROT) {
|
||||
LOG_ERROR("Carrot outputs found in CryptoNote protocol transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (carrot_found || hard_fork_version >= HF_VERSION_ENFORCE_CARROT)
|
||||
if (carrot_found || (!noncarrot_found && hard_fork_version >= HF_VERSION_CARROT))
|
||||
{
|
||||
|
||||
// Ensure the TX version is correct
|
||||
tx.version = TRANSACTION_VERSION_CARROT;
|
||||
try
|
||||
@@ -594,7 +597,6 @@ namespace cryptonote
|
||||
case HF_VERSION_AUDIT2:
|
||||
case HF_VERSION_AUDIT2_PAUSE:
|
||||
case HF_VERSION_CARROT:
|
||||
case HF_VERSION_ENFORCE_CARROT:
|
||||
// SRCG: subtract 20% that will be rewarded to staking users
|
||||
CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero");
|
||||
tx.amount_burnt = amount / 5;
|
||||
@@ -1438,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1749,6 +1749,16 @@ namespace cryptonote
|
||||
LOG_PRINT_L2(" key images already seen");
|
||||
continue;
|
||||
}
|
||||
if (version < HF_VERSION_CARROT && tx.version >= TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
LOG_PRINT_L2(" is a Carrot transaction - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
if (version >= HF_VERSION_CARROT && tx.version < TRANSACTION_VERSION_CARROT)
|
||||
{
|
||||
LOG_PRINT_L2(" is not a Carrot transaction - cannot be mined");
|
||||
continue;
|
||||
}
|
||||
|
||||
bl.tx_hashes.push_back(sorted_it->second);
|
||||
total_weight += meta.weight;
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
set(device_sources
|
||||
device.cpp
|
||||
device_default.cpp
|
||||
device_io_dummy.cpp
|
||||
log.cpp
|
||||
)
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
|
||||
set(device_sources
|
||||
${device_sources}
|
||||
device_ledger.cpp
|
||||
@@ -45,10 +46,11 @@ set(device_headers
|
||||
device_io.hpp
|
||||
device_default.hpp
|
||||
device_cold.hpp
|
||||
device_io_dummy.hpp
|
||||
log.hpp
|
||||
)
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
|
||||
set(device_headers
|
||||
${device_headers}
|
||||
device_ledger.hpp
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "device.hpp"
|
||||
#include "device_default.hpp"
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
#include "device_ledger.hpp"
|
||||
#endif
|
||||
#include "misc_log_ex.h"
|
||||
@@ -57,7 +57,7 @@ namespace hw {
|
||||
|
||||
device_registry::device_registry(){
|
||||
hw::core::register_all(registry);
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
hw::ledger::register_all(registry);
|
||||
#endif
|
||||
atexit(clear_device_registry);
|
||||
@@ -83,11 +83,13 @@ namespace hw {
|
||||
|
||||
auto device = registry.find(device_descriptor_lookup);
|
||||
if (device == registry.end()) {
|
||||
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: ");
|
||||
std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: ");
|
||||
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n");
|
||||
for( const auto& sm_pair : registry ) {
|
||||
ss << "\n- " + sm_pair.first;
|
||||
MERROR(" - " << sm_pair.first);
|
||||
}
|
||||
throw std::runtime_error("device not found: " + device_descriptor);
|
||||
throw std::runtime_error("device not found: " + device_descriptor + "\n" + ss.str());
|
||||
}
|
||||
return *device->second;
|
||||
}
|
||||
|
||||
+1
-11
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2017-2022, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
// device_io_dummy
|
||||
// Main goal of device_io_dummy is to emulate a hw::io::device_io without the need to actually
|
||||
// connect a device.
|
||||
// Many operating systems do not support giving raw USB access to a process (android), or don't
|
||||
// support that at all (hi iOS), therefore other means of connection can be used, either USB
|
||||
// abstraction provided by the OS (monerujo), or BLE (also monerujo).
|
||||
// Monerujo implementation is written in Java, which makes it a nice fit for iOS, but makes the
|
||||
// code extremely unportable, so for this reason the code in here is written in CPP.
|
||||
// Data transport is made available in wallet2_api.h, so wallet developers can easily plug their
|
||||
// own USB/BLE/other transport layer.
|
||||
|
||||
#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)
|
||||
#include <boost/scope_exit.hpp>
|
||||
#include "log.hpp"
|
||||
#include "device_io_dummy.hpp"
|
||||
#include "device_ledger.hpp"
|
||||
|
||||
|
||||
bool hw::io::device_io_dummy::stateIsConnected = false;
|
||||
unsigned char* hw::io::device_io_dummy::sendToDevice = {};
|
||||
size_t hw::io::device_io_dummy::sendToDeviceLength = 0;
|
||||
unsigned char* hw::io::device_io_dummy::receivedFromDevice = {};
|
||||
size_t hw::io::device_io_dummy::receivedFromDeviceLength = 0;
|
||||
bool hw::io::device_io_dummy::waitsForDeviceSend = false;
|
||||
bool hw::io::device_io_dummy::waitsForDeviceReceive = false;
|
||||
void (*hw::io::device_io_dummy::sendToLedgerDeviceCallback)(unsigned char *command, unsigned int cmd_len) = nullptr;
|
||||
std::mutex hw::io::device_io_dummy::mutex;
|
||||
std::condition_variable hw::io::device_io_dummy::cv_send;
|
||||
std::condition_variable hw::io::device_io_dummy::cv_receive;
|
||||
|
||||
namespace hw {
|
||||
namespace io {
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "device.io_dummy"
|
||||
device_io_dummy::device_io_dummy(int a, int b, int c, int d) {
|
||||
MDEBUG("device_io_dummy(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<")");
|
||||
}
|
||||
|
||||
void device_io_dummy::init() {
|
||||
MDEBUG("init()");
|
||||
}
|
||||
|
||||
void device_io_dummy::connect(void *params) {
|
||||
MDEBUG("connect(" << params << ")");
|
||||
stateIsConnected = true;
|
||||
}
|
||||
|
||||
void device_io_dummy::connect(const std::vector<hw::io::hid_conn_params>& known_devices) {
|
||||
MDEBUG("connect([");
|
||||
for (const auto &item: known_devices) {
|
||||
MDEBUG("{ interface_number: " << item.interface_number);
|
||||
MDEBUG(" pid : " << item.pid);
|
||||
MDEBUG(" usage_page : " << item.usage_page);
|
||||
MDEBUG(" vid : " << item.vid << " },");
|
||||
}
|
||||
MDEBUG("])");
|
||||
stateIsConnected = true;
|
||||
}
|
||||
|
||||
bool device_io_dummy::connected() const {
|
||||
MDEBUG("connected()");
|
||||
return stateIsConnected;
|
||||
}
|
||||
|
||||
int device_io_dummy::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) {
|
||||
MDEBUG("exchange(): locking mutex");
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
sendToDevice = command;
|
||||
sendToDeviceLength = cmd_len;
|
||||
waitsForDeviceSend = true;
|
||||
waitsForDeviceReceive = true;
|
||||
|
||||
// Call the callback if it's set
|
||||
if (sendToLedgerDeviceCallback != nullptr) {
|
||||
MDEBUG("exchange(): calling sendToLedgerDeviceCallback");
|
||||
sendToLedgerDeviceCallback(command, cmd_len);
|
||||
}
|
||||
MDEBUG("exchange(): waitsForDeviceSend");
|
||||
// Wait for the send flag to be cleared by external code
|
||||
while (waitsForDeviceSend) {
|
||||
cv_send.wait(lock);
|
||||
MDEBUG("exchange(): waitsForDeviceSend notified");
|
||||
}
|
||||
|
||||
MDEBUG("exchange(): waitsForDeviceReceive");
|
||||
// Wait for the receive flag to be cleared by external code
|
||||
while (waitsForDeviceReceive) {
|
||||
cv_receive.wait(lock);
|
||||
MDEBUG("exchange(): waitsForDeviceReceive notified");
|
||||
}
|
||||
|
||||
if (receivedFromDeviceLength > max_resp_len) {
|
||||
MDEBUG("exchange(): receivedFromDeviceLength ("<<receivedFromDeviceLength<<") is larger than max_resp_len ("<<max_resp_len<<")");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
memset(response, 0, max_resp_len);
|
||||
memcpy(response, receivedFromDevice, receivedFromDeviceLength);
|
||||
return receivedFromDeviceLength;
|
||||
}
|
||||
|
||||
void device_io_dummy::disconnect() {
|
||||
MDEBUG("disconnect()");
|
||||
}
|
||||
|
||||
void device_io_dummy::release() {
|
||||
MDEBUG("release()");
|
||||
}
|
||||
|
||||
void device_io_dummy::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)) {
|
||||
MDEBUG("setLedgerCallback()");
|
||||
sendToLedgerDeviceCallback = sendToLedgerDevice;
|
||||
}
|
||||
|
||||
void device_io_dummy::setDeviceReceivedData(unsigned char* data, size_t len) {
|
||||
MDEBUG("setDeviceReceivedData(len: " << len << ")");
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
receivedFromDevice = static_cast<unsigned char *>(malloc(len));
|
||||
receivedFromDeviceLength = len;
|
||||
memset(receivedFromDevice, 0, len);
|
||||
memcpy(receivedFromDevice, data, len);
|
||||
waitsForDeviceReceive = false;
|
||||
waitsForDeviceSend = false;
|
||||
cv_send.notify_all();
|
||||
cv_receive.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HAVE_HIDAPI
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2017-2022, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#ifdef HIDAPI_DUMMY
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "device_io.hpp"
|
||||
#include "device_io_hid.hpp"
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace hw {
|
||||
namespace io {
|
||||
struct hid_conn_params {
|
||||
unsigned int vid;
|
||||
unsigned int pid;
|
||||
int interface_number;
|
||||
unsigned short usage_page;
|
||||
};
|
||||
class device_io_dummy : device_io {
|
||||
private:
|
||||
static std::mutex mutex;
|
||||
|
||||
public:
|
||||
static std::condition_variable cv_send;
|
||||
static std::condition_variable cv_receive;
|
||||
static bool stateIsConnected;
|
||||
static unsigned char* sendToDevice;
|
||||
static size_t sendToDeviceLength;
|
||||
static unsigned char* receivedFromDevice;
|
||||
static size_t receivedFromDeviceLength;
|
||||
static bool waitsForDeviceSend;
|
||||
static bool waitsForDeviceReceive;
|
||||
static void (*sendToLedgerDeviceCallback)(unsigned char *command, unsigned int cmd_len);
|
||||
|
||||
device_io_dummy() = default;
|
||||
device_io_dummy(int a, int b, int c, int d);
|
||||
~device_io_dummy() = default;
|
||||
|
||||
void init();
|
||||
void release();
|
||||
|
||||
void connect(void *parms);
|
||||
void connect(const std::vector<hw::io::hid_conn_params>& known_devices);
|
||||
void disconnect();
|
||||
bool connected() const;
|
||||
|
||||
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input);
|
||||
|
||||
static void setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len));
|
||||
static void setDeviceReceivedData(unsigned char* data, size_t len);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#endif // HAVE_HIDAPI
|
||||
@@ -38,7 +38,7 @@ namespace hw {
|
||||
|
||||
namespace ledger {
|
||||
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
|
||||
namespace {
|
||||
bool apdu_verbose =true;
|
||||
@@ -300,7 +300,7 @@ namespace hw {
|
||||
|
||||
device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) {
|
||||
this->id = device_id++;
|
||||
this->reset_buffer();
|
||||
this->reset_buffer();
|
||||
this->mode = NONE;
|
||||
this->has_view_key = false;
|
||||
this->tx_in_progress = false;
|
||||
@@ -533,7 +533,9 @@ namespace hw {
|
||||
|
||||
bool device_ledger::connect(void) {
|
||||
this->disconnect();
|
||||
#if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))
|
||||
hw_device.connect(known_devices);
|
||||
#endif
|
||||
this->reset();
|
||||
#ifdef DEBUG_HWDEVICE
|
||||
cryptonote::account_public_address pubkey;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "device.hpp"
|
||||
#include "log.hpp"
|
||||
#include "device_io_hid.hpp"
|
||||
#include "device_io_dummy.hpp"
|
||||
#include <mutex>
|
||||
|
||||
namespace hw {
|
||||
@@ -55,7 +56,7 @@ namespace hw {
|
||||
|
||||
void register_all(std::map<std::string, std::unique_ptr<device>> ®istry);
|
||||
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
|
||||
// Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h
|
||||
#define SW_OK 0x9000
|
||||
@@ -143,7 +144,11 @@ namespace hw {
|
||||
mutable std::mutex command_locker;
|
||||
|
||||
//IO
|
||||
#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)
|
||||
hw::io::device_io_dummy hw_device;
|
||||
#else
|
||||
hw::io::device_io_hid hw_device;
|
||||
#endif
|
||||
unsigned int length_send;
|
||||
unsigned char buffer_send[BUFFER_SEND_SIZE];
|
||||
unsigned int length_recv;
|
||||
|
||||
@@ -58,6 +58,9 @@ const hardfork_t mainnet_hard_forks[] = {
|
||||
|
||||
// version 9 starts from block 179200, which is on or around the 10th of March, 2025. Fork time finalised on 2025-02-24. No fork voting occurs for the v9 fork.
|
||||
{ 9, 179200, 0, 1740393800 },
|
||||
|
||||
// version 10 Carrot - including treasury mint - starts from block 334750, which is on or around the 13th of October, 2025. Fork time finalised on 2025-09-29. No fork voting occurs for the v10 fork.
|
||||
{10, 334750, 0, 1759142500 },
|
||||
};
|
||||
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
|
||||
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
@@ -92,9 +95,6 @@ const hardfork_t testnet_hard_forks[] = {
|
||||
|
||||
// version 10 Carrot - including treasury mint - starts from block 1100
|
||||
{10, 1100, 0, 1739780005 },
|
||||
|
||||
// version 11 Carrot and CryptoNote starts from block 1200
|
||||
{11, 1200, 0, 1756811153 },
|
||||
};
|
||||
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
|
||||
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
set(polyseed_sources
|
||||
pbkdf2.c
|
||||
polyseed.cpp
|
||||
)
|
||||
|
||||
monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
monero_private_headers(polyseed_wrapper
|
||||
${polyseed_private_headers}
|
||||
)
|
||||
|
||||
monero_add_library(polyseed_wrapper
|
||||
${polyseed_sources}
|
||||
${polyseed_headers}
|
||||
${polyseed_private_headers}
|
||||
)
|
||||
|
||||
target_link_libraries(polyseed_wrapper
|
||||
PUBLIC
|
||||
polyseed
|
||||
utf8proc
|
||||
${SODIUM_LIBRARY}
|
||||
PRIVATE
|
||||
${EXTRA_LIBRARIES}
|
||||
)
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
// Copyright (c) 2005,2007,2009 Colin Percival
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <sodium/crypto_auth_hmacsha256.h>
|
||||
#include <sodium/utils.h>
|
||||
|
||||
static inline void
|
||||
store32_be(uint8_t dst[4], uint32_t w)
|
||||
{
|
||||
dst[3] = (uint8_t) w; w >>= 8;
|
||||
dst[2] = (uint8_t) w; w >>= 8;
|
||||
dst[1] = (uint8_t) w; w >>= 8;
|
||||
dst[0] = (uint8_t) w;
|
||||
}
|
||||
|
||||
void
|
||||
crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
|
||||
const uint8_t* salt, size_t saltlen, uint64_t c,
|
||||
uint8_t* buf, size_t dkLen)
|
||||
{
|
||||
crypto_auth_hmacsha256_state Phctx, PShctx, hctx;
|
||||
size_t i;
|
||||
uint8_t ivec[4];
|
||||
uint8_t U[32];
|
||||
uint8_t T[32];
|
||||
uint64_t j;
|
||||
int k;
|
||||
size_t clen;
|
||||
|
||||
crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen);
|
||||
PShctx = Phctx;
|
||||
crypto_auth_hmacsha256_update(&PShctx, salt, saltlen);
|
||||
|
||||
for (i = 0; i * 32 < dkLen; i++) {
|
||||
store32_be(ivec, (uint32_t)(i + 1));
|
||||
hctx = PShctx;
|
||||
crypto_auth_hmacsha256_update(&hctx, ivec, 4);
|
||||
crypto_auth_hmacsha256_final(&hctx, U);
|
||||
|
||||
memcpy(T, U, 32);
|
||||
for (j = 2; j <= c; j++) {
|
||||
hctx = Phctx;
|
||||
crypto_auth_hmacsha256_update(&hctx, U, 32);
|
||||
crypto_auth_hmacsha256_final(&hctx, U);
|
||||
|
||||
for (k = 0; k < 32; k++) {
|
||||
T[k] ^= U[k];
|
||||
}
|
||||
}
|
||||
|
||||
clen = dkLen - i * 32;
|
||||
if (clen > 32) {
|
||||
clen = 32;
|
||||
}
|
||||
memcpy(&buf[i * 32], T, clen);
|
||||
}
|
||||
sodium_memzero((void*)&Phctx, sizeof Phctx);
|
||||
sodium_memzero((void*)&PShctx, sizeof PShctx);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#ifndef PBKDF2_H
|
||||
#define PBKDF2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void
|
||||
crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
|
||||
const uint8_t* salt, size_t saltlen, uint64_t c,
|
||||
uint8_t* buf, size_t dkLen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#include "polyseed.hpp"
|
||||
#include "pbkdf2.h"
|
||||
|
||||
#include <sodium/core.h>
|
||||
#include <sodium/utils.h>
|
||||
#include <sodium/randombytes.h>
|
||||
#include <utf8proc.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace polyseed {
|
||||
|
||||
inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) {
|
||||
utf8proc_int32_t buffer[POLYSEED_STR_SIZE];
|
||||
utf8proc_ssize_t result;
|
||||
|
||||
result = utf8proc_decompose(reinterpret_cast<const uint8_t*>(str), 0, buffer, POLYSEED_STR_SIZE, options);
|
||||
if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) {
|
||||
throw std::runtime_error("Unicode normalization failed");
|
||||
}
|
||||
|
||||
result = utf8proc_reencode(buffer, result, options);
|
||||
if (result < 0 || result > POLYSEED_STR_SIZE) {
|
||||
throw std::runtime_error("Unicode normalization failed");
|
||||
}
|
||||
|
||||
strcpy(norm, reinterpret_cast<const char*>(buffer));
|
||||
sodium_memzero(buffer, POLYSEED_STR_SIZE);
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t utf8_nfc(const char* str, polyseed_str norm) {
|
||||
// Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases
|
||||
// to allow wallets to split on ' '.
|
||||
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA));
|
||||
}
|
||||
|
||||
static size_t utf8_nfkd(const char* str, polyseed_str norm) {
|
||||
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA));
|
||||
}
|
||||
|
||||
struct dependency {
|
||||
dependency();
|
||||
std::vector<language> languages;
|
||||
};
|
||||
|
||||
static dependency deps;
|
||||
|
||||
dependency::dependency() {
|
||||
if (sodium_init() == -1) {
|
||||
throw std::runtime_error("sodium_init failed");
|
||||
}
|
||||
|
||||
polyseed_dependency pd;
|
||||
pd.randbytes = &randombytes_buf;
|
||||
pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256;
|
||||
pd.memzero = &sodium_memzero;
|
||||
pd.u8_nfc = &utf8_nfc;
|
||||
pd.u8_nfkd = &utf8_nfkd;
|
||||
pd.time = nullptr;
|
||||
pd.alloc = nullptr;
|
||||
pd.free = nullptr;
|
||||
|
||||
polyseed_inject(&pd);
|
||||
|
||||
for (int i = 0; i < polyseed_get_num_langs(); ++i) {
|
||||
languages.push_back(language(polyseed_get_lang(i)));
|
||||
}
|
||||
}
|
||||
|
||||
static language invalid_lang;
|
||||
|
||||
const std::vector<language>& get_langs() {
|
||||
return deps.languages;
|
||||
}
|
||||
|
||||
const language& get_lang_by_name(const std::string& name) {
|
||||
for (auto& lang : deps.languages) {
|
||||
if (name == lang.name_en()) {
|
||||
return lang;
|
||||
}
|
||||
if (name == lang.name()) {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
return invalid_lang;
|
||||
}
|
||||
|
||||
inline void data::check_init() const {
|
||||
if (valid()) {
|
||||
throw std::runtime_error("already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
static std::array<const char*, 8> error_desc = {
|
||||
"Success",
|
||||
"Wrong number of words in the phrase",
|
||||
"Unknown language or unsupported words",
|
||||
"Checksum mismatch",
|
||||
"Unsupported seed features",
|
||||
"Invalid seed format",
|
||||
"Memory allocation failure",
|
||||
"Unicode normalization failed"
|
||||
};
|
||||
|
||||
static error get_error(polyseed_status status) {
|
||||
if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) {
|
||||
return error(error_desc[(int)status], status);
|
||||
}
|
||||
return error("Unknown error", status);
|
||||
}
|
||||
|
||||
void data::create(feature_type features) {
|
||||
check_init();
|
||||
auto status = polyseed_create(features, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
void data::split(const language& lang, polyseed_phrase& words) {
|
||||
check_init();
|
||||
if (!lang.valid()) {
|
||||
throw std::runtime_error("invalid language");
|
||||
}
|
||||
}
|
||||
|
||||
void data::load(polyseed_storage storage) {
|
||||
check_init();
|
||||
auto status = polyseed_load(storage, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
void data::load(const crypto::secret_key &key) {
|
||||
polyseed_storage d;
|
||||
memcpy(&d, &key.data, 32);
|
||||
auto status = polyseed_load(d, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
language data::decode(const char* phrase) {
|
||||
check_init();
|
||||
const polyseed_lang* lang;
|
||||
auto status = polyseed_decode(phrase, m_coin, &lang, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
return language(lang);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#ifndef POLYSEED_HPP
|
||||
#define POLYSEED_HPP
|
||||
|
||||
#include <polyseed/include/polyseed.h>
|
||||
#include <polyseed/src/lang.h>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
namespace polyseed {
|
||||
|
||||
class data;
|
||||
|
||||
class language {
|
||||
public:
|
||||
language() : m_lang(nullptr) {}
|
||||
language(const language&) = default;
|
||||
language(const polyseed_lang* lang) : m_lang(lang) {}
|
||||
const char* name() const {
|
||||
return polyseed_get_lang_name(m_lang);
|
||||
}
|
||||
const char* name_en() const {
|
||||
return polyseed_get_lang_name_en(m_lang);
|
||||
}
|
||||
const char* separator() const {
|
||||
return m_lang->separator;
|
||||
}
|
||||
bool valid() const {
|
||||
return m_lang != nullptr;
|
||||
}
|
||||
|
||||
const polyseed_lang* m_lang;
|
||||
private:
|
||||
|
||||
friend class data;
|
||||
};
|
||||
|
||||
const std::vector<language>& get_langs();
|
||||
const language& get_lang_by_name(const std::string& name);
|
||||
|
||||
class error : public std::runtime_error {
|
||||
public:
|
||||
error(const char* msg, polyseed_status status)
|
||||
: std::runtime_error(msg), m_status(status)
|
||||
{
|
||||
}
|
||||
polyseed_status status() const {
|
||||
return m_status;
|
||||
}
|
||||
private:
|
||||
polyseed_status m_status;
|
||||
};
|
||||
|
||||
using feature_type = unsigned int;
|
||||
|
||||
inline int enable_features(feature_type features) {
|
||||
return polyseed_enable_features(features);
|
||||
}
|
||||
|
||||
class data {
|
||||
public:
|
||||
data(const data&) = delete;
|
||||
data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {}
|
||||
~data() {
|
||||
polyseed_free(m_data);
|
||||
}
|
||||
|
||||
void create(feature_type features);
|
||||
|
||||
void load(polyseed_storage storage);
|
||||
|
||||
void load(const crypto::secret_key &key);
|
||||
|
||||
language decode(const char* phrase);
|
||||
|
||||
template<class str_type>
|
||||
void encode(const language& lang, str_type& str) const {
|
||||
check_valid();
|
||||
if (!lang.valid()) {
|
||||
throw std::runtime_error("invalid language");
|
||||
}
|
||||
str.resize(POLYSEED_STR_SIZE);
|
||||
auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]);
|
||||
str.resize(size);
|
||||
}
|
||||
|
||||
void split(const language& lang, polyseed_phrase& words);
|
||||
|
||||
void save(polyseed_storage storage) const {
|
||||
check_valid();
|
||||
polyseed_store(m_data, storage);
|
||||
}
|
||||
|
||||
void save(void *storage) const {
|
||||
check_valid();
|
||||
polyseed_store(m_data, (uint8_t*)storage);
|
||||
}
|
||||
|
||||
void crypt(const char* password) {
|
||||
check_valid();
|
||||
polyseed_crypt(m_data, password);
|
||||
}
|
||||
|
||||
void keygen(void* ptr, size_t key_size) const {
|
||||
check_valid();
|
||||
polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr);
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
bool encrypted() const {
|
||||
check_valid();
|
||||
return polyseed_is_encrypted(m_data);
|
||||
}
|
||||
|
||||
uint64_t birthday() const {
|
||||
check_valid();
|
||||
return polyseed_get_birthday(m_data);
|
||||
}
|
||||
|
||||
bool has_feature(feature_type feature) const {
|
||||
check_valid();
|
||||
return polyseed_get_feature(m_data, feature) != 0;
|
||||
}
|
||||
private:
|
||||
void check_valid() const {
|
||||
if (m_data == nullptr) {
|
||||
throw std::runtime_error("invalid object");
|
||||
}
|
||||
}
|
||||
void check_init() const;
|
||||
|
||||
polyseed_data* m_data;
|
||||
polyseed_coin m_coin;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //POLYSEED_HPP
|
||||
@@ -316,7 +316,7 @@ namespace
|
||||
const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info");
|
||||
const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc [<number_of_threads>]");
|
||||
const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc");
|
||||
const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]");
|
||||
const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>] [<address_type>]");
|
||||
const char* USAGE_VERSION("version");
|
||||
const char* USAGE_HELP("help [<command> | all]");
|
||||
const char* USAGE_APROPOS("apropos <keyword> [<keyword> ...]");
|
||||
@@ -2559,6 +2559,7 @@ bool simple_wallet::stop_mining_for_rpc(const std::vector<std::string> &args)
|
||||
bool simple_wallet::show_qr_code(const std::vector<std::string> &args)
|
||||
{
|
||||
uint32_t subaddress_index = 0;
|
||||
carrot::AddressDeriveType derive_type;
|
||||
if (args.size() >= 1)
|
||||
{
|
||||
if (!string_tools::get_xtype_from_string(subaddress_index, args[0]))
|
||||
@@ -2572,6 +2573,25 @@ bool simple_wallet::show_qr_code(const std::vector<std::string> &args)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (args.size() >= 2)
|
||||
{
|
||||
// Convert string to AddressDeriveType enum
|
||||
if (args[1] == "carrot" || args[1] == "Carrot")
|
||||
{
|
||||
derive_type = carrot::AddressDeriveType::Carrot;
|
||||
}
|
||||
else if (args[1] == "precarrot" || args[1] == "PreCarrot")
|
||||
{
|
||||
derive_type = carrot::AddressDeriveType::PreCarrot;
|
||||
}
|
||||
else
|
||||
{
|
||||
fail_msg_writer() << tr("invalid derive type: must be 'carrot' or 'precarrot'");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
derive_type = carrot::AddressDeriveType::Auto;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PRINT_UTF8(pre, x) std::wcout << pre ## x
|
||||
@@ -2586,7 +2606,8 @@ bool simple_wallet::show_qr_code(const std::vector<std::string> &args)
|
||||
WTEXTON();
|
||||
try
|
||||
{
|
||||
const std::string address = "salvium:" + m_wallet->get_subaddress_as_str({m_current_subaddress_account, subaddress_index});
|
||||
const std::string address = "salvium:" + m_wallet->get_subaddress_as_str({m_current_subaddress_account, subaddress_index, derive_type, false});
|
||||
message_writer() << "Showing QR code for address: " << address.c_str() << std::endl;
|
||||
const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(address.c_str(), qrcodegen::QrCode::Ecc::LOW);
|
||||
for (int y = -2; y < qr.getSize() + 2; y+=2)
|
||||
{
|
||||
@@ -6472,7 +6493,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
|
||||
if (!detailed || balance_per_subaddress.empty())
|
||||
continue;
|
||||
success_msg_writer() << tr("Balance per address:");
|
||||
success_msg_writer() << boost::format("%15s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Outputs") % tr("Label");
|
||||
success_msg_writer() << boost::format("%16s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Outputs") % tr("Label");
|
||||
std::vector<tools::wallet2::transfer_details> transfers;
|
||||
m_wallet->get_transfers(transfers);
|
||||
for (const auto& i : balance_per_subaddress)
|
||||
@@ -6480,9 +6501,9 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
|
||||
carrot::subaddress_index_extended subaddr = {{m_current_subaddress_account, i.first},
|
||||
is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot};
|
||||
cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first};
|
||||
std::string address_str = m_wallet->get_subaddress_as_str(subaddr).substr(0, 6);
|
||||
std::string address_str = m_wallet->get_subaddress_as_str(subaddr).substr(0, 8);
|
||||
uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == subaddr_index; });
|
||||
success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
|
||||
success_msg_writer() << boost::format(tr("%8u %8s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -7316,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;
|
||||
}
|
||||
}
|
||||
@@ -8594,7 +8615,13 @@ bool simple_wallet::burn(const std::vector<std::string> &args_)
|
||||
}
|
||||
|
||||
std::vector<std::string> local_args;
|
||||
local_args.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account,0}));
|
||||
carrot::AddressDeriveType derive_type;
|
||||
if (m_wallet->use_fork_rules(HF_VERSION_CARROT, 0)) {
|
||||
derive_type = carrot::AddressDeriveType::Carrot;
|
||||
} else {
|
||||
derive_type = carrot::AddressDeriveType::PreCarrot;
|
||||
}
|
||||
local_args.push_back(m_wallet->get_subaddress_as_str({{m_current_subaddress_account, 0}, derive_type, false}));
|
||||
local_args.insert(local_args.end(), args_.begin(), args_.end());
|
||||
|
||||
// Get the asset type
|
||||
@@ -8747,7 +8774,13 @@ bool simple_wallet::stake(const std::vector<std::string> &args_)
|
||||
}
|
||||
|
||||
std::vector<std::string> local_args;
|
||||
local_args.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account,0}));
|
||||
carrot::AddressDeriveType derive_type;
|
||||
if (m_wallet->use_fork_rules(HF_VERSION_CARROT, 0)) {
|
||||
derive_type = carrot::AddressDeriveType::Carrot;
|
||||
} else {
|
||||
derive_type = carrot::AddressDeriveType::PreCarrot;
|
||||
}
|
||||
local_args.push_back(m_wallet->get_subaddress_as_str({{m_current_subaddress_account, 0}, derive_type, false}));
|
||||
local_args.insert(local_args.end(), args_.begin(), args_.end());
|
||||
|
||||
if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) {
|
||||
@@ -9890,7 +9923,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
|
||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||
payment_id = payment_id.substr(0,16);
|
||||
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
|
||||
std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
|
||||
std::string destination = m_wallet->get_subaddress_as_str({{m_current_subaddress_account, pd.m_subaddr_index.minor}, pd.m_is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
const std::string type =
|
||||
pd.m_tx_type == cryptonote::transaction_type::STAKE ? "stake" :
|
||||
pd.m_coinbase ? tr("block") : tr("in");
|
||||
@@ -10093,7 +10126,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (!destinations.empty())
|
||||
destinations += ", ";
|
||||
destinations += (transfer.direction == "in" ? output.first.substr(0, 6) : output.first) + ":" + print_money(output.second);
|
||||
destinations += ((transfer.direction == "in" || transfer.direction == "block") ? output.first.substr(0, 8) : output.first) + ":" + print_money(output.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10619,7 +10652,7 @@ std::string simple_wallet::get_prompt() const
|
||||
{
|
||||
if (m_locked)
|
||||
return std::string("[") + tr("locked due to inactivity") + "]";
|
||||
std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
|
||||
std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 8);
|
||||
std::string prompt = std::string("[") + tr("wallet") + " " + addr_start;
|
||||
if (!m_wallet->check_connection(NULL))
|
||||
prompt += tr(" (no daemon)");
|
||||
@@ -10842,7 +10875,7 @@ void simple_wallet::print_accounts(const std::string& tag)
|
||||
success_msg_writer() << tr("Accounts with tag: ") << tag;
|
||||
success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
|
||||
}
|
||||
success_msg_writer() << boost::format(" %15s %21s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Asset") % tr("Label");
|
||||
success_msg_writer() << boost::format(" %18s %21s %21s %6s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Asset") % tr("Label");
|
||||
std::map<std::string, std::pair<uint64_t, uint64_t>> total_balances;
|
||||
std::vector<std::string> asset_types_in_wallet = m_wallet->list_asset_types();
|
||||
for (const auto& asset: asset_types_in_wallet) {
|
||||
@@ -10856,10 +10889,10 @@ void simple_wallet::print_accounts(const std::string& tag)
|
||||
auto unlocked_balance = m_wallet->unlocked_balance(account_index, asset, false);
|
||||
if (balance == 0)
|
||||
continue;
|
||||
success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s %21s"))
|
||||
success_msg_writer() << boost::format(tr(" %c%8u %8s %21s %21s %6s %21s"))
|
||||
% (m_current_subaddress_account == account_index ? '*' : ' ')
|
||||
% account_index
|
||||
% m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
|
||||
% m_wallet->get_subaddress_as_str({{account_index, 0}, carrot::AddressDeriveType::Auto}).substr(0, 8)
|
||||
% print_money(balance)
|
||||
% print_money(unlocked_balance)
|
||||
% asset
|
||||
@@ -10870,9 +10903,9 @@ void simple_wallet::print_accounts(const std::string& tag)
|
||||
if (total_balance > 0)
|
||||
total_balances[asset] = std::pair<uint64_t, uint64_t>(total_balance, total_unlocked_balance);
|
||||
}
|
||||
success_msg_writer() << tr("------------------------------------------------------------------------------------");
|
||||
success_msg_writer() << tr("------------------------------------------------------------------------------------------------------");
|
||||
for (const auto& it: total_balances)
|
||||
success_msg_writer() << boost::format(tr("%15s %21s %21s %15s")) % "Total" % print_money(it.second.first) % print_money(it.second.second) % it.first;
|
||||
success_msg_writer() << boost::format(tr("%18s %21s %21s %6s")) % "Total" % print_money(it.second.first) % print_money(it.second.second) % it.first;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
||||
@@ -10888,16 +10921,35 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
|
||||
std::vector<std::string> local_args = args;
|
||||
tools::wallet2::transfer_container transfers;
|
||||
m_wallet->get_transfers(transfers);
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
auto print_address_sub = [this, &transfers, is_carrot](uint32_t index)
|
||||
auto print_address_sub = [this, &transfers](uint32_t index, bool cryptonote = true, bool carrot = true)
|
||||
{
|
||||
bool used = std::find_if(
|
||||
transfers.begin(), transfers.end(),
|
||||
[this, &index](const tools::wallet2::transfer_details& td) {
|
||||
return td.m_subaddr_index == cryptonote::subaddress_index{ m_current_subaddress_account, index };
|
||||
}) != transfers.end();
|
||||
success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
|
||||
if (!cryptonote) {
|
||||
success_msg_writer() << boost::format(tr("%8u %96s %21s %6s"))
|
||||
% index
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::Carrot})
|
||||
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
|
||||
% (used ? tr("(used)") : "");
|
||||
} else if (!carrot) {
|
||||
success_msg_writer() << boost::format(tr("%8u %96s %21s %6s"))
|
||||
% index
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::PreCarrot})
|
||||
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
|
||||
% (used ? tr("(used)") : "");
|
||||
} else {
|
||||
success_msg_writer() << boost::format(tr("%8u CN %96s %21s %6s\n Carrot %96s\n"))
|
||||
% index
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::PreCarrot})
|
||||
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
|
||||
% (used ? tr("(used)") : "")
|
||||
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::Carrot});
|
||||
}
|
||||
//success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
|
||||
};
|
||||
|
||||
uint32_t index = 0;
|
||||
@@ -10905,11 +10957,22 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
|
||||
{
|
||||
print_address_sub(index);
|
||||
}
|
||||
else if (local_args.size() == 1 && local_args[0] == "all")
|
||||
else if (local_args[0] == "all")
|
||||
{
|
||||
local_args.erase(local_args.begin());
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index);
|
||||
if (local_args.size() == 0) {
|
||||
message_writer(console_color_green, true) << tr(" INDEX TYPE ADDRESS");
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index);
|
||||
} else if (local_args[0] == "cn") {
|
||||
message_writer(console_color_green, true) << tr(" INDEX ADDRESS");
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index, true, false);
|
||||
} else {
|
||||
message_writer(console_color_green, true) << tr(" INDEX ADDRESS");
|
||||
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
|
||||
print_address_sub(index, false, true);
|
||||
}
|
||||
}
|
||||
else if (local_args[0] == "new")
|
||||
{
|
||||
@@ -11309,7 +11372,9 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args)
|
||||
}
|
||||
message_writer() << tr("Filename: ") << m_wallet->get_wallet_file();
|
||||
message_writer() << tr("Description: ") << description;
|
||||
message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
message_writer() << tr("CN Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
message_writer() << tr("Carrot Address: ") << m_wallet->get_account().get_carrot_public_address_str(m_wallet->nettype());
|
||||
|
||||
std::string type;
|
||||
if (m_wallet->watch_only())
|
||||
type = tr("Watch only");
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION "1.0.0-rc4"
|
||||
#define DEF_SALVIUM_VERSION "1.0.0"
|
||||
#define DEF_MONERO_VERSION_TAG "release"
|
||||
#define DEF_MONERO_VERSION "0.18.3.4"
|
||||
#define DEF_MONERO_RELEASE_NAME "One"
|
||||
|
||||
@@ -38,6 +38,7 @@ set(wallet_sources
|
||||
message_store.cpp
|
||||
message_transporter.cpp
|
||||
wallet_rpc_payments.cpp
|
||||
wallet_cache_to_json.cpp
|
||||
tx_builder.cpp
|
||||
)
|
||||
|
||||
@@ -51,6 +52,7 @@ monero_add_library(wallet
|
||||
target_link_libraries(wallet
|
||||
PUBLIC
|
||||
rpc_base
|
||||
bc-ur
|
||||
multisig
|
||||
carrot_impl
|
||||
common
|
||||
|
||||
@@ -41,7 +41,9 @@ set(wallet_api_sources
|
||||
address_book.cpp
|
||||
subaddress.cpp
|
||||
subaddress_account.cpp
|
||||
unsigned_transaction.cpp)
|
||||
unsigned_transaction.cpp
|
||||
coins.cpp
|
||||
coins_info.cpp)
|
||||
|
||||
set(wallet_api_headers
|
||||
wallet2_api.h)
|
||||
@@ -57,7 +59,9 @@ set(wallet_api_private_headers
|
||||
address_book.h
|
||||
subaddress.h
|
||||
subaddress_account.h
|
||||
unsigned_transaction.h)
|
||||
unsigned_transaction.h
|
||||
coins.h
|
||||
coins_info.h)
|
||||
|
||||
monero_private_headers(wallet_api
|
||||
${wallet_api_private_headers})
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "coins.h"
|
||||
#include "coins_info.h"
|
||||
#include "wallet.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "wallet/wallet2.h"
|
||||
#include "common_defines.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace epee;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
Coins::~Coins() = default;
|
||||
|
||||
CoinsImpl::CoinsImpl(WalletImpl *wallet)
|
||||
: m_wallet(wallet) {}
|
||||
|
||||
CoinsImpl::~CoinsImpl()
|
||||
{
|
||||
for (auto t : m_rows)
|
||||
delete t;
|
||||
}
|
||||
|
||||
int CoinsImpl::count() const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
int result = m_rows.size();
|
||||
return result;
|
||||
}
|
||||
|
||||
CoinsInfo *CoinsImpl::coin(int index) const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
// sanity check
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
auto index_ = static_cast<unsigned>(index);
|
||||
return index_ < m_rows.size() ? m_rows[index_] : nullptr;
|
||||
}
|
||||
|
||||
std::vector<CoinsInfo *> CoinsImpl::getAll() const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
return m_rows;
|
||||
}
|
||||
|
||||
|
||||
void CoinsImpl::refresh()
|
||||
{
|
||||
LOG_PRINT_L2("Refreshing coins");
|
||||
|
||||
boost::unique_lock<boost::shared_mutex> lock(m_rowsMutex);
|
||||
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_wallet->m_transfers_mutex);
|
||||
|
||||
// delete old outputs;
|
||||
for (auto t : m_rows)
|
||||
delete t;
|
||||
m_rows.clear();
|
||||
|
||||
for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i)
|
||||
{
|
||||
const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i);
|
||||
|
||||
// Make a subaddress_index_extended from td.m_subaddr_index
|
||||
carrot::subaddress_index_extended csub{
|
||||
{td.m_subaddr_index.major, td.m_subaddr_index.minor},
|
||||
td.is_carrot() ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot,
|
||||
false};
|
||||
|
||||
auto ci = new CoinsInfoImpl();
|
||||
ci->m_blockHeight = td.m_block_height;
|
||||
ci->m_hash = string_tools::pod_to_hex(td.m_txid);
|
||||
ci->m_internalOutputIndex = td.m_internal_output_index;
|
||||
ci->m_globalOutputIndex = td.m_global_output_index;
|
||||
ci->m_spent = td.m_spent;
|
||||
ci->m_frozen = td.m_frozen;
|
||||
ci->m_spentHeight = td.m_spent_height;
|
||||
ci->m_amount = td.m_amount;
|
||||
ci->m_rct = td.m_rct;
|
||||
ci->m_keyImageKnown = td.m_key_image_known;
|
||||
ci->m_pkIndex = td.m_pk_index;
|
||||
ci->m_subaddrIndex = td.m_subaddr_index.minor;
|
||||
ci->m_subaddrAccount = td.m_subaddr_index.major;
|
||||
ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(csub); // todo: this is expensive, cache maybe?
|
||||
ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index);
|
||||
ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image);
|
||||
ci->m_unlockTime = td.m_tx.unlock_time;
|
||||
ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td);
|
||||
ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key());
|
||||
ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
|
||||
ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid);
|
||||
ci->m_asset = td.asset_type;
|
||||
ci->m_type = td.m_tx.type;
|
||||
|
||||
m_rows.push_back(ci);
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::setFrozen(std::string public_key)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
if (!epee::string_tools::hex_to_pod(public_key, pk))
|
||||
{
|
||||
LOG_ERROR("Invalid public key: " << public_key);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->m_wallet->freeze(pk);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("setFrozen: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::setFrozen(int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
LOG_ERROR("Freezing coin: " << index);
|
||||
m_wallet->m_wallet->freeze(index);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("setLabel: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::thaw(std::string public_key)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
if (!epee::string_tools::hex_to_pod(public_key, pk))
|
||||
{
|
||||
LOG_ERROR("Invalid public key: " << public_key);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->m_wallet->thaw(pk);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("thaw: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CoinsImpl::thaw(int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_wallet->m_wallet->thaw(index);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("thaw: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) {
|
||||
return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight);
|
||||
}
|
||||
|
||||
void CoinsImpl::setDescription(const std::string &public_key, const std::string &description)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
if (!epee::string_tools::hex_to_pod(public_key, pk))
|
||||
{
|
||||
LOG_ERROR("Invalid public key: " << public_key);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const size_t index = m_wallet->m_wallet->get_transfer_details(pk);
|
||||
const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index);
|
||||
m_wallet->m_wallet->set_tx_note(td.m_txid, description);
|
||||
refresh();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("setDescription: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef FEATHER_COINS_H
|
||||
#define FEATHER_COINS_H
|
||||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
namespace Monero {
|
||||
|
||||
class WalletImpl;
|
||||
|
||||
class CoinsImpl : public Coins
|
||||
{
|
||||
public:
|
||||
explicit CoinsImpl(WalletImpl * wallet);
|
||||
~CoinsImpl() override;
|
||||
int count() const override;
|
||||
CoinsInfo * coin(int index) const override;
|
||||
std::vector<CoinsInfo*> getAll() const override;
|
||||
void refresh() override;
|
||||
|
||||
void setFrozen(std::string public_key) override;
|
||||
void setFrozen(int index) override;
|
||||
void thaw(std::string public_key) override;
|
||||
void thaw(int index) override;
|
||||
|
||||
bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override;
|
||||
|
||||
void setDescription(const std::string &public_key, const std::string &description) override;
|
||||
|
||||
private:
|
||||
WalletImpl *m_wallet;
|
||||
std::vector<CoinsInfo*> m_rows;
|
||||
mutable boost::shared_mutex m_rowsMutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Bitmonero = Monero;
|
||||
|
||||
#endif //FEATHER_COINS_H
|
||||
@@ -0,0 +1,132 @@
|
||||
#include "coins_info.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
CoinsInfo::~CoinsInfo() = default;
|
||||
|
||||
CoinsInfoImpl::CoinsInfoImpl()
|
||||
: m_blockHeight(0)
|
||||
, m_internalOutputIndex(0)
|
||||
, m_globalOutputIndex(0)
|
||||
, m_spent(false)
|
||||
, m_frozen(false)
|
||||
, m_spentHeight(0)
|
||||
, m_amount(0)
|
||||
, m_rct(false)
|
||||
, m_keyImageKnown(false)
|
||||
, m_pkIndex(0)
|
||||
, m_subaddrAccount(0)
|
||||
, m_subaddrIndex(0)
|
||||
, m_unlockTime(0)
|
||||
, m_unlocked(false)
|
||||
, m_type(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CoinsInfoImpl::~CoinsInfoImpl() = default;
|
||||
|
||||
uint64_t CoinsInfoImpl::blockHeight() const
|
||||
{
|
||||
return m_blockHeight;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::hash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
size_t CoinsInfoImpl::internalOutputIndex() const {
|
||||
return m_internalOutputIndex;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::globalOutputIndex() const
|
||||
{
|
||||
return m_globalOutputIndex;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::spent() const
|
||||
{
|
||||
return m_spent;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::frozen() const
|
||||
{
|
||||
return m_frozen;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::spentHeight() const
|
||||
{
|
||||
return m_spentHeight;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::amount() const
|
||||
{
|
||||
return m_amount;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::rct() const {
|
||||
return m_rct;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::keyImageKnown() const {
|
||||
return m_keyImageKnown;
|
||||
}
|
||||
|
||||
size_t CoinsInfoImpl::pkIndex() const {
|
||||
return m_pkIndex;
|
||||
}
|
||||
|
||||
uint32_t CoinsInfoImpl::subaddrIndex() const {
|
||||
return m_subaddrIndex;
|
||||
}
|
||||
|
||||
uint32_t CoinsInfoImpl::subaddrAccount() const {
|
||||
return m_subaddrAccount;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::address() const {
|
||||
return m_address;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::addressLabel() const {
|
||||
return m_addressLabel;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::keyImage() const {
|
||||
return m_keyImage;
|
||||
}
|
||||
|
||||
uint64_t CoinsInfoImpl::unlockTime() const {
|
||||
return m_unlockTime;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::unlocked() const {
|
||||
return m_unlocked;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::pubKey() const {
|
||||
return m_pubKey;
|
||||
}
|
||||
|
||||
bool CoinsInfoImpl::coinbase() const {
|
||||
return m_coinbase;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::description() const {
|
||||
return m_description;
|
||||
}
|
||||
|
||||
string CoinsInfoImpl::asset() const {
|
||||
return m_asset;
|
||||
}
|
||||
|
||||
uint8_t CoinsInfoImpl::type() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Bitmonero = Monero;
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef FEATHER_COINS_INFO_H
|
||||
#define FEATHER_COINS_INFO_H
|
||||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
namespace Monero {
|
||||
|
||||
class CoinsImpl;
|
||||
|
||||
class CoinsInfoImpl : public CoinsInfo
|
||||
{
|
||||
public:
|
||||
CoinsInfoImpl();
|
||||
~CoinsInfoImpl();
|
||||
|
||||
virtual uint64_t blockHeight() const override;
|
||||
virtual std::string hash() const override;
|
||||
virtual size_t internalOutputIndex() const override;
|
||||
virtual uint64_t globalOutputIndex() const override;
|
||||
virtual bool spent() const override;
|
||||
virtual bool frozen() const override;
|
||||
virtual uint64_t spentHeight() const override;
|
||||
virtual uint64_t amount() const override;
|
||||
virtual bool rct() const override;
|
||||
virtual bool keyImageKnown() const override;
|
||||
virtual size_t pkIndex() const override;
|
||||
virtual uint32_t subaddrIndex() const override;
|
||||
virtual uint32_t subaddrAccount() const override;
|
||||
virtual std::string address() const override;
|
||||
virtual std::string addressLabel() const override;
|
||||
virtual std::string keyImage() const override;
|
||||
virtual uint64_t unlockTime() const override;
|
||||
virtual bool unlocked() const override;
|
||||
virtual std::string pubKey() const override;
|
||||
virtual bool coinbase() const override;
|
||||
virtual std::string description() const override;
|
||||
virtual std::string asset() const override;
|
||||
virtual uint8_t type() const override;
|
||||
|
||||
private:
|
||||
uint64_t m_blockHeight;
|
||||
std::string m_hash;
|
||||
size_t m_internalOutputIndex;
|
||||
uint64_t m_globalOutputIndex;
|
||||
bool m_spent;
|
||||
bool m_frozen;
|
||||
uint64_t m_spentHeight;
|
||||
uint64_t m_amount;
|
||||
bool m_rct;
|
||||
bool m_keyImageKnown;
|
||||
size_t m_pkIndex;
|
||||
uint32_t m_subaddrIndex;
|
||||
uint32_t m_subaddrAccount;
|
||||
std::string m_address;
|
||||
std::string m_addressLabel;
|
||||
std::string m_keyImage;
|
||||
uint64_t m_unlockTime;
|
||||
bool m_unlocked;
|
||||
std::string m_pubKey;
|
||||
bool m_coinbase;
|
||||
std::string m_description;
|
||||
std::string m_asset;
|
||||
uint8_t m_type;
|
||||
|
||||
friend class CoinsImpl;
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Bitmonero = Monero;
|
||||
|
||||
#endif //FEATHER_COINS_INFO_H
|
||||
@@ -42,6 +42,8 @@
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "bc-ur/src/bc-ur.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
@@ -78,6 +80,22 @@ std::vector<std::string> PendingTransactionImpl::txid() const
|
||||
return txid;
|
||||
}
|
||||
|
||||
std::vector<std::string> PendingTransactionImpl::hex() const
|
||||
{
|
||||
std::vector<std::string> hexs;
|
||||
for (const auto &pt: m_pending_tx)
|
||||
hexs.push_back(epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(pt.tx)));
|
||||
return hexs;
|
||||
}
|
||||
|
||||
std::vector<std::string> PendingTransactionImpl::txKey() const
|
||||
{
|
||||
std::vector<std::string> keys;
|
||||
for (const auto& pt: m_pending_tx)
|
||||
keys.push_back(epee::string_tools::pod_to_hex(pt.tx_key));
|
||||
return keys;
|
||||
}
|
||||
|
||||
bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
|
||||
{
|
||||
|
||||
@@ -162,13 +180,50 @@ 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;
|
||||
for (const auto &ptx : m_pending_tx) {
|
||||
if (ptx.tx.type == cryptonote::transaction_type::AUDIT ||
|
||||
ptx.tx.type == cryptonote::transaction_type::BURN ||
|
||||
ptx.tx.type == cryptonote::transaction_type::STAKE) {
|
||||
result += ptx.tx.amount_burnt;
|
||||
} else {
|
||||
for (const auto &dest : ptx.dests) {
|
||||
result += dest.amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
int status() const override;
|
||||
std::string errorString() const override;
|
||||
bool commit(const std::string &filename = "", bool overwrite = false) override;
|
||||
std::string commitUR(int max_fragment_length = 130) override;
|
||||
uint64_t amount() const override;
|
||||
uint64_t dust() const override;
|
||||
uint64_t fee() const override;
|
||||
@@ -58,6 +59,8 @@ public:
|
||||
std::string multisigSignData() override;
|
||||
void signMultisigTx() override;
|
||||
std::vector<std::string> signersKeys() const override;
|
||||
std::vector<std::string> hex() const override;
|
||||
std::vector<std::string> txKey() const override;
|
||||
|
||||
private:
|
||||
friend class WalletImpl;
|
||||
|
||||
@@ -34,16 +34,60 @@
|
||||
#include "wallet.h"
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace epee;
|
||||
|
||||
namespace Monero {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string to_hex_or_empty(const crypto::public_key &key)
|
||||
{
|
||||
if (key == crypto::null_pkey)
|
||||
return {};
|
||||
return string_tools::pod_to_hex(key);
|
||||
}
|
||||
|
||||
std::vector<std::string> extract_return_addresses(const cryptonote::transaction_prefix &tx)
|
||||
{
|
||||
std::vector<std::string> addresses;
|
||||
if (tx.type == cryptonote::transaction_type::STAKE)
|
||||
{
|
||||
auto hex = to_hex_or_empty(tx.protocol_tx_data.return_address);
|
||||
if (hex.empty())
|
||||
hex = to_hex_or_empty(tx.return_address);
|
||||
if (!hex.empty())
|
||||
addresses.push_back(hex);
|
||||
return addresses;
|
||||
}
|
||||
if (tx.type != cryptonote::transaction_type::PROTOCOL)
|
||||
return addresses;
|
||||
|
||||
std::unordered_set<std::string> seen;
|
||||
for (const auto &out : tx.vout)
|
||||
{
|
||||
crypto::public_key output_key = crypto::null_pkey;
|
||||
if (!cryptonote::get_output_public_key(out, output_key))
|
||||
continue;
|
||||
auto hex = to_hex_or_empty(output_key);
|
||||
if (hex.empty())
|
||||
continue;
|
||||
if (seen.emplace(hex).second)
|
||||
addresses.push_back(hex);
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TransactionHistory::~TransactionHistory() {}
|
||||
|
||||
|
||||
@@ -130,6 +174,7 @@ void TransactionHistoryImpl::refresh()
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> in_payments;
|
||||
m_wallet->m_wallet->get_payments(in_payments, min_height, max_height);
|
||||
std::unordered_map<crypto::hash, TransactionInfoImpl*> protocol_by_hash;
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = in_payments.begin(); i != in_payments.end(); ++i) {
|
||||
const tools::wallet2::payment_details &pd = i->second;
|
||||
std::string payment_id = string_tools::pod_to_hex(i->first);
|
||||
@@ -152,6 +197,8 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_unlock_time = pd.m_unlock_time;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
|
||||
ti->m_asset = pd.m_asset_type;
|
||||
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
|
||||
protocol_by_hash.emplace(pd.m_tx_hash, ti);
|
||||
m_history.push_back(ti);
|
||||
|
||||
}
|
||||
@@ -197,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) {
|
||||
@@ -235,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});
|
||||
@@ -266,11 +315,23 @@ void TransactionHistoryImpl::refresh()
|
||||
ti->m_confirmations = 0;
|
||||
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
|
||||
ti->m_asset = pd.m_asset_type;
|
||||
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
|
||||
protocol_by_hash.emplace(pd.m_tx_hash, ti);
|
||||
m_history.push_back(ti);
|
||||
|
||||
LOG_PRINT_L1(__FUNCTION__ << ": Unconfirmed payment found " << pd.m_amount);
|
||||
}
|
||||
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_protocol_payments;
|
||||
m_wallet->m_wallet->get_payments_out(confirmed_protocol_payments, min_height, max_height);
|
||||
for (const auto &entry : confirmed_protocol_payments)
|
||||
{
|
||||
const auto it = protocol_by_hash.find(entry.first);
|
||||
if (it == protocol_by_hash.end())
|
||||
continue;
|
||||
it->second->m_return_addresses = extract_return_addresses(entry.second.m_tx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -134,6 +134,11 @@ string TransactionInfoImpl::paymentId() const
|
||||
return m_paymentid;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &TransactionInfoImpl::returnAddresses() const
|
||||
{
|
||||
return m_return_addresses;
|
||||
}
|
||||
|
||||
const std::vector<TransactionInfo::Transfer> &TransactionInfoImpl::transfers() const
|
||||
{
|
||||
return m_transfers;
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
virtual std::string hash() const override;
|
||||
virtual std::time_t timestamp() const override;
|
||||
virtual std::string paymentId() const override;
|
||||
virtual const std::vector<std::string> &returnAddresses() const override;
|
||||
virtual const std::vector<Transfer> &transfers() const override;
|
||||
virtual uint64_t confirmations() const override;
|
||||
virtual uint64_t unlockTime() const override;
|
||||
@@ -81,6 +82,7 @@ private:
|
||||
std::string m_hash;
|
||||
std::time_t m_timestamp;
|
||||
std::string m_paymentid;
|
||||
std::vector<std::string> m_return_addresses;
|
||||
std::vector<Transfer> m_transfers;
|
||||
uint64_t m_confirmations;
|
||||
uint64_t m_unlock_time;
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <sstream>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "bc-ur/src/bc-ur.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Monero {
|
||||
@@ -96,6 +98,46 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string UnsignedTransactionImpl::signUR(int max_fragment_length)
|
||||
{
|
||||
if(m_wallet.watchOnly())
|
||||
{
|
||||
m_errorString = tr("This is a watch only wallet");
|
||||
m_status = Status_Error;
|
||||
return "";
|
||||
}
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
try
|
||||
{
|
||||
tools::wallet2::signed_tx_set signed_txes;
|
||||
std::string signedTx = m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes);
|
||||
if (signedTx.empty())
|
||||
{
|
||||
m_errorString = tr("Failed to sign transaction");
|
||||
m_status = Status_Error;
|
||||
return "";
|
||||
}
|
||||
auto urMessage = ur::string_to_bytes(signedTx);
|
||||
ur::ByteVector cbor;
|
||||
ur::CborLite::encodeBytes(cbor, urMessage);
|
||||
std::string type = "xmr-txsigned";
|
||||
ur::UR urData = ur::UR(type, cbor);
|
||||
auto encoder = ur::UREncoder(urData, max_fragment_length);
|
||||
std::string output;
|
||||
for(size_t i = 0; i < encoder.seq_len(); i++) {
|
||||
output.append("\n"+encoder.next_part());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
m_errorString = string(tr("Failed to sign transaction")) + e.what();
|
||||
m_status = Status_Error;
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message)
|
||||
{
|
||||
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
uint64_t txCount() const override;
|
||||
// sign txs and save to file
|
||||
bool sign(const std::string &signedFileName) override;
|
||||
std::string signUR(int max_fragment_length = 130) override;
|
||||
std::string confirmationMessage() const override {return m_confirmationMessage;}
|
||||
uint64_t minMixinCount() const override;
|
||||
|
||||
|
||||
+738
-66
File diff suppressed because it is too large
Load Diff
+55
-5
@@ -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 *) {}
|
||||
@@ -93,13 +110,18 @@ public:
|
||||
const std::string& getPassword() const override;
|
||||
bool setDevicePin(const std::string &password) override;
|
||||
bool setDevicePassphrase(const std::string &password) override;
|
||||
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
|
||||
std::string integratedAddress(const std::string &payment_id) const override;
|
||||
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const override;
|
||||
std::string integratedAddress(const std::string &payment_id, bool carrot = true) const override;
|
||||
std::string secretViewKey() const override;
|
||||
std::string publicViewKey() const override;
|
||||
std::string secretSpendKey() const override;
|
||||
std::string publicSpendKey() const override;
|
||||
std::vector<std::string> carrotKeys() const override;
|
||||
std::string publicMultisigSignerKey() const override;
|
||||
std::string secretViewBalance() const override;
|
||||
std::string secretProveSpend() const override;
|
||||
std::string secretGenerateAddress() const override;
|
||||
std::string secretGenerateImage() const override;
|
||||
std::string path() const override;
|
||||
void stop() override;
|
||||
bool store(const std::string &path) override;
|
||||
@@ -113,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;
|
||||
@@ -168,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;
|
||||
@@ -196,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;
|
||||
@@ -268,6 +301,7 @@ private:
|
||||
friend class TransactionHistoryImpl;
|
||||
friend struct Wallet2CallbackImpl;
|
||||
friend class AddressBookImpl;
|
||||
friend class CoinsImpl;
|
||||
friend class SubaddressImpl;
|
||||
friend class SubaddressAccountImpl;
|
||||
|
||||
@@ -284,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;
|
||||
@@ -308,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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+172
-11
@@ -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)
|
||||
@@ -513,8 +567,8 @@ struct Wallet
|
||||
virtual const std::string& getPassword() const = 0;
|
||||
virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; };
|
||||
virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; };
|
||||
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
|
||||
std::string mainAddress() const { return address(0, 0); }
|
||||
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const = 0;
|
||||
std::string mainAddress(bool carrot = true) const { return address(0, 0, carrot); }
|
||||
virtual std::string path() const = 0;
|
||||
virtual NetworkType nettype() const = 0;
|
||||
bool mainnet() const { return nettype() == MAINNET; }
|
||||
@@ -533,7 +587,7 @@ struct Wallet
|
||||
* generated
|
||||
* \return - 106 characters string representing integrated address
|
||||
*/
|
||||
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
|
||||
virtual std::string integratedAddress(const std::string &payment_id, bool carrot = true) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretViewKey - returns secret view key
|
||||
@@ -559,12 +613,48 @@ struct Wallet
|
||||
*/
|
||||
virtual std::string publicSpendKey() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief allCarrotKeys - returns all Carrot keys
|
||||
* [0] - s_master
|
||||
* [1] - k_prove_spend
|
||||
* [2] - s_view_balance
|
||||
* [3] - k_view_incoming
|
||||
* [4] - k_generate_image
|
||||
* [5] - s_generate_address
|
||||
* \return - vector of all Carrot keys
|
||||
*/
|
||||
virtual std::vector<std::string> carrotKeys() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief publicMultisigSignerKey - returns public signer key
|
||||
* \return - public multisignature signer key or empty string if wallet is not multisig
|
||||
*/
|
||||
virtual std::string publicMultisigSignerKey() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretViewBalance - returns Carrot "view balance" secret
|
||||
* \return - Carrot s_vb
|
||||
*/
|
||||
virtual std::string secretViewBalance() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretProveSpend - returns Carrot "prove spend" secret
|
||||
* \return - Carrot secret k_ps
|
||||
*/
|
||||
virtual std::string secretProveSpend() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretGenerateAddress - returns Carrot "generate address" secret
|
||||
* \return - Carrot secret s_ga
|
||||
*/
|
||||
virtual std::string secretGenerateAddress() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretGenerateImage - returns Carrot "generate key image" secret
|
||||
* \return - Carrot secret k_gi
|
||||
*/
|
||||
virtual std::string secretGenerateImage() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief stop - interrupts wallet refresh() loop once (doesn't stop background refresh thread)
|
||||
*/
|
||||
@@ -674,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
|
||||
@@ -754,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)
|
||||
*/
|
||||
@@ -933,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
|
||||
@@ -955,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.
|
||||
@@ -971,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
|
||||
@@ -993,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
|
||||
@@ -1000,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
|
||||
@@ -1021,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
|
||||
@@ -1067,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;
|
||||
@@ -1222,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;
|
||||
};
|
||||
@@ -1331,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
|
||||
@@ -1382,6 +1522,27 @@ struct WalletManager
|
||||
uint64_t kdf_rounds = 1,
|
||||
WalletListener * listener = nullptr) = 0;
|
||||
|
||||
/*!
|
||||
* \brief creates a wallet from a polyseed mnemonic phrase
|
||||
* \param path Name of the wallet file to be created
|
||||
* \param password Password of wallet file
|
||||
* \param nettype Network type
|
||||
* \param mnemonic Polyseed mnemonic
|
||||
* \param passphrase Optional seed offset passphrase
|
||||
* \param newWallet Whether it is a new wallet
|
||||
* \param restoreHeight Override the embedded restore height
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* @return
|
||||
*/
|
||||
virtual Wallet * createWalletFromPolyseed(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
const std::string &mnemonic,
|
||||
const std::string &passphrase = "",
|
||||
bool newWallet = true,
|
||||
uint64_t restore_height = 0,
|
||||
uint64_t kdf_rounds = 1) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
|
||||
* \param wallet previously opened / created wallet instance
|
||||
|
||||
@@ -127,6 +127,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
NetworkType nettype,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &spendkey_string,
|
||||
uint64_t kdf_rounds)
|
||||
{
|
||||
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
|
||||
if(restoreHeight > 0){
|
||||
wallet->setRefreshFromBlockHeight(restoreHeight);
|
||||
}
|
||||
wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
@@ -156,6 +172,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype,
|
||||
const std::string &mnemonic, const std::string &passphrase,
|
||||
bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds)
|
||||
{
|
||||
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
|
||||
wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
|
||||
{
|
||||
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
|
||||
@@ -188,10 +213,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name,
|
||||
|
||||
bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const
|
||||
{
|
||||
hw::device::device_type type;
|
||||
bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds);
|
||||
device_type = static_cast<Wallet::Device>(type);
|
||||
return r;
|
||||
try {
|
||||
hw::device::device_type type;
|
||||
bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds);
|
||||
device_type = static_cast<Wallet::Device>(type);
|
||||
return r;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path)
|
||||
|
||||
@@ -67,6 +67,13 @@ public:
|
||||
const std::string &addressString,
|
||||
const std::string &viewKeyString,
|
||||
const std::string &spendKeyString = "") override;
|
||||
virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
NetworkType nettype,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &spendkey_string,
|
||||
uint64_t kdf_rounds) override;
|
||||
virtual Wallet * createWalletFromDevice(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
@@ -75,6 +82,16 @@ public:
|
||||
const std::string &subaddressLookahead = "",
|
||||
uint64_t kdf_rounds = 1,
|
||||
WalletListener * listener = nullptr) override;
|
||||
|
||||
virtual Wallet * createWalletFromPolyseed(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
const std::string &mnemonic,
|
||||
const std::string &passphrase,
|
||||
bool newWallet = true,
|
||||
uint64_t restore_height = 0,
|
||||
uint64_t kdf_rounds = 1) override;
|
||||
|
||||
virtual bool closeWallet(Wallet *wallet, bool store = true) override;
|
||||
bool walletExists(const std::string &path) override;
|
||||
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override;
|
||||
|
||||
+311
-80
@@ -95,6 +95,7 @@ using namespace epee;
|
||||
#include "device/device_cold.hpp"
|
||||
#include "device_trezor/device_trezor.hpp"
|
||||
#include "net/socks_connect.h"
|
||||
#include "polyseed/include/polyseed.h"
|
||||
#include "carrot_impl/format_utils.h"
|
||||
#include "tx_builder.h"
|
||||
#include "scanning_tools.h"
|
||||
@@ -968,6 +969,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
|
||||
return idx + extra;
|
||||
}
|
||||
|
||||
bool is_preferred_input(const std::vector<crypto::key_image>& preferred_input_list, const crypto::key_image& input) {
|
||||
if (!preferred_input_list.empty()) {
|
||||
auto it = std::find(preferred_input_list.begin(), preferred_input_list.end(), input);
|
||||
if (it == preferred_input_list.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
|
||||
{
|
||||
shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1);
|
||||
@@ -1208,6 +1219,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
|
||||
m_upper_transaction_weight_limit(0),
|
||||
m_run(true),
|
||||
m_callback(0),
|
||||
m_refreshEnabled(false),
|
||||
m_trusted_daemon(false),
|
||||
m_nettype(nettype),
|
||||
m_multisig_rounds_passed(0),
|
||||
@@ -1279,7 +1291,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
|
||||
m_send_change_back_to_subaddress(false),
|
||||
m_pool_info_query_time(0),
|
||||
m_has_ever_refreshed_from_node(false),
|
||||
m_allow_mismatched_daemon_version(false)
|
||||
m_allow_mismatched_daemon_version(false),
|
||||
m_polyseed(false)
|
||||
{
|
||||
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
|
||||
}
|
||||
@@ -1424,6 +1437,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
|
||||
return ret;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::get_refresh_enabled() {
|
||||
return m_refreshEnabled;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::set_refresh_enabled(bool val) {
|
||||
m_refreshEnabled = val;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::set_proxy(const std::string &address)
|
||||
{
|
||||
return m_http_client->set_proxy(address);
|
||||
@@ -1452,11 +1473,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab
|
||||
bool keys_deterministic = is_deterministic();
|
||||
if (!keys_deterministic)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "This is not a deterministic wallet");
|
||||
std::cout << "This is not a deterministic wallet" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (seed_language.empty())
|
||||
{
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "seed_language not set");
|
||||
std::cout << "seed_language not set" << std::endl;
|
||||
return false;
|
||||
}
|
||||
@@ -1466,10 +1489,25 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab
|
||||
key = cryptonote::encrypt_key(key, passphrase);
|
||||
if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language))
|
||||
{
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to create seed from key for language: "+seed_language+", falling back to English.");
|
||||
std::cout << "Failed to create seed from key for language: " << seed_language << std::endl;
|
||||
crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
bool wallet2::get_polyseed(epee::wipeable_string& polyseed, epee::wipeable_string& passphrase) const
|
||||
{
|
||||
if (!m_polyseed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
polyseed::data data(POLYSEED_COIN);
|
||||
data.load(get_account().get_keys().m_polyseed);
|
||||
data.encode(polyseed::get_lang_by_name(seed_language), polyseed);
|
||||
passphrase = get_account().get_keys().m_passphrase;
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -1584,20 +1622,41 @@ crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::su
|
||||
return hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::get_subaddress_as_str(const carrot::subaddress_index_extended& subaddr) const
|
||||
std::string wallet2::get_subaddress_as_str(const carrot::subaddress_index_extended& subaddr_const) const
|
||||
{
|
||||
// Make a non-const copy of subaddr
|
||||
carrot::subaddress_index_extended subaddr(subaddr_const);
|
||||
|
||||
// Handle "auto" - the account class doesn't know the HF version, so this is the cleanest solution
|
||||
if (subaddr.derive_type == carrot::AddressDeriveType::Auto) {
|
||||
|
||||
// Get the HF version
|
||||
uint32_t hf_version = estimate_current_hard_fork();
|
||||
THROW_WALLET_EXCEPTION_IF(hf_version == 0, error::wallet_internal_error, "unable to estimate the current hard fork - cannot generate subaddress as string");
|
||||
|
||||
// Change the non-const derivation type
|
||||
subaddr.derive_type = (hf_version >= HF_VERSION_CARROT) ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot;
|
||||
}
|
||||
|
||||
// Build the cryptonote::account_public_address
|
||||
carrot::CarrotDestinationV1 address = m_account.subaddress(subaddr);
|
||||
account_public_address addr{address.address_spend_pubkey, address.address_view_pubkey};
|
||||
addr.m_is_carrot = subaddr.derive_type == carrot::AddressDeriveType::Carrot;
|
||||
|
||||
// change this code into base 58
|
||||
return cryptonote::get_account_address_as_str(m_nettype, address.is_subaddress, addr, addr.m_is_carrot);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id, bool carrot) const
|
||||
{
|
||||
carrot::subaddress_index_extended subaddr{{0, 0}, carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot};
|
||||
carrot::CarrotDestinationV1 address = m_account.subaddress(subaddr);
|
||||
|
||||
// Build the cryptonote::account_public_address
|
||||
account_public_address addr{address.address_spend_pubkey, address.address_view_pubkey};
|
||||
|
||||
// change this code into base 58
|
||||
return cryptonote::get_account_address_as_str(m_nettype, address.is_subaddress, addr, subaddr.derive_type == carrot::AddressDeriveType::Carrot);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id) const
|
||||
{
|
||||
return cryptonote::get_account_integrated_address_as_str(m_nettype, get_address(), payment_id);
|
||||
addr.m_is_carrot = carrot;
|
||||
|
||||
return cryptonote::get_account_integrated_address_as_str(m_nettype, addr, payment_id, carrot);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::add_subaddress_account(const std::string& label)
|
||||
@@ -2097,12 +2156,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const
|
||||
|
||||
return false;
|
||||
}
|
||||
void wallet2::freeze(const crypto::public_key &pk)
|
||||
{
|
||||
freeze(get_transfer_details(pk));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::freeze(const crypto::key_image &ki)
|
||||
{
|
||||
freeze(get_transfer_details(ki));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::thaw(const crypto::public_key &pk)
|
||||
{
|
||||
thaw(get_transfer_details(pk));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::thaw(const crypto::key_image &ki)
|
||||
{
|
||||
thaw(get_transfer_details(ki));
|
||||
@@ -2113,6 +2181,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const
|
||||
return frozen(get_transfer_details(ki));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
size_t wallet2::get_transfer_details(const crypto::public_key &pk) const
|
||||
{
|
||||
for (size_t idx = 0; idx < m_transfers.size(); ++idx)
|
||||
{
|
||||
const transfer_details &td = m_transfers[idx];
|
||||
if (td.get_public_key() == pk) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_THROW_MES(false, "Public key not found");
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
size_t wallet2::get_transfer_details(const crypto::key_image &ki) const
|
||||
{
|
||||
for (size_t idx = 0; idx < m_transfers.size(); ++idx)
|
||||
@@ -2621,6 +2701,7 @@ void wallet2::process_new_scanned_transaction(
|
||||
continue;
|
||||
|
||||
// update m_transfers view-incoming scan info, and default values
|
||||
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
|
||||
transfer_details& td = m_transfers.emplace_back();
|
||||
td.m_block_height = height;
|
||||
td.m_internal_output_index = local_output_index;
|
||||
@@ -2927,6 +3008,8 @@ void wallet2::process_new_scanned_transaction(
|
||||
payment.m_timestamp = ts;
|
||||
payment.m_coinbase = miner_tx;
|
||||
payment.m_subaddr_index = i.first;
|
||||
payment.m_tx_type = tx.type;
|
||||
payment.m_is_carrot = (tx.version >= TRANSACTION_VERSION_CARROT);
|
||||
if (pool) {
|
||||
if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}))
|
||||
all_same = false;
|
||||
@@ -4175,7 +4258,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||
// infer when we get an incoming output
|
||||
|
||||
bool first = true, last = false;
|
||||
while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks)
|
||||
while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks && m_refreshEnabled)
|
||||
{
|
||||
uint64_t next_blocks_start_height;
|
||||
std::vector<cryptonote::block_complete_entry> next_blocks;
|
||||
@@ -4518,6 +4601,7 @@ bool wallet2::clear()
|
||||
m_transfers.clear();
|
||||
m_transfers_indices.clear();
|
||||
m_locked_coins.clear();
|
||||
m_salvium_txs.clear();
|
||||
m_key_images.clear();
|
||||
m_pub_keys.clear();
|
||||
m_unconfirmed_txs.clear();
|
||||
@@ -4536,6 +4620,8 @@ bool wallet2::clear()
|
||||
m_pool_info_query_time = 0;
|
||||
m_skip_to_height = 0;
|
||||
m_background_sync_data = background_sync_data_t{};
|
||||
m_subaddresses_extended.clear();
|
||||
m_return_output_info.clear();
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -4545,6 +4631,7 @@ void wallet2::clear_soft(bool keep_key_images)
|
||||
m_transfers.clear();
|
||||
m_transfers_indices.clear();
|
||||
m_locked_coins.clear();
|
||||
m_salvium_txs.clear();
|
||||
if (!keep_key_images)
|
||||
m_key_images.clear();
|
||||
m_pub_keys.clear();
|
||||
@@ -4557,6 +4644,8 @@ void wallet2::clear_soft(bool keep_key_images)
|
||||
m_pool_info_query_time = 0;
|
||||
m_skip_to_height = 0;
|
||||
m_background_sync_data = background_sync_data_t{};
|
||||
m_subaddresses_extended.clear();
|
||||
m_return_output_info.clear();
|
||||
|
||||
cryptonote::block b;
|
||||
generate_genesis(b);
|
||||
@@ -4846,6 +4935,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const crypt
|
||||
value2.SetInt(m_enable_multisig ? 1 : 0);
|
||||
json.AddMember("enable_multisig", value2, json.GetAllocator());
|
||||
|
||||
value2.SetInt(m_polyseed ? 1 : 0);
|
||||
json.AddMember("polyseed", value2, json.GetAllocator());
|
||||
|
||||
value2.SetInt(m_freeze_incoming_payments ? 1 : 0);
|
||||
json.AddMember("freeze_incoming_payments", value2, json.GetAllocator());
|
||||
|
||||
@@ -5093,6 +5185,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
|
||||
m_send_change_back_to_subaddress = false;
|
||||
m_allow_mismatched_daemon_version = false;
|
||||
m_custom_background_key = boost::none;
|
||||
m_polyseed = false;
|
||||
}
|
||||
else if(json.IsObject())
|
||||
{
|
||||
@@ -5337,6 +5430,9 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff);
|
||||
m_background_sync_type = field_background_sync_type;
|
||||
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false);
|
||||
m_polyseed = field_polyseed;
|
||||
|
||||
// Load encryption key used to encrypt background cache
|
||||
crypto::chacha_key custom_background_key;
|
||||
m_custom_background_key = boost::none;
|
||||
@@ -5666,6 +5762,48 @@ void wallet2::init_type(hw::device::device_type device_type)
|
||||
m_key_device_type = device_type;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Generates a polyseed wallet or restores one.
|
||||
* \param wallet_ Name of wallet file
|
||||
* \param password Password of wallet file
|
||||
* \param passphrase Seed offset passphrase
|
||||
* \param recover Whether it is a restore
|
||||
* \param seed_words If it is a restore, the polyseed
|
||||
* \param create_address_file Whether to create an address file
|
||||
* \return The secret key of the generated wallet
|
||||
*/
|
||||
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
|
||||
const polyseed::data &seed, const epee::wipeable_string& passphrase, bool recover, uint64_t restoreHeight, bool create_address_file)
|
||||
{
|
||||
clear();
|
||||
prepare_file_names(wallet_);
|
||||
|
||||
if (!wallet_.empty()) {
|
||||
boost::system::error_code ignored_ec;
|
||||
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
|
||||
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
|
||||
}
|
||||
|
||||
m_account.create_from_polyseed(seed, passphrase);
|
||||
|
||||
init_type(hw::device::device_type::SOFTWARE);
|
||||
m_polyseed = true;
|
||||
setup_keys(password);
|
||||
|
||||
if (recover) {
|
||||
m_refresh_from_block_height = estimate_blockchain_height(restoreHeight > 0 ? restoreHeight : seed.birthday());
|
||||
} else {
|
||||
m_refresh_from_block_height = estimate_blockchain_height();
|
||||
}
|
||||
|
||||
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
|
||||
|
||||
setup_new_blockchain();
|
||||
|
||||
if (!wallet_.empty())
|
||||
store();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one. Assumes the multisig setup
|
||||
* has already completed for the provided multisig info.
|
||||
@@ -5793,7 +5931,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint64_t wallet2::estimate_blockchain_height()
|
||||
uint64_t wallet2::estimate_blockchain_height(uint64_t time)
|
||||
{
|
||||
// -1 month for fluctuations in block time and machine date/time setup.
|
||||
// avg seconds per block
|
||||
@@ -5817,7 +5955,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
|
||||
// the daemon is currently syncing.
|
||||
// If we use the approximate height we subtract one month as
|
||||
// a safety margin.
|
||||
height = get_approximate_blockchain_height();
|
||||
height = get_approximate_blockchain_height(time);
|
||||
uint64_t target_height = get_daemon_blockchain_target_height(err);
|
||||
if (err.empty()) {
|
||||
if (target_height < height)
|
||||
@@ -7152,6 +7290,29 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, const std::string& asse
|
||||
return amount;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::view_only_balance(uint32_t index_major, const std::vector<crypto::key_image>& selected_inputs, const std::string& asset_type)
|
||||
{
|
||||
uint64_t amount = 0;
|
||||
if (m_transfers_indices.count(asset_type) > 0) {
|
||||
for (const auto& idx: m_transfers_indices.at(asset_type))
|
||||
{
|
||||
const transfer_details& td = m_transfers[idx];
|
||||
if (is_preferred_input(selected_inputs, td.m_key_image) &&
|
||||
!is_spent(td, false) &&
|
||||
!td.m_frozen &&
|
||||
!td.m_key_image_partial &&
|
||||
td.m_key_image_known &&
|
||||
td.is_rct() &&
|
||||
is_transfer_unlocked(td) &&
|
||||
td.m_subaddr_index.major == index_major)
|
||||
{
|
||||
amount += td.m_amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_major, const std::string& asset_type, bool strict) const
|
||||
{
|
||||
std::map<uint32_t, uint64_t> amount_per_subaddr;
|
||||
@@ -8072,9 +8233,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
|
||||
crypto::key_derivation derivation;
|
||||
std::vector<crypto::key_derivation> additional_derivations;
|
||||
|
||||
// compute public keys from out secret keys
|
||||
crypto::public_key tx_pub_key;
|
||||
crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key);
|
||||
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
|
||||
std::vector<crypto::public_key> additional_tx_pub_keys;
|
||||
for (const crypto::secret_key &skey: txs[n].additional_tx_keys)
|
||||
{
|
||||
@@ -10610,7 +10769,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
LOG_PRINT_L2("transfer_selected_rct done");
|
||||
}
|
||||
|
||||
std::vector<size_t> wallet2::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> wallet2::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)
|
||||
{
|
||||
std::vector<size_t> picks;
|
||||
float current_output_relatdness = 1.0f;
|
||||
@@ -10627,6 +10786,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
|
||||
for (size_t i: m_transfers_indices[asset_type])
|
||||
{
|
||||
const transfer_details& td = m_transfers[i];
|
||||
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
|
||||
{
|
||||
if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
|
||||
@@ -10648,6 +10810,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
|
||||
{
|
||||
size_t idx = *i;
|
||||
const transfer_details& td = m_transfers[idx];
|
||||
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
|
||||
{
|
||||
if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
|
||||
@@ -10663,6 +10828,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
|
||||
{
|
||||
size_t idx2 = *j;
|
||||
const transfer_details& td2 = m_transfers[idx2];
|
||||
if (!is_preferred_input(preferred_input_list, td2.m_key_image)) {
|
||||
continue;
|
||||
}
|
||||
if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below)
|
||||
{
|
||||
MDEBUG("Ignoring output " << idx2 << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]");
|
||||
@@ -10765,15 +10933,20 @@ static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &tr
|
||||
// This system allows for sending (almost) the entire balance, since it does
|
||||
// not generate spurious change in all txes, thus decreasing the instantaneous
|
||||
// usable balance.
|
||||
std::vector<wallet2::pending_tx> wallet2::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)
|
||||
std::vector<wallet2::pending_tx> wallet2::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)
|
||||
{
|
||||
//ensure device is let in NONE mode in any case
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
boost::unique_lock<hw::device> hwdev_lock (hwdev);
|
||||
hw::reset_mode rst(hwdev);
|
||||
|
||||
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
|
||||
if (do_carrot_tx_construction)
|
||||
// Sanity check correct CN/Carrot address is being used
|
||||
const uint8_t hf_version = get_current_hard_fork();
|
||||
for (const auto &entry: dsts) {
|
||||
THROW_WALLET_EXCEPTION_IF(entry.addr.m_is_carrot && hf_version < HF_VERSION_CARROT, error::wallet_internal_error, "Carrot address supplied, but Carrot not yet active");
|
||||
THROW_WALLET_EXCEPTION_IF(!entry.addr.m_is_carrot && hf_version >= HF_VERSION_CARROT, error::wallet_internal_error, "CryptoNote address supplied, but Carrot is now active");
|
||||
}
|
||||
if (hf_version >= HF_VERSION_CARROT)
|
||||
{
|
||||
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_transfer(*this, dsts, priority, extra, tx_type, subaddr_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
std::vector<pending_tx> ptx_vector;
|
||||
@@ -11027,12 +11200,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
for (size_t i: m_transfers_indices[source_asset])
|
||||
{
|
||||
const transfer_details& td = m_transfers[i];
|
||||
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
||||
continue;
|
||||
}
|
||||
if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
|
||||
{
|
||||
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold));
|
||||
continue;
|
||||
}
|
||||
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
|
||||
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.m_key_image_known && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
|
||||
{
|
||||
if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
|
||||
{
|
||||
@@ -11082,9 +11258,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
|
||||
LOG_PRINT_L2("Starting with " << num_nondust_outputs << " non-dust outputs and " << num_dust_outputs << " dust outputs");
|
||||
|
||||
if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty())
|
||||
return std::vector<wallet2::pending_tx>();
|
||||
// use tobotoht's code path on view-only wallet, otherwise default to upstream
|
||||
bool throwOnNoEnotes = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet;
|
||||
|
||||
if (throwOnNoEnotes) {
|
||||
THROW_WALLET_EXCEPTION_IF(unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty(), error::wallet_internal_error, "No enotes available to spend")
|
||||
} else {
|
||||
if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty())
|
||||
return std::vector<wallet2::pending_tx>();
|
||||
}
|
||||
// if empty, put dummy entry so that the front can be referenced later in the loop
|
||||
if (unused_dust_indices_per_subaddr.empty())
|
||||
unused_dust_indices_per_subaddr.push_back({});
|
||||
@@ -11112,7 +11294,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
// will get us a known fee.
|
||||
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
|
||||
total_needed_money = needed_money + (subtract_fee_from_outputs.size() ? 0 : estimated_fee);
|
||||
preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices, source_asset);
|
||||
preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices, source_asset, preferred_input_list);
|
||||
if (!preferred_inputs.empty())
|
||||
{
|
||||
string s;
|
||||
@@ -11606,7 +11788,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<wallet2::pending_tx> wallet2::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> wallet2::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)
|
||||
{
|
||||
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
|
||||
if (do_carrot_tx_construction)
|
||||
@@ -11652,6 +11834,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
|
||||
{
|
||||
//size_t i = m_transfers_indices[asset_type][idx];
|
||||
const transfer_details& td = m_transfers[i];
|
||||
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
|
||||
continue;
|
||||
}
|
||||
if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
|
||||
{
|
||||
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
|
||||
@@ -12292,6 +12477,31 @@ void wallet2::device_show_address(uint32_t account_index, uint32_t address_index
|
||||
hwdev.display_address(subaddress_index{account_index, address_index}, payment_id);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint8_t wallet2::estimate_current_hard_fork(const uint64_t height) const
|
||||
{
|
||||
// Get the last-seen top height by the wallet
|
||||
uint64_t guessed_height = (height > 0) ? height : m_blockchain.size();
|
||||
|
||||
// Get the correct hardfork table, based on current net type
|
||||
const hardfork_t *hfs =
|
||||
(m_nettype == cryptonote::MAINNET) ? mainnet_hard_forks :
|
||||
(m_nettype == cryptonote::TESTNET) ? testnet_hard_forks :
|
||||
stagenet_hard_forks;
|
||||
size_t hfs_count =
|
||||
(m_nettype == cryptonote::MAINNET) ? num_mainnet_hard_forks :
|
||||
(m_nettype == cryptonote::TESTNET) ? num_testnet_hard_forks :
|
||||
num_stagenet_hard_forks;
|
||||
|
||||
// Iterate over the hard fork table, to see what the current fork is for the guessed height
|
||||
for (size_t i = hfs_count-1; i>=0; --i) {
|
||||
if (hfs[i].height <= guessed_height)
|
||||
return hfs[i].version;
|
||||
}
|
||||
|
||||
// return "no value found" to the caller
|
||||
return 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint8_t wallet2::get_current_hard_fork()
|
||||
{
|
||||
if (m_offline)
|
||||
@@ -12321,7 +12531,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks)
|
||||
boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
|
||||
THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get height");
|
||||
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
|
||||
THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get earliest fork height");
|
||||
THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get earliest fork height. Please check your connection and/or switch node you are connected to currently.");
|
||||
|
||||
bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
|
||||
if (close_enough)
|
||||
@@ -13655,7 +13865,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
|
||||
return target_height;
|
||||
}
|
||||
|
||||
uint64_t wallet2::get_approximate_blockchain_height() const
|
||||
uint64_t wallet2::get_approximate_blockchain_height(uint64_t t) const
|
||||
{
|
||||
if (m_nettype != MAINNET) return 0;
|
||||
|
||||
@@ -13666,7 +13876,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const
|
||||
// avg seconds per block
|
||||
const int seconds_per_block = DIFFICULTY_TARGET_V2;
|
||||
// Calculated blockchain height
|
||||
uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
|
||||
uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block;
|
||||
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
|
||||
return approx_blockchain_height;
|
||||
}
|
||||
@@ -13952,33 +14162,40 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::export_key_images(const std::string &filename, bool all) const
|
||||
{
|
||||
PERF_TIMER(export_key_images);
|
||||
std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images(all);
|
||||
std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC));
|
||||
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
|
||||
const uint32_t offset = ski.first;
|
||||
|
||||
std::string data;
|
||||
data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key));
|
||||
data.resize(4);
|
||||
data[0] = offset & 0xff;
|
||||
data[1] = (offset >> 8) & 0xff;
|
||||
data[2] = (offset >> 16) & 0xff;
|
||||
data[3] = (offset >> 24) & 0xff;
|
||||
data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
|
||||
data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
|
||||
for (const auto &i: ski.second)
|
||||
{
|
||||
data += std::string((const char *)&i.first, sizeof(crypto::key_image));
|
||||
data += std::string((const char *)&i.second, sizeof(crypto::signature));
|
||||
}
|
||||
|
||||
// encrypt data, keep magic plaintext
|
||||
PERF_TIMER(export_key_images_encrypt);
|
||||
std::string ciphertext = encrypt_with_view_secret_key(data);
|
||||
return save_to_file(filename, magic + ciphertext);
|
||||
std::string data = export_key_images_str(all);
|
||||
return save_to_file(filename, data);
|
||||
}
|
||||
|
||||
std::string wallet2::export_key_images_str(bool all) const
|
||||
{
|
||||
PERF_TIMER(export_key_images);
|
||||
std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images(all);
|
||||
std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC));
|
||||
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
|
||||
const uint32_t offset = ski.first;
|
||||
|
||||
std::string data;
|
||||
data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key));
|
||||
data.resize(4);
|
||||
data[0] = offset & 0xff;
|
||||
data[1] = (offset >> 8) & 0xff;
|
||||
data[2] = (offset >> 16) & 0xff;
|
||||
data[3] = (offset >> 24) & 0xff;
|
||||
data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
|
||||
data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
|
||||
for (const auto &i: ski.second)
|
||||
{
|
||||
data += std::string((const char *)&i.first, sizeof(crypto::key_image));
|
||||
data += std::string((const char *)&i.second, sizeof(crypto::signature));
|
||||
}
|
||||
|
||||
// encrypt data, keep magic plaintext
|
||||
PERF_TIMER(export_key_images_encrypt);
|
||||
std::string ciphertext = encrypt_with_view_secret_key(data);
|
||||
return magic + ciphertext;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const
|
||||
{
|
||||
@@ -14038,53 +14255,60 @@ std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>
|
||||
return std::make_pair(offset, ski);
|
||||
}
|
||||
|
||||
uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent)
|
||||
uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) {
|
||||
std::string data;
|
||||
|
||||
bool r = load_from_file(filename, data);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename);
|
||||
|
||||
return import_key_images_str(data, spent, unspent);
|
||||
}
|
||||
|
||||
uint64_t wallet2::import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent)
|
||||
{
|
||||
PERF_TIMER(import_key_images_fsu);
|
||||
std::string data;
|
||||
bool r = load_from_file(filename, data);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename);
|
||||
std::string data_local = data;
|
||||
|
||||
const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC);
|
||||
if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen))
|
||||
{
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename);
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic"));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PERF_TIMER(import_key_images_decrypt);
|
||||
data = decrypt_with_view_secret_key(std::string(data, magiclen));
|
||||
data_local = decrypt_with_view_secret_key(std::string(data, magiclen));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what());
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + ": " + e.what());
|
||||
}
|
||||
|
||||
const size_t headerlen = 4 + 2 * sizeof(crypto::public_key);
|
||||
THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename);
|
||||
const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24);
|
||||
const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4];
|
||||
const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)];
|
||||
THROW_WALLET_EXCEPTION_IF(data_local.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file "));
|
||||
const uint32_t offset = (uint8_t)data_local[0] | (((uint8_t)data_local[1]) << 8) | (((uint8_t)data_local[2]) << 16) | (((uint8_t)data_local[3]) << 24);
|
||||
const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data_local[4];
|
||||
const crypto::public_key &public_view_key = *(const crypto::public_key*)&data_local[4 + sizeof(crypto::public_key)];
|
||||
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
|
||||
if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account");
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + " are for a different account");
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs");
|
||||
|
||||
const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
|
||||
THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size,
|
||||
error::wallet_internal_error, std::string("Bad data size from file ") + filename);
|
||||
size_t nki = (data.size() - headerlen) / record_size;
|
||||
THROW_WALLET_EXCEPTION_IF((data_local.size() - headerlen) % record_size,
|
||||
error::wallet_internal_error, std::string("Bad data size from file "));
|
||||
size_t nki = (data_local.size() - headerlen) / record_size;
|
||||
|
||||
std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
|
||||
ski.reserve(nki);
|
||||
for (size_t n = 0; n < nki; ++n)
|
||||
{
|
||||
crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data[headerlen + n * record_size]);
|
||||
crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&data[headerlen + n * record_size + sizeof(crypto::key_image)]);
|
||||
crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data_local[headerlen + n * record_size]);
|
||||
crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&data_local[headerlen + n * record_size + sizeof(crypto::key_image)]);
|
||||
|
||||
ski.push_back(std::make_pair(key_image, signature));
|
||||
}
|
||||
@@ -15776,6 +16000,21 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
|
||||
{
|
||||
std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
date.tm_year = year - 1900;
|
||||
date.tm_mon = month - 1;
|
||||
date.tm_mday = day;
|
||||
if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
|
||||
{
|
||||
throw std::runtime_error("month or day out of range");
|
||||
}
|
||||
|
||||
uint64_t timestamp_target = std::mktime(&date);
|
||||
|
||||
return get_blockchain_height_by_timestamp(timestamp_target);
|
||||
}
|
||||
|
||||
uint64_t wallet2::get_blockchain_height_by_timestamp(uint64_t timestamp_target) {
|
||||
uint32_t version;
|
||||
if (!check_connection(&version))
|
||||
{
|
||||
@@ -15785,15 +16024,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
|
||||
{
|
||||
throw std::runtime_error("this function requires RPC version 1.6 or higher");
|
||||
}
|
||||
std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
date.tm_year = year - 1900;
|
||||
date.tm_mon = month - 1;
|
||||
date.tm_mday = day;
|
||||
if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
|
||||
{
|
||||
throw std::runtime_error("month or day out of range");
|
||||
}
|
||||
uint64_t timestamp_target = std::mktime(&date);
|
||||
|
||||
std::string err;
|
||||
uint64_t height_min = 0;
|
||||
uint64_t height_max = get_daemon_blockchain_height(err) - 1;
|
||||
|
||||
+73
-12
@@ -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"
|
||||
@@ -496,9 +497,10 @@ private:
|
||||
bool m_coinbase;
|
||||
cryptonote::subaddress_index m_subaddr_index;
|
||||
cryptonote::transaction_type m_tx_type;
|
||||
bool m_is_carrot;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VERSION_FIELD(0)
|
||||
VERSION_FIELD(1)
|
||||
FIELD(m_tx_hash)
|
||||
VARINT_FIELD(m_amount)
|
||||
FIELD(m_asset_type)
|
||||
@@ -510,6 +512,11 @@ private:
|
||||
FIELD(m_coinbase)
|
||||
FIELD(m_subaddr_index)
|
||||
VARINT_FIELD(m_tx_type)
|
||||
if (version < 1) {
|
||||
m_is_carrot = false;
|
||||
return true;
|
||||
}
|
||||
FIELD(m_is_carrot)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
@@ -987,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
|
||||
@@ -1165,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(); }
|
||||
|
||||
@@ -1180,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
|
||||
*/
|
||||
@@ -1197,8 +1229,9 @@ private:
|
||||
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
|
||||
//std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
|
||||
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index) const;
|
||||
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index, const uint64_t height) const;
|
||||
std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
|
||||
std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const;
|
||||
std::string get_integrated_address_as_str(const crypto::hash8& payment_id, bool carrot = true) const;
|
||||
void add_subaddress_account(const std::string& label);
|
||||
size_t get_num_subaddress_accounts() const { return m_subaddress_labels.size(); }
|
||||
size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; }
|
||||
@@ -1239,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);
|
||||
@@ -1287,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);
|
||||
@@ -1419,8 +1453,6 @@ private:
|
||||
if (ver < 20)
|
||||
return;
|
||||
a & m_subaddresses.parent();
|
||||
a & m_subaddresses_extended.parent();
|
||||
a & m_return_output_info.parent();
|
||||
std::unordered_map<cryptonote::subaddress_index, crypto::public_key> dummy_subaddresses_inv;
|
||||
a & dummy_subaddresses_inv;
|
||||
a & m_subaddress_labels;
|
||||
@@ -1461,9 +1493,13 @@ private:
|
||||
if(ver < 31)
|
||||
{
|
||||
m_background_sync_data = background_sync_data_t{};
|
||||
m_subaddresses_extended = {};
|
||||
m_return_output_info = {};
|
||||
return;
|
||||
}
|
||||
a & m_background_sync_data;
|
||||
a & m_subaddresses_extended.parent();
|
||||
a & m_return_output_info.parent();
|
||||
}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
@@ -1487,8 +1523,6 @@ private:
|
||||
FIELD(m_scanned_pool_txs[0])
|
||||
FIELD(m_scanned_pool_txs[1])
|
||||
FIELD(m_subaddresses)
|
||||
FIELD(m_subaddresses_extended)
|
||||
FIELD(m_return_output_info)
|
||||
FIELD(m_subaddress_labels)
|
||||
FIELD(m_additional_tx_keys)
|
||||
FIELD(m_attributes)
|
||||
@@ -1508,11 +1542,21 @@ private:
|
||||
if (version < 2)
|
||||
{
|
||||
m_background_sync_data = background_sync_data_t{};
|
||||
m_subaddresses_extended = {};
|
||||
m_return_output_info = {};
|
||||
return true;
|
||||
}
|
||||
FIELD(m_background_sync_data)
|
||||
FIELD(m_subaddresses_extended)
|
||||
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
|
||||
@@ -1656,7 +1700,9 @@ 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();
|
||||
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
|
||||
bool use_fork_rules(uint8_t version, int64_t early_blocks = 0);
|
||||
@@ -1673,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();
|
||||
@@ -1743,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;
|
||||
@@ -1766,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();
|
||||
|
||||
@@ -1866,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;
|
||||
@@ -1907,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);
|
||||
@@ -2054,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);
|
||||
@@ -2167,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;
|
||||
@@ -2175,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;
|
||||
@@ -2289,7 +2344,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 6)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
|
||||
@@ -2618,6 +2673,12 @@ namespace boost
|
||||
a & x.m_amounts;
|
||||
a & x.m_asset_type;
|
||||
a & x.m_tx_type;
|
||||
if (ver < 6)
|
||||
{
|
||||
x.m_is_carrot = false;
|
||||
return;
|
||||
}
|
||||
a & x.m_is_carrot;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
||||
@@ -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
|
||||
@@ -439,7 +439,7 @@ namespace tools
|
||||
entry.subaddr_index = pd.m_subaddr_index;
|
||||
entry.subaddr_indices.push_back(pd.m_subaddr_index);
|
||||
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
|
||||
}
|
||||
@@ -473,7 +473,7 @@ namespace tools
|
||||
for (uint32_t i: pd.m_subaddr_indices)
|
||||
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
|
||||
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
|
||||
}
|
||||
@@ -508,7 +508,7 @@ namespace tools
|
||||
for (uint32_t i: pd.m_subaddr_indices)
|
||||
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
|
||||
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_tx.unlock_time);
|
||||
}
|
||||
@@ -533,7 +533,7 @@ namespace tools
|
||||
entry.subaddr_index = pd.m_subaddr_index;
|
||||
entry.subaddr_indices.push_back(pd.m_subaddr_index);
|
||||
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
|
||||
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
|
||||
}
|
||||
@@ -542,8 +542,8 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
std::vector<std::string> assets_in_wallet = m_wallet->list_asset_types();
|
||||
std::string asset_type = req.asset_type.empty() ? "SAL1" : boost::algorithm::to_upper_copy(req.asset_type);
|
||||
// verify that the asset is in the list of in-wallet assets
|
||||
@@ -627,8 +627,6 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
try
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
|
||||
@@ -651,8 +649,9 @@ namespace tools
|
||||
res.addresses.resize(res.addresses.size() + 1);
|
||||
auto& info = res.addresses.back();
|
||||
const cryptonote::subaddress_index index = {req.account_index, i};
|
||||
//info.address = m_wallet->get_subaddress_as_str(index);
|
||||
info.address = m_wallet->get_subaddress_as_str({{req.account_index, i}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
info.address = m_wallet->get_subaddress_as_str({req.account_index, i});
|
||||
info.address_cn = req.cryptonote ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::PreCarrot}) : "";
|
||||
info.address_carrot = req.carrot ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::Carrot}) : "";
|
||||
info.label = m_wallet->get_subaddress_label(index);
|
||||
info.address_index = index.minor;
|
||||
info.used = std::find_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return td.m_subaddr_index == index; }) != transfers.end();
|
||||
@@ -746,8 +745,6 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
try
|
||||
{
|
||||
res.total_balance = 0;
|
||||
@@ -768,8 +765,7 @@ namespace tools
|
||||
continue;
|
||||
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
|
||||
info.account_index = subaddr_index.major;
|
||||
//info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
|
||||
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, 0}, carrot::AddressDeriveType::Auto});
|
||||
|
||||
//for (const auto& asset: asset_types) {
|
||||
info.balance = m_wallet->balance(subaddr_index.major, "SAL1", req.strict_balances);
|
||||
@@ -1306,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())
|
||||
{
|
||||
@@ -2103,8 +2099,6 @@ namespace tools
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
crypto::hash payment_id;
|
||||
crypto::hash8 payment_id8;
|
||||
cryptonote::blobdata payment_id_blob;
|
||||
@@ -2145,7 +2139,7 @@ namespace tools
|
||||
rpc_payment.unlock_time = payment.m_unlock_time;
|
||||
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
|
||||
rpc_payment.subaddr_index = payment.m_subaddr_index;
|
||||
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
|
||||
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
res.payments.push_back(rpc_payment);
|
||||
}
|
||||
@@ -2157,8 +2151,6 @@ namespace tools
|
||||
{
|
||||
res.payments.clear();
|
||||
|
||||
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
|
||||
|
||||
if (!m_wallet) return not_open(er);
|
||||
|
||||
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
|
||||
@@ -2176,7 +2168,7 @@ namespace tools
|
||||
rpc_payment.block_height = payment.second.m_block_height;
|
||||
rpc_payment.unlock_time = payment.second.m_unlock_time;
|
||||
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
|
||||
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.second.m_block_height) >= HF_VERSION_CARROT;
|
||||
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.second.m_subaddr_index.major, payment.second.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.second.m_unlock_time, payment.second.m_block_height);
|
||||
res.payments.push_back(std::move(rpc_payment));
|
||||
@@ -2232,7 +2224,7 @@ namespace tools
|
||||
rpc_payment.block_height = payment.m_block_height;
|
||||
rpc_payment.unlock_time = payment.m_unlock_time;
|
||||
rpc_payment.subaddr_index = payment.m_subaddr_index;
|
||||
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
|
||||
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
|
||||
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
|
||||
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
|
||||
res.payments.push_back(std::move(rpc_payment));
|
||||
@@ -2363,6 +2355,55 @@ namespace tools
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_master") == 0)
|
||||
{
|
||||
if (m_wallet->watch_only())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
|
||||
er.message = "The wallet is watch-only. Cannot retrieve s_master key.";
|
||||
return false;
|
||||
}
|
||||
CHECK_IF_BACKGROUND_SYNCING();
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_master);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("k_prove_spend") == 0)
|
||||
{
|
||||
if (m_wallet->watch_only())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
|
||||
er.message = "The wallet is watch-only. Cannot retrieve k_prove_spend key.";
|
||||
return false;
|
||||
}
|
||||
CHECK_IF_BACKGROUND_SYNCING();
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_prove_spend);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_view_balance") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_view_balance") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("k_view_incoming") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_view_incoming);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("k_generate_image") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_generate_image);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else if(req.key_type.compare("s_generate_address") == 0)
|
||||
{
|
||||
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_generate_address);
|
||||
res.key = std::string(key.data(), key.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
er.message = "key_type " + req.key_type + " not found";
|
||||
|
||||
@@ -139,9 +139,13 @@ namespace wallet_rpc
|
||||
{
|
||||
uint32_t account_index;
|
||||
std::vector<uint32_t> address_index;
|
||||
bool carrot;
|
||||
bool cryptonote;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(address_index)
|
||||
KV_SERIALIZE_OPT(carrot, true);
|
||||
KV_SERIALIZE_OPT(cryptonote, true);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
@@ -149,12 +153,16 @@ namespace wallet_rpc
|
||||
struct address_info
|
||||
{
|
||||
std::string address;
|
||||
std::string address_cn;
|
||||
std::string address_carrot;
|
||||
std::string label;
|
||||
uint32_t address_index;
|
||||
bool used;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(address)
|
||||
KV_SERIALIZE(address_cn)
|
||||
KV_SERIALIZE(address_carrot)
|
||||
KV_SERIALIZE(label)
|
||||
KV_SERIALIZE(address_index)
|
||||
KV_SERIALIZE(used)
|
||||
|
||||
@@ -29,11 +29,13 @@
|
||||
set(functional_tests_sources
|
||||
main.cpp
|
||||
transactions_flow_test.cpp
|
||||
stake_tx.cpp
|
||||
transactions_generation_from_blockchain.cpp)
|
||||
|
||||
set(functional_tests_headers
|
||||
transactions_flow_test.h
|
||||
transactions_generation_from_blockchain.h)
|
||||
transactions_generation_from_blockchain.h
|
||||
stake_tx.h)
|
||||
|
||||
monero_add_minimal_executable(functional_tests
|
||||
${functional_tests_sources}
|
||||
|
||||
@@ -37,20 +37,24 @@ using namespace epee;
|
||||
#include "common/command_line.h"
|
||||
#include "common/util.h"
|
||||
#include "transactions_flow_test.h"
|
||||
#include "stake_tx.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
namespace
|
||||
{
|
||||
const command_line::arg_descriptor<bool> arg_test_transactions_flow = {"test_transactions_flow", ""};
|
||||
const command_line::arg_descriptor<bool> arg_test_stake_tx = {"test_stake_tx", "Test stake transactions"};
|
||||
|
||||
const command_line::arg_descriptor<std::string> arg_working_folder = {"working-folder", "", "."};
|
||||
const command_line::arg_descriptor<std::string> arg_source_wallet = {"source-wallet", "", "", true};
|
||||
const command_line::arg_descriptor<std::string> arg_dest_wallet = {"dest-wallet", "", "", true};
|
||||
const command_line::arg_descriptor<std::string> arg_daemon_addr_a = {"daemon-addr-a", "", "127.0.0.1:8080"};
|
||||
const command_line::arg_descriptor<std::string> arg_daemon_addr_b = {"daemon-addr-b", "", "127.0.0.1:8082"};
|
||||
const command_line::arg_descriptor<std::string> arg_daemon_addr_stake = {"daemon-addr-stake", "", "127.0.0.1:29081"};
|
||||
|
||||
const command_line::arg_descriptor<uint64_t> arg_transfer_amount = {"transfer_amount", "", 60000000000000};
|
||||
const command_line::arg_descriptor<uint64_t> arg_stake_amount = {"stake_amount", "Amount to stake", 100000000};
|
||||
const command_line::arg_descriptor<size_t> arg_mix_in_factor = {"mix-in-factor", "", 15};
|
||||
const command_line::arg_descriptor<size_t> arg_tx_count = {"tx-count", "", 100};
|
||||
const command_line::arg_descriptor<size_t> arg_tx_per_second = {"tx-per-second", "", 20};
|
||||
@@ -71,14 +75,17 @@ int main(int argc, char* argv[])
|
||||
command_line::add_arg(desc_options, command_line::arg_help);
|
||||
|
||||
command_line::add_arg(desc_options, arg_test_transactions_flow);
|
||||
command_line::add_arg(desc_options, arg_test_stake_tx);
|
||||
|
||||
command_line::add_arg(desc_options, arg_working_folder);
|
||||
command_line::add_arg(desc_options, arg_source_wallet);
|
||||
command_line::add_arg(desc_options, arg_dest_wallet);
|
||||
command_line::add_arg(desc_options, arg_daemon_addr_a);
|
||||
command_line::add_arg(desc_options, arg_daemon_addr_b);
|
||||
command_line::add_arg(desc_options, arg_daemon_addr_stake);
|
||||
|
||||
command_line::add_arg(desc_options, arg_transfer_amount);
|
||||
command_line::add_arg(desc_options, arg_stake_amount);
|
||||
command_line::add_arg(desc_options, arg_mix_in_factor);
|
||||
command_line::add_arg(desc_options, arg_tx_count);
|
||||
command_line::add_arg(desc_options, arg_tx_per_second);
|
||||
@@ -126,6 +133,23 @@ int main(int argc, char* argv[])
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (command_line::get_arg(vm, arg_test_stake_tx))
|
||||
{
|
||||
std::string working_folder = command_line::get_arg(vm, arg_working_folder);
|
||||
std::string wallet_name;
|
||||
if(command_line::has_arg(vm, arg_source_wallet))
|
||||
wallet_name = command_line::get_arg(vm, arg_source_wallet);
|
||||
std::string daemon_addr_stake = command_line::get_arg(vm, arg_daemon_addr_stake);
|
||||
uint64_t amount_to_stake = command_line::get_arg(vm, arg_stake_amount);
|
||||
|
||||
if(!stake_transaction_test(working_folder, wallet_name, daemon_addr_stake, amount_to_stake))
|
||||
return 1;
|
||||
|
||||
std::string s;
|
||||
std::cin >> s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << desc_options << std::endl;
|
||||
|
||||
@@ -0,0 +1,438 @@
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <boost/uuid/random_generator.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
using namespace epee;
|
||||
#include "wallet/wallet2.h"
|
||||
|
||||
#include "transactions_flow_test.h"
|
||||
#include "stake_tx.h"
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
// fixed-difficulty should be at least 7.
|
||||
|
||||
|
||||
bool stop_mining(epee::net_utils::http::http_simple_client& http_client){
|
||||
COMMAND_RPC_STOP_MINING::request stop_mine_req = AUTO_VAL_INIT(stop_mine_req);
|
||||
COMMAND_RPC_STOP_MINING::response stop_mine_rsp = AUTO_VAL_INIT(stop_mine_rsp);
|
||||
bool r = net_utils::invoke_http_json("/stop_mining", stop_mine_req, stop_mine_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to stop mining");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stake_transaction_test(std::string& working_folder,
|
||||
std::string wallet_name,
|
||||
std::string& daemon_addr_a,
|
||||
uint64_t amount_to_stake) {
|
||||
LOG_PRINT_L0("-----------------------STARTING STAKE TRANSACTION TEST-----------------------");
|
||||
tools::wallet2 w1(network_type::TESTNET);
|
||||
w1.inactivity_lock_timeout(0);
|
||||
w1.ask_password(tools::wallet2::AskPasswordType::AskPasswordNever);
|
||||
w1.setup_background_mining(tools::wallet2::BackgroundMiningSetupType::BackgroundMiningNo); // disable background mining
|
||||
if(wallet_name.empty())
|
||||
wallet_name = generate_random_wallet_name();
|
||||
|
||||
try
|
||||
{
|
||||
w1.generate(working_folder + "/" + wallet_name, "");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("failed to generate wallet: " << e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
w1.init(daemon_addr_a);
|
||||
|
||||
epee::net_utils::http::http_simple_client http_client;
|
||||
bool r = http_client.set_server(daemon_addr_a, boost::none);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to connect to daemon");
|
||||
|
||||
// stop mining just in case
|
||||
stop_mining(http_client);
|
||||
|
||||
// get current height
|
||||
COMMAND_RPC_GET_HEIGHT::request height_req = AUTO_VAL_INIT(height_req);
|
||||
COMMAND_RPC_GET_HEIGHT::response height_rsp = AUTO_VAL_INIT(height_rsp);
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
|
||||
|
||||
// pop blocks to make sure of the start height
|
||||
COMMAND_RPC_POP_BLOCKS::request pop_blocks_req = AUTO_VAL_INIT(pop_blocks_req);
|
||||
COMMAND_RPC_POP_BLOCKS::response pop_blocks_rsp = AUTO_VAL_INIT(pop_blocks_rsp);
|
||||
pop_blocks_req.nblocks = height_rsp.height - 1; //keep at least one block
|
||||
r = net_utils::invoke_http_json("/pop_blocks", pop_blocks_req, pop_blocks_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to pop blocks");
|
||||
|
||||
// flush tx pool to make sure of the start state
|
||||
COMMAND_RPC_FLUSH_TRANSACTION_POOL::request flush_req = AUTO_VAL_INIT(flush_req);
|
||||
COMMAND_RPC_FLUSH_TRANSACTION_POOL::response flush_rsp = AUTO_VAL_INIT(flush_rsp);
|
||||
r = net_utils::invoke_http_json_rpc("/json_rpc", "flush_txpool", flush_req, flush_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to flush tx pool");
|
||||
|
||||
w1.rescan_blockchain(true, true, false);
|
||||
MGINFO_GREEN("Using wallets: " << ENDL
|
||||
<< "Source: " << w1.get_account().get_public_address_str(TESTNET) << ENDL << "Path: " << working_folder + "/" + wallet_name << ENDL);
|
||||
|
||||
//lets make some money
|
||||
COMMAND_RPC_START_MINING::request start_mining_req = AUTO_VAL_INIT(start_mining_req);
|
||||
COMMAND_RPC_START_MINING::response start_mining_rsp = AUTO_VAL_INIT(start_mining_rsp);
|
||||
start_mining_req.miner_address = w1.get_account().get_public_address_str(TESTNET);
|
||||
start_mining_req.threads_count = 1;
|
||||
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
|
||||
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
|
||||
|
||||
// mine until we reach to block 710
|
||||
height_rsp.height = 1;
|
||||
while (height_rsp.height < 710)
|
||||
{
|
||||
misc_utils::sleep_no_w(1000); // The difficulty should be high enough to stop mining before reaching block 800.”
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon 1");
|
||||
}
|
||||
|
||||
// stop mining
|
||||
stop_mining(http_client);
|
||||
|
||||
// refresh wallet
|
||||
uint64_t blocks_fetched = 0;
|
||||
bool received_money;
|
||||
w1.refresh(true, 0, blocks_fetched, received_money);
|
||||
|
||||
MGINFO_GREEN("balance: " << w1.balance(0, "SAL", false));
|
||||
|
||||
// create and submit CN SAL stake transaction ~ block 710
|
||||
std::vector<cryptonote::tx_destination_entry> dsts;
|
||||
cryptonote::tx_destination_entry de;
|
||||
de.addr = w1.get_account().get_keys().m_account_address;
|
||||
de.amount = amount_to_stake;
|
||||
de.is_subaddress = false;
|
||||
dsts.push_back(de);
|
||||
try
|
||||
{
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
ptx = w1.create_transactions_2(dsts, "SAL", "SAL", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
|
||||
for (auto &p: ptx)
|
||||
w1.commit_tx(p);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG_ERROR("failed to create/commit CN stake transaction at height: " << height_rsp.height << "with SAL");
|
||||
return false;
|
||||
}
|
||||
|
||||
// lets make some money
|
||||
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
|
||||
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
|
||||
|
||||
// mine until we reach to block 1010
|
||||
while (height_rsp.height < 1010) // difficulty should be high enough to stop mining before reaching block 1080
|
||||
{
|
||||
misc_utils::sleep_no_w(1000);
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
|
||||
}
|
||||
|
||||
// stop mining if not stopped already
|
||||
stop_mining(http_client);
|
||||
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
|
||||
|
||||
// Check the height to ensure we are receiving yield payments before the Carrot hard fork at block 1100
|
||||
if (!r)
|
||||
{
|
||||
LOG_ERROR("failed to get height from daemon");
|
||||
return false;
|
||||
}
|
||||
if (height_rsp.height > 1080)
|
||||
{
|
||||
LOG_PRINT_L0("----!! The current height (" << height_rsp.height << ") exceeds the acceptable range for this test. Please restart the node with an increased fixed difficulty. !!----");
|
||||
}
|
||||
|
||||
|
||||
// refresh wallet
|
||||
blocks_fetched = 0;
|
||||
received_money = false;
|
||||
bool ok = false;
|
||||
if(!w1.refresh(true, blocks_fetched, received_money, ok))
|
||||
{
|
||||
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we have received yield from CN SAL stake transaction ~1010
|
||||
tools::wallet2::transfer_container incoming_transfers;
|
||||
// scan payments
|
||||
w1.get_transfers(incoming_transfers);
|
||||
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
|
||||
bool found = false;
|
||||
for (const auto& p: incoming_transfers)
|
||||
{
|
||||
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
|
||||
{
|
||||
if (p.m_amount > amount_to_stake)
|
||||
{
|
||||
LOG_PRINT_L0("found yield from CN SAL stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("invalid yield amount from CN SAL stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
return false;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from CN SAL stake transaction");
|
||||
|
||||
//create and submit CN SAL1 stake transaction
|
||||
dsts.clear();
|
||||
de.addr = w1.get_account().get_keys().m_account_address;
|
||||
de.amount = amount_to_stake;
|
||||
de.is_subaddress = false;
|
||||
dsts.push_back(de);
|
||||
try
|
||||
{
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
ptx = w1.create_transactions_2(dsts, "SAL1", "SAL1", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
|
||||
for (auto &p: ptx)
|
||||
w1.commit_tx(p);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG_ERROR("failed to create/commit CN stake transaction at height: " << height_rsp.height << "with SAL1");
|
||||
return false;
|
||||
}
|
||||
|
||||
// lets make some money
|
||||
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
|
||||
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
|
||||
|
||||
// mine until we reach to block 1091
|
||||
while (height_rsp.height < 1091)
|
||||
{
|
||||
misc_utils::sleep_no_w(100);
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(1));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
|
||||
}
|
||||
|
||||
// stop mining if not stopped already
|
||||
stop_mining(http_client);
|
||||
|
||||
// refresh wallet
|
||||
blocks_fetched = 0;
|
||||
received_money = false;
|
||||
ok = false;
|
||||
if(!w1.refresh(true, blocks_fetched, received_money, ok))
|
||||
{
|
||||
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we have received yield from CN SAL stake transaction
|
||||
incoming_transfers.clear();
|
||||
// scan payments
|
||||
w1.get_transfers(incoming_transfers);
|
||||
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
|
||||
found = false;
|
||||
for (const auto& p: incoming_transfers)
|
||||
{
|
||||
if (p.m_block_height < 1010) continue; // skip payments from the first stake transaction
|
||||
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
|
||||
{
|
||||
if (p.m_amount > amount_to_stake)
|
||||
{
|
||||
LOG_PRINT_L0("found yield from CN SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("invalid yield amount from CN SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
return false;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from CN SAL1 stake transaction");
|
||||
|
||||
// pop blocks to make sure of the start height
|
||||
pop_blocks_req = AUTO_VAL_INIT(pop_blocks_req);
|
||||
pop_blocks_req.nblocks = height_rsp.height - 1090; //keep at least 1090 blocks
|
||||
r = net_utils::invoke_http_json("/pop_blocks", pop_blocks_req, pop_blocks_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to pop blocks");
|
||||
|
||||
// flush tx pool to make sure of the start state
|
||||
r = net_utils::invoke_http_json_rpc("/json_rpc", "flush_txpool", flush_req, flush_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to flush tx pool");
|
||||
|
||||
// rescan blockchain to ensure wallet is in sync after popping blocks
|
||||
w1.rescan_blockchain(true, true, false);
|
||||
MGINFO_GREEN("Using wallets: " << ENDL
|
||||
<< "Source: " << w1.get_account().get_public_address_str(TESTNET) << ENDL << "Path: " << working_folder + "/" + wallet_name << ENDL);
|
||||
|
||||
// Create and stake CN SAL1 stake transaction and get yield after the Carrot hard fork at block 1100 ~ block 1090
|
||||
dsts.clear();
|
||||
de.addr = w1.get_account().get_keys().m_account_address;
|
||||
de.amount = amount_to_stake;
|
||||
de.is_subaddress = false;
|
||||
dsts.push_back(de);
|
||||
try
|
||||
{
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
ptx = w1.create_transactions_2(dsts, "SAL1", "SAL1", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
|
||||
for (auto &p: ptx)
|
||||
w1.commit_tx(p);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG_ERROR("failed to create/commit CN stake transaction at height: " << height_rsp.height << "with SAL1");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// lets make some money
|
||||
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
|
||||
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
|
||||
|
||||
// mine until we reach to block 1100
|
||||
while (height_rsp.height < 1100)
|
||||
{
|
||||
misc_utils::sleep_no_w(1000);
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
|
||||
}
|
||||
|
||||
// stop mining if not stopped already
|
||||
stop_mining(http_client);
|
||||
|
||||
// Carrot will not be able to connect to the network until the node is restarted
|
||||
LOG_PRINT_L0("---------------------- PLEASE RESTART THE NODE NOW ----------------------");
|
||||
misc_utils::sleep_no_w(1000); // wait for the log to be printed
|
||||
LOG_PRINT_L2("Press any key to continue once the node is back online");
|
||||
std::string input;
|
||||
std::cin >> input;
|
||||
|
||||
// restart the http client connection
|
||||
r = http_client.set_server(daemon_addr_a, boost::none);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to connect to daemon");
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "Daemon is not yet back online");
|
||||
|
||||
LOG_PRINT_L0("Daemon is back online, continuing tests");
|
||||
LOG_PRINT_L2("Carrot has been enabled.");
|
||||
|
||||
// restart the wallet connection
|
||||
w1.init(daemon_addr_a);
|
||||
|
||||
// Create and stake Carrot SAL1 stake transaction ~ block 1100
|
||||
dsts.clear();
|
||||
de.addr = w1.get_account().get_keys().m_carrot_main_address;
|
||||
de.amount = amount_to_stake;
|
||||
de.is_subaddress = false;
|
||||
dsts.push_back(de);
|
||||
try
|
||||
{
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
ptx = w1.create_transactions_2(dsts, "SAL1", "SAL1", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
|
||||
for (auto &p: ptx)
|
||||
w1.commit_tx(p);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG_ERROR("failed to create/commit Carrot stake transaction at height: " << height_rsp.height << " with SAL1");
|
||||
return false;
|
||||
}
|
||||
|
||||
// lets make some money
|
||||
start_mining_req.miner_address = w1.get_account().get_carrot_public_address_str(TESTNET);
|
||||
start_mining_req.threads_count = 1;
|
||||
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
|
||||
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
|
||||
|
||||
// mine until we reach to block 1300
|
||||
while (height_rsp.height < 1300)
|
||||
{
|
||||
misc_utils::sleep_no_w(1000);
|
||||
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
|
||||
}
|
||||
|
||||
// stop mining
|
||||
stop_mining(http_client);
|
||||
|
||||
// refresh wallet
|
||||
blocks_fetched = 0;
|
||||
received_money = false;
|
||||
ok = false;
|
||||
if(!w1.refresh(true, blocks_fetched, received_money, ok))
|
||||
{
|
||||
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we have received yield from Carrot SAL1 stake transaction
|
||||
incoming_transfers.clear();
|
||||
// scan payments
|
||||
w1.get_transfers(incoming_transfers);
|
||||
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
|
||||
found = false;
|
||||
for (const auto& p: incoming_transfers)
|
||||
{
|
||||
if (p.m_block_height > 1112 || p.m_block_height < 1090) continue; // Skip payments originating from CryptoNote stake transactions
|
||||
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
|
||||
{
|
||||
if (p.m_amount > amount_to_stake)
|
||||
{
|
||||
LOG_PRINT_L0("found yield from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
LOG_PRINT_L2("Stake was made before the Carrot hard fork and yield received after Carrot hard fork");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("invalid yield amount from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
return false;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from Carrot SAL1 stake transaction");
|
||||
|
||||
|
||||
// check if we have received yield from Carrot SAL1 stake transaction
|
||||
incoming_transfers.clear();
|
||||
// scan payments
|
||||
w1.get_transfers(incoming_transfers);
|
||||
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
|
||||
found = false;
|
||||
for (const auto& p: incoming_transfers)
|
||||
{
|
||||
if (p.m_block_height <= 1112) continue; // // Skip payments originating from CryptoNote stake transactions
|
||||
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
|
||||
{
|
||||
if (p.m_amount > amount_to_stake)
|
||||
{
|
||||
LOG_PRINT_L0("found yield from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
LOG_PRINT_L0("All yields from stake transactions have been successfully found!");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("invalid yield amount from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
|
||||
return false;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from Carrot SAL1 stake transaction");
|
||||
LOG_PRINT_L2("All yields from stake transactions have been successfully found!");
|
||||
LOG_PRINT_L0("-----------------------STAKE TRANSACTION TEST PASSED-----------------------");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2014-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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
bool stake_transaction_test(std::string& working_folder,
|
||||
std::string wallet_name,
|
||||
std::string& daemon_addr_a,
|
||||
uint64_t amount_to_stake);
|
||||
@@ -28,6 +28,8 @@
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
std::string generate_random_wallet_name();
|
||||
|
||||
bool transactions_flow_test(std::string& working_folder,
|
||||
std::string path_source_wallet,
|
||||
std::string path_target_wallet,
|
||||
|
||||
@@ -323,13 +323,10 @@ TEST(carrot_core, main_address_special_scan_completeness)
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
RCTOutputEnoteProposal return_enote;
|
||||
get_output_proposal_special_v1(proposal,
|
||||
keys.k_view_incoming_dev,
|
||||
tx_first_key_image,
|
||||
cryptonote::transaction_type::TRANSFER, // tx_type
|
||||
std::nullopt,
|
||||
return_enote,
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
@@ -403,13 +400,10 @@ TEST(carrot_core, subaddress_special_scan_completeness)
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
RCTOutputEnoteProposal return_enote;
|
||||
get_output_proposal_special_v1(proposal,
|
||||
keys.k_view_incoming_dev,
|
||||
tx_first_key_image,
|
||||
cryptonote::transaction_type::TRANSFER, // tx_type
|
||||
std::nullopt,
|
||||
return_enote,
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
@@ -460,7 +454,7 @@ TEST(carrot_core, subaddress_special_scan_completeness)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
{
|
||||
mock::mock_carrot_and_legacy_keys keys;
|
||||
carrot::carrot_and_legacy_account keys;
|
||||
keys.generate();
|
||||
|
||||
const CarrotDestinationV1 main_address = keys.cryptonote_address();
|
||||
@@ -481,10 +475,13 @@ TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
RCTOutputEnoteProposal return_proposal;
|
||||
get_output_proposal_internal_v1(proposal,
|
||||
keys.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
std::nullopt,
|
||||
cryptonote::transaction_type::TRANSFER, // tx_type
|
||||
return_proposal,
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
@@ -498,15 +495,19 @@ TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
janus_anchor_t recovered_internal_message;
|
||||
crypto::public_key return_address_out;
|
||||
bool is_return_out;
|
||||
const bool scan_success = try_scan_carrot_enote_internal_receiver(enote_proposal.enote,
|
||||
keys.s_view_balance_dev,
|
||||
keys,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
recovered_address_spend_pubkey,
|
||||
recovered_amount,
|
||||
recovered_amount_blinding_factor,
|
||||
recovered_enote_type,
|
||||
recovered_internal_message);
|
||||
recovered_internal_message,
|
||||
return_address_out,
|
||||
is_return_out);
|
||||
|
||||
ASSERT_TRUE(scan_success);
|
||||
|
||||
@@ -527,7 +528,7 @@ TEST(carrot_core, main_address_internal_scan_completeness)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||
{
|
||||
mock::mock_carrot_and_legacy_keys keys;
|
||||
carrot::carrot_and_legacy_account keys;
|
||||
keys.generate();
|
||||
|
||||
const uint32_t j_major = crypto::rand_idx(mock::MAX_SUBADDRESS_MAJOR_INDEX);
|
||||
@@ -551,10 +552,13 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||
|
||||
RCTOutputEnoteProposal enote_proposal;
|
||||
RCTOutputEnoteProposal return_proposal;
|
||||
get_output_proposal_internal_v1(proposal,
|
||||
keys.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
std::nullopt,
|
||||
cryptonote::transaction_type::TRANSFER, // tx_type
|
||||
return_proposal,
|
||||
enote_proposal);
|
||||
|
||||
ASSERT_EQ(proposal.amount, enote_proposal.amount);
|
||||
@@ -568,15 +572,19 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor;
|
||||
CarrotEnoteType recovered_enote_type;
|
||||
janus_anchor_t recovered_internal_message;
|
||||
crypto::public_key return_address_out;
|
||||
bool is_return_out;
|
||||
const bool scan_success = try_scan_carrot_enote_internal_receiver(enote_proposal.enote,
|
||||
keys.s_view_balance_dev,
|
||||
keys,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
recovered_address_spend_pubkey,
|
||||
recovered_amount,
|
||||
recovered_amount_blinding_factor,
|
||||
recovered_enote_type,
|
||||
recovered_internal_message);
|
||||
recovered_internal_message,
|
||||
return_address_out,
|
||||
is_return_out);
|
||||
|
||||
ASSERT_TRUE(scan_success);
|
||||
|
||||
|
||||
@@ -808,11 +808,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_10)
|
||||
tx_proposal.self_sender_index = 2;
|
||||
|
||||
// 1 subaddress payment (subtractable)
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
|
||||
.destination = acc0.first.subaddress({{2, 3}}),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
|
||||
// 1 main address payment
|
||||
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
|
||||
@@ -826,7 +826,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_10)
|
||||
.destination = acc3.first.cryptonote_address(gen_payment_id()),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
|
||||
// specify fee per weight
|
||||
tx_proposal.fee_per_weight = 314159;
|
||||
@@ -914,11 +914,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
|
||||
tx_proposal.self_sender_index = 2;
|
||||
|
||||
// 2 subaddress payment (1 subtractable)
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
|
||||
acc0.second.emplace_back().first =CarrotPaymentProposalV1{
|
||||
.destination = acc0.first.subaddress({{2, 3}}),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
acc0.second.push_back(acc0.second.front());
|
||||
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
|
||||
acc0.second.back().second = false; //set not subtractable, first already is
|
||||
@@ -937,7 +937,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
|
||||
.destination = acc3.first.cryptonote_address(gen_payment_id()),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
|
||||
// 1 main address selfsend
|
||||
tx_proposal.explicit_selfsend_proposals.emplace_back().first.proposal = CarrotPaymentProposalSelfSendV1{
|
||||
@@ -955,7 +955,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
|
||||
.enote_type = CarrotEnoteType::CHANGE
|
||||
},
|
||||
.subaddr_index = {{4, 19}}
|
||||
}, true};
|
||||
};
|
||||
|
||||
// specify fee per weight
|
||||
tx_proposal.fee_per_weight = 314159;
|
||||
@@ -1021,11 +1021,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_14)
|
||||
tx_proposal.self_sender_index = 2;
|
||||
|
||||
// 1 subaddress payment (subtractable)
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
|
||||
.destination = acc0.first.subaddress({{2, 3}}),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
|
||||
// 1 main address payment
|
||||
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
|
||||
@@ -1039,7 +1039,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_14)
|
||||
.destination = acc3.first.cryptonote_address(gen_payment_id()),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
|
||||
// specify fee per weight
|
||||
tx_proposal.fee_per_weight = 314159;
|
||||
@@ -1071,11 +1071,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_15)
|
||||
tx_proposal.self_sender_index = 2;
|
||||
|
||||
// 2 subaddress payment (subtractable)
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
|
||||
.destination = acc0.first.subaddress({{2, 3}}),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
acc0.second.push_back(acc0.second.front());
|
||||
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
|
||||
|
||||
@@ -1084,7 +1084,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_15)
|
||||
.destination = acc1.first.cryptonote_address(),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
acc1.second.push_back(acc1.second.front());
|
||||
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
|
||||
|
||||
@@ -1093,7 +1093,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_15)
|
||||
.destination = acc3.first.cryptonote_address(gen_payment_id()),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
|
||||
// specify fee per weight
|
||||
tx_proposal.fee_per_weight = 314159;
|
||||
@@ -1125,11 +1125,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
|
||||
tx_proposal.self_sender_index = 2;
|
||||
|
||||
// 2 subaddress payment (subtractable)
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
|
||||
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
|
||||
.destination = acc0.first.subaddress({{2, 3}}),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
acc0.second.push_back(acc0.second.front());
|
||||
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
|
||||
|
||||
@@ -1138,7 +1138,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
|
||||
.destination = acc1.first.cryptonote_address(),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
acc1.second.push_back(acc1.second.front());
|
||||
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
|
||||
|
||||
@@ -1147,14 +1147,16 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
|
||||
.destination = acc3.first.cryptonote_address(gen_payment_id()),
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.randomness = gen_janus_anchor()
|
||||
}, true};
|
||||
};
|
||||
|
||||
// 1 main address selfsend (subtractable)
|
||||
tx_proposal.explicit_selfsend_proposals.emplace_back() = {CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = acc2.first.carrot_account_spend_pubkey,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.enote_type = CarrotEnoteType::PAYMENT,
|
||||
// no internal messages for legacy self-sends
|
||||
tx_proposal.explicit_selfsend_proposals.emplace_back() = {CarrotPaymentProposalVerifiableSelfSendV1{
|
||||
.proposal = CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = acc2.first.carrot_account_spend_pubkey,
|
||||
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
|
||||
.enote_type = CarrotEnoteType::PAYMENT,
|
||||
// no internal messages for legacy self-sends
|
||||
}
|
||||
}, true};
|
||||
|
||||
// 1 subaddress selfsend (subtractable)
|
||||
|
||||
@@ -359,15 +359,20 @@ void mock_scan_enote_set(const std::vector<CarrotEnoteV1> &enotes,
|
||||
const CarrotEnoteV1 &enote = enotes.at(output_index);
|
||||
|
||||
mock_scan_result_t scan_result{};
|
||||
carrot::carrot_and_legacy_account account;
|
||||
crypto::public_key return_address_out;
|
||||
bool is_return_out;
|
||||
const bool r = try_scan_carrot_enote_internal_receiver(enote,
|
||||
keys.s_view_balance_dev,
|
||||
account,
|
||||
scan_result.sender_extension_g,
|
||||
scan_result.sender_extension_t,
|
||||
scan_result.address_spend_pubkey,
|
||||
scan_result.amount,
|
||||
scan_result.amount_blinding_factor,
|
||||
scan_result.enote_type,
|
||||
scan_result.internal_message);
|
||||
scan_result.internal_message,
|
||||
return_address_out,
|
||||
is_return_out);
|
||||
|
||||
scan_result.output_index = output_index;
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ static auto auto_wiper(T &obj)
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_tx(
|
||||
mock::mock_carrot_and_legacy_keys &alice,
|
||||
carrot::carrot_and_legacy_account &alice,
|
||||
CarrotDestinationV1 &bob_address
|
||||
) {
|
||||
// spend input
|
||||
@@ -62,9 +62,10 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_
|
||||
|
||||
// make change output
|
||||
RCTOutputEnoteProposal enote_proposal_change;
|
||||
RCTOutputEnoteProposal return_proposal;
|
||||
get_output_proposal_internal_v1(
|
||||
CarrotPaymentProposalSelfSendV1{
|
||||
.destination_address_spend_pubkey = alice.carrot_account_spend_pubkey,
|
||||
.destination_address_spend_pubkey = alice.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
.amount = crypto::rand<rct::xmr_amount>(),
|
||||
.enote_type = CarrotEnoteType::CHANGE,
|
||||
.enote_ephemeral_pubkey = gen_x25519_pubkey(),
|
||||
@@ -72,6 +73,8 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_
|
||||
alice.s_view_balance_dev,
|
||||
tx_first_key_image,
|
||||
std::nullopt,
|
||||
cryptonote::transaction_type::TRANSFER, // tx_type
|
||||
return_proposal,
|
||||
enote_proposal_change
|
||||
);
|
||||
|
||||
@@ -138,7 +141,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_tx(
|
||||
mock::mock_carrot_and_legacy_keys &bob,
|
||||
carrot::carrot_and_legacy_account &bob,
|
||||
std::vector<RCTOutputEnoteProposal> &origin_tx_outputs
|
||||
) {
|
||||
// [0] enote is change, [1] enote bob received
|
||||
@@ -169,7 +172,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
|
||||
received_output,
|
||||
std::nullopt,
|
||||
origin_tx_shared_secret_unctx,
|
||||
{&bob.carrot_account_spend_pubkey, 1},
|
||||
{&bob.get_keys().m_carrot_account_address.m_spend_public_key, 1},
|
||||
bob.k_view_incoming_dev,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
@@ -182,7 +185,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
|
||||
EXPECT_TRUE(scan_success);
|
||||
|
||||
// check we can spend it
|
||||
EXPECT_TRUE(bob.can_open_fcmp_onetime_address(bob.carrot_account_spend_pubkey,
|
||||
EXPECT_TRUE(bob.can_open_fcmp_onetime_address(bob.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
recovered_sender_extension_g,
|
||||
recovered_sender_extension_t,
|
||||
received_output.onetime_address));
|
||||
@@ -237,7 +240,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
|
||||
TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness)
|
||||
{
|
||||
// these will generate a new format carrot address.
|
||||
mock::mock_carrot_and_legacy_keys alice, bob;
|
||||
carrot::carrot_and_legacy_account alice, bob;
|
||||
alice.generate();
|
||||
bob.generate();
|
||||
|
||||
@@ -332,20 +335,23 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness)
|
||||
crypto::secret_key recovered_amount_blinding_factor_change;
|
||||
CarrotEnoteType recovered_enote_type_change;
|
||||
janus_anchor_t recovered_internal_message_out_change;
|
||||
crypto::public_key return_address_out;
|
||||
bool is_return_out;
|
||||
const bool scan_success_change = try_scan_carrot_enote_internal_receiver(change_output,
|
||||
alice.s_view_balance_dev,
|
||||
alice,
|
||||
recovered_sender_extension_g_change,
|
||||
recovered_sender_extension_t_change,
|
||||
recovered_address_spend_pubkey_change,
|
||||
recovered_amount_change,
|
||||
recovered_amount_blinding_factor_change,
|
||||
recovered_enote_type_change,
|
||||
recovered_internal_message_out_change);
|
||||
|
||||
recovered_internal_message_out_change,
|
||||
return_address_out,
|
||||
is_return_out);
|
||||
ASSERT_TRUE(scan_success_change);
|
||||
|
||||
// check spendability of the change output
|
||||
EXPECT_TRUE(alice.can_open_fcmp_onetime_address(alice.carrot_account_spend_pubkey,
|
||||
EXPECT_TRUE(alice.can_open_fcmp_onetime_address(alice.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
recovered_sender_extension_g_change,
|
||||
recovered_sender_extension_t_change,
|
||||
change_output.onetime_address));
|
||||
@@ -353,7 +359,7 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness)
|
||||
// check spendability of the return_payment
|
||||
crypto::secret_key sum_g;
|
||||
sc_add(to_bytes(sum_g), to_bytes(recovered_sender_extension_g_change), to_bytes(k_return));
|
||||
ASSERT_TRUE(alice.can_open_fcmp_onetime_address(alice.carrot_account_spend_pubkey,
|
||||
ASSERT_TRUE(alice.can_open_fcmp_onetime_address(alice.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
sum_g,
|
||||
recovered_sender_extension_t_change,
|
||||
return_output.onetime_address));
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
const uint64_t AMOUNT_BURNT = 1000000000000; // 1 SAL
|
||||
const uint64_t STAKE_REWARD = 10000000000000; // 10 SAL
|
||||
const uint64_t STAKE_PAYOUT = 216001000000000000; // 216k SAL
|
||||
const uint64_t AMOUNT_BURNT = 1000000000000; // 10000 SAL
|
||||
const uint64_t STAKE_REWARD = 10000000000000; // 100000 SAL
|
||||
const uint64_t STAKE_PAYOUT = 216001000000000000; // 2160000k SAL
|
||||
const uint64_t STAKE_LOCK_PERIOD = get_config(network_type::FAKECHAIN).STAKE_LOCK_PERIOD;
|
||||
const auto AUDIT_HARD_FORKS = get_config(network_type::FAKECHAIN).AUDIT_HARD_FORKS;
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ TEST(wallet_tx_builder, input_selection_basic)
|
||||
tools::wallet2::transfer_container transfers;
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
tools::wallet2::transfer_details &td = tools::add_element(transfers);
|
||||
tools::wallet2::transfer_details &td = transfers.emplace_back();
|
||||
td = gen_transfer_details();
|
||||
td.m_block_height = transfers.size(); // small ascending block heights
|
||||
}
|
||||
@@ -206,7 +206,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_transfer_2)
|
||||
std::unordered_map<crypto::key_image, std::size_t> allowed_transfers;
|
||||
for (size_t i = 0; i < FCMP_PLUS_PLUS_MAX_INPUTS + 2; ++i)
|
||||
{
|
||||
tools::wallet2::transfer_details &td = tools::add_element(transfers);
|
||||
tools::wallet2::transfer_details &td = transfers.emplace_back();
|
||||
td = gen_transfer_details();
|
||||
td.m_subaddr_index.major = (i % 2 == 0) ? spending_subaddr_account : (spending_subaddr_account - 1);
|
||||
td.m_subaddr_index.minor = crypto::rand_range<std::uint32_t>(0, carrot::mock::MAX_SUBADDRESS_MINOR_INDEX);
|
||||
|
||||
Reference in New Issue
Block a user