From a6733fcf2b77688a306ad65dad4cddfcb88c04a9 Mon Sep 17 00:00:00 2001 From: akildemir Date: Wed, 7 May 2025 13:52:52 +0300 Subject: [PATCH] carrot_impl compilation --- src/crypto/crypto.cpp | 27 +- src/crypto/crypto.h | 59 +++-- .../cryptonote_format_utils.cpp | 73 +----- .../cryptonote_format_utils.h | 4 +- src/cryptonote_config.h | 1 + src/cryptonote_core/cryptonote_tx_utils.cpp | 2 +- src/multisig/multisig.cpp | 2 +- src/multisig/multisig_tx_builder_ringct.cpp | 5 +- src/ringct/rctTypes.h | 4 +- src/simplewallet/simplewallet.cpp | 4 +- src/wallet/scanning_tools.cpp | 2 +- src/wallet/scanning_tools.h | 2 + src/wallet/wallet2.cpp | 237 ++++++------------ src/wallet/wallet2.h | 12 +- 14 files changed, 170 insertions(+), 264 deletions(-) diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 77a36069a..f122317ff 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2022, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -618,6 +618,12 @@ namespace crypto { ge_p1p1_to_p3(&res, &point2); } + void crypto_ops::derive_key_image_generator(const public_key &pub, ec_point &ki_gen) { + ge_p3 point; + hash_to_ec(pub, point); + ge_p3_tobytes(&ki_gen, &point); + } + void crypto_ops::generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { ge_p3 point; ge_p2 point2; @@ -773,4 +779,21 @@ POP_WARNINGS static_assert(sizeof(crypto::view_tag) <= sizeof(view_tag_full), "view tag should not be larger than hash result"); memcpy(&view_tag, &view_tag_full, sizeof(crypto::view_tag)); } -} + + bool crypto_ops::key_image_to_y(const key_image &ki, key_image_y &ki_y) { + static_assert(sizeof(key_image) == 32 && sizeof(key_image_y) == 32, "unexpected size of key image"); + memcpy(&ki_y, &ki, 32); + // clear the sign bit, leaving us with the y coord + ki_y.data[31] &= 0x7F; + // return true if sign bit is set on the original key image + return (ki.data[31] & 0x80) > 0; + } + + void crypto_ops::key_image_from_y(const key_image_y &ki_y, const bool sign, key_image &ki) { + static_assert(sizeof(key_image) == 32 && sizeof(key_image_y) == 32, "unexpected size of key image"); + memcpy(&ki, &ki_y, 32); + if (sign) { + ki.data[31] ^= 0x80; + } + } +} \ No newline at end of file diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 78529f18d..968765292 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -71,22 +71,6 @@ namespace crypto { using secret_key = epee::mlocked>; - POD_CLASS public_keyV { - std::vector keys; - int rows; - }; - - POD_CLASS secret_keyV { - std::vector keys; - int rows; - }; - - POD_CLASS public_keyM { - int cols; - int rows; - std::vector column_vectors; - }; - POD_CLASS key_derivation: ec_point { friend class crypto_ops; }; @@ -95,6 +79,10 @@ namespace crypto { friend class crypto_ops; }; + POD_CLASS key_image_y: ec_point { + friend class crypto_ops; + }; + POD_CLASS signature { ec_scalar c, r; friend class crypto_ops; @@ -110,7 +98,7 @@ namespace crypto { static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 && sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 && - sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && + sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(key_image_y) == 32 && sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size"); class crypto_ops { @@ -145,6 +133,8 @@ namespace crypto { friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional &, const public_key &, const secret_key &, signature &); static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional &, const public_key &, const signature &, const int); friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional &, const public_key &, const signature &, const int); + static void derive_key_image_generator(const public_key &, ec_point &); + friend void derive_key_image_generator(const public_key &, ec_point &); static void generate_key_image(const public_key &, const secret_key &, key_image &); friend void generate_key_image(const public_key &, const secret_key &, key_image &); static void generate_ring_signature(const hash &, const key_image &, @@ -157,6 +147,10 @@ namespace crypto { const public_key *const *, std::size_t, const signature *); static void derive_view_tag(const key_derivation &, std::size_t, view_tag &); friend void derive_view_tag(const key_derivation &, std::size_t, view_tag &); + static bool key_image_to_y(const key_image &, key_image_y &); + friend bool key_image_to_y(const key_image &, key_image_y &); + static void key_image_from_y(const key_image_y &, const bool, key_image &); + friend void key_image_from_y(const key_image_y &, const bool, key_image &); }; void generate_random_bytes_thread_safe(size_t N, uint8_t *bytes); @@ -172,8 +166,8 @@ namespace crypto { */ template T rand() { - static_assert(std::is_standard_layout(), "cannot write random bytes into non-standard layout type"); - static_assert(std::is_trivially_copyable(), "cannot write random bytes into non-trivially copyable type"); + 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; @@ -270,6 +264,10 @@ namespace crypto { return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version); } + inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) { + crypto_ops::derive_key_image_generator(pub, ki_gen); + } + /* To send money to a key: * * The sender generates an ephemeral key and includes it in transaction output. * * To spend the money, the receiver generates a key image from it. @@ -313,6 +311,21 @@ namespace crypto { crypto_ops::derive_view_tag(derivation, output_index, vt); } + /** Clear the sign bit on the key image (i.e. get just the y coordinate). + * Return true if the sign bit is set, false if not. + * Since fcmp's allow construction of key images with sign bit cleared, while + * the same key image with sign bit set may already exist in the chain, we + * prevent double spends by converting all existing key images in the chain to + * their y coordinate and preventing duplicate key image y's. + */ + inline bool key_image_to_y(const key_image &ki, key_image_y &ki_y) { + return crypto_ops::key_image_to_y(ki, ki_y); + } + + inline void key_image_from_y(const key_image_y &ki_y, const bool sign, key_image &ki) { + return crypto_ops::key_image_from_y(ki_y, sign, ki); + } + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; } @@ -331,6 +344,9 @@ namespace crypto { inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image_y &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; } @@ -345,6 +361,8 @@ namespace crypto { inline bool operator>(const public_key &p1, const public_key &p2) { return p2 < p1; } inline bool operator<(const key_image &p1, const key_image &p2) { return memcmp(&p1, &p2, sizeof(key_image)) < 0; } inline bool operator>(const key_image &p1, const key_image &p2) { return p2 < p1; } + inline bool operator<(const key_image_y &p1, const key_image_y &p2) { return memcmp(&p1, &p2, sizeof(key_image_y)) < 0; } + inline bool operator>(const key_image_y &p1, const key_image_y &p2) { return p2 < p1; } } // type conversions for easier calls to sc_add(), sc_sub(), hash functions @@ -357,5 +375,6 @@ CRYPTO_MAKE_HASHABLE(public_key) CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(secret_key) CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe) CRYPTO_MAKE_HASHABLE(key_image) +CRYPTO_MAKE_HASHABLE(key_image_y) CRYPTO_MAKE_COMPARABLE(signature) -CRYPTO_MAKE_COMPARABLE(view_tag) +CRYPTO_MAKE_COMPARABLE(view_tag) \ No newline at end of file diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index d4ccabe68..c7e696f7d 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -290,7 +290,7 @@ namespace cryptonote return is_v1_tx(blobdata_ref{tx_blob.data(), tx_blob.size()}); } //--------------------------------------------------------------- - bool generate_key_image_helper(const account_keys& ack, const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid) + bool generate_key_image_helper(const account_keys& ack, const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev) { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); @@ -317,13 +317,11 @@ namespace cryptonote boost::optional subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev); CHECK_AND_ASSERT_MES(subaddr_recv_info, false, "key image helper: given output pubkey doesn't seem to belong to this address"); - - sid.aR = subaddr_recv_info->derivation; - sid.i = real_output_index; - return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev, use_origin_data, od, sid); + + return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev); } //--------------------------------------------------------------- - bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid) + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev) { if (hwdev.compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki)) { @@ -394,69 +392,6 @@ namespace cryptonote } } - if (in_ephemeral.pub != out_key) { - if (use_origin_data) { - - // 1. Obtain P_change from the output (it is the subaddress public key) - crypto::public_key P_change = crypto::null_pkey; - // SRCG: This is a confusing one - for some reason I was using the line below, and it _seemed_ to work... - // ... but I think it was luck! the "od.output_index" would only work for the TD_ORIGIN data, of course... - //hwdev.derive_subaddress_public_key(out_key, recv_derivation, od.output_index, P_change); - if (od.tx_type == cryptonote::transaction_type::CONVERT || od.tx_type == cryptonote::transaction_type::STAKE || od.tx_type == cryptonote::transaction_type::AUDIT) { - hwdev.derive_subaddress_public_key(out_key, recv_derivation, 0, P_change); - } else { - hwdev.derive_subaddress_public_key(out_key, recv_derivation, real_output_index, P_change); - } - - // 2. Obtain a separate key_derivation for the _original_ P_change output - // (using the TX public key from the CONVERT TX and the sender's private view key) - crypto::key_derivation derivation_P_change_tx = AUTO_VAL_INIT(derivation_P_change_tx); - CHECK_AND_ASSERT_MES(hwdev.generate_key_derivation(od.tx_pub_key, ack.m_view_secret_key, derivation_P_change_tx), false, "Failed to generate key_derivation for P_change"); - - // 3. Calculate the secret spend key "x_change" for the change output of the CONVERT TX - crypto::secret_key sk_spend = crypto::null_skey; - CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(derivation_P_change_tx, od.output_index, spend_skey, sk_spend), false, "Failed to derive secret key for P_change"); - - // 3.5 Handle subaddresses - if (!received_index.is_zero()) { - crypto::secret_key scalar_step3; - hwdev.sc_secret_add(scalar_step3, sk_spend, subaddr_sk); - sk_spend = scalar_step3; - } - - // 4. Derive the public key from the secret key for verification purposes - crypto::public_key change_pk; - CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(sk_spend, change_pk), false, "Failed to derive public key for P_change"); - CHECK_AND_ASSERT_MES(P_change == change_pk, false, "derived P_change public key does not match P_change"); - - // 5. Calculate the secret spend key "x_return" - if (od.tx_type == cryptonote::transaction_type::CONVERT || od.tx_type == cryptonote::transaction_type::STAKE || od.tx_type == cryptonote::transaction_type::AUDIT) { - CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(recv_derivation, 0, sk_spend, scalar_step1), false, "Failed to derive one-time output secret key 'x_return'"); - } else { - CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(recv_derivation, real_output_index, sk_spend, scalar_step1), false, "Failed to derive one-time output secret key 'x_return'"); - } - in_ephemeral.sec = scalar_step1; - CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub), false, "Failed to derive one-time output public key 'P_return'"); - CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key, - false, "key image helper precomp: given output pubkey doesn't match the derived one"); - - // 6. Create the key_image needed to be able to spend the output - hwdev.generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); - - // Update the SID to have the correct derivation for P_change as well - sid.aR_stake = derivation_P_change_tx; - sid.i_stake = od.output_index; - - return true; - - } else { - - // Not really anything to do here except throw an exception - CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key, - false, "key image helper precomp: given output pubkey doesn't match the derived one"); - } - } - CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key, false, "key image helper precomp: given output pubkey doesn't match the derived one"); } diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 7f38bd0ee..5652939fc 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -105,8 +105,8 @@ namespace cryptonote bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered); bool get_tx_fee(const transaction& tx, uint64_t & fee); uint64_t get_tx_fee(const transaction& tx); - bool generate_key_image_helper(const account_keys& ack, const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid); - bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev, const bool use_origin_data, const origin_data& od, rct::salvium_input_data_t& sid); + bool generate_key_image_helper(const account_keys& ack, const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev); + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev); void get_blob_hash(const blobdata& blob, crypto::hash& res); void get_blob_hash(const blobdata_ref& blob, crypto::hash& res); crypto::hash get_blob_hash(const blobdata& blob); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index d0fa1c62c..709832bd3 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -242,6 +242,7 @@ #define HF_VERSION_AUDIT2 8 #define HF_VERSION_AUDIT2_PAUSE 9 #define HF_VERSION_TREASURY_SAL1_MINT 10 +#define HF_VERSION_CARROT 11 #define HF_VERSION_REQUIRE_VIEW_TAGS 255 #define HF_VERSION_ENABLE_CONVERT 255 diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 7e211b9fb..598aa4f46 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -787,7 +787,7 @@ namespace cryptonote const auto& out_key = reinterpret_cast(src_entr.outputs[src_entr.real_output].second.dest); bool use_origin_data = (src_entr.origin_tx_data.tx_type != cryptonote::transaction_type::UNSET); sid.origin_tx_type = src_entr.origin_tx_data.tx_type; - if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral,img, hwdev, use_origin_data, src_entr.origin_tx_data, sid)) + if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral,img, hwdev)) { LOG_ERROR("Key image generation failed!"); return false; diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index 70f734b35..ecf91f49b 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -101,7 +101,7 @@ namespace multisig // - later, we add in the components held by other participants cryptonote::keypair in_ephemeral; - if (!cryptonote::generate_key_image_helper(keys, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, in_ephemeral, ki, keys.get_device(), use_origin_data, origin_tx_data, sid)) + if (!cryptonote::generate_key_image_helper(keys, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, in_ephemeral, ki, keys.get_device())) return false; std::unordered_set used; diff --git a/src/multisig/multisig_tx_builder_ringct.cpp b/src/multisig/multisig_tx_builder_ringct.cpp index f9a510a69..4a211a8b8 100644 --- a/src/multisig/multisig_tx_builder_ringct.cpp +++ b/src/multisig/multisig_tx_builder_ringct.cpp @@ -186,10 +186,7 @@ static bool compute_keys_for_sources( src.real_output_in_tx_index, tmp_keys, tmp_key_image, - hwdev, - use_origin_data, - src.origin_tx_data, - sid + hwdev )) { return false; } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index ff3ebf502..17b390c59 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -318,7 +318,8 @@ namespace rct { RCTTypeCLSAG = 5, RCTTypeBulletproofPlus = 6, RCTTypeFullProofs = 7, - RCTTypeSalviumOne = 8 + RCTTypeSalviumOne = 8, + RCTTypeFcmpPlusPlus = 9 }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; struct RCTConfig { @@ -822,6 +823,7 @@ namespace rct { static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } static inline const rct::key &ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; } static inline const rct::key &hash2rct(const crypto::hash &h) { return (const rct::key&)h; } + static inline const rct::key &pt2rct(const crypto::ec_point &pt) { return (const rct::key&)pt; } static inline const crypto::public_key &rct2pk(const rct::key &k) { return (const crypto::public_key&)k; } static inline const crypto::secret_key &rct2sk(const rct::key &k) { return (const crypto::secret_key&)k; } static inline const crypto::key_image &rct2ki(const rct::key &k) { return (const crypto::key_image&)k; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 25a04f9de..60bc1e930 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -119,11 +119,11 @@ typedef cryptonote::simple_wallet sw; m_idle_cond.notify_one(); \ }) -#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \ + #define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \ LOCK_IDLE_SCOPE(); \ boost::optional pwd_container = boost::none; \ if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \ - tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); + tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container ? &pwd_container->password() : nullptr); #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) diff --git a/src/wallet/scanning_tools.cpp b/src/wallet/scanning_tools.cpp index a3e63174b..b9adb61b8 100644 --- a/src/wallet/scanning_tools.cpp +++ b/src/wallet/scanning_tools.cpp @@ -572,7 +572,7 @@ void view_incoming_scan_transaction( // 3. view-incoming scan output enotes for (size_t local_output_index = 0; local_output_index < n_outputs; ++local_output_index) { - auto &enote_scan_info = enote_scan_infos_out[local_output_index]; + auto &enote_scan_info = const_cast&>(enote_scan_infos_out[local_output_index]); const cryptonote::tx_out &enote_destination = tx.vout.at(local_output_index); diff --git a/src/wallet/scanning_tools.h b/src/wallet/scanning_tools.h index 6f4c0b82e..eb0a541e5 100644 --- a/src/wallet/scanning_tools.h +++ b/src/wallet/scanning_tools.h @@ -67,6 +67,8 @@ struct enote_view_incoming_scan_info_t rct::xmr_amount amount; // z rct::key amount_blinding_factor; + // asset type + std::string asset_type; // legacy: 8 k_v R, carrot: s^ctx_sr crypto::key_derivation derivation; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 693cfcb1d..6ce724b4f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1098,8 +1098,8 @@ uint64_t gamma_picker::pick() }; boost::mutex wallet_keys_unlocker::lockers_lock; -unsigned int wallet_keys_unlocker::lockers = 0; -wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional &password): +std::map wallet_keys_unlocker::lockers_per_wallet = {}; +wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const epee::wipeable_string *password): w(w), can_relock(true) { @@ -1108,40 +1108,34 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optionalpassword(); - w.generate_chacha_key_from_password(pass, key); - w.decrypt_keys(key); -} -wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password): - w(w), - locked(locked) -{ boost::lock_guard lock(lockers_lock); - if (lockers++ > 0) - locked = false; - if (!locked) + if (lockers_per_wallet[std::addressof(w)]++ > 0) return; - w.generate_chacha_key_from_password(password, key); + w.generate_chacha_key_from_password(*password, key); w.decrypt_keys(key); } wallet_keys_unlocker::~wallet_keys_unlocker() { + if (!can_relock) + return; + try { boost::lock_guard lock(lockers_lock); - if (lockers == 0) + wallet2* w_ptr = std::addressof(w); + if (lockers_per_wallet[w_ptr] == 0) { MERROR("There are no lockers in wallet_keys_unlocker dtor"); return; } - --lockers; - if (!locked) - return; + if (--lockers_per_wallet[w_ptr] > 0) + return; // there are other unlock-ers for this wallet, do nothing for now + lockers_per_wallet.erase(w_ptr); w.encrypt_keys(key); } catch (...) @@ -2397,6 +2391,7 @@ void wallet2::process_new_transaction( const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, + const std::vector &asset_type_o_indices, const uint64_t height, const uint8_t block_version, const uint64_t ts, @@ -2529,7 +2524,7 @@ void wallet2::process_new_scanned_transaction( const rct::xmr_amount extra_received_money = enote_scan_info->amount - (burning_td ? burning_td->amount() : 0); const cryptonote::subaddress_index subaddr_index_cn{ enote_scan_info->subaddr_index->index.major, enote_scan_info->subaddr_index->index.minor}; - tx_money_got_in_outs[subaddr_index_cn] += extra_received_money; + tx_money_got_in_outs[subaddr_index_cn][enote_scan_info->asset_type] += extra_received_money; tx_amounts_individual_outs[subaddr_index_cn].push_back(extra_received_money); // set payment id @@ -2609,7 +2604,7 @@ void wallet2::process_new_scanned_transaction( // money received callbacks LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (!ignore_callbacks && 0 != m_callback) - m_callback->on_money_received(height, txid, tx, td.m_amount, 0, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); + m_callback->on_money_received(height, txid, tx, td.m_amount, td.asset_type, 0, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time, td.m_td_origin_idx); } THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs.size() != tx_amounts_individual_outs.size(), error::wallet_internal_error, "Inconsistent size of output arrays"); @@ -2739,12 +2734,10 @@ void wallet2::process_new_scanned_transaction( { if (subaddr_account && i->first.major == *subaddr_account) { - // sub_change += i->second; // tx_amounts_individual_outs.erase(i->first); // i = tx_money_got_in_outs.erase(i); // delete individual change asset output for (auto assets_it = i->second.begin(); assets_it != i->second.end();) { - sub_change += i->second[source_asset]; if (assets_it->first == source_asset) assets_it = i->second.erase(assets_it); else @@ -2764,70 +2757,33 @@ void wallet2::process_new_scanned_transaction( if (tx_money_got_in_outs.size() > 0) { bool all_same = true; - std::vector temp_payments; - for (auto& tsi : tx_scan_info) { - if (!tsi.received) continue; - cryptonote::subaddress_index si = tsi.received->index; - bool updated_payment = false; - for (auto& payment : temp_payments) { - if (payment.m_subaddr_index == si && payment.m_unlock_time == tsi.unlock_time) { - // Add to existing payment - payment.m_amount += tsi.amount; - payment.m_amounts.push_back(tsi.amount); - updated_payment = true; + for (const auto& i : tx_money_got_in_outs) + { + for (const auto& j : i.second) + { + payment_details payment; + payment.m_tx_hash = txid; + payment.m_fee = fee; + payment.m_asset_type = j.first; + payment.m_amount = j.second; + payment.m_amounts = tx_amounts_individual_outs[i.first]; + payment.m_block_height = height; + payment.m_unlock_time = tx.unlock_time; + payment.m_timestamp = ts; + payment.m_coinbase = miner_tx; + payment.m_subaddr_index = i.first; + if (pool) { + if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen})) + all_same = false; + if (0 != m_callback) + m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index); } - if (updated_payment) break; - } - if (updated_payment) continue; - - // Create a new payment - temp_payments.push_back(payment_details{}); - payment_details& payment = temp_payments.back(); - payment.m_tx_hash = txid; - payment.m_fee = fee; - // SRCG - figure out what this needs to be (pretty sure we should never get here with CONVERT!) - payment.m_amount = tsi.amount; - payment.m_asset_type = tsi.asset_type; - payment.m_amounts.push_back(tsi.amount); - payment.m_block_height = height; - payment.m_unlock_time = tsi.unlock_time; - payment.m_timestamp = ts; - payment.m_coinbase = miner_tx; - payment.m_subaddr_index = si; - if (tx.type == cryptonote::transaction_type::MINER) { - - payment.m_unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; // All protocol_tx payments are coinbase - payment.m_tx_type = cryptonote::transaction_type::MINER; - - } else if (tx.type == cryptonote::transaction_type::PROTOCOL) { - - payment.m_unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; // All protocol_tx payments are coinbase - if (td_origin_idx != ((uint64_t)-1)) { - // Get the origin TD information - payment.m_amount = payment.m_amounts.empty() ? 0 : payment.m_amounts[0]; - payment.m_tx_type = m_transfers[td_origin_idx].m_tx.type; - payment.m_fee = m_transfers[td_origin_idx].m_tx.amount_burnt; - } else { - assert(false); - } - - } else { - payment.m_tx_type = tx.type; + else + m_payments.emplace(payment_id, payment); + LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount); } } - // Now iterate over the temp_payment entries - for (auto& payment : temp_payments) { - if (pool) { - if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen})) - all_same = false; - if (0 != m_callback) - m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index); - } - else - m_payments.emplace(payment_id, payment); - LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount); - } // if it's a pool tx and we already had it, don't notify again if (pool && all_same) notify = false; @@ -2961,10 +2917,6 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, output_key_images.remove_prefix(n_outs_in_tx); TIME_MEASURE_FINISH(miner_tx_handle_time); - // Store the current cache offset for later use - size_t protocol_tx_cache_data_offset = tx_cache_data_offset; - ++tx_cache_data_offset; - TIME_MEASURE_START(txs_handle_time); THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); @@ -2989,10 +2941,6 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, } TIME_MEASURE_FINISH(txs_handle_time); - TIME_MEASURE_START(protocol_tx_handle_time); - process_new_transaction(get_transaction_hash(b.protocol_tx), b.protocol_tx, parsed_block.o_indices.indices[1].indices, parsed_block.asset_type_output_indices.indices[1].indices, height, b.major_version, b.timestamp, true, false, false, tx_cache_data[protocol_tx_cache_data_offset], output_tracker_cache); - TIME_MEASURE_FINISH(protocol_tx_handle_time); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); }else @@ -3122,33 +3070,29 @@ void wallet2::process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_ update_pool_state_from_pool_data(res.pool_info_extent == COMMAND_RPC_GET_BLOCKS_FAST::INCREMENTAL, res.removed_pool_txids, added_pool_txs, process_txs, refreshed); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, std::vector &asset_type_output_indices, uint64_t ¤t_height) +void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, std::vector &asset_type_output_indices, uint64_t ¤t_height, std::vector>& process_pool_txs) { cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); req.block_ids = short_chain_history; - MDEBUG("Pulling blocks: start_height " << start_height); - req.prune = true; req.start_height = start_height; req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; + MDEBUG("Pulling blocks: start_height " << start_height); + req.requested_info = (first && !m_background_syncing) ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; if (try_incremental && !m_background_syncing) req.pool_info_since = m_pool_info_query_time; { const boost::lock_guard lock{m_daemon_rpc_mutex}; - uint64_t pre_call_credits = m_rpc_payment_state.credits; - req.client = get_client_signature(); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status)); THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error, "mismatched blocks (" + boost::lexical_cast(res.blocks.size()) + ") and output_indices (" + boost::lexical_cast(res.output_indices.size()) + ") sizes from daemon"); - uint64_t pool_info_cost = res.added_pool_txs.size() * COST_PER_TX + (res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH; - check_rpc_cost("/getblocks.bin", res.credits, pre_call_credits, 1 + res.blocks.size() * COST_PER_BLOCK + pool_info_cost); } blocks_start_height = res.start_height; @@ -3167,14 +3111,14 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh { if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) { - process_pool_info_extent(res, m_process_pool_txs, true); + process_pool_info_extent(res, process_pool_txs, true); } else { // If we did not get any pool info, neither incremental nor the whole pool, we probably talk // to a daemon that does not yet support giving back pool info with the 'getblocks' call, // and we have to update in the "old way" - update_pool_state_by_pool_query(m_process_pool_txs, true); + update_pool_state_by_pool_query(process_pool_txs, true); } } @@ -3203,40 +3147,26 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, //---------------------------------------------------------------------------------------------------- void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added, std::map, size_t> &output_tracker_cache) { - size_t current_index = start_height; blocks_added = 0; THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch"); - THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error); - - //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - //Force processing of genesis transaction - if ((m_blockchain.size() == 1) && (start_height == 0)) { - cryptonote::block genesis; - generate_genesis(genesis); - - if (m_blockchain[0] == get_block_hash(genesis)) { - LOG_PRINT_L2("Processing genesis transaction: " << string_tools::pod_to_hex(get_transaction_hash(genesis.miner_tx))); - std::vector o_indices_genesis = {0}; //genesis transaction output - std::vector a_indices_genesis = {0}; //genesis transaction asset output - process_new_transaction(get_transaction_hash(genesis.miner_tx), genesis.miner_tx, o_indices_genesis, a_indices_genesis, 0, 0, genesis.timestamp, true, false, false, {} ); - } else { - LOG_ERROR("Skip processing of genesis transaction, genesis block hash does not match: " << string_tools::pod_to_hex(get_block_hash(genesis))); - } - } - //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(start_height), error::out_of_hashchain_bounds_error); tools::threadpool& tpool = tools::threadpool::getInstanceForCompute(); size_t num_txes = 0; - for (size_t i = 0; i < blocks.size(); ++i) - num_txes += 2 + parsed_blocks[i].txes.size(); - tx_cache_data.resize(num_txes); - size_t txidx = 0; - for (size_t i = 0; i < blocks.size(); ++i) + size_t num_tx_outputs = 0; + for (const parsed_block &par_blk : parsed_blocks) { - THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(), - error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()"); + num_txes += 1 + par_blk.txes.size(); + num_tx_outputs += par_blk.block.miner_tx.vout.size(); + for (const cryptonote::transaction &tx : par_blk.txes) + num_tx_outputs += tx.vout.size(); + } + crypto::hash prev_block_id; + bool has_prev_block = m_blockchain.is_in_bounds(start_height - 1); + if (has_prev_block) { + prev_block_id = m_blockchain[start_height - 1]; } hw::device &hwdev = m_account.get_device(); @@ -3364,7 +3294,7 @@ void check_block_hard_fork_version(cryptonote::network_type nettype, uint8_t hf_ daemon_is_outdated = height < start_height || height >= end_height; } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_and_parse_next_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception) +void wallet2::pull_and_parse_next_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, std::vector>& process_pool_txs, bool &last, bool &error, std::exception_ptr &exception) { error = false; last = false; @@ -3388,7 +3318,7 @@ void wallet2::pull_and_parse_next_blocks(bool first, bool try_incremental, uint6 std::vector asset_type_output_indices; uint64_t current_height; - pull_blocks(first, try_incremental, start_height, blocks_start_height, short_chain_history, blocks, o_indices, asset_type_output_indices, current_height); + pull_blocks(first, try_incremental, start_height, blocks_start_height, short_chain_history, blocks, o_indices, asset_type_output_indices, current_height, process_pool_txs); THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices"); THROW_WALLET_EXCEPTION_IF(blocks.size() != asset_type_output_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and asset_type_output_indices"); @@ -3815,7 +3745,7 @@ void wallet2::process_pool_state(const std::vector(e); const crypto::hash &tx_hash = std::get<1>(e); const bool double_spend_seen = std::get<2>(e); - process_new_transaction(tx_hash, tx, std::vector(), 0, 0, now, false, true, double_spend_seen); + process_new_transaction(tx_hash, tx, std::vector(), std::vector(), 0, 0, now, false, true, double_spend_seen); m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) { @@ -3964,6 +3894,10 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo uint64_t blocks_start_height; std::vector blocks; std::vector parsed_blocks; + // TODO moneromooo-monero says this about the "refreshed" variable: + // "I had to reorder some code to fix... a timing info leak IIRC. In turn, this undid something I had fixed before, ... a subtle race condition with the txpool. + // It was pretty subtle IIRC, and so I needed time to think about how to refix it after the move, and I never got to it." + // https://github.com/monero-project/monero/pull/6097 bool refreshed = false; std::map, size_t> output_tracker_cache = create_output_tracker_cache(); hw::device &hwdev = m_account.get_device(); @@ -3996,7 +3930,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler([&](){hwdev.computing_key_images(false);}); - m_process_pool_txs.clear(); + std::vector> process_pool_txs; // Getting and processing the pool state has moved down into method 'pull_blocks' to // allow for "conventional" as well as "incremental" update. However the following // principle of getting all info first (pool AND blocks) and only process txs afterwards @@ -4028,7 +3962,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo break; } if (!last) - tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(first, try_incremental, start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, last, error, exception);}); + tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(first, try_incremental, start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, process_pool_txs, last, error, exception);}); if (!first) { @@ -4046,7 +3980,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo cryptonote::block b; generate_genesis(b); m_blockchain.clear(); - m_blockchain.push_back(get_block_hash(b)); + const crypto::hash genesis_hash = get_block_hash(b); + m_blockchain.push_back(genesis_hash); short_chain_history.clear(); get_short_chain_history(short_chain_history); fast_refresh(stop_height, blocks_start_height, short_chain_history, true); @@ -4099,12 +4034,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); throw; } - catch (const error::payment_required&) - { - // no point in trying again, it'd just eat up credits - THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); - throw; - } catch (const error::reorg_depth_error&) { THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); @@ -4144,8 +4073,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo try { // If stop() is called we don't need to check pending transactions - if (check_pool && m_run.load(std::memory_order_relaxed) && !m_process_pool_txs.empty()) - process_pool_state(m_process_pool_txs); + if (check_pool && m_run.load(std::memory_order_relaxed) && !process_pool_txs.empty()) + process_pool_state(process_pool_txs); } catch (...) { @@ -4157,9 +4086,9 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo m_background_sync_data.first_refresh_done = true; LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): "); - for(const auto& asset: m_transfers_indices) { + for(const auto& asset: m_transfers_indices) LOG_PRINT_L1(asset.first << " : balance " << print_money(balance_all(false, asset.first)) << ", unlocked: " << print_money(unlocked_balance_all(false, asset.first))); - } + } //---------------------------------------------------------------------------------------------------- bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok) @@ -6351,15 +6280,12 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer"); } - wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password); + wallet_keys_unlocker unlocker(*this, &password); //keys loaded ok! //try to load wallet cache. but even if we failed, it is not big problem load_wallet_cache(use_fs, cache_buf); - if (!m_persistent_rpc_client_id) - set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); - // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. // Here we erase these multisig keys if they're zero'd out to free up space. for (auto &td : m_transfers) @@ -7825,7 +7751,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx); keypair in_ephemeral; crypto::key_image in_img; - THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device(), false, origin_tx_data, sid), + THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device()), error::wallet_internal_error, "failed to generate key image"); THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch"); @@ -12950,7 +12876,7 @@ std::string wallet2::get_reserve_proof(const boost::optional> // generate ephemeral secret key crypto::key_image ki; cryptonote::keypair in_ephemeral; - bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device(), false, od, sid); + bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); THROW_WALLET_EXCEPTION_IF(td.m_key_image_known && !td.m_key_image_partial && ki != td.m_key_image, @@ -13805,18 +13731,19 @@ uint64_t wallet2::import_key_images(const std::vector tx_money_got_in_outs; const auto enote_scan_infos = wallet::view_incoming_scan_transaction(spent_tx, m_account.get_keys(), m_subaddresses); for (const auto &enote_scan_info : enote_scan_infos) if (enote_scan_info && enote_scan_info->subaddr_index) - tx_money_got_in_outs += enote_scan_info->amount; //! @TODO: check overflow + tx_money_got_in_outs[enote_scan_info->asset_type] += enote_scan_info->amount; //! @TODO: check overflow // get asset types std::string source_asset; std::string dest_asset; crypto::hash txid; + bool miner_tx = cryptonote::is_coinbase(spent_tx); epee::string_tools::hex_to_pod(e.tx_hash, txid); THROW_WALLET_EXCEPTION_IF(!cryptonote::get_tx_asset_types(spent_tx, txid, source_asset, dest_asset, miner_tx), error::wallet_internal_error, "Failed to get asset types"); @@ -14041,7 +13968,7 @@ void wallet2::process_background_cache(const background_sync_data_t &background_ { MDEBUG("Processing background synced tx " << bgs_tx.first); - process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp, + process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.asset_output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp, cryptonote::is_coinbase(bgs_tx.second.tx), false/*pool*/, bgs_tx.second.double_spend_seen, true/*ignore_callbacks*/); // Re-set destination addresses if they were previously set @@ -14532,7 +14459,7 @@ process: crypto::public_key out_key = td.get_public_key(); if (should_expand(td.m_subaddr_index)) create_one_off_subaddress(td.m_subaddr_index); - bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device(), false, od, sid); + bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); if (should_expand(td.m_subaddr_index)) expand_subaddresses(td.m_subaddr_index); @@ -14641,7 +14568,7 @@ size_t wallet2::import_outputs(const std::tuple &password); - wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password); + wallet_keys_unlocker(wallet2 &w, const epee::wipeable_string *password); ~wallet_keys_unlocker(); private: wallet2 &w; - bool locked; + bool can_relock; crypto::chacha_key key; static boost::mutex lockers_lock; - static unsigned int lockers; + static std::map lockers_per_wallet; }; class i_wallet2_callback @@ -847,6 +846,7 @@ private: uint64_t index_in_background_sync_data; cryptonote::transaction tx; std::vector output_indices; + std::vector asset_output_indices; uint64_t height; uint64_t block_timestamp; bool double_spend_seen; @@ -1878,6 +1878,7 @@ private: const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, + const std::vector &asset_type_o_indices, const uint64_t height, const uint8_t block_version, const uint64_t ts, @@ -1923,7 +1924,7 @@ private: * that this function deletes data that is not useful for background syncing */ void clear_user_data(); - void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, std::vector &asset_type_output_indices, uint64_t ¤t_height); + void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, std::vector &asset_type_output_indices, uint64_t ¤t_height, std::vector>& process_pool_txs); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); void pull_and_parse_next_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, std::vector>& process_pool_txs, bool &last, bool &error, std::exception_ptr &exception); @@ -2083,7 +2084,6 @@ private: // m_refresh_from_block_height was defaulted to zero.*/ bool m_explicit_refresh_from_block_height; uint64_t m_pool_info_query_time; - std::vector> m_process_pool_txs; uint64_t m_skip_to_height; // m_skip_to_height is useful when we don't want to modify the wallet's restore height. // m_refresh_from_block_height is also a wallet's restore height which should remain constant unless explicitly modified by the user.