Compare commits
26 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 |
@@ -22,7 +22,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build-cross:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
strategy:
|
||||
|
||||
+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)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Salvium One v1.0.6
|
||||
# Salvium One v1.0.0
|
||||
|
||||
Copyright (c) 2023-2025, Salvium
|
||||
Portions Copyright (c) 2014-2023, The Monero Project
|
||||
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
|
||||
|
||||
```bash
|
||||
cd salvium
|
||||
git checkout v1.0.6
|
||||
git checkout v1.0.0
|
||||
make
|
||||
```
|
||||
|
||||
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
|
||||
```bash
|
||||
git clone https://github.com/salvium/salvium
|
||||
cd salvium
|
||||
git checkout v1.0.6
|
||||
git checkout v1.0.0
|
||||
```
|
||||
|
||||
* Build:
|
||||
@@ -370,10 +370,10 @@ application.
|
||||
cd salvium
|
||||
```
|
||||
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.6'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.0'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v1.0.6
|
||||
git checkout v1.0.0
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE)
|
||||
message(STATUS "Looking for ${flag} linker flag")
|
||||
endif()
|
||||
|
||||
set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
set(_cle_source ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||
|
||||
set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
|
||||
set(CMAKE_C_FLAGS "${flag}")
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "memwipe.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -162,7 +162,6 @@ namespace epee
|
||||
{
|
||||
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
|
||||
static_assert(std::is_standard_layout<T>(), "type must have standard layout");
|
||||
static_assert(std::is_trivially_copyable<T>(), "type must be trivially copyable");
|
||||
static_assert(alignof(T) == 1, "type may have padding");
|
||||
return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <string>
|
||||
#include "memwipe.h"
|
||||
#include "fnv1.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
@@ -75,6 +76,12 @@ namespace epee
|
||||
bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; }
|
||||
wipeable_string &operator=(wipeable_string &&other);
|
||||
wipeable_string &operator=(const wipeable_string &other);
|
||||
char& operator[](size_t idx);
|
||||
const char& operator[](size_t idx) const;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
private:
|
||||
void grow(size_t sz, size_t reserved = 0);
|
||||
|
||||
@@ -261,4 +261,14 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
char& wipeable_string::operator[](size_t idx) {
|
||||
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
|
||||
return buffer[idx];
|
||||
}
|
||||
|
||||
const char& wipeable_string::operator[](size_t idx) const {
|
||||
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
|
||||
return buffer[idx];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+3
@@ -69,5 +69,8 @@ endif()
|
||||
add_subdirectory(db_drivers)
|
||||
add_subdirectory(easylogging++)
|
||||
add_subdirectory(qrcodegen)
|
||||
add_subdirectory(polyseed EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(utf8proc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(bc-ur)
|
||||
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(mx25519)
|
||||
|
||||
+1
Submodule external/bc-ur added at d82e7c753e
+1
Submodule external/polyseed added at bd79f5014c
Vendored
+1
-1
Submodule external/randomx updated: 102f8acf90...ce72c9bb9c
+1
Submodule external/utf8proc added at 3de4596fbe
@@ -98,6 +98,7 @@ add_subdirectory(net)
|
||||
add_subdirectory(hardforks)
|
||||
add_subdirectory(blockchain_db)
|
||||
add_subdirectory(mnemonics)
|
||||
add_subdirectory(polyseed)
|
||||
add_subdirectory(rpc)
|
||||
add_subdirectory(seraphis_crypto)
|
||||
if(NOT IOS)
|
||||
|
||||
@@ -282,13 +282,13 @@ crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::publ
|
||||
return L;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size)
|
||||
void carrot_and_legacy_account::generate_subaddress_map()
|
||||
{
|
||||
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
|
||||
|
||||
for (uint32_t major_index = 0; major_index <= lookahead_size.first; ++major_index)
|
||||
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
|
||||
{
|
||||
for (uint32_t minor_index = 0; minor_index <= lookahead_size.first; ++minor_index)
|
||||
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
|
||||
{
|
||||
for (const AddressDeriveType derive_type : derive_types)
|
||||
{
|
||||
@@ -361,6 +361,7 @@ void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_pu
|
||||
);
|
||||
|
||||
this->default_derive_type = AddressDeriveType::Carrot;
|
||||
generate_subaddress_map();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
|
||||
@@ -392,19 +393,13 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
|
||||
m_keys.m_carrot_main_address.m_is_carrot = true;
|
||||
|
||||
this->default_derive_type = default_derive_type;
|
||||
generate_subaddress_map();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map_cn)
|
||||
{
|
||||
for (const auto &p : subaddress_map_cn) {
|
||||
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
|
||||
if (p.second.derive_type == AddressDeriveType::PreCarrot) {
|
||||
// Create a matching Carrot address
|
||||
const subaddress_index_extended subaddr_index{{p.second.index.major, p.second.index.minor}, AddressDeriveType::Carrot, p.second.is_return_spend_key};
|
||||
const CarrotDestinationV1 addr = subaddress(subaddr_index);
|
||||
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
|
||||
}
|
||||
}
|
||||
for (const auto &p : subaddress_map_cn)
|
||||
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace carrot
|
||||
const crypto::secret_key &sender_extension_t,
|
||||
const crypto::public_key &onetime_address) const;
|
||||
|
||||
void generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size);
|
||||
void generate_subaddress_map();
|
||||
|
||||
crypto::secret_key generate(
|
||||
const crypto::secret_key& recovery_key = crypto::secret_key(),
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
namespace carrot
|
||||
{
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
|
||||
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
|
||||
|
||||
class carrot_logic_error: public std::logic_error { using std::logic_error::logic_error; };
|
||||
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
|
||||
@@ -55,7 +55,7 @@ CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error)
|
||||
|
||||
class carrot_runtime_error: public std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
|
||||
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
|
||||
|
||||
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
|
||||
}
|
||||
else if (!need_change_output)
|
||||
{
|
||||
return AdditionalOutputType::CHANGE_UNIQUE;
|
||||
return AdditionalOutputType::DUMMY;
|
||||
}
|
||||
else // num_selfsend == 1 && need_change_output
|
||||
{
|
||||
|
||||
@@ -519,7 +519,7 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
crypto::secret_key sum_g;
|
||||
sc_add(to_bytes(sum_g), to_bytes(sender_extension_g_out), to_bytes(k_return));
|
||||
crypto::key_image key_image = account.derive_key_image(
|
||||
address_spend_pubkey_out, // THIS WAS WRONG!!! -> account.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
account.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
sum_g,
|
||||
sender_extension_t_out,
|
||||
K_r
|
||||
@@ -527,7 +527,7 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
|
||||
|
||||
crypto::secret_key x, y;
|
||||
account.try_searching_for_opening_for_onetime_address(
|
||||
address_spend_pubkey_out, // THIS WAS WRONG!!! -> account.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
account.get_keys().m_carrot_account_address.m_spend_public_key,
|
||||
sum_g,
|
||||
sender_extension_t_out,
|
||||
x,
|
||||
|
||||
@@ -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;
|
||||
@@ -283,9 +304,9 @@ DISABLE_VS_WARNINGS(4244 4345)
|
||||
std::string account_base::get_carrot_public_address_str(network_type nettype) const
|
||||
{
|
||||
// Build the cryptonote::account_public_address
|
||||
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key, true};
|
||||
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key};
|
||||
// change this code into base 58
|
||||
return get_account_address_as_str(nettype, false, addr);
|
||||
return get_account_address_as_str(nettype, false, addr, true);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "cryptonote_basic.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
#include "polyseed/polyseed.hpp"
|
||||
|
||||
#include "carrot_core/account_secrets.h"
|
||||
#include "carrot_core/address_utils.h"
|
||||
@@ -52,6 +53,8 @@ namespace cryptonote
|
||||
std::vector<crypto::secret_key> m_multisig_keys;
|
||||
hw::device *m_device = &hw::get_device("default");
|
||||
crypto::chacha_iv m_encryption_iv;
|
||||
crypto::secret_key m_polyseed;
|
||||
epee::wipeable_string m_passphrase; // Only used with polyseed
|
||||
|
||||
// carrot secret keys (minus k_v, which is shared with legacy k_v)
|
||||
crypto::secret_key s_master;
|
||||
@@ -75,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;
|
||||
|
||||
@@ -155,9 +155,10 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, bool subaddress
|
||||
, account_public_address const & adr
|
||||
, bool is_carrot
|
||||
)
|
||||
{
|
||||
uint64_t address_prefix = adr.m_is_carrot
|
||||
uint64_t address_prefix = is_carrot
|
||||
? (subaddress ? get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX)
|
||||
: (subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
|
||||
|
||||
@@ -168,9 +169,10 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, account_public_address const & adr
|
||||
, crypto::hash8 const & payment_id
|
||||
, bool is_carrot
|
||||
)
|
||||
{
|
||||
uint64_t integrated_address_prefix = adr.m_is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
uint64_t integrated_address_prefix = is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
|
||||
|
||||
integrated_address iadr = {
|
||||
adr, payment_id
|
||||
|
||||
@@ -88,12 +88,14 @@ namespace cryptonote {
|
||||
network_type nettype
|
||||
, bool subaddress
|
||||
, const account_public_address& adr
|
||||
, bool is_carrot = false
|
||||
);
|
||||
|
||||
std::string get_account_integrated_address_as_str(
|
||||
network_type nettype
|
||||
, const account_public_address& adr
|
||||
, const crypto::hash8& payment_id
|
||||
, bool is_carrot = false
|
||||
);
|
||||
|
||||
bool get_account_address_from_str(
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#include "boost/logic/tribool.hpp"
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
|
||||
#include <sys/times.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/ps/IOPSKeys.h>
|
||||
@@ -902,7 +902,7 @@ namespace cryptonote
|
||||
|
||||
return true;
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
|
||||
|
||||
mach_msg_type_number_t count;
|
||||
kern_return_t status;
|
||||
@@ -968,7 +968,7 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#elif (defined(__linux__) && defined(_SC_CLK_TCK)) || (defined(__APPLE__) && !defined(TARGET_OS_IPHONE)) || defined(__FreeBSD__)
|
||||
|
||||
struct tms tms;
|
||||
if ( times(&tms) != (clock_t)-1 )
|
||||
@@ -997,7 +997,7 @@ namespace cryptonote
|
||||
return boost::logic::tribool(power_status.ACLineStatus != 1);
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
|
||||
|
||||
#if TARGET_OS_MAC && (!defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)
|
||||
return boost::logic::tribool(IOPSGetTimeRemainingEstimate() != kIOPSTimeRemainingUnlimited);
|
||||
|
||||
@@ -274,6 +274,8 @@
|
||||
|
||||
#define DNS_BLOCKLIST_LIFETIME (86400 * 8)
|
||||
|
||||
#define POLYSEED_COIN POLYSEED_MONERO
|
||||
|
||||
#define PRICING_RECORD_VALID_BLOCKS 10
|
||||
#define PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK 120 // seconds
|
||||
|
||||
|
||||
@@ -1440,16 +1440,24 @@ namespace cryptonote
|
||||
|
||||
bool get_block_longhash(const Blockchain *pbc, const blobdata& bd, crypto::hash& res, const uint64_t height, const int major_version, const crypto::hash *seed_hash, const int miners)
|
||||
{
|
||||
crypto::hash hash;
|
||||
if (pbc != NULL)
|
||||
{
|
||||
crypto::hash hash;
|
||||
const uint64_t seed_height = rx_seedheight(height);
|
||||
hash = seed_hash ? *seed_hash : pbc->get_pending_block_id_by_height(seed_height);
|
||||
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
|
||||
} else
|
||||
{
|
||||
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
|
||||
// only happens when generating genesis block
|
||||
// Hardcoded genesis for ios compat
|
||||
const char* hex = "4ade63d5ccb8cfae075e8b882514c471f35da95f85dd1b20fdcd6f3a95caabc5";
|
||||
char bytes[32];
|
||||
for (int i = 0; i < 32; i++) {
|
||||
char byte_str[3] = { hex[i * 2], hex[i * 2 + 1], '\0' };
|
||||
bytes[i] = (char)strtol(byte_str, NULL, 16);
|
||||
}
|
||||
memcpy(res.data, bytes, sizeof(bytes));
|
||||
}
|
||||
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1355,7 +1355,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
|
||||
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining, bool ignore_battery, bool is_carrot) {
|
||||
cryptonote::COMMAND_RPC_START_MINING::request req;
|
||||
cryptonote::COMMAND_RPC_START_MINING::response res;
|
||||
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address);
|
||||
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address, is_carrot);
|
||||
req.threads_count = num_threads;
|
||||
req.do_background_mining = do_background_mining;
|
||||
req.ignore_battery = ignore_battery;
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
set(device_sources
|
||||
device.cpp
|
||||
device_default.cpp
|
||||
device_io_dummy.cpp
|
||||
log.cpp
|
||||
)
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
|
||||
set(device_sources
|
||||
${device_sources}
|
||||
device_ledger.cpp
|
||||
@@ -45,10 +46,11 @@ set(device_headers
|
||||
device_io.hpp
|
||||
device_default.hpp
|
||||
device_cold.hpp
|
||||
device_io_dummy.hpp
|
||||
log.hpp
|
||||
)
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
if(HIDAPI_FOUND OR HIDAPI_DUMMY)
|
||||
set(device_headers
|
||||
${device_headers}
|
||||
device_ledger.hpp
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "device.hpp"
|
||||
#include "device_default.hpp"
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
#include "device_ledger.hpp"
|
||||
#endif
|
||||
#include "misc_log_ex.h"
|
||||
@@ -57,7 +57,7 @@ namespace hw {
|
||||
|
||||
device_registry::device_registry(){
|
||||
hw::core::register_all(registry);
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY)
|
||||
hw::ledger::register_all(registry);
|
||||
#endif
|
||||
atexit(clear_device_registry);
|
||||
@@ -83,11 +83,13 @@ namespace hw {
|
||||
|
||||
auto device = registry.find(device_descriptor_lookup);
|
||||
if (device == registry.end()) {
|
||||
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: ");
|
||||
std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: ");
|
||||
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n");
|
||||
for( const auto& sm_pair : registry ) {
|
||||
ss << "\n- " + sm_pair.first;
|
||||
MERROR(" - " << sm_pair.first);
|
||||
}
|
||||
throw std::runtime_error("device not found: " + device_descriptor);
|
||||
throw std::runtime_error("device not found: " + device_descriptor + "\n" + ss.str());
|
||||
}
|
||||
return *device->second;
|
||||
}
|
||||
|
||||
+1
-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;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
set(polyseed_sources
|
||||
pbkdf2.c
|
||||
polyseed.cpp
|
||||
)
|
||||
|
||||
monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
monero_private_headers(polyseed_wrapper
|
||||
${polyseed_private_headers}
|
||||
)
|
||||
|
||||
monero_add_library(polyseed_wrapper
|
||||
${polyseed_sources}
|
||||
${polyseed_headers}
|
||||
${polyseed_private_headers}
|
||||
)
|
||||
|
||||
target_link_libraries(polyseed_wrapper
|
||||
PUBLIC
|
||||
polyseed
|
||||
utf8proc
|
||||
${SODIUM_LIBRARY}
|
||||
PRIVATE
|
||||
${EXTRA_LIBRARIES}
|
||||
)
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
// Copyright (c) 2005,2007,2009 Colin Percival
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <sodium/crypto_auth_hmacsha256.h>
|
||||
#include <sodium/utils.h>
|
||||
|
||||
static inline void
|
||||
store32_be(uint8_t dst[4], uint32_t w)
|
||||
{
|
||||
dst[3] = (uint8_t) w; w >>= 8;
|
||||
dst[2] = (uint8_t) w; w >>= 8;
|
||||
dst[1] = (uint8_t) w; w >>= 8;
|
||||
dst[0] = (uint8_t) w;
|
||||
}
|
||||
|
||||
void
|
||||
crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
|
||||
const uint8_t* salt, size_t saltlen, uint64_t c,
|
||||
uint8_t* buf, size_t dkLen)
|
||||
{
|
||||
crypto_auth_hmacsha256_state Phctx, PShctx, hctx;
|
||||
size_t i;
|
||||
uint8_t ivec[4];
|
||||
uint8_t U[32];
|
||||
uint8_t T[32];
|
||||
uint64_t j;
|
||||
int k;
|
||||
size_t clen;
|
||||
|
||||
crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen);
|
||||
PShctx = Phctx;
|
||||
crypto_auth_hmacsha256_update(&PShctx, salt, saltlen);
|
||||
|
||||
for (i = 0; i * 32 < dkLen; i++) {
|
||||
store32_be(ivec, (uint32_t)(i + 1));
|
||||
hctx = PShctx;
|
||||
crypto_auth_hmacsha256_update(&hctx, ivec, 4);
|
||||
crypto_auth_hmacsha256_final(&hctx, U);
|
||||
|
||||
memcpy(T, U, 32);
|
||||
for (j = 2; j <= c; j++) {
|
||||
hctx = Phctx;
|
||||
crypto_auth_hmacsha256_update(&hctx, U, 32);
|
||||
crypto_auth_hmacsha256_final(&hctx, U);
|
||||
|
||||
for (k = 0; k < 32; k++) {
|
||||
T[k] ^= U[k];
|
||||
}
|
||||
}
|
||||
|
||||
clen = dkLen - i * 32;
|
||||
if (clen > 32) {
|
||||
clen = 32;
|
||||
}
|
||||
memcpy(&buf[i * 32], T, clen);
|
||||
}
|
||||
sodium_memzero((void*)&Phctx, sizeof Phctx);
|
||||
sodium_memzero((void*)&PShctx, sizeof PShctx);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#ifndef PBKDF2_H
|
||||
#define PBKDF2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void
|
||||
crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
|
||||
const uint8_t* salt, size_t saltlen, uint64_t c,
|
||||
uint8_t* buf, size_t dkLen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#include "polyseed.hpp"
|
||||
#include "pbkdf2.h"
|
||||
|
||||
#include <sodium/core.h>
|
||||
#include <sodium/utils.h>
|
||||
#include <sodium/randombytes.h>
|
||||
#include <utf8proc.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace polyseed {
|
||||
|
||||
inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) {
|
||||
utf8proc_int32_t buffer[POLYSEED_STR_SIZE];
|
||||
utf8proc_ssize_t result;
|
||||
|
||||
result = utf8proc_decompose(reinterpret_cast<const uint8_t*>(str), 0, buffer, POLYSEED_STR_SIZE, options);
|
||||
if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) {
|
||||
throw std::runtime_error("Unicode normalization failed");
|
||||
}
|
||||
|
||||
result = utf8proc_reencode(buffer, result, options);
|
||||
if (result < 0 || result > POLYSEED_STR_SIZE) {
|
||||
throw std::runtime_error("Unicode normalization failed");
|
||||
}
|
||||
|
||||
strcpy(norm, reinterpret_cast<const char*>(buffer));
|
||||
sodium_memzero(buffer, POLYSEED_STR_SIZE);
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t utf8_nfc(const char* str, polyseed_str norm) {
|
||||
// Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases
|
||||
// to allow wallets to split on ' '.
|
||||
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA));
|
||||
}
|
||||
|
||||
static size_t utf8_nfkd(const char* str, polyseed_str norm) {
|
||||
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA));
|
||||
}
|
||||
|
||||
struct dependency {
|
||||
dependency();
|
||||
std::vector<language> languages;
|
||||
};
|
||||
|
||||
static dependency deps;
|
||||
|
||||
dependency::dependency() {
|
||||
if (sodium_init() == -1) {
|
||||
throw std::runtime_error("sodium_init failed");
|
||||
}
|
||||
|
||||
polyseed_dependency pd;
|
||||
pd.randbytes = &randombytes_buf;
|
||||
pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256;
|
||||
pd.memzero = &sodium_memzero;
|
||||
pd.u8_nfc = &utf8_nfc;
|
||||
pd.u8_nfkd = &utf8_nfkd;
|
||||
pd.time = nullptr;
|
||||
pd.alloc = nullptr;
|
||||
pd.free = nullptr;
|
||||
|
||||
polyseed_inject(&pd);
|
||||
|
||||
for (int i = 0; i < polyseed_get_num_langs(); ++i) {
|
||||
languages.push_back(language(polyseed_get_lang(i)));
|
||||
}
|
||||
}
|
||||
|
||||
static language invalid_lang;
|
||||
|
||||
const std::vector<language>& get_langs() {
|
||||
return deps.languages;
|
||||
}
|
||||
|
||||
const language& get_lang_by_name(const std::string& name) {
|
||||
for (auto& lang : deps.languages) {
|
||||
if (name == lang.name_en()) {
|
||||
return lang;
|
||||
}
|
||||
if (name == lang.name()) {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
return invalid_lang;
|
||||
}
|
||||
|
||||
inline void data::check_init() const {
|
||||
if (valid()) {
|
||||
throw std::runtime_error("already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
static std::array<const char*, 8> error_desc = {
|
||||
"Success",
|
||||
"Wrong number of words in the phrase",
|
||||
"Unknown language or unsupported words",
|
||||
"Checksum mismatch",
|
||||
"Unsupported seed features",
|
||||
"Invalid seed format",
|
||||
"Memory allocation failure",
|
||||
"Unicode normalization failed"
|
||||
};
|
||||
|
||||
static error get_error(polyseed_status status) {
|
||||
if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) {
|
||||
return error(error_desc[(int)status], status);
|
||||
}
|
||||
return error("Unknown error", status);
|
||||
}
|
||||
|
||||
void data::create(feature_type features) {
|
||||
check_init();
|
||||
auto status = polyseed_create(features, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
void data::split(const language& lang, polyseed_phrase& words) {
|
||||
check_init();
|
||||
if (!lang.valid()) {
|
||||
throw std::runtime_error("invalid language");
|
||||
}
|
||||
}
|
||||
|
||||
void data::load(polyseed_storage storage) {
|
||||
check_init();
|
||||
auto status = polyseed_load(storage, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
void data::load(const crypto::secret_key &key) {
|
||||
polyseed_storage d;
|
||||
memcpy(&d, &key.data, 32);
|
||||
auto status = polyseed_load(d, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
}
|
||||
|
||||
language data::decode(const char* phrase) {
|
||||
check_init();
|
||||
const polyseed_lang* lang;
|
||||
auto status = polyseed_decode(phrase, m_coin, &lang, &m_data);
|
||||
if (status != POLYSEED_OK) {
|
||||
throw get_error(status);
|
||||
}
|
||||
return language(lang);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// Copyright (c) 2021, tevador <tevador@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#ifndef POLYSEED_HPP
|
||||
#define POLYSEED_HPP
|
||||
|
||||
#include <polyseed/include/polyseed.h>
|
||||
#include <polyseed/src/lang.h>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
namespace polyseed {
|
||||
|
||||
class data;
|
||||
|
||||
class language {
|
||||
public:
|
||||
language() : m_lang(nullptr) {}
|
||||
language(const language&) = default;
|
||||
language(const polyseed_lang* lang) : m_lang(lang) {}
|
||||
const char* name() const {
|
||||
return polyseed_get_lang_name(m_lang);
|
||||
}
|
||||
const char* name_en() const {
|
||||
return polyseed_get_lang_name_en(m_lang);
|
||||
}
|
||||
const char* separator() const {
|
||||
return m_lang->separator;
|
||||
}
|
||||
bool valid() const {
|
||||
return m_lang != nullptr;
|
||||
}
|
||||
|
||||
const polyseed_lang* m_lang;
|
||||
private:
|
||||
|
||||
friend class data;
|
||||
};
|
||||
|
||||
const std::vector<language>& get_langs();
|
||||
const language& get_lang_by_name(const std::string& name);
|
||||
|
||||
class error : public std::runtime_error {
|
||||
public:
|
||||
error(const char* msg, polyseed_status status)
|
||||
: std::runtime_error(msg), m_status(status)
|
||||
{
|
||||
}
|
||||
polyseed_status status() const {
|
||||
return m_status;
|
||||
}
|
||||
private:
|
||||
polyseed_status m_status;
|
||||
};
|
||||
|
||||
using feature_type = unsigned int;
|
||||
|
||||
inline int enable_features(feature_type features) {
|
||||
return polyseed_enable_features(features);
|
||||
}
|
||||
|
||||
class data {
|
||||
public:
|
||||
data(const data&) = delete;
|
||||
data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {}
|
||||
~data() {
|
||||
polyseed_free(m_data);
|
||||
}
|
||||
|
||||
void create(feature_type features);
|
||||
|
||||
void load(polyseed_storage storage);
|
||||
|
||||
void load(const crypto::secret_key &key);
|
||||
|
||||
language decode(const char* phrase);
|
||||
|
||||
template<class str_type>
|
||||
void encode(const language& lang, str_type& str) const {
|
||||
check_valid();
|
||||
if (!lang.valid()) {
|
||||
throw std::runtime_error("invalid language");
|
||||
}
|
||||
str.resize(POLYSEED_STR_SIZE);
|
||||
auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]);
|
||||
str.resize(size);
|
||||
}
|
||||
|
||||
void split(const language& lang, polyseed_phrase& words);
|
||||
|
||||
void save(polyseed_storage storage) const {
|
||||
check_valid();
|
||||
polyseed_store(m_data, storage);
|
||||
}
|
||||
|
||||
void save(void *storage) const {
|
||||
check_valid();
|
||||
polyseed_store(m_data, (uint8_t*)storage);
|
||||
}
|
||||
|
||||
void crypt(const char* password) {
|
||||
check_valid();
|
||||
polyseed_crypt(m_data, password);
|
||||
}
|
||||
|
||||
void keygen(void* ptr, size_t key_size) const {
|
||||
check_valid();
|
||||
polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr);
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
bool encrypted() const {
|
||||
check_valid();
|
||||
return polyseed_is_encrypted(m_data);
|
||||
}
|
||||
|
||||
uint64_t birthday() const {
|
||||
check_valid();
|
||||
return polyseed_get_birthday(m_data);
|
||||
}
|
||||
|
||||
bool has_feature(feature_type feature) const {
|
||||
check_valid();
|
||||
return polyseed_get_feature(m_data, feature) != 0;
|
||||
}
|
||||
private:
|
||||
void check_valid() const {
|
||||
if (m_data == nullptr) {
|
||||
throw std::runtime_error("invalid object");
|
||||
}
|
||||
}
|
||||
void check_init() const;
|
||||
|
||||
polyseed_data* m_data;
|
||||
polyseed_coin m_coin;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //POLYSEED_HPP
|
||||
@@ -1879,29 +1879,12 @@ namespace cryptonote
|
||||
}
|
||||
blobdata block_blob = t_serializable_object_to_blob(b);
|
||||
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
|
||||
const std::vector<crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx);
|
||||
if(tx_pub_key == crypto::null_pkey)
|
||||
{
|
||||
// Check for Carrot treasury payout
|
||||
const uint8_t hf_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
|
||||
if (hf_version >= HF_VERSION_CARROT && b.miner_tx.vout.size() == 2) {
|
||||
|
||||
const auto treasury_payout_data = get_config(nettype()).TREASURY_SAL1_MINT_OUTPUT_DATA;
|
||||
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
|
||||
if (!treasury_payout_exists) {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template (missing treasury payout)";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra (missing treasury payout)");
|
||||
return false;
|
||||
}
|
||||
tx_pub_key = additional_tx_pub_keys.back();
|
||||
|
||||
} else {
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra");
|
||||
return false;
|
||||
}
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
LOG_ERROR("Failed to get tx pub key in coinbase extra");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t next_height;
|
||||
|
||||
@@ -5335,7 +5335,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||
.m_is_carrot = true
|
||||
};
|
||||
message_writer(console_color_white, true) << tr("Generated new Carrot wallet: ")
|
||||
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address);
|
||||
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address, true);
|
||||
PAUSE_READLINE();
|
||||
std::cout << tr("View key: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||
@@ -7337,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;
|
||||
}
|
||||
}
|
||||
@@ -11231,9 +11231,9 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
|
||||
success_msg_writer() << tr("Index: ") << i;
|
||||
std::string address;
|
||||
if (row.m_has_payment_id)
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id);
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id, row.m_is_carrot);
|
||||
else
|
||||
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
|
||||
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address, row.m_is_carrot);
|
||||
success_msg_writer() << tr("Address: ") << address;
|
||||
success_msg_writer() << tr("Description: ") << row.m_description << "\n";
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_SALVIUM_VERSION "1.0.6"
|
||||
#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;
|
||||
|
||||
|
||||
+730
-60
File diff suppressed because it is too large
Load Diff
+53
-3
@@ -47,6 +47,7 @@ class PendingTransactionImpl;
|
||||
class UnsignedTransactionImpl;
|
||||
class AddressBookImpl;
|
||||
class SubaddressImpl;
|
||||
class CoinsImpl;
|
||||
class SubaddressAccountImpl;
|
||||
struct Wallet2CallbackImpl;
|
||||
|
||||
@@ -77,12 +78,28 @@ public:
|
||||
const std::string &address_string,
|
||||
const std::string &viewkey_string,
|
||||
const std::string &spendkey_string = "");
|
||||
bool recoverDeterministicWalletFromSpendKey(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
const std::string &spendkey_string);
|
||||
bool recoverFromDevice(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &device_name);
|
||||
|
||||
bool createFromPolyseed(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &seed,
|
||||
const std::string &passphrase = "",
|
||||
bool newWallet = true,
|
||||
uint64_t restoreHeight = 0);
|
||||
|
||||
Device getDeviceType() const override;
|
||||
bool close(bool store = true);
|
||||
std::string seed(const std::string& seed_offset = "") const override;
|
||||
bool getPolyseed(std::string &seed_words, std::string &passphrase) const override;
|
||||
void setStoreTxInfo(bool store) override;
|
||||
bool storeTxInfo() const override;
|
||||
|
||||
std::string getSeedLanguage() const override;
|
||||
void setSeedLanguage(const std::string &arg) override;
|
||||
// void setListener(Listener *) {}
|
||||
@@ -99,7 +116,12 @@ public:
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
|
||||
@@ -148,7 +148,7 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
|
||||
const carrot::CarrotPaymentProposalV1 &payment_proposal)
|
||||
{
|
||||
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.amount,
|
||||
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey, /*m_is_carrot*/true},
|
||||
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey},
|
||||
payment_proposal.destination.is_subaddress);
|
||||
dest.is_integrated = payment_proposal.destination.payment_id != carrot::null_payment_id;
|
||||
return dest;
|
||||
@@ -166,8 +166,8 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
|
||||
"make_tx_destination_entry: view-key multiplication failed");
|
||||
|
||||
return cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
|
||||
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey, /*m_is_carrot*/true},
|
||||
payment_proposal.subaddr_index.index.is_subaddress());
|
||||
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey},
|
||||
payment_proposal.subaddr_index.index.is_subaddress());
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1360,16 +1360,11 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
|
||||
ptx.change_dts = change_dts;
|
||||
ptx.selected_transfers = std::move(selected_transfers);
|
||||
ptx.key_images = key_images_string.str();
|
||||
if (ephemeral_privkeys.size() == 1) {
|
||||
ptx.tx_key = ephemeral_privkeys.at(0);
|
||||
ptx.additional_tx_keys.clear();
|
||||
} else if (ephemeral_privkeys.size() == 2 && shared_ephemeral_pubkey) {
|
||||
ptx.tx_key = (ephemeral_privkeys.at(0) == crypto::null_skey) ? ephemeral_privkeys.at(1) : ephemeral_privkeys.at(0);
|
||||
ptx.additional_tx_keys.clear();
|
||||
} else {
|
||||
ptx.tx_key = crypto::null_skey;
|
||||
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
|
||||
}
|
||||
ptx.tx_key = shared_ephemeral_pubkey ? ephemeral_privkeys.at(0) : crypto::null_skey;
|
||||
if (shared_ephemeral_pubkey)
|
||||
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
|
||||
else
|
||||
ptx.additional_tx_keys.clear();
|
||||
ptx.dests = std::move(dests);
|
||||
ptx.multisig_sigs = {};
|
||||
ptx.multisig_tx_key_entropy = {};
|
||||
|
||||
+253
-111
@@ -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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -1572,12 +1610,10 @@ cryptonote::account_public_address wallet2::get_subaddress(const cryptonote::sub
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
|
||||
{
|
||||
const auto subaddress_map = m_account.get_subaddress_map_ref();
|
||||
auto carrot_index = subaddress_map.find(address.m_spend_public_key);
|
||||
if (carrot_index == subaddress_map.end())
|
||||
auto index = m_subaddresses.find(address.m_spend_public_key);
|
||||
if (index == m_subaddresses.end())
|
||||
return boost::none;
|
||||
cryptonote::subaddress_index index{carrot_index->second.index.major, carrot_index->second.index.minor};
|
||||
return index;
|
||||
return index->second;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const
|
||||
@@ -1608,7 +1644,7 @@ std::string wallet2::get_subaddress_as_str(const carrot::subaddress_index_extend
|
||||
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);
|
||||
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
|
||||
@@ -1620,7 +1656,7 @@ std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_
|
||||
account_public_address addr{address.address_spend_pubkey, address.address_view_pubkey};
|
||||
addr.m_is_carrot = carrot;
|
||||
|
||||
return cryptonote::get_account_integrated_address_as_str(m_nettype, addr, payment_id);
|
||||
return cryptonote::get_account_integrated_address_as_str(m_nettype, addr, payment_id, carrot);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::add_subaddress_account(const std::string& label)
|
||||
@@ -1653,7 +1689,6 @@ bool wallet2::should_expand(const cryptonote::subaddress_index &index) const
|
||||
void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
|
||||
{
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
std::unordered_map<crypto::public_key, carrot::subaddress_index_extended> new_addresses;
|
||||
if (m_subaddress_labels.size() <= index.major)
|
||||
{
|
||||
// add new accounts
|
||||
@@ -1667,7 +1702,6 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
|
||||
{
|
||||
const crypto::public_key &D = pkeys[index2.minor];
|
||||
m_subaddresses[D] = index2;
|
||||
new_addresses.insert({D, {{index2.major, index2.minor}, carrot::AddressDeriveType::PreCarrot, false}});
|
||||
}
|
||||
}
|
||||
m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
|
||||
@@ -1685,12 +1719,9 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
|
||||
{
|
||||
const crypto::public_key &D = pkeys[index2.minor - begin];
|
||||
m_subaddresses[D] = index2;
|
||||
new_addresses.insert({D, {{index2.major, index2.minor}, carrot::AddressDeriveType::PreCarrot, false}});
|
||||
}
|
||||
m_subaddress_labels[index.major].resize(index.minor + 1);
|
||||
}
|
||||
// Add to the Carrot account
|
||||
m_account.insert_subaddresses(new_addresses);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::create_one_off_subaddress(const cryptonote::subaddress_index& index)
|
||||
@@ -2125,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));
|
||||
@@ -2141,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)
|
||||
@@ -2649,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;
|
||||
@@ -4205,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;
|
||||
@@ -4882,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());
|
||||
|
||||
@@ -5129,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())
|
||||
{
|
||||
@@ -5373,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;
|
||||
@@ -5630,8 +5690,7 @@ void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, cons
|
||||
|
||||
if (create_address_file)
|
||||
{
|
||||
std::string addresses = m_account.get_public_address_str(m_nettype) + "\n" + m_account.get_carrot_public_address_str(m_nettype) + "\n";
|
||||
r = save_to_file(m_wallet_file + ".address.txt", addresses, true);
|
||||
r = save_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype), true);
|
||||
if(!r) MERROR("String with address text not saved");
|
||||
}
|
||||
}
|
||||
@@ -5703,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.
|
||||
@@ -5830,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
|
||||
@@ -5854,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)
|
||||
@@ -6614,9 +6715,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
|
||||
}
|
||||
LOG_PRINT_L0("Loaded wallet keys file, with public addresses: ");
|
||||
LOG_PRINT_L0(" CN : " << m_account.get_public_address_str(m_nettype));
|
||||
LOG_PRINT_L0(" Carrot : " << m_account.get_carrot_public_address_str(m_nettype));
|
||||
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
|
||||
lock_keys_file();
|
||||
}
|
||||
else if (!load_keys_buf(keys_buf, password))
|
||||
@@ -6663,8 +6762,6 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
||||
if (get_num_subaddress_accounts() == 0)
|
||||
add_subaddress_account(tr("Primary account"));
|
||||
|
||||
m_account.generate_subaddress_map(get_subaddress_lookahead());
|
||||
|
||||
// populate account subaddress list
|
||||
if (!m_subaddresses.empty())
|
||||
{
|
||||
@@ -7193,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;
|
||||
@@ -7836,7 +7956,7 @@ void wallet2::commit_tx(pending_tx& ptx)
|
||||
amount_in += m_transfers[idx].amount();
|
||||
}
|
||||
add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, subaddr_account, subaddr_indices);
|
||||
if (store_tx_info() && (ptx.tx_key != crypto::null_skey || ptx.additional_tx_keys.size() != 0))
|
||||
if (store_tx_info() && ptx.tx_key != crypto::null_skey)
|
||||
{
|
||||
m_tx_keys[txid] = ptx.tx_key;
|
||||
m_additional_tx_keys[txid] = ptx.additional_tx_keys;
|
||||
@@ -8113,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)
|
||||
{
|
||||
@@ -10651,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;
|
||||
@@ -10668,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)
|
||||
@@ -10689,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)
|
||||
@@ -10704,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) << "]");
|
||||
@@ -10806,7 +10933,7 @@ 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();
|
||||
@@ -11073,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)
|
||||
{
|
||||
@@ -11128,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({});
|
||||
@@ -11158,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;
|
||||
@@ -11652,14 +11788,11 @@ 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)
|
||||
{
|
||||
// Sanity checks
|
||||
THROW_WALLET_EXCEPTION_IF(!address.m_is_carrot, error::wallet_internal_error, "CryptoNote address supplied, but Carrot is now active");
|
||||
|
||||
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep_all(*this, below, address, is_subaddress, outputs, priority, extra, tx_type, subaddr_account, subaddr_indices);
|
||||
std::vector<pending_tx> ptx_vector;
|
||||
ptx_vector.reserve(tx_proposals.size());
|
||||
@@ -11668,9 +11801,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
|
||||
return ptx_vector;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
THROW_WALLET_EXCEPTION_IF(address.m_is_carrot, error::wallet_internal_error, "Carrot address supplied, but Carrot not yet active");
|
||||
|
||||
std::vector<size_t> unused_transfers_indices;
|
||||
std::vector<size_t> unused_dust_indices;
|
||||
const bool use_per_byte_fee = true;
|
||||
@@ -11704,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));
|
||||
@@ -11753,9 +11886,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
|
||||
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
|
||||
if (do_carrot_tx_construction)
|
||||
{
|
||||
// Sanity checks
|
||||
THROW_WALLET_EXCEPTION_IF(!address.m_is_carrot, error::wallet_internal_error, "CryptoNote address supplied, but Carrot is now active");
|
||||
|
||||
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep(*this, {ki}, address, is_subaddress, outputs, priority, extra, tx_type);
|
||||
std::vector<pending_tx> ptx_vector;
|
||||
ptx_vector.reserve(tx_proposals.size());
|
||||
@@ -11765,9 +11895,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
|
||||
return ptx_vector;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
THROW_WALLET_EXCEPTION_IF(address.m_is_carrot, error::wallet_internal_error, "Carrot address supplied, but Carrot not yet active");
|
||||
|
||||
std::vector<size_t> unused_transfers_indices;
|
||||
std::vector<size_t> unused_dust_indices;
|
||||
const bool use_rct = use_fork_rules(4, 0);
|
||||
@@ -11855,13 +11982,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_return(std::vector
|
||||
} else {
|
||||
THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Unsupported input type for return_payment");
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(!main_derivations.size() && !additional_derivations.size(), error::wallet_internal_error, "No derivations found");
|
||||
THROW_WALLET_EXCEPTION_IF(!main_derivations.size() && additional_derivations.size() < td_origin.m_internal_output_index + 1, error::wallet_internal_error, "No derivations found");
|
||||
const mx25519_pubkey origin_tx_shared_secret_unctx = carrot::raw_byte_convert<mx25519_pubkey>(main_derivations[0]);
|
||||
|
||||
const key_derivation derivation = (main_derivations.size() == 1) ? main_derivations[0] : additional_derivations[td_origin.m_internal_output_index];
|
||||
const mx25519_pubkey origin_tx_shared_secret_unctx = carrot::raw_byte_convert<mx25519_pubkey>(derivation);
|
||||
|
||||
|
||||
// 4. compute m_return
|
||||
crypto::public_key output_key;
|
||||
carrot::encrypted_return_pubkey_t m_return;
|
||||
@@ -11891,7 +12013,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_return(std::vector
|
||||
|
||||
// 6. compute the change index
|
||||
crypto::secret_key z_i;
|
||||
derivation_to_scalar(derivation, td_origin.m_internal_output_index, z_i);
|
||||
derivation_to_scalar(main_derivations[0], td_origin.m_internal_output_index, z_i);
|
||||
struct {
|
||||
char domain_separator[8];
|
||||
crypto::secret_key output_index_key;
|
||||
@@ -11915,7 +12037,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_return(std::vector
|
||||
cryptonote::account_public_address address;
|
||||
address.m_spend_public_key = change_key;
|
||||
address.m_view_public_key = return_pub;
|
||||
address.m_is_carrot = true;
|
||||
|
||||
return create_transactions_from(address, cryptonote::transaction_type::RETURN, asset_type, is_subaddress, outputs, transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra);
|
||||
}
|
||||
@@ -12410,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)
|
||||
@@ -12600,8 +12721,8 @@ bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx
|
||||
if (i == m_tx_keys.end())
|
||||
return false;
|
||||
tx_key = i->second;
|
||||
//if (tx_key == crypto::null_skey)
|
||||
// return false;
|
||||
if (tx_key == crypto::null_skey)
|
||||
return false;
|
||||
const auto j = m_additional_tx_keys.find(txid);
|
||||
if (j != m_additional_tx_keys.end())
|
||||
additional_tx_keys = j->second;
|
||||
@@ -13744,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;
|
||||
|
||||
@@ -13755,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;
|
||||
}
|
||||
@@ -14041,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
|
||||
{
|
||||
@@ -14127,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));
|
||||
}
|
||||
@@ -15865,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))
|
||||
{
|
||||
@@ -15874,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;
|
||||
|
||||
+48
-5
@@ -78,6 +78,7 @@
|
||||
#include "message_store.h"
|
||||
#include "wallet_light_rpc.h"
|
||||
#include "wallet_rpc_helpers.h"
|
||||
#include "polyseed/polyseed.hpp"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
|
||||
@@ -993,6 +994,20 @@ private:
|
||||
void generate(const std::string& wallet_, const epee::wipeable_string& password,
|
||||
const epee::wipeable_string& multisig_data, bool create_address_file = false);
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet from a polyseed.
|
||||
* @param wallet_ Name of wallet file
|
||||
* @param password Password of wallet file
|
||||
* @param seed Polyseed data
|
||||
* @param passphrase Optional seed offset passphrase
|
||||
* @param recover Whether it is a restore
|
||||
* @param restoreHeight Override the embedded restore height
|
||||
* @param create_address_file Whether to create an address file
|
||||
*/
|
||||
void generate(const std::string& wallet_, const epee::wipeable_string& password,
|
||||
const polyseed::data &seed, const epee::wipeable_string& passphrase = "",
|
||||
bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false);
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \param wallet_ Name of wallet file
|
||||
@@ -1171,6 +1186,8 @@ private:
|
||||
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
|
||||
const std::string &proxy = "");
|
||||
bool set_proxy(const std::string &address);
|
||||
bool get_refresh_enabled();
|
||||
void set_refresh_enabled(bool val);
|
||||
|
||||
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
|
||||
|
||||
@@ -1186,6 +1203,15 @@ private:
|
||||
bool is_deterministic() const;
|
||||
bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
|
||||
|
||||
/*!
|
||||
* \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet.
|
||||
* @param seed Polyseed mnemonic phrase
|
||||
* @param passphrase Seed offset passphrase that was used to restore the wallet
|
||||
* @return Returns true if the wallet has a polyseed.
|
||||
* Note: both the mnemonic phrase and the passphrase are needed to recover the wallet
|
||||
*/
|
||||
bool get_polyseed(epee::wipeable_string& seed, epee::wipeable_string &passphrase) const;
|
||||
|
||||
/*!
|
||||
* \brief Gets the seed language
|
||||
*/
|
||||
@@ -1246,6 +1272,7 @@ private:
|
||||
// locked & unlocked balance of given or current subaddress account
|
||||
uint64_t balance(uint32_t subaddr_index_major, const std::string& asset_type, bool strict) const;
|
||||
uint64_t unlocked_balance(uint32_t subaddr_index_major, const std::string& asset_type, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL);
|
||||
uint64_t view_only_balance(uint32_t index_major, const std::vector<crypto::key_image>& selected_inputs = {}, const std::string& asset_type = "SAL1");
|
||||
// locked & unlocked balance per subaddress of given or current subaddress account
|
||||
std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major, const std::string& asset_type, bool strict) const;
|
||||
std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, const std::string& asset_type, bool strict);
|
||||
@@ -1294,8 +1321,8 @@ private:
|
||||
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
|
||||
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
||||
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {});
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
std::vector<wallet2::pending_tx> create_transactions_return(std::vector<size_t> transfers_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, const std::string& asset_type, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
@@ -1524,6 +1551,12 @@ private:
|
||||
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
|
||||
@@ -1667,6 +1700,7 @@ private:
|
||||
uint64_t get_num_rct_outputs();
|
||||
size_t get_num_transfer_details() const { return m_transfers.size(); }
|
||||
const transfer_details &get_transfer_details(size_t idx) const;
|
||||
size_t get_transfer_details(const crypto::public_key &pk) const;
|
||||
|
||||
uint8_t estimate_current_hard_fork(const uint64_t height = 0) const;
|
||||
uint8_t get_current_hard_fork();
|
||||
@@ -1685,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();
|
||||
@@ -1755,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;
|
||||
@@ -1778,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();
|
||||
|
||||
@@ -1878,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;
|
||||
@@ -1919,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);
|
||||
@@ -2066,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);
|
||||
@@ -2179,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;
|
||||
@@ -2187,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;
|
||||
|
||||
@@ -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
|
||||
@@ -1302,7 +1302,7 @@ namespace tools
|
||||
{
|
||||
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
|
||||
uint32_t priority = m_wallet->adjust_priority(req.priority);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, type, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, type, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, {}, req.subtract_fee_from_outputs);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -2355,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";
|
||||
@@ -3305,9 +3354,9 @@ namespace tools
|
||||
const auto &entry = ab[idx];
|
||||
std::string address;
|
||||
if (entry.m_has_payment_id)
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id);
|
||||
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id, entry.m_is_carrot);
|
||||
else
|
||||
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
|
||||
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address, entry.m_is_carrot);
|
||||
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description});
|
||||
}
|
||||
}
|
||||
|
||||
+3375
-3637
File diff suppressed because it is too large
Load Diff
+3572
-3833
File diff suppressed because one or more lines are too long
+3571
-3832
File diff suppressed because it is too large
Load Diff
+3712
-3973
File diff suppressed because one or more lines are too long
+3571
-3832
File diff suppressed because it is too large
Load Diff
+3571
-3832
File diff suppressed because one or more lines are too long
+3571
-3832
File diff suppressed because it is too large
Load Diff
+3571
-3832
File diff suppressed because it is too large
Load Diff
+3571
-3832
File diff suppressed because one or more lines are too long
+3571
-3832
File diff suppressed because it is too large
Load Diff
+3571
-3832
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user