From c51ca53daaefc432f17a9c73406ea7772f09aba6 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Wed, 14 Aug 2024 16:13:35 +0200 Subject: [PATCH 1/6] epee: string_tools: remove dot from get_extension Fixes a regression introduced in #9254. Previously it did not include the dot. --- contrib/epee/src/string_tools.cpp | 7 ++++++- tests/unit_tests/epee_utils.cpp | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/contrib/epee/src/string_tools.cpp b/contrib/epee/src/string_tools.cpp index 525af1c46f..aeb490d5d7 100644 --- a/contrib/epee/src/string_tools.cpp +++ b/contrib/epee/src/string_tools.cpp @@ -178,7 +178,12 @@ namespace string_tools std::string get_extension(const std::string& str) { - return boost::filesystem::path(str).extension().string(); + std::string ext_with_dot = boost::filesystem::path(str).extension().string(); + + if (ext_with_dot.empty()) + return {}; + + return ext_with_dot.erase(0, 1); } //---------------------------------------------------------------------------- diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 31bdc698d5..46b27d85b1 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -1443,6 +1443,14 @@ TEST(StringTools, GetIpInt32) EXPECT_EQ(htonl(0xff0aff00), ip); } +TEST(StringTools, GetExtension) +{ + EXPECT_EQ(std::string{}, epee::string_tools::get_extension("")); + EXPECT_EQ(std::string{}, epee::string_tools::get_extension(".")); + EXPECT_EQ(std::string{"keys"}, epee::string_tools::get_extension("wallet.keys")); + EXPECT_EQ(std::string{"3"}, epee::string_tools::get_extension("1.2.3")); +} + TEST(NetUtils, IPv4NetworkAddress) { static_assert(epee::net_utils::ipv4_network_address::get_type_id() == epee::net_utils::address_type::ipv4, "bad ipv4 type id"); From 89ad8ac8b1307788940966aa373526153f2191d8 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Wed, 14 Aug 2024 19:45:55 +0200 Subject: [PATCH 2/6] epee: string_tools: keep full path in cut_off_extension --- contrib/epee/src/string_tools.cpp | 2 +- tests/unit_tests/epee_utils.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/contrib/epee/src/string_tools.cpp b/contrib/epee/src/string_tools.cpp index aeb490d5d7..36356d77e6 100644 --- a/contrib/epee/src/string_tools.cpp +++ b/contrib/epee/src/string_tools.cpp @@ -189,7 +189,7 @@ namespace string_tools //---------------------------------------------------------------------------- std::string cut_off_extension(const std::string& str) { - return boost::filesystem::path(str).stem().string(); + return boost::filesystem::path(str).replace_extension("").string(); } #ifdef _WIN32 diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 46b27d85b1..64ed83694e 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -1451,6 +1451,13 @@ TEST(StringTools, GetExtension) EXPECT_EQ(std::string{"3"}, epee::string_tools::get_extension("1.2.3")); } +TEST(StringTools, CutOffExtension) +{ + EXPECT_EQ(std::string{}, epee::string_tools::cut_off_extension("")); + EXPECT_EQ(std::string{"/home/user/Monero/wallets/wallet"}, epee::string_tools::cut_off_extension("/home/user/Monero/wallets/wallet")); + EXPECT_EQ(std::string{"/home/user/Monero/wallets/wallet"}, epee::string_tools::cut_off_extension("/home/user/Monero/wallets/wallet.keys")); +} + TEST(NetUtils, IPv4NetworkAddress) { static_assert(epee::net_utils::ipv4_network_address::get_type_id() == epee::net_utils::address_type::ipv4, "bad ipv4 type id"); From ed955bf751e304569cd4c04f558360154e19610e Mon Sep 17 00:00:00 2001 From: jeffro256 Date: Fri, 23 Aug 2024 12:15:17 -0500 Subject: [PATCH 3/6] build: fix build with Boost 1.85 and remove instances of viewkey logging 1. Use `std::is_standard_layout` and `std::is_trivially_copyable` instead of `std::is_pod` for KV byte-wise serialization, which fixes compile issue for Boost UUIDs 2. Use `std::has_unique_object_representations` instead of `alignof(T) == 1` for epee byte spans and epee hex functions 3. Removed reimplementation of `std::hash` for `boost::uuids::uuid 4. Removed `<<` operator overload for `crypto::secret_key` 5. Removed instances in code where private view key was dumped to the log in plaintext --- CMakeLists.txt | 1 + .../serialization/keyvalue_serialization.h | 18 ++++++++++-------- contrib/epee/include/span.h | 19 +++++++++---------- contrib/epee/include/string_tools.h | 2 ++ src/crypto/crypto.h | 14 +++++++++++--- .../cryptonote_format_utils.cpp | 4 ++-- src/cryptonote_core/cryptonote_tx_utils.cpp | 4 ++-- src/cryptonote_protocol/block_queue.cpp | 13 ++----------- src/device/device_default.cpp | 8 +++++--- src/lmdb/util.h | 4 ++-- src/simplewallet/simplewallet.cpp | 4 ++-- src/wallet/api/wallet.cpp | 8 ++++---- src/wallet/wallet2.cpp | 2 +- src/wallet/wallet_rpc_server.cpp | 4 ++-- tests/benchmark.cpp | 2 +- tests/core_tests/multisig.cpp | 10 +++++----- tests/functional_tests/make_test_signature.cc | 2 +- tests/unit_tests/crypto.cpp | 2 +- tests/unit_tests/multisig.cpp | 2 +- tests/unit_tests/serialization.cpp | 2 +- 20 files changed, 65 insertions(+), 60 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97d6aee818..78350338d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1091,6 +1091,7 @@ endif() find_package(Boost 1.58 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) add_definitions(-DBOOST_NO_AUTO_PTR) +add_definitions(-DBOOST_UUID_DISABLE_ALIGNMENT) # This restores UUID's std::has_unique_object_representations property set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) if(NOT Boost_FOUND) diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h index ea767865cf..ccfa2f15ed 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -98,16 +98,18 @@ public: \ #define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ epee::serialization::selector::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); -#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \ - static_assert(std::is_pod::value, "t_type must be a POD type."); \ - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) +#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(variable, val_name) \ + static_assert(std::is_trivially_copyable_v, "t_type must be a trivially copyable type."); \ + static_assert(std::is_standard_layout_v, "t_type must be a standard layout type."); \ + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name) -#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \ +#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(variable, val_name, default_value) \ do { \ - static_assert(std::is_pod::value, "t_type must be a POD type."); \ - bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ + static_assert(std::is_trivially_copyable_v, "t_type must be a trivially copyable type."); \ + static_assert(std::is_standard_layout_v, "t_type must be a standard layout type."); \ + bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name) \ if (!ret) \ - epee::serialize_default(this_ref.varialble, default_value); \ + epee::serialize_default(this_ref.variable, default_value); \ } while(0); #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \ @@ -118,7 +120,7 @@ public: \ #define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble) #define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) #define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def) -#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check +#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_trivially_copyable and is_standard_layout compile time check #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) #define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value) diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index c11e6dd2bd..af02d46341 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -133,17 +133,13 @@ namespace epee return {src.data(), src.size()}; } - template - constexpr bool has_padding() noexcept - { - return !std::is_standard_layout() || alignof(T) != 1; - } - //! \return Cast data from `src` as `span`. template span to_byte_span(const span src) noexcept { - static_assert(!has_padding(), "source type may have padding"); + static_assert(!std::is_empty(), "empty value types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout_v, "type must have standard layout"); + static_assert(std::has_unique_object_representations_v, "type must be trivially copyable with no padding"); return {reinterpret_cast(src.data()), src.size_bytes()}; } @@ -153,7 +149,8 @@ namespace epee { using value_type = typename T::value_type; static_assert(!std::is_empty(), "empty value types will not work -> sizeof == 1"); - static_assert(!has_padding(), "source value type may have padding"); + static_assert(std::is_standard_layout_v, "value type must have standard layout"); + static_assert(std::has_unique_object_representations_v, "value type must be trivially copyable with no padding"); return {reinterpret_cast(src.data()), src.size() * sizeof(value_type)}; } @@ -162,7 +159,8 @@ namespace epee span as_byte_span(const T& src) noexcept { static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); - static_assert(!has_padding(), "source type may have padding"); + static_assert(std::is_standard_layout_v, "type must have standard layout"); + static_assert(std::has_unique_object_representations_v, "type must be trivially copyable with no padding"); return {reinterpret_cast(std::addressof(src)), sizeof(T)}; } @@ -171,7 +169,8 @@ namespace epee span as_mut_byte_span(T& src) noexcept { static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); - static_assert(!has_padding(), "source type may have padding"); + static_assert(std::is_standard_layout_v, "type must have standard layout"); + static_assert(std::has_unique_object_representations_v, "type must be trivially copyable with no padding"); return {reinterpret_cast(std::addressof(src)), sizeof(T)}; } diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 6f129908e9..a8f50554d0 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -89,6 +89,7 @@ namespace string_tools std::string pod_to_hex(const t_pod_type& s) { static_assert(std::is_standard_layout(), "expected standard layout type"); + static_assert(std::has_unique_object_representations_v, "type may have padding"); return to_hex::string(as_byte_span(s)); } //---------------------------------------------------------------------------- @@ -96,6 +97,7 @@ namespace string_tools bool hex_to_pod(const boost::string_ref hex_str, t_pod_type& s) { static_assert(std::is_standard_layout(), "expected standard layout type"); + static_assert(std::has_unique_object_representations_v, "type may have padding"); return from_hex::to_buffer(as_mut_byte_span(s), hex_str); } //---------------------------------------------------------------------------- diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 6b4126246d..9252017823 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -171,7 +171,9 @@ namespace crypto { /* Generate a value filled with random bytes. */ template - typename std::enable_if::value, T>::type rand() { + T rand() { + static_assert(std::is_standard_layout_v, "cannot write random bytes into non-standard layout type"); + static_assert(std::is_trivially_copyable_v, "cannot write random bytes into non-trivially copyable type"); typename std::remove_cv::type res; generate_random_bytes_thread_safe(sizeof(T), (uint8_t*)&res); return res; @@ -314,8 +316,14 @@ namespace crypto { inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + /* Do NOT overload the << operator for crypto::secret_key here. Use secret_key_explicit_print_ref + * instead to prevent accidental implicit dumping of secret key material to the logs (which has + * happened before). For the same reason, do not overload it for crypto::ec_scalar either since + * crypto::secret_key is a subclass. I'm not sorry that it's obtuse; that's the point, bozo. + */ + struct secret_key_explicit_print_ref { const crypto::secret_key &sk; }; + inline std::ostream &operator <<(std::ostream &o, const secret_key_explicit_print_ref v) { + epee::to_hex::formatted(o, epee::as_byte_span(unwrap(unwrap(v.sk)))); return o; } inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index ca56c2bc34..62ddd86fb4 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -292,7 +292,7 @@ namespace cryptonote bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); if (!r) { - MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", )"); memcpy(&recv_derivation, rct::identity().bytes, sizeof(recv_derivation)); } @@ -303,7 +303,7 @@ namespace cryptonote r = hwdev.generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation); if (!r) { - MWARNING("key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")"); + MWARNING("key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", )"); } else { diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index ed5b285e89..6935451b39 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -144,7 +144,7 @@ namespace cryptonote crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << crypto::secret_key_explicit_print_ref{txkey.sec} << ")"); r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); @@ -484,7 +484,7 @@ namespace cryptonote crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); ss_ring_s << "signatures:" << ENDL; std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); - ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL; + ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << crypto::secret_key_explicit_print_ref{in_contexts[i].in_ephemeral.sec} << ENDL << "real_output: " << src_entr.real_output << ENDL; i++; } diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 9f4a93723a..2c3e31f593 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -40,15 +40,6 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn.block_queue" -namespace std { - static_assert(sizeof(size_t) <= sizeof(boost::uuids::uuid), "boost::uuids::uuid too small"); - template<> struct hash { - std::size_t operator()(const boost::uuids::uuid &_v) const { - return reinterpret_cast(_v); - } - }; -} - namespace cryptonote { @@ -472,7 +463,7 @@ bool block_queue::has_spans(const boost::uuids::uuid &connection_id) const float block_queue::get_speed(const boost::uuids::uuid &connection_id) const { boost::unique_lock lock(mutex); - std::unordered_map speeds; + std::unordered_map> speeds; for (const auto &span: blocks) { if (span.blocks.empty()) @@ -480,7 +471,7 @@ float block_queue::get_speed(const boost::uuids::uuid &connection_id) const // note that the average below does not average over the whole set, but over the // previous pseudo average and the latest rate: this gives much more importance // to the latest measurements, which is fine here - std::unordered_map::iterator i = speeds.find(span.connection_id); + const auto i = speeds.find(span.connection_id); if (i == speeds.end()) speeds.insert(std::make_pair(span.connection_id, span.rate)); else diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 5bfdc2a875..30bb4a2e83 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -317,13 +317,15 @@ namespace hw { { // sending change to yourself; derivation = a*R r = generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")"); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", )"); } else { // sending to the recipient; derivation = r*A (or s*C in the subaddress scheme) - r = generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")"); + const crypto::secret_key &tx_privkey{dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key}; + r = generate_key_derivation(dst_entr.addr.m_view_public_key, tx_privkey, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" + << dst_entr.addr.m_view_public_key << ", " << crypto::secret_key_explicit_print_ref{tx_privkey} << ")"); } if (need_additional_txkeys) diff --git a/src/lmdb/util.h b/src/lmdb/util.h index 64ad3752a0..493fc0bd8e 100644 --- a/src/lmdb/util.h +++ b/src/lmdb/util.h @@ -127,7 +127,7 @@ namespace lmdb /*! A LMDB comparison function that uses `std::memcmp`. - \toaram T is `!epee::has_padding` + \toaram T has standard layout and an alignment of 1 \tparam offset to `T` within the value. \return The result of `std::memcmp` over the value. @@ -135,7 +135,7 @@ namespace lmdb template inline int compare(MDB_val const* left, MDB_val const* right) noexcept { - static_assert(!epee::has_padding(), "memcmp will not work"); + static_assert(std::is_standard_layout_v && alignof(T) == 1, "memcmp will not work"); if (!left || !right || left->mv_size < sizeof(T) + offset || right->mv_size < sizeof(T) + offset) { assert("invalid use of custom comparison" == 0); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 153d5f2acf..1863d11b82 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7872,9 +7872,9 @@ bool simple_wallet::submit_transfer(const std::vector &args_) std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector additional_tx_keys) { ostringstream oss; - oss << epee::string_tools::pod_to_hex(tx_key); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(tx_key))); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(additional_tx_keys[i]))); return oss.str(); } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c8257919dd..6c50002dd1 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -895,7 +895,7 @@ std::string WalletImpl::integratedAddress(const std::string &payment_id) const std::string WalletImpl::secretViewKey() const { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_view_secret_key))); } std::string WalletImpl::publicViewKey() const @@ -905,7 +905,7 @@ std::string WalletImpl::publicViewKey() const std::string WalletImpl::secretSpendKey() const { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key); + return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key))); } std::string WalletImpl::publicSpendKey() const @@ -1999,9 +1999,9 @@ std::string WalletImpl::getTxKey(const std::string &txid_str) const { clearStatus(); std::ostringstream oss; - oss << epee::string_tools::pod_to_hex(tx_key); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(tx_key))); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(additional_tx_keys[i]))); return oss.str(); } else diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index fa9c51bb27..3874ce8504 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4721,7 +4721,7 @@ boost::optional wallet2::get_keys_file_data(const crypt original_address = get_account_address_as_str(m_nettype, false, m_original_address); value.SetString(original_address.c_str(), original_address.length()); json.AddMember("original_address", value, json.GetAllocator()); - original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key); + original_view_secret_key = epee::string_tools::pod_to_hex(unwrap(unwrap(m_original_view_secret_key))); value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length()); json.AddMember("original_view_secret_key", value, json.GetAllocator()); } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index bb65304d4a..478bf37d8f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1311,9 +1311,9 @@ namespace tools res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); if (req.get_tx_keys) { - res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key)); + res.tx_key_list.push_back(epee::string_tools::pod_to_hex(unwrap(unwrap(ptx.tx_key)))); for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys) - res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key); + res.tx_key_list.back() += epee::string_tools::pod_to_hex(unwrap(unwrap(additional_tx_key))); } } diff --git a/tests/benchmark.cpp b/tests/benchmark.cpp index 289368a62c..5680a7089c 100644 --- a/tests/benchmark.cpp +++ b/tests/benchmark.cpp @@ -109,7 +109,7 @@ namespace template bool compare(const T& lhs, const T& rhs) noexcept { - static_assert(!epee::has_padding(), "type might have padding"); + static_assert(std::is_standard_layout_v && alignof(T) == 1, "type might have padding"); return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(T)) == 0; } diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 8c479ed507..9cec96ff1f 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -227,13 +227,13 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector()); EXPECT_TRUE(is_formatted()); EXPECT_TRUE(is_formatted()); - EXPECT_TRUE(is_formatted()); EXPECT_TRUE(is_formatted()); EXPECT_TRUE(is_formatted()); EXPECT_TRUE(is_formatted()); + EXPECT_TRUE(is_formatted()); } TEST(Crypto, null_keys) diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 75dfaf20c9..78d4bedfcc 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -80,7 +80,7 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet) wallet.generate("", "", spendkey, true, false); ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET)); wallet.decrypt_keys(""); - ASSERT_TRUE(test_addresses[idx].spendkey == epee::string_tools::pod_to_hex(wallet.get_account().get_keys().m_spend_secret_key)); + ASSERT_TRUE(test_addresses[idx].spendkey == epee::string_tools::pod_to_hex(unwrap(unwrap(wallet.get_account().get_keys().m_spend_secret_key)))); wallet.encrypt_keys(""); } catch (const std::exception &e) diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 9daa44351c..1ca9687a84 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1113,7 +1113,7 @@ TEST(Serialization, portability_signed_tx) ASSERT_TRUE(ptx.selected_transfers.front() == 2); // ptx.{key_images, tx_key} ASSERT_TRUE(ptx.key_images == "<6c3cd6af97c4070a7aef9b1344e7463e29c7cd245076fdb65da447a34da3ca76> "); - ASSERT_TRUE(epee::string_tools::pod_to_hex(ptx.tx_key) == "0100000000000000000000000000000000000000000000000000000000000000"); + ASSERT_TRUE(epee::string_tools::pod_to_hex(unwrap(unwrap(ptx.tx_key))) == "0100000000000000000000000000000000000000000000000000000000000000"); // ptx.dests ASSERT_TRUE(ptx.dests.size() == 1); ASSERT_TRUE(ptx.dests[0].amount == 1400000000000); From 9c7e6ab04d4bf5b593785d59cb3952f6ab6eec9e Mon Sep 17 00:00:00 2001 From: tobtoht Date: Fri, 13 Sep 2024 15:42:20 +0200 Subject: [PATCH 4/6] ci: fix windows msys2 build --- .github/workflows/build.yml | 7 +--- cmake/CheckTrezor.cmake | 67 ++++++------------------------------- 2 files changed, 12 insertions(+), 62 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 89c86af2e7..0954bce45c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,12 +68,7 @@ jobs: - uses: msys2/setup-msys2@v2 with: update: true - install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-ccache mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-libusb mingw-w64-x86_64-unbound git - - shell: msys2 {0} - run: | - curl -O https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-protobuf-c-1.4.1-1-any.pkg.tar.zst - curl -O https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-protobuf-21.9-1-any.pkg.tar.zst - pacman --noconfirm -U mingw-w64-x86_64-protobuf-c-1.4.1-1-any.pkg.tar.zst mingw-w64-x86_64-protobuf-21.9-1-any.pkg.tar.zst + install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-ccache mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf mingw-w64-x86_64-libusb mingw-w64-x86_64-unbound git pkg-config - name: build run: | ${{env.CCACHE_SETTINGS}} diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake index 87582a7133..a6b0605dc9 100644 --- a/cmake/CheckTrezor.cmake +++ b/cmake/CheckTrezor.cmake @@ -23,32 +23,6 @@ OPTION(USE_DEVICE_TREZOR_UDP_RELEASE "Trezor UdpTransport in release mode" $ENV{ OPTION(USE_DEVICE_TREZOR_DEBUG "Trezor Debugging enabled" $ENV{USE_DEVICE_TREZOR_DEBUG}) OPTION(TREZOR_DEBUG "Main Trezor debugging switch" $ENV{TREZOR_DEBUG}) -# Helper function to fix cmake < 3.6.0 FindProtobuf variables -function(_trezor_protobuf_fix_vars) - if(${CMAKE_VERSION} VERSION_LESS "3.6.0") - foreach(UPPER - PROTOBUF_SRC_ROOT_FOLDER - PROTOBUF_IMPORT_DIRS - PROTOBUF_DEBUG - PROTOBUF_LIBRARY - PROTOBUF_PROTOC_LIBRARY - PROTOBUF_INCLUDE_DIR - PROTOBUF_PROTOC_EXECUTABLE - PROTOBUF_LIBRARY_DEBUG - PROTOBUF_PROTOC_LIBRARY_DEBUG - PROTOBUF_LITE_LIBRARY - PROTOBUF_LITE_LIBRARY_DEBUG - ) - if (DEFINED ${UPPER}) - string(REPLACE "PROTOBUF_" "Protobuf_" Camel ${UPPER}) - if (NOT DEFINED ${Camel}) - set(${Camel} ${${UPPER}} PARENT_SCOPE) - endif() - endif() - endforeach() - endif() -endfunction() - macro(trezor_fatal_msg msg) if ($ENV{USE_DEVICE_TREZOR_MANDATORY}) message(FATAL_ERROR @@ -72,40 +46,21 @@ endmacro() # Use Trezor master switch if (USE_DEVICE_TREZOR) - # Protobuf is required to build protobuf messages for Trezor - include(FindProtobuf OPTIONAL) + # Look for protobuf-config.cmake, provided by Protobuf + find_package(Protobuf CONFIG) - # PkgConfig works better with new Protobuf - find_package(PkgConfig QUIET) - pkg_check_modules(PROTOBUF protobuf) - - if (NOT Protobuf_FOUND) - FIND_PACKAGE(Protobuf CONFIG) - endif() - if (NOT Protobuf_FOUND) - FIND_PACKAGE(Protobuf) + if (Protobuf_FOUND) + # https://github.com/protocolbuffers/protobuf/issues/14576 + find_program(Protobuf_PROTOC_EXECUTABLE protoc REQUIRED) + set(Protobuf_LIBRARY protobuf::libprotobuf) # Compatibility with FindProtobuf.cmake + else() + # Look for FindProtobuf.cmake, provided by CMake + find_package(Protobuf) endif() - _trezor_protobuf_fix_vars() - # Early fail for optional Trezor support - if(NOT Protobuf_FOUND AND NOT Protobuf_LIBRARY AND NOT Protobuf_PROTOC_EXECUTABLE AND NOT Protobuf_INCLUDE_DIR) - trezor_fatal_msg("Trezor: Could not find Protobuf") - elseif(${CMAKE_CXX_STANDARD} LESS 17 AND ${Protobuf_VERSION} GREATER 21) - trezor_fatal_msg("Trezor: Unsupported Protobuf version ${Protobuf_VERSION} with C++ ${CMAKE_CXX_STANDARD}. Please, use Protobuf v21.") - elseif(NOT Protobuf_LIBRARY) - trezor_fatal_msg("Trezor: Protobuf library not found: ${Protobuf_LIBRARY}") - unset(Protobuf_FOUND) - elseif(NOT Protobuf_PROTOC_EXECUTABLE OR NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") - trezor_fatal_msg("Trezor: Protobuf executable not found: ${Protobuf_PROTOC_EXECUTABLE}") - unset(Protobuf_FOUND) - elseif(NOT Protobuf_INCLUDE_DIR OR NOT EXISTS "${Protobuf_INCLUDE_DIR}") - trezor_fatal_msg("Trezor: Protobuf include dir not found: ${Protobuf_INCLUDE_DIR}") - unset(Protobuf_FOUND) - else() - message(STATUS "Trezor: Protobuf lib: ${Protobuf_LIBRARY}, inc: ${Protobuf_INCLUDE_DIR}, protoc: ${Protobuf_PROTOC_EXECUTABLE}") - set(Protobuf_INCLUDE_DIRS ${Protobuf_INCLUDE_DIR}) - set(Protobuf_FOUND 1) # override found if all required info was provided by variables + if (NOT Protobuf_FOUND) + trezor_fatal_msg("Trezor: protobuf library not found") endif() if(TREZOR_DEBUG) From 84f402b47a6edfb72fae52b5fa8858294841660e Mon Sep 17 00:00:00 2001 From: DiosDelRayo Date: Fri, 27 Sep 2024 01:29:05 -0600 Subject: [PATCH 5/6] Modifications to enable Offline Signing in monero-gui and possible other projects via UR or other mediums of exchange. Added the following signatures in the mention files: wallet/wallet2.h + std::string export_key_images_string(bool all = false) const; + uint64_t import_key_images_string(const std::string &data, uint64_t &spent, uint64_t &unspent); M bool wallet2::export_key_images(const std::string &filename, bool all) const wallet/api/pending_transaction.h + std::string commit_string() override; wallet/api/unsigned_transaction.h + std::string signAsString() override; wallet/api/wallet.h: + bool submitTransactionFromString(const std::string &fileName) override; + virtual UnsignedTransaction * loadUnsignedTxFromString(const std::string &unsigned_filename) override; + std::string exportKeyImagesAsString(bool all = false) override; + bool importKeyImagesFromString(const std::string &data) override; + std::string exportOutputsAsString(bool all = false) override; + bool importOutputsFromString(const std::string &data) override; wallet/api/wallet2_api.h + virtual std::string commit_string() = 0; + virtual std::string signAsString() = 0; + virtual UnsignedTransaction * loadUnsignedTxFromString(const std::string &unsigned_filename) = 0; + virtual bool submitTransactionFromString(const std::string &fileName) = 0; + virtual bool importKeyImagesFromString(const std::string &data) = 0; + virtual std::string exportOutputsAsString(bool all = false) = 0; + virtual bool importOutputsFromString(const std::string &data) = 0; + uint64_t import_key_images_string(const std::string &data, uint64_t &spent, uint64_t &unspent); And the implementations in: wallet/wallet2.cpp wallet/api/pending_transaction.cpp wallet/api/unsigned_transaction.cpp wallet/api/wallet.cpp The method `bool wallet2::export_key_images(const std::string &filename, bool all) const` is modified to use `std::string export_key_images_string(bool all = false) const;` to get the string to write to the file. IMO that would be the perfect way to do it everywhere, but in the other methods it would require more modifications, so the other I duplicated and removed the part writing to the file and return instead a std::string, or use a std::string for the actual payload instead of a file path. One thing to mention is I remove in one or two log messages the filename, and the other is in `export_key_images` probably(almost sure) is now the performance messed up. This modifications was done to get all the necessary data for offline signing via UR or any other channel not using files as medium. IMO it had been better to not implement the filehandling direct in wallet2 or in the wallet api but rather in monero-wallet-cli and monero-gui itself, but it is like it is. --- src/wallet/api/pending_transaction.cpp | 38 +++++++ src/wallet/api/pending_transaction.h | 1 + src/wallet/api/unsigned_transaction.cpp | 22 ++++ src/wallet/api/unsigned_transaction.h | 1 + src/wallet/api/wallet.cpp | 137 ++++++++++++++++++++++++ src/wallet/api/wallet.h | 6 ++ src/wallet/api/wallet2_api.h | 49 ++++++++- src/wallet/wallet2.cpp | 28 +++-- src/wallet/wallet2.h | 2 + 9 files changed, 274 insertions(+), 10 deletions(-) diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 9783da5bcd..ba84a37333 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -162,6 +162,44 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) return m_status == Status_Ok; } +std::string PendingTransactionImpl::commit_string() +{ + + std::string tx; + LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size()); + + try { + tx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); + m_status = Status_Ok; + } catch (const tools::error::daemon_busy&) { + // TODO: make it translatable with "tr"? + m_errorString = tr("daemon is busy. Please try again later."); + m_status = Status_Error; + } catch (const tools::error::no_connection_to_daemon&) { + m_errorString = tr("no connection to daemon. Please make sure daemon is running."); + m_status = Status_Error; + } catch (const tools::error::tx_rejected& e) { + std::ostringstream writer(m_errorString); + writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); + std::string reason = e.reason(); + m_status = Status_Error; + m_errorString = writer.str(); + if (!reason.empty()) + m_errorString += string(tr(". Reason: ")) + reason; + } catch (const std::exception &e) { + m_errorString = string(tr("Unknown exception: ")) + e.what(); + m_status = Status_Error; + } catch (...) { + m_errorString = tr("Unhandled exception"); + LOG_ERROR(m_errorString); + m_status = Status_Error; + } + m_wallet.startRefresh(); + if (m_status != Status_Ok) + return ""; + return tx; +} + uint64_t PendingTransactionImpl::amount() const { uint64_t result = 0; diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 9d8d754c0c..0b9b09eced 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -45,6 +45,7 @@ class PendingTransactionImpl : public PendingTransaction ~PendingTransactionImpl(); int status() const override; std::string errorString() const override; + std::string commit_string() override; bool commit(const std::string &filename = "", bool overwrite = false) override; uint64_t amount() const override; uint64_t dust() const override; diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index c549539e5b..378e1886f2 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -96,6 +96,28 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) return true; } +std::string UnsignedTransactionImpl::signAsString() +{ + if(m_wallet.watchOnly()) + { + m_errorString = tr("This is a watch only wallet"); + m_status = Status_Error; + return ""; + } + tools::wallet2::signed_tx_set signed_txes; + std::vector ptx; + try + { + return m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes); + } + catch (const std::exception &e) + { + m_errorString = string(tr("Failed to sign transaction")) + e.what(); + m_status = Status_Error; + return ""; + } +} + //---------------------------------------------------------------------------------------------------- bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) { diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index b07d43fb11..6a4d87b530 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -53,6 +53,7 @@ class UnsignedTransactionImpl : public UnsignedTransaction uint64_t txCount() const override; // sign txs and save to file bool sign(const std::string &signedFileName) override; + std::string signAsString() override; std::string confirmationMessage() const override {return m_confirmationMessage;} uint64_t minMixinCount() const override; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c8257919dd..44b386f3d0 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1134,6 +1134,27 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file return transaction; } +UnsignedTransaction *WalletImpl::loadUnsignedTxFromString(const std::string &data) { + clearStatus(); + UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); + if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){ + setStatusError(tr("Failed to load unsigned transactions")); + transaction->m_status = UnsignedTransaction::Status::Status_Error; + transaction->m_errorString = errorString(); + + return transaction; + } + + // Check tx data and construct confirmation message + std::string extra_message; + if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); + transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); + setStatus(transaction->status(), transaction->errorString()); + + return transaction; +} + bool WalletImpl::submitTransaction(const string &fileName) { clearStatus(); if (checkBackgroundSync("cannot submit tx")) @@ -1154,6 +1175,48 @@ bool WalletImpl::submitTransaction(const string &fileName) { return true; } +bool WalletImpl::submitTransactionFromString(const string &data) { + clearStatus(); + if (checkBackgroundSync("cannot submit tx")) + return false; + std::unique_ptr transaction(new PendingTransactionImpl(*this)); + + bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL); + if (!r) { + setStatus(Status_Ok, tr("Failed to load transaction from string")); + return false; + } + + if(!transaction->commit()) { + setStatusError(transaction->m_errorString); + return false; + } + + return true; +} + +std::string WalletImpl::exportKeyImagesAsString(bool all) +{ + if (m_wallet->watch_only()) + { + setStatusError(tr("Wallet is view only")); + return ""; + } + if (checkBackgroundSync("cannot export key images")) + return ""; + + try + { + return m_wallet->export_key_images_string(all); + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting key images: " << e.what()); + setStatusError(e.what()); + return ""; + } +} + bool WalletImpl::exportKeyImages(const string &filename, bool all) { if (m_wallet->watch_only()) @@ -1181,6 +1244,31 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) return true; } +bool WalletImpl::importKeyImagesFromString(const std::string &data) +{ + if (checkBackgroundSync("cannot import key images")) + return false; + if (!trustedDaemon()) { + setStatusError(tr("Key images can only be imported with a trusted daemon")); + return false; + } + try + { + uint64_t spent = 0, unspent = 0; + uint64_t height = m_wallet->import_key_images_string(data, spent, unspent); + LOG_PRINT_L2("Signed key images imported to height " << height << ", " + << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting key images: " << e.what()); + setStatusError(string(tr("Failed to import key images: ")) + e.what()); + return false; + } + + return true; +} + bool WalletImpl::importKeyImages(const string &filename) { if (checkBackgroundSync("cannot import key images")) @@ -1206,6 +1294,29 @@ bool WalletImpl::importKeyImages(const string &filename) return true; } +std::string WalletImpl::exportOutputsAsString(bool all) +{ + if (checkBackgroundSync("cannot export outputs")) + return ""; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets."))); + return ""; + } + + try + { + return m_wallet->export_outputs_to_str(all); + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting outputs: " << e.what()); + setStatusError(string(tr("Error exporting outputs: ")) + e.what()); + return ""; + } + return ""; +} + bool WalletImpl::exportOutputs(const string &filename, bool all) { if (checkBackgroundSync("cannot export outputs")) @@ -1238,6 +1349,32 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) return true; } +bool WalletImpl::importOutputsFromString(const std::string &data) +{ + if (checkBackgroundSync("cannot import outputs")) + return false; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets."))); + return false; + } + + + try + { + size_t n_outputs = m_wallet->import_outputs_from_str(data); + LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); + } + catch (const std::exception &e) + { + LOG_ERROR("Failed to import outputs: " << e.what()); + setStatusError(string(tr("Failed to import outputs: ")) + e.what()); + return false; + } + + return true; +} + bool WalletImpl::importOutputs(const string &filename) { if (checkBackgroundSync("cannot import outputs")) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index d48d7f130e..2364f07419 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -166,10 +166,16 @@ class WalletImpl : public Wallet std::set subaddr_indices = {}) override; virtual PendingTransaction * createSweepUnmixableTransaction() override; bool submitTransaction(const std::string &fileName) override; + bool submitTransactionFromString(const std::string &fileName) override; virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; + virtual UnsignedTransaction * loadUnsignedTxFromString(const std::string &unsigned_filename) override; + std::string exportKeyImagesAsString(bool all = false) override; bool exportKeyImages(const std::string &filename, bool all = false) override; + bool importKeyImagesFromString(const std::string &data) override; bool importKeyImages(const std::string &filename) override; + std::string exportOutputsAsString(bool all = false) override; bool exportOutputs(const std::string &filename, bool all = false) override; + bool importOutputsFromString(const std::string &data) override; bool importOutputs(const std::string &filename) override; bool scanTransactions(const std::vector &txids) override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index c374d1574b..cd4206e6cf 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -89,6 +89,8 @@ struct PendingTransaction virtual ~PendingTransaction() = 0; virtual int status() const = 0; virtual std::string errorString() const = 0; + // return string of transaction gives the same content which would be saved to file with commit(filename) + virtual std::string commit_string() = 0; // commit transaction or save to file if filename is provided. virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; virtual uint64_t amount() const = 0; @@ -161,6 +163,11 @@ struct UnsignedTransaction * return - true on success */ virtual bool sign(const std::string &signedFileName) = 0; + /*! + * @brief sign - Sign txs and return as string + * return - true on success + */ + virtual std::string signAsString() = 0; }; /** @@ -895,11 +902,24 @@ struct Wallet */ virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; + /*! + * \brief loadUnsignedTxFromString - creates transaction from unsigned tx string + * \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status() + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTxFromString(const std::string &unsigned_filename) = 0; + /*! * \brief submitTransaction - submits transaction in signed tx file * \return - true on success */ virtual bool submitTransaction(const std::string &fileName) = 0; + + /*! + * \brief submitTransactionFromString - submits transaction in signed tx file + * \return - true on success + */ + virtual bool submitTransactionFromString(const std::string &fileName) = 0; /*! @@ -916,6 +936,13 @@ struct Wallet virtual uint64_t estimateTransactionFee(const std::vector> &destinations, PendingTransaction::Priority priority) const = 0; + /*! + * \brief exportKeyImages - exports key images as string + * \param all - export all key images or only those that have not yet been exported + * \return - key images as std::string + */ + virtual std::string exportKeyImagesAsString(bool all = false) = 0; + /*! * \brief exportKeyImages - exports key images to file * \param filename @@ -923,6 +950,13 @@ struct Wallet * \return - true on success */ virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; + + /*! + * \brief importKeyImagesFromString - imports key images from string for UR use. + * \param data + * \return - true on success + */ + virtual bool importKeyImagesFromString(const std::string &data) = 0; /*! * \brief importKeyImages - imports key images from file @@ -932,12 +966,25 @@ struct Wallet virtual bool importKeyImages(const std::string &filename) = 0; /*! - * \brief importOutputs - exports outputs to file + * \brief exportOutputsAsString - exports outputs to a string for UR + * \return - true on success + */ + virtual std::string exportOutputsAsString(bool all = false) = 0; + + /*! + * \brief exportOutputs - exports outputs to file * \param filename * \return - true on success */ virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; + /*! + * \brief importOutputsFromString - imports outputs from string for UR + * \param filename + * \return - true on success + */ + virtual bool importOutputsFromString(const std::string &data) = 0; + /*! * \brief importOutputs - imports outputs from file * \param filename diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index fa9c51bb27..0bf3ff8c13 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -13262,9 +13262,8 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle return tx_pub_key; } -bool wallet2::export_key_images(const std::string &filename, bool all) const +std::string wallet2::export_key_images_string(bool all) const { - PERF_TIMER(export_key_images); std::pair>> 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; @@ -13287,8 +13286,13 @@ bool wallet2::export_key_images(const std::string &filename, bool all) const // 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); + return magic + encrypt_with_view_secret_key(data); +} + +bool wallet2::export_key_images(const std::string &filename, bool all) const +{ + PERF_TIMER(export_key_images); + return save_to_file(filename, export_key_images_string(all)); } //---------------------------------------------------------------------------------------------------- @@ -13353,10 +13357,16 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); + return import_key_images_string(data, spent, unspent); +} + +uint64_t wallet2::import_key_images_string(const std::string &keyImages, uint64_t &spent, uint64_t &unspent) +{ + std::string data = keyImages; 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 @@ -13366,24 +13376,24 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent } 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); + THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size")); 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)]; 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 are from 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); + error::wallet_internal_error, std::string("Bad data size")); size_t nki = (data.size() - headerlen) / record_size; std::vector> ski; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7228f19077..1da82e734f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1630,10 +1630,12 @@ namespace tools void import_payments_out(const std::list> &confirmed_payments); std::tuple> export_blockchain() const; void import_blockchain(const std::tuple> &bc); + std::string export_key_images_string(bool all = false) const; bool export_key_images(const std::string &filename, bool all = false) const; std::pair>> export_key_images(bool all = false) const; uint64_t import_key_images(const std::vector> &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_string(const std::string &data, uint64_t &spent, uint64_t &unspent); bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> 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; From 170844bc5978f0b5c1126edbb0d10169d2489593 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Wed, 2 Oct 2024 03:18:01 +0200 Subject: [PATCH 6/6] cmake: boost: fix header-only library search, bump minimum --- CMakeLists.txt | 46 +++++++++++++++++++++++++++------------------- README.md | 2 +- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78350338d3..60cda040ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1084,33 +1084,41 @@ if(STATIC) set(Boost_USE_STATIC_RUNTIME ON) endif() -set(BOOST_COMPONENTS system filesystem thread date_time chrono regex serialization program_options) -if (WIN32) - list(APPEND BOOST_COMPONENTS locale) -endif() -find_package(Boost 1.58 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) -add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) -add_definitions(-DBOOST_NO_AUTO_PTR) -add_definitions(-DBOOST_UUID_DISABLE_ALIGNMENT) # This restores UUID's std::has_unique_object_representations property +# Find Boost headers +set(BOOST_MIN_VER 1.62) +find_package(Boost ${BOOST_MIN_VER} QUIET REQUIRED) -set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) if(NOT Boost_FOUND) - die("Could not find Boost libraries, please make sure you have installed Boost or libboost-all-dev (>=1.58) or the equivalent") + die("Could not find Boost libraries, please make sure you have installed Boost or libboost-all-dev (>=${BOOST_MIN_VER}) or the equivalent") elseif(Boost_FOUND) - message(STATUS "Found Boost Version: ${Boost_VERSION}") - if (Boost_VERSION VERSION_LESS 10 AND Boost_VERSION VERSION_LESS 1.62.0 AND NOT (OPENSSL_VERSION VERSION_LESS 1.1)) - set(BOOST_BEFORE_1_62 true) + message(STATUS "Found Boost Version: ${Boost_VERSION_STRING}") + + set(BOOST_COMPONENTS filesystem thread date_time chrono serialization program_options) + if (WIN32) + list(APPEND BOOST_COMPONENTS locale) endif() - if (NOT Boost_VERSION VERSION_LESS 10 AND Boost_VERSION VERSION_LESS 106200 AND NOT (OPENSSL_VERSION VERSION_LESS 1.1)) - set(BOOST_BEFORE_1_62 true) + + # Boost System is header-only since 1.69 + if (Boost_VERSION_STRING VERSION_LESS 1.69.0) + list(APPEND BOOST_COMPONENTS system) endif() - if (BOOST_BEFORE_1_62) - message(FATAL_ERROR "Boost ${Boost_VERSION} (older than 1.62) is too old to link with OpenSSL ${OPENSSL_VERSION} (1.1 or newer) found at ${OPENSSL_INCLUDE_DIR} and ${OPENSSL_LIBRARIES}. " - "Update Boost or install OpenSSL 1.0 and set path to it when running cmake: " - "cmake -DOPENSSL_ROOT_DIR='/usr/include/openssl-1.0'") + + # Boost Regex is header-only since 1.77 + if (Boost_VERSION_STRING VERSION_LESS 1.77.0) + list(APPEND BOOST_COMPONENTS regex) endif() + + message(STATUS "Boost components: ${BOOST_COMPONENTS}") + + # Find required Boost libraries + find_package(Boost ${BOOST_MIN_VER} QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) endif() +add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) +add_definitions(-DBOOST_NO_AUTO_PTR) +add_definitions(-DBOOST_UUID_DISABLE_ALIGNMENT) # This restores UUID's std::has_unique_object_representations property + include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") diff --git a/README.md b/README.md index 931207205b..653a1a3893 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ library archives (`.a`). | GCC | 7 | NO | `build-essential` | `base-devel` | `base-devel` | `gcc` | NO | | | CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | `cmake` | NO | | | pkg-config | any | NO | `pkg-config` | `base-devel` | `base-devel` | `pkgconf` | NO | | -| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | `boost-devel` | NO | C++ libraries | +| Boost | 1.62 | NO | `libboost-all-dev` | `boost` | `boost-devel` | `boost-devel` | NO | C++ libraries | | OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | `openssl-devel` | NO | sha256 sum | | libzmq | 4.2.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | `zeromq-devel` | NO | ZeroMQ library | | OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | | `openpgm-devel` | NO | For ZeroMQ |