Compare commits

...

45 Commits

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

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

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

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

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

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

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

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

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

Also imo store() functin should store the wallet, no matter
the current state.
2026-04-06 14:43:29 +02:00
Czarek Nakamoto 60713cc8e5 fix missing ___clear_cache when targetting iOS 2026-04-06 14:43:29 +02:00
Some Random Crypto Guy f60b7209f8 fixed wallet API regression on spend+view key access 2025-10-09 14:25:26 +01:00
Some Random Crypto Guy 3b00a41fff fixed broken carrotKeys() API method 2025-10-08 20:40:33 +01:00
somerandomcryptoguy 119a7fab57 added query_key wallet-RPC support for all Carrot keys (#69)
Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-10-08 20:29:10 +01:00
auruya 6d0a4a4d7b add carrotkeys fn to wallet api (#68) 2025-10-08 20:28:50 +01:00
Some Random Crypto Guy 45404ecc71 API fixes 2025-10-07 15:12:49 +01:00
Some Random Crypto Guy e5bfc2f6ad wallet API additions for better Carrot support 2025-10-07 13:32:15 +01:00
Some Random Crypto Guy c5be51053a added support for AUDIT, BURN, STAKE TXs to PendingTransaction wallet API 2025-10-07 12:34:58 +01:00
Some Random Crypto Guy d7a9facee9 improved UX for address command 2025-10-04 13:36:37 +01:00
Some Random Crypto Guy ce6f6e0593 Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-10-03 15:06:17 +01:00
somerandomcryptoguy f1efb700d2 improvements to wallet address formatting for RPC (#66)
* bumped version to v1.0.0

* added fast sync points up to 325,000

* added fast sync points up to 325,000 (part 2)

* RPC address formatting fixes

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-10-03 15:06:05 +01:00
auruya dd246296b9 Fix show qr code (#65)
* enable dual address type support in show_qr_code command

* delete comments
2025-10-03 15:05:49 +01:00
Some Random Crypto Guy 22334d7c6c Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-10-03 13:42:27 +01:00
somerandomcryptoguy c07698fcc8 improvements to wallet address formatting (#64)
* bumped version to v1.0.0

* added fast sync points up to 325,000

* added fast sync points up to 325,000 (part 2)

* initial import of CLI wallet improvements for address printing

* updated wallet address display in various places

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-10-03 13:10:28 +01:00
Some Random Crypto Guy ce69272638 Merge branch 'main' into develop 2025-10-02 14:41:30 +01:00
Some Random Crypto Guy 2b95d100b3 added fast sync points up to 325,000 (part 2) 2025-10-01 17:35:35 +01:00
Some Random Crypto Guy b7706d4def added fast sync points up to 325,000 2025-10-01 17:05:49 +01:00
Some Random Crypto Guy 845d46d7b3 bumped version to v1.0.0 2025-10-01 14:46:23 +01:00
Some Random Crypto Guy e4f8929f2c Merge branch 'develop' 2025-10-01 14:44:09 +01:00
Some Random Crypto Guy c03402d525 updated API methods for 3rd-party wallets 2025-10-01 14:40:47 +01:00
Some Random Crypto Guy d8b18b17f6 bumped RC version 2025-09-30 22:02:11 +01:00
Some Random Crypto Guy 5347266ed9 fixed a couple of wallet cache issues; fixed rescan_bc bug 2025-09-30 22:01:35 +01:00
somerandomcryptoguy 419c26adbf added tx_type setting for payment details - needed for wallet API (#61)
Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-09-12 16:34:02 +01:00
auruya 7727bdb51e Add stake tests (#60)
* update unit tests

* add stake transaction functional test
2025-09-12 09:39:02 +01:00
Some Random Crypto Guy 37a58646fd bumped RC version 2025-09-10 09:20:17 +01:00
somerandomcryptoguy 0e747e3f15 remove 2nd fork - STAKE issues resolved cleanly (#58)
* removed HF11

* fixed problem with AUDIT generation; fixed issue with empty PROTOCOL TX after HF10

* fixed protocol_tx version if empty at HF10 commencement; added validation of correct address type for destinations on TRANSFER

* fix is_carrot for stake txs

---------

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