diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 77a36069a..db4f87f6e 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -261,6 +261,67 @@ namespace crypto { return true; } + /** + * The following functions are designed to perform the correct encoding / decoding for protocol_tx outputs, + * which use a hash of a crypto::key_image for uniqueness + */ + void crypto_ops::derivation_to_scalar(const key_derivation &derivation, const hash& uniqueness, ec_scalar &res) { + struct { + hash uniqueness; + key_derivation derivation; + } buf; + buf.uniqueness = uniqueness; + buf.derivation = derivation; + hash_to_scalar(&buf, sizeof(buf), res); + } + + bool crypto_ops::derive_public_key(const key_derivation &derivation, const hash& uniqueness, + const public_key &base, public_key &derived_key) { + ec_scalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, &base) != 0) { + return false; + } + derivation_to_scalar(derivation, uniqueness, scalar); + ge_scalarmult_base(&point2, &scalar); + ge_p3_to_cached(&point3, &point2); + ge_add(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(&derived_key, &point5); + return true; + } + + void crypto_ops::derive_secret_key(const key_derivation &derivation, const hash& uniqueness, + const secret_key &base, secret_key &derived_key) { + ec_scalar scalar; + assert(sc_check(&base) == 0); + derivation_to_scalar(derivation, uniqueness, scalar); + sc_add(&unwrap(derived_key), &unwrap(base), &scalar); + } + + bool crypto_ops::derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, const hash& uniqueness, public_key &derived_key) { + ec_scalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, &out_key) != 0) { + return false; + } + derivation_to_scalar(derivation, uniqueness, scalar); + ge_scalarmult_base(&point2, &scalar); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(&derived_key, &point5); + return true; + } + struct s_comm { hash h; ec_point key; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index d8cd6c6a0..987daed1f 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -135,6 +135,20 @@ namespace crypto { friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); static bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &); friend bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &); + + /** + * The following functions are designed to perform the correct encoding / decoding for protocol_tx outputs, + * which use a hash of a crypto::key_image for uniqueness + */ + static void derivation_to_scalar(const key_derivation &derivation, const hash& uniqueness, ec_scalar &res); + friend void derivation_to_scalar(const key_derivation &derivation, const hash& uniqueness, ec_scalar &res); + static bool derive_public_key(const key_derivation &, const hash&, const public_key &, public_key &); + friend bool derive_public_key(const key_derivation &, const hash&, const public_key &, public_key &); + static void derive_secret_key(const key_derivation &, const hash&, const secret_key &, secret_key &); + friend void derive_secret_key(const key_derivation &, const hash&, const secret_key &, secret_key &); + static bool derive_subaddress_public_key(const public_key &, const key_derivation &, const hash&, public_key &); + friend bool derive_subaddress_public_key(const public_key &, const key_derivation &, const hash&, public_key &); + static void generate_signature(const hash &, const public_key &, const secret_key &, signature &); friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &); static bool check_signature(const hash &, const public_key &, const signature &); @@ -245,6 +259,25 @@ namespace crypto { return crypto_ops::derive_subaddress_public_key(out_key, derivation, output_index, result); } + /** + * The following functions are designed to perform the correct encoding / decoding for protocol_tx outputs, + * which use a hash of a crypto::key_image for uniqueness + */ + inline bool derive_public_key(const key_derivation &derivation, const hash& uniqueness, + const public_key &base, public_key &derived_key) { + return crypto_ops::derive_public_key(derivation, uniqueness, base, derived_key); + } + inline void derivation_to_scalar(const key_derivation &derivation, const hash& uniqueness, ec_scalar &res) { + return crypto_ops::derivation_to_scalar(derivation, uniqueness, res); + } + inline void derive_secret_key(const key_derivation &derivation, const hash& uniqueness, + const secret_key &base, secret_key &derived_key) { + crypto_ops::derive_secret_key(derivation, uniqueness, base, derived_key); + } + inline bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, const hash& uniqueness, public_key &result) { + return crypto_ops::derive_subaddress_public_key(out_key, derivation, uniqueness, result); + } + /* Generation and checking of a standard signature. */ inline void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { diff --git a/src/crypto/wallet/crypto.h b/src/crypto/wallet/crypto.h index cee0ca18e..c782ec59f 100644 --- a/src/crypto/wallet/crypto.h +++ b/src/crypto/wallet/crypto.h @@ -43,10 +43,10 @@ namespace crypto { } inline - bool derive_subaddress_public_key(const public_key &output_pub, const key_derivation &d, std::size_t index, public_key &out) + bool derive_subaddress_public_key(const public_key &output_pub, const key_derivation &d, const crypto::hash& uniqueness, public_key &out) { ec_scalar scalar; - derivation_to_scalar(d, index, scalar); + derivation_to_scalar(d, uniqueness, scalar); return monero_crypto_generate_subaddress_public_key(out.data, output_pub.data, scalar.data) == 0; } #else diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index b470f329f..27dcde201 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -289,7 +289,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) + 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, const crypto::hash& uniqueness, 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,10 +317,10 @@ 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"); - return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev); + return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, uniqueness, 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) + 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 crypto::hash& uniqueness, 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)) { @@ -355,8 +355,9 @@ namespace cryptonote } } - // computes Hs(a*R || idx) + b - hwdev.derive_secret_key(recv_derivation, real_output_index, spend_skey, scalar_step1); + // computes Hs(a*R || uniqueness) + b + //crypto::hash uniqueness = cn_fast_hash(reinterpret_cast(&real_output_index), sizeof(size_t)); + hwdev.derive_secret_key(recv_derivation, uniqueness, spend_skey, scalar_step1); // step 2: add Hs(a || index_major || index_minor) crypto::secret_key subaddr_sk; @@ -381,7 +382,7 @@ namespace cryptonote else { // when in multisig, we only know the partial spend secret key. but we do know the full spend public key, so the output pubkey can be obtained by using the standard CN key derivation - CHECK_AND_ASSERT_MES(hwdev.derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub), false, "Failed to derive public key"); + CHECK_AND_ASSERT_MES(hwdev.derive_public_key(recv_derivation, uniqueness, ack.m_account_address.m_spend_public_key, in_ephemeral.pub), false, "Failed to derive public key"); // and don't forget to add the contribution from the subaddress part if (!received_index.is_zero()) { @@ -1234,14 +1235,27 @@ namespace cryptonote //--------------------------------------------------------------- bool is_out_to_acc(const account_keys& acc, const crypto::public_key& output_public_key, const crypto::public_key& tx_pub_key, const std::vector& additional_tx_pub_keys, size_t output_index, const boost::optional& view_tag_opt) { + // Calculate the uniqueness + crypto::hash uniqueness = cn_fast_hash(reinterpret_cast(&output_index), sizeof(size_t)); + crypto::key_derivation derivation; bool r = acc.get_device().generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); + + LOG_ERROR("*****************************************************************************"); + LOG_ERROR("derivation: " << derivation); + LOG_ERROR("uniqueness: " << uniqueness); + LOG_ERROR("txkey_pub : " << tx_pub_key); + crypto::public_key pk; if (out_can_be_to_acc(view_tag_opt, derivation, output_index, &acc.get_device())) { - r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + r = acc.get_device().derive_public_key(derivation, uniqueness, acc.m_account_address.m_spend_public_key, pk); CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key"); + + LOG_ERROR("output_key: " << pk); + LOG_ERROR("*****************************************************************************"); + if (pk == output_public_key) return true; } @@ -1254,7 +1268,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); if (out_can_be_to_acc(view_tag_opt, derivation, output_index, &acc.get_device())) { - r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + r = acc.get_device().derive_public_key(derivation, uniqueness, acc.m_account_address.m_spend_public_key, pk); CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key"); return pk == output_public_key; } @@ -1264,11 +1278,14 @@ namespace cryptonote //--------------------------------------------------------------- boost::optional is_out_to_acc_precomp(const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector& additional_derivations, size_t output_index, hw::device &hwdev, const boost::optional& view_tag_opt) { + // Calculate the uniqueness + crypto::hash uniqueness = cn_fast_hash(reinterpret_cast(&output_index), sizeof(size_t)); + // try the shared tx pubkey crypto::public_key subaddress_spendkey; if (out_can_be_to_acc(view_tag_opt, derivation, output_index, &hwdev)) { - CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); + CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, derivation, uniqueness, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); auto found = subaddresses.find(subaddress_spendkey); if (found != subaddresses.end()) return subaddress_receive_info{ found->second, derivation }; @@ -1280,7 +1297,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations"); if (out_can_be_to_acc(view_tag_opt, additional_derivations[output_index], output_index, &hwdev)) { - CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); + CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], uniqueness, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); auto found = subaddresses.find(subaddress_spendkey); if (found != subaddresses.end()) return subaddress_receive_info{ found->second, additional_derivations[output_index] }; @@ -1289,6 +1306,39 @@ namespace cryptonote return boost::none; } //--------------------------------------------------------------- + boost::optional is_out_to_acc_precomp(const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector& additional_derivations, const crypto::hash& uniqueness, hw::device &hwdev, const boost::optional& view_tag_opt) + { + // try the shared tx pubkey + crypto::public_key subaddress_spendkey; + // Cannot check view tags for protocol_tx outputs + //if (out_can_be_to_acc(view_tag_opt, derivation, output_index, &hwdev)) + { + CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, derivation, uniqueness, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); + auto found = subaddresses.find(subaddress_spendkey); + if (found != subaddresses.end()) + return subaddress_receive_info{ found->second, derivation }; + } + + /** + * additional_derivations are NOT supported, because there can only be ONE output on a CONVERT or YIELD TX + */ + /* + // try additional tx pubkeys if available + if (!additional_derivations.empty()) + { + CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations"); + //if (out_can_be_to_acc(view_tag_opt, additional_derivations[output_index], output_index, &hwdev)) + { + CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], ki, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); + auto found = subaddresses.find(subaddress_spendkey); + if (found != subaddresses.end()) + return subaddress_receive_info{ found->second, additional_derivations[output_index] }; + } + } + */ + return boost::none; + } + //--------------------------------------------------------------- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered) { crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index ab843a0d1..1de72e676 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -99,12 +99,13 @@ namespace cryptonote crypto::key_derivation derivation; }; boost::optional is_out_to_acc_precomp(const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector& additional_derivations, size_t output_index, hw::device &hwdev, const boost::optional& view_tag_opt = boost::optional()); + boost::optional is_out_to_acc_precomp(const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector& additional_derivations, const crypto::key_image& ki, hw::device &hwdev, const boost::optional& view_tag_opt = boost::optional()); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector& additional_tx_public_keys, std::vector& outs, uint64_t& money_transfered); 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); - 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); + 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, const crypto::hash& uniqueness, 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 crypto::hash& uniqueness, 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_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 2e663edf4..d613d2df8 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -285,9 +285,19 @@ namespace cryptonote 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 << ")"); - r = crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key); + // Calculate the uniqueness + size_t output_index = 0; + crypto::hash uniqueness = cn_fast_hash(reinterpret_cast(&output_index), sizeof(size_t)); + r = crypto::derive_public_key(derivation, /*output_index*/uniqueness, 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 << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")"); + LOG_ERROR("*****************************************************************************"); + LOG_ERROR("derivation: " << derivation); + LOG_ERROR("uniqueness: " << uniqueness); + LOG_ERROR("txkey_pub : " << txkey.pub); + LOG_ERROR("output_key: " << out_eph_public_key); + LOG_ERROR("*****************************************************************************"); + uint64_t amount = block_reward; summary_amounts += amount; @@ -338,6 +348,28 @@ namespace cryptonote return addr.m_view_public_key; } //--------------------------------------------------------------- + bool get_protocol_destination_address(const size_t tx_version, const crypto::key_image& ki, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, crypto::public_key& tx_destination_address, hw::device& hwdev) { + + // With a protocol destination address, you are always sending the payment to yourself; derivation = a*R + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + bool r = hwdev.generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "at get_protocol_destination_address: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")"); + + crypto::hash uniqueness = cn_fast_hash(&ki.data[0], 32); + r = hwdev.derive_public_key(derivation, uniqueness, sender_account_keys.m_account_address.m_spend_public_key, tx_destination_address); + CHECK_AND_ASSERT_MES(r, false, "at get_protocol_destination_address: failed to derive_public_key()"); + + LOG_ERROR("*****************************************************************************"); + LOG_ERROR("derivation: " << derivation); + LOG_ERROR("key_image : " << ki); + LOG_ERROR("uniqueness: " << uniqueness); + LOG_ERROR("txkey_pub : " << txkey_pub); + LOG_ERROR("tx_address: " << tx_destination_address); + LOG_ERROR("*****************************************************************************"); + + return true; + } + //--------------------------------------------------------------- bool get_tx_type(const std::string& source, const std::string& destination, transaction_type& type) { // check both source and destination are supported. @@ -411,15 +443,6 @@ namespace cryptonote tx.type = tx_type; - // Is this a CONVERT tx? - if (tx_type == cryptonote::transaction_type::CONVERT) { - // Set the destination address to be something only our wallet can identify - // This is where Fulmo gets interesting... we need to include the input key images - // so that we get uniqueness and prevent either Monero burning bug or key leakage. - // tx.d_a = Hs("convert" || input_key_images || 8rAG) + B - tx.destination_address = get_destination_view_key_pub(destinations, change_addr); - } - // Set the source and destination asset_type values tx.source_asset_type = source_asset; tx.destination_asset_type = dest_asset; @@ -523,8 +546,13 @@ namespace cryptonote in_contexts.push_back(input_generation_context_data()); keypair& in_ephemeral = in_contexts.back().in_ephemeral; crypto::key_image img; + + // Calculate the uniqueness + size_t output_index_wrapper = src_entr.real_output_in_tx_index; + crypto::hash uniqueness = cn_fast_hash(reinterpret_cast(&output_index_wrapper), sizeof(size_t)); + const auto& out_key = reinterpret_cast(src_entr.outputs[src_entr.real_output].second.dest); - 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)) + 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, uniqueness, in_ephemeral,img, hwdev)) { LOG_ERROR("Key image generation failed!"); return false; @@ -602,6 +630,16 @@ namespace cryptonote if (need_additional_txkeys) CHECK_AND_ASSERT_MES(destinations.size() == additional_tx_keys.size(), false, "Wrong amount of additional tx keys"); + // Is this a CONVERT tx? + if (tx_type == cryptonote::transaction_type::CONVERT) { + // Set the destination address to be something only our wallet can identify + // This is where Fulmo gets interesting... we need to include the input key images + // so that we get uniqueness and prevent either Monero burning bug or key leakage. + // tx.d_a = Hs("convert" || input_key_images || 8rAG) + B + const txin_to_key &in = boost::get(tx.vin[0]); + CHECK_AND_ASSERT_MES(get_protocol_destination_address(tx.version, in.k_image, sender_account_keys, txkey_pub, tx_key, tx.destination_address, hwdev), false, "Failed to get protocol destination address"); + } + uint64_t summary_outs_money = 0; //fill outputs size_t output_index = 0; @@ -611,11 +649,14 @@ namespace cryptonote crypto::public_key out_eph_public_key; crypto::view_tag view_tag; + // Calculate the uniqueness + crypto::hash uniqueness = cn_fast_hash(reinterpret_cast(&output_index), sizeof(size_t)); + hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key, dst_entr, change_addr, output_index, need_additional_txkeys, additional_tx_keys, additional_tx_public_keys, amount_keys, out_eph_public_key, - use_view_tags, view_tag); + use_view_tags, view_tag, uniqueness); // Is this a BURN or CONVERT TX? if (tx_type == cryptonote::transaction_type::BURN || tx_type == cryptonote::transaction_type::CONVERT) { @@ -630,7 +671,7 @@ namespace cryptonote } } tx_out out; - cryptonote::set_tx_out(dst_entr.amount, dst_entr.asset_type, unlock_time, out_eph_public_key, use_view_tags, view_tag, out); + cryptonote::set_tx_out(dst_entr.amount, dst_entr.asset_type, dst_entr.is_change ? 0 : unlock_time, out_eph_public_key, use_view_tags, view_tag, out); tx.vout.push_back(out); output_index++; summary_outs_money += dst_entr.amount; diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 0f3d50244..d83c33ace 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -109,9 +109,10 @@ namespace cryptonote std::string asset_type; bool is_subaddress; bool is_integrated; + bool is_change; - tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), asset_type("FULM"), is_subaddress(false), is_integrated(false) { } - tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { } + tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), asset_type("FULM"), is_subaddress(false), is_integrated(false), is_change(false) { } + tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false), is_change(false) { } tx_destination_entry(const std::string &o, uint64_t a, const account_public_address &ad, bool is_subaddress) : original(o), amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { } std::string address(network_type nettype, const crypto::hash &payment_id) const @@ -136,6 +137,7 @@ namespace cryptonote FIELD(asset_type) FIELD(is_subaddress) FIELD(is_integrated) + FIELD(is_change) END_SERIALIZE() }; diff --git a/src/device/device.hpp b/src/device/device.hpp index 392703a24..b52baa9f2 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -156,7 +156,7 @@ namespace hw { /* ======================================================================= */ /* SUB ADDRESS */ /* ======================================================================= */ - virtual bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_pub) = 0; + virtual bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::public_key &derived_pub) = 0; virtual crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index) = 0; virtual std::vector get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end) = 0; virtual cryptonote::account_public_address get_subaddress(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) = 0; @@ -172,9 +172,9 @@ namespace hw { virtual crypto::secret_key generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false) = 0; virtual bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) = 0; virtual bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector &additional_derivations) = 0; - virtual bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) = 0; - virtual bool derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) = 0; - virtual bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) = 0; + virtual bool derivation_to_scalar(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::ec_scalar &res) = 0; + virtual bool derive_secret_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::secret_key &sec, crypto::secret_key &derived_sec) = 0; + virtual bool derive_public_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::public_key &pub, crypto::public_key &derived_pub) = 0; virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) = 0; virtual bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) = 0; virtual bool derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag) = 0; @@ -224,7 +224,8 @@ namespace hw { std::vector &additional_tx_public_keys, std::vector &amount_keys, crypto::public_key &out_eph_public_key, - const bool use_view_tags, crypto::view_tag &view_tag) = 0; + const bool use_view_tags, crypto::view_tag &view_tag, + const crypto::hash& uniqueness) = 0; virtual bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) = 0; virtual bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) = 0; diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index d70ece229..b1c84d097 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -120,8 +120,8 @@ namespace hw { /* SUB ADDRESS */ /* ======================================================================= */ - bool device_default::derive_subaddress_public_key(const crypto::public_key &out_key, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_key) { - return crypto::wallet::derive_subaddress_public_key(out_key, derivation, output_index,derived_key); + bool device_default::derive_subaddress_public_key(const crypto::public_key &out_key, const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::public_key &derived_key) { + return crypto::wallet::derive_subaddress_public_key(out_key, derivation, uniqueness, derived_key); } crypto::public_key device_default::get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) { @@ -240,18 +240,18 @@ namespace hw { return crypto::wallet::generate_key_derivation(key1, key2, derivation); } - bool device_default::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res){ - crypto::derivation_to_scalar(derivation,output_index, res); + bool device_default::derivation_to_scalar(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::ec_scalar &res){ + crypto::derivation_to_scalar(derivation, uniqueness, res); return true; } - bool device_default::derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &base, crypto::secret_key &derived_key){ - crypto::derive_secret_key(derivation, output_index, base, derived_key); + bool device_default::derive_secret_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::secret_key &base, crypto::secret_key &derived_key){ + crypto::derive_secret_key(derivation, uniqueness, base, derived_key); return true; } - bool device_default::derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &base, crypto::public_key &derived_key){ - return crypto::derive_public_key(derivation, output_index, base, derived_key); + bool device_default::derive_public_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::public_key &base, crypto::public_key &derived_key){ + return crypto::derive_public_key(derivation, uniqueness, base, derived_key); } bool device_default::secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) { @@ -297,7 +297,8 @@ namespace hw { const bool &need_additional_txkeys, const std::vector &additional_tx_keys, std::vector &additional_tx_public_keys, std::vector &amount_keys, crypto::public_key &out_eph_public_key, - const bool use_view_tags, crypto::view_tag &view_tag) { + const bool use_view_tags, crypto::view_tag &view_tag, + const crypto::hash& uniqueness) { crypto::key_derivation derivation; @@ -334,7 +335,7 @@ namespace hw { if (tx_version > 1) { crypto::secret_key scalar1; - derivation_to_scalar(derivation, output_index, scalar1); + derivation_to_scalar(derivation, uniqueness, scalar1); amount_keys.push_back(rct::sk2rct(scalar1)); } @@ -343,8 +344,8 @@ namespace hw { derive_view_tag(derivation, output_index, view_tag); } - r = derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + r = derive_public_key(derivation, uniqueness, dst_entr.addr.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << uniqueness << ", "<< dst_entr.addr.m_spend_public_key << ")"); return r; } diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 58149cdbf..fb7875d92 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -80,7 +80,7 @@ namespace hw { /* ======================================================================= */ /* SUB ADDRESS */ /* ======================================================================= */ - bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_pub) override; + bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::public_key &derived_pub) override; crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index) override; std::vector get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end) override; cryptonote::account_public_address get_subaddress(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) override; @@ -96,9 +96,9 @@ namespace hw { crypto::secret_key generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false) override; bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) override; bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector &additional_derivations) override; - bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) override; - bool derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) override; - bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override; + bool derivation_to_scalar(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::ec_scalar &res) override; + bool derive_secret_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::secret_key &sec, crypto::secret_key &derived_sec) override; + bool derive_public_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::public_key &pub, crypto::public_key &derived_pub) override; bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override; bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override; bool derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag) override; @@ -128,7 +128,8 @@ namespace hw { std::vector &additional_tx_public_keys, std::vector &amount_keys, crypto::public_key &out_eph_public_key, - bool use_view_tags, crypto::view_tag &view_tag) override; + bool use_view_tags, crypto::view_tag &view_tag, + const crypto::hash& uniqueness) override; bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override; bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override; diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index a4b5f3ef0..c92bbc758 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -682,7 +682,7 @@ namespace hw { /* SUB ADDRESS */ /* ======================================================================= */ - bool device_ledger::derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_pub){ + bool device_ledger::derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::public_key &derived_pub){ #ifdef DEBUG_HWDEVICE const crypto::public_key pub_x = pub; crypto::key_derivation derivation_x; @@ -691,12 +691,13 @@ namespace hw { } else { derivation_x = hw::ledger::decrypt(derivation); } + const crypto::hash uniqueness_x = uniqueness; const std::size_t output_index_x = output_index; crypto::public_key derived_pub_x; log_hexbuffer("derive_subaddress_public_key: [[IN]] pub ", pub_x.data, 32); log_hexbuffer("derive_subaddress_public_key: [[IN]] derivation", derivation_x.data, 32); - log_message ("derive_subaddress_public_key: [[IN]] index ", std::to_string((int)output_index_x)); - if (!this->controle_device->derive_subaddress_public_key(pub_x, derivation_x,output_index_x,derived_pub_x)) + log_message ("derive_subaddress_public_key: [[IN]] uniqueness", uniqueness_x.data, 32); + if (!this->controle_device->derive_subaddress_public_key(pub_x, derivation_x, uniqueness_x, derived_pub_x)) return false; log_hexbuffer("derive_subaddress_public_key: [[OUT]] derived_pub", derived_pub_x.data, 32); #endif @@ -705,7 +706,7 @@ namespace hw { //If we are in TRANSACTION_PARSE, the given derivation has been retrieved uncrypted (wihtout the help //of the device), so continue that way. MDEBUG( "derive_subaddress_public_key : PARSE mode with known viewkey"); - if (!crypto::derive_subaddress_public_key(pub, derivation, output_index,derived_pub)) + if (!crypto::derive_subaddress_public_key(pub, derivation, uniqueness, derived_pub)) return false; } else { AUTO_LOCK_CMD(); @@ -715,14 +716,12 @@ namespace hw { offset += 32; //derivation this->send_secret((unsigned char*)derivation.data, offset); - //index - this->buffer_send[offset+0] = output_index>>24; - this->buffer_send[offset+1] = output_index>>16; - this->buffer_send[offset+2] = output_index>>8; - this->buffer_send[offset+3] = output_index>>0; - offset += 4; - + //uniqueness + memmove(this->buffer_send+offset, uniqueness.data, 32); + offset += 32; + // set message payload size this->buffer_send[4] = offset-5; + // set message length this->length_send = offset; this->exchange(); @@ -1118,16 +1117,16 @@ namespace hw { return this->generate_key_derivation(*pkey, crypto::null_skey, derivation); } - bool device_ledger::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) { + bool device_ledger::derivation_to_scalar(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::ec_scalar &res) { AUTO_LOCK_CMD(); #ifdef DEBUG_HWDEVICE const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); - const size_t output_index_x = output_index; + const crypto::hash uniqueness_x = uniqueness; crypto::ec_scalar res_x; - log_hexbuffer("derivation_to_scalar: [[IN]] derivation ", derivation_x.data, 32); - log_message ("derivation_to_scalar: [[IN]] output_index ", std::to_string(output_index_x)); - this->controle_device->derivation_to_scalar(derivation_x, output_index_x, res_x); + log_hexbuffer("derivation_to_scalar: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derivation_to_scalar: [[IN]] uniqueness ", uniqueness_x.data, 32); + this->controle_device->derivation_to_scalar(derivation_x, uniqueness_x, res_x); log_hexbuffer("derivation_to_scalar: [[OUT]] res ", res_x.data, 32); #endif @@ -1135,12 +1134,9 @@ namespace hw { //derivation this->send_secret((unsigned char*)derivation.data, offset); - //index - this->buffer_send[offset+0] = output_index>>24; - this->buffer_send[offset+1] = output_index>>16; - this->buffer_send[offset+2] = output_index>>8; - this->buffer_send[offset+3] = output_index>>0; - offset += 4; + //uniqueness + memmove(this->buffer_send+offset, uniqueness.data, 32); + offset += 32; this->buffer_send[4] = offset-5; this->length_send = offset; @@ -1158,30 +1154,28 @@ namespace hw { return true; } - bool device_ledger::derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) { + bool device_ledger::derive_secret_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::secret_key &sec, crypto::secret_key &derived_sec) { AUTO_LOCK_CMD(); #ifdef DEBUG_HWDEVICE const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); - const std::size_t output_index_x = output_index; + const crypto::hash uniqueness_x = uniqueness; const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::secret_key derived_sec_x; log_hexbuffer("derive_secret_key: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derive_secret_key: [[IN]] uniqueness ", uniqueness_x.data, 32); log_message ("derive_secret_key: [[IN]] index ", std::to_string(output_index_x)); log_hexbuffer("derive_secret_key: [[IN]] sec ", sec_x.data, 32); - this->controle_device->derive_secret_key(derivation_x, output_index_x, sec_x, derived_sec_x); + this->controle_device->derive_secret_key(derivation_x, uniqueness_x, sec_x, derived_sec_x); log_hexbuffer("derive_secret_key: [[OUT]] derived_sec", derived_sec_x.data, 32); #endif int offset = set_command_header_noopt(INS_DERIVE_SECRET_KEY); //derivation this->send_secret((unsigned char*)derivation.data, offset); - //index - this->buffer_send[offset+0] = output_index>>24; - this->buffer_send[offset+1] = output_index>>16; - this->buffer_send[offset+2] = output_index>>8; - this->buffer_send[offset+3] = output_index>>0; - offset += 4; + //uniqueness + memmove(this->buffer_send+offset, uniqueness.data, 32); + offset += 32; //sec this->send_secret((unsigned char*)sec.data, offset); @@ -1201,18 +1195,18 @@ namespace hw { return true; } - bool device_ledger::derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub){ + bool device_ledger::derive_public_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::public_key &pub, crypto::public_key &derived_pub){ AUTO_LOCK_CMD(); #ifdef DEBUG_HWDEVICE const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); - const std::size_t output_index_x = output_index; + const crypto::hash uniqueness_x = uniqueness; const crypto::public_key pub_x = pub; crypto::public_key derived_pub_x; log_hexbuffer("derive_public_key: [[IN]] derivation ", derivation_x.data, 32); - log_message ("derive_public_key: [[IN]] output_index", std::to_string(output_index_x)); + log_message ("derive_public_key: [[IN]] uniqueness ", uniqueness_x.data, 32); log_hexbuffer("derive_public_key: [[IN]] pub ", pub_x.data, 32); - if (!this->controle_device->derive_public_key(derivation_x, output_index_x, pub_x, derived_pub_x)) + if (!this->controle_device->derive_public_key(derivation_x, uniqueness_x, pub_x, derived_pub_x)) return false; log_hexbuffer("derive_public_key: [[OUT]] derived_pub ", derived_pub_x.data, 32); #endif @@ -1220,12 +1214,9 @@ namespace hw { int offset = set_command_header_noopt(INS_DERIVE_PUBLIC_KEY); //derivation this->send_secret((unsigned char*)derivation.data, offset); - //index - this->buffer_send[offset+0] = output_index>>24; - this->buffer_send[offset+1] = output_index>>16; - this->buffer_send[offset+2] = output_index>>8; - this->buffer_send[offset+3] = output_index>>0; - offset += 4; + //uniqueness + memmove(this->buffer_send+offset, uniqueness.data, 32); + offset += 32; //pub memmove(this->buffer_send+offset, pub.data, 32); offset += 32; @@ -1583,7 +1574,8 @@ namespace hw { std::vector &additional_tx_public_keys, std::vector &amount_keys, crypto::public_key &out_eph_public_key, - bool use_view_tags, crypto::view_tag &view_tag) { + bool use_view_tags, crypto::view_tag &view_tag, + const crypto::hash& uniqueness) { AUTO_LOCK_CMD(); #ifdef DEBUG_HWDEVICE @@ -1598,7 +1590,7 @@ namespace hw { const size_t output_index_x = output_index; const bool need_additional_txkeys_x = need_additional_txkeys; const bool use_view_tags_x = use_view_tags; - + const crypto::hash uniqueness_x = uniqueness; std::vector additional_tx_keys_x; for (const auto &k: additional_tx_keys) { additional_tx_keys_x.push_back(hw::ledger::decrypt(k)); @@ -1626,6 +1618,7 @@ namespace hw { log_hexbuffer("generate_output_ephemeral_keys: [[IN]] additional_tx_keys[oi]", additional_tx_keys_x[output_index].data, 32); } log_message ("generate_output_ephemeral_keys: [[IN]] use_view_tags", std::to_string(use_view_tags_x)); + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] uniqueness", uniqueness_x, 32); this->controle_device->generate_output_ephemeral_keys(tx_version_x, sender_account_keys_x, txkey_pub_x, tx_key_x, dst_entr_x, change_addr_x, output_index_x, need_additional_txkeys_x, additional_tx_keys_x, additional_tx_public_keys_x, amount_keys_x, out_eph_public_key_x, use_view_tags_x, view_tag_x); if(need_additional_txkeys_x) { diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 03058c4f1..7640280e4 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -228,7 +228,7 @@ namespace hw { /* ======================================================================= */ /* SUB ADDRESS */ /* ======================================================================= */ - bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_pub) override; + bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::public_key &derived_pub) override; crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index) override; std::vector get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end) override; cryptonote::account_public_address get_subaddress(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) override; @@ -244,9 +244,9 @@ namespace hw { crypto::secret_key generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false) override; bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) override; bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector &additional_derivations) override; - bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) override; - bool derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) override; - bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override; + bool derivation_to_scalar(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, crypto::ec_scalar &res) override; + bool derive_secret_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::secret_key &sec, crypto::secret_key &derived_sec) override; + bool derive_public_key(const crypto::key_derivation &derivation, const crypto::hash& uniqueness, const crypto::public_key &pub, crypto::public_key &derived_pub) override; bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override; bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override; bool derive_view_tag(const crypto::key_derivation &derivation, const size_t output_index, crypto::view_tag &view_tag) override; @@ -275,7 +275,8 @@ namespace hw { std::vector &additional_tx_public_keys, std::vector &amount_keys, crypto::public_key &out_eph_public_key, - const bool use_view_tags, crypto::view_tag &view_tag) override; + const bool use_view_tags, crypto::view_tag &view_tag, + const crypto::hash& uniqueness) override; bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override; bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override; diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index fabffdd02..6c57f31d8 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "crypto/crypto.h" +#include "crypto/hash.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_config.h" @@ -97,7 +98,8 @@ namespace multisig // - the 'multisig priv keys' here are those held by the local account // - 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())) + crypto::hash uniqueness = crypto::cn_fast_hash(reinterpret_cast(&real_output_index), sizeof(size_t)); + if (!cryptonote::generate_key_image_helper(keys, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, uniqueness, 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 82b62c1db..0bd4dd434 100644 --- a/src/multisig/multisig_tx_builder_ringct.cpp +++ b/src/multisig/multisig_tx_builder_ringct.cpp @@ -31,6 +31,7 @@ #include "int-util.h" #include "memwipe.h" +#include "crypto/hash.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_format_utils.h" @@ -107,6 +108,8 @@ static bool compute_keys_for_sources( cryptonote::keypair tmp_keys; if (src.real_output >= src.outputs.size()) return false; + size_t real_output_wrapper = src.real_output_in_tx_index; + crypto::hash uniqueness = crypto::cn_fast_hash(reinterpret_cast(&real_output_wrapper), sizeof(size_t)); if (not cryptonote::generate_key_image_helper( account_keys, subaddresses, @@ -114,6 +117,7 @@ static bool compute_keys_for_sources( src.real_out_tx_key, src.real_out_additional_tx_keys, src.real_output_in_tx_index, + uniqueness, tmp_keys, tmp_key_image, hwdev @@ -422,6 +426,8 @@ static bool compute_keys_for_destinations( crypto::public_key temp_output_public_key; for (std::size_t i = 0; i < num_destinations; ++i) { + + crypto::hash uniqueness = crypto::cn_fast_hash(reinterpret_cast(&i), sizeof(size_t)); if (not hwdev.generate_output_ephemeral_keys( unsigned_tx.version, account_keys, @@ -436,7 +442,8 @@ static bool compute_keys_for_destinations( output_amount_secret_keys, temp_output_public_key, use_view_tags, - view_tags[i] //unused variable if use_view_tags is not set + view_tags[i], //unused variable if use_view_tags is not set + uniqueness )) { return false; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7162eeb9b..70e9d7757 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1205,12 +1205,6 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std m_account_public_address{crypto::null_pkey, crypto::null_pkey}, m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR), - m_light_wallet(false), - m_light_wallet_scanned_block_height(0), - m_light_wallet_blockchain_height(0), - m_light_wallet_connected(false), - m_light_wallet_balance(0), - m_light_wallet_unlocked_balance(0), m_original_keys_available(false), m_message_store(http_client_factory->create()), m_key_device_type(hw::device::device_type::SOFTWARE), @@ -1794,6 +1788,13 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio LOG_ERROR("wrong type id in transaction out"); return; } + + // Check for presence in the map of protocol_tx entries we are expecting + if (auto search = m_protocol_txs.find(output_public_key); search != m_protocol_txs.end()) + { + LOG_ERROR("********************** FOUND A PROTOCOL_TX - WHAT TO DO NEXT??? **********************"); + } + tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, output_public_key, derivation, additional_derivations, i, hwdev, get_output_view_tag(o)); if(tx_scan_info.received) { @@ -1811,7 +1812,101 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio if (!is_out_data || i >= is_out_data->received.size()) return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info); - tx_scan_info.received = is_out_data->received[i]; + crypto::public_key output_public_key; + if (!get_output_public_key(o, output_public_key)) + { + tx_scan_info.error = true; + LOG_ERROR("wrong type id in transaction out"); + return; + } + + // Check for presence in the map of protocol_tx entries we are expecting + if (auto search = m_protocol_txs.find(output_public_key); search != m_protocol_txs.end()) + { + LOG_ERROR("********************** FOUND A PROTOCOL_TX - WHAT TO DO NEXT??? **********************"); + + size_t idx = search->second; + if (idx >= get_num_transfer_details()) { + LOG_ERROR("cannot locate protocol_txs index in m_transfers - idx = " << idx); + tx_scan_info.error = true; + return; + } + const transfer_details& td = get_transfer_details(idx); + if (td.m_tx.type != cryptonote::transaction_type::CONVERT && td.m_tx.type != cryptonote::transaction_type::YIELD) { + // We can only accept CONVERT & YIELD payments + LOG_ERROR("incorrect TX type for protocol_tx origin in m_transfers - idx = " << idx); + tx_scan_info.error = true; + return; + } + + // We now have access to the sorted tx.vin vector - we need the key_image from the first entry to decode the public_key + if (td.m_tx.vin[0].type() != typeid(cryptonote::txin_to_key)) { + // We can only accept txin_to_key inputs for our KI + LOG_ERROR("incorrect TX vin[0] type for protocol_tx origin in m_transfers - idx = " << idx); + tx_scan_info.error = true; + return; + } + const txin_to_key &in = boost::get(td.m_tx.vin[0]); + crypto::key_image ki = in.k_image; + + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(td.m_tx); + if (tx_pub_key == null_pkey) { + LOG_ERROR("Tx pubkey was not found"); + tx_scan_info.error = true; + return; + } + + // Generate a derivation using the _correct_ TX pubkey + crypto::key_derivation convert_tx_derivation; + bool ok = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, convert_tx_derivation); + if (!ok) { + // Failed to generate a derivation + LOG_ERROR("Failed to generate a derivation image - idx = " << idx); + tx_scan_info.error = true; + return; + } + + // Derive a public key now, using the information we have + crypto::hash uniqueness = cn_fast_hash(&ki.data[0], 32); + crypto::public_key pk = crypto::null_pkey; + ok = m_account.get_device().derive_public_key(convert_tx_derivation, uniqueness, get_account().get_keys().m_account_address.m_spend_public_key, pk); + if (!ok) { + // Failed to derive a public key + LOG_ERROR("Failed to derive a public key given the necessary key image - idx = " << idx); + tx_scan_info.error = true; + return; + } + + LOG_ERROR("*****************************************************************************"); + LOG_ERROR("derivation: " << derivation); + LOG_ERROR("key_image : " << ki); + LOG_ERROR("uniqueness: " << uniqueness); + LOG_ERROR("txkey_pub : " << tx_pub_key); + LOG_ERROR("tx_address: " << output_public_key); + LOG_ERROR("*****************************************************************************"); + + // Do the public keys match? + if (pk == output_public_key) { + LOG_ERROR("********************** DECODED A PROTOCOL_TX - CAN WE SPEND IT??? **********************"); + auto index = m_subaddresses.find(get_account().get_keys().m_account_address.m_spend_public_key); + if (index == m_subaddresses.end()) { + // Failed to find the subaddress for this account + LOG_ERROR("Failed to find subaddress"); + tx_scan_info.error = true; + return; + } + tx_scan_info.received = subaddress_receive_info{ index->second, convert_tx_derivation }; + tx_scan_info.uniqueness = uniqueness; + } else { + LOG_ERROR("Public keys do not match"); + tx_scan_info.error = true; + return; + } + } else { + tx_scan_info.received = is_out_data->received[i]; + tx_scan_info.uniqueness = cn_fast_hash(reinterpret_cast(&i), sizeof(size_t)); + } + if(tx_scan_info.received) { tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs @@ -1833,10 +1928,10 @@ void wallet2::check_acc_out_precomp_once(const tx_out &o, const crypto::key_deri already_seen = true; } //---------------------------------------------------------------------------------------------------- -static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev) +static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, size_t output_index, crypto::hash& uniqueness, rct::key & mask, hw::device &hwdev) { crypto::secret_key scalar1; - hwdev.derivation_to_scalar(derivation, i, scalar1); + hwdev.derivation_to_scalar(derivation, uniqueness, scalar1); try { switch (rv.type) @@ -1846,9 +1941,9 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & case rct::RCTTypeBulletproof2: case rct::RCTTypeCLSAG: case rct::RCTTypeBulletproofPlus: - return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); + return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), output_index, mask, hwdev); case rct::RCTTypeFull: - return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); + return rct::decodeRct(rv, rct::sk2rct(scalar1), output_index, mask, hwdev); default: LOG_ERROR("Unsupported rct type: " << rv.type); return 0; @@ -1856,7 +1951,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & } catch (const std::exception &e) { - LOG_ERROR("Failed to decode input " << i); + LOG_ERROR("Failed to decode input " << output_index); return 0; } } @@ -1890,7 +1985,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons } else { - bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device()); + bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.uniqueness, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != output_public_key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); @@ -1899,7 +1994,86 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice"); if (tx_scan_info.money_transfered == 0 && !miner_tx) { - tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); + tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.uniqueness, tx_scan_info.mask, m_account.get_device()); + } + if (tx_scan_info.money_transfered == 0) + { + MERROR("Invalid output amount, skipping"); + tx_scan_info.error = true; + return; + } + outs.push_back(i); + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index][tx_scan_info.asset_type] >= std::numeric_limits::max() - tx_scan_info.money_transfered, + error::wallet_internal_error, "Overflow in received amounts"); + tx_money_got_in_outs[tx_scan_info.received->index][tx_scan_info.asset_type] += tx_scan_info.money_transfered; + tx_scan_info.amount = tx_scan_info.money_transfered; + ++num_vouts_received; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::scan_protocol_tx_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map> &tx_money_got_in_outs, std::vector &outs, bool pool) +{ + THROW_WALLET_EXCEPTION_IF(tx.type != cryptonote::transaction_type::PROTOCOL, error::wallet_internal_error, "Only PROTOCOL transactions may use the scan_protocol_tx_output() method"); + THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); + + // if keys are encrypted, ask for password + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k) + { + static critical_section password_lock; + CRITICAL_REGION_LOCAL(password_lock); + if (!m_encrypt_keys_after_refresh) + { + boost::optional pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received"); + THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero")); + THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero")); + m_encrypt_keys_after_refresh.reset(new wallet_keys_unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, *pwd)); + } + } + + // Scan our map of protocol_tx entries for the appropriate progenitor for this TX + crypto::public_key output_public_key; + THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key"); + + // Check for presence in the map of protocol_tx entries we are expecting + auto search = m_protocol_txs.find(output_public_key); + if (search == m_protocol_txs.end()) + { + tx_scan_info.error = true; + MERROR("failed to locate progenitor for specified output_public_key" << output_public_key); + return; + } + + size_t idx = search->second; + if (idx >= get_num_transfer_details()) { + MERROR("cannot locate protocol_txs index in m_transfers - idx = " << idx); + tx_scan_info.error = true; + return; + } + const transfer_details& td = get_transfer_details(idx); + if (td.m_tx.type != cryptonote::transaction_type::CONVERT && td.m_tx.type != cryptonote::transaction_type::YIELD) { + // We can only accept CONVERT & YIELD payments + MERROR("incorrect TX type for protocol_tx origin in m_transfers - idx = " << idx); + tx_scan_info.error = true; + return; + } + + if (m_multisig) + { + tx_scan_info.in_ephemeral.pub = output_public_key; + tx_scan_info.in_ephemeral.sec = crypto::null_skey; + tx_scan_info.ki = rct::rct2ki(rct::zero()); + } + else + { + bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.uniqueness, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device()); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); + THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != output_public_key, + error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); + } + + THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice"); + if (tx_scan_info.money_transfered == 0 && !miner_tx) + { + tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.uniqueness, tx_scan_info.mask, m_account.get_device()); } if (tx_scan_info.money_transfered == 0) { @@ -2203,7 +2377,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote boost::unique_lock hwdev_lock (hwdev); hwdev.set_mode(hw::device::NONE); hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); - scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (tx.type == cryptonote::transaction_type::PROTOCOL) { + scan_protocol_tx_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + } else { + scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + } if (!tx_scan_info[i].error) { tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); @@ -2475,6 +2653,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee; + if (tx_money_spent_in_ins > 0 && (tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::YIELD)) + { + // The CONVERT/YIELD TX was created by us - therefore we need to expect an output in the PROTOCOL_TX + // It could be a refund or a conversion + m_protocol_txs.insert({tx.destination_address, m_transfers.size()-1}); + } + if (tx_money_spent_in_ins > 0 && !pool) { std::map self_received; @@ -2730,7 +2915,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry 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[tx_cache_data_offset], output_tracker_cache); + 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); @@ -3580,38 +3765,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo return; } - if(m_light_wallet) { - - // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. - // This call is not really needed for other purposes and can be removed if mymonero changes their backend. - tools::COMMAND_RPC_GET_ADDRESS_INFO::response res; - - // Get basic info - if(light_wallet_get_address_info(res)) { - // Last stored block height - uint64_t prev_height = m_light_wallet_blockchain_height; - // Update lw heights - m_light_wallet_scanned_block_height = res.scanned_block_height; - m_light_wallet_blockchain_height = res.blockchain_height; - // If new height - call new_block callback - if(m_light_wallet_blockchain_height != prev_height) - { - MDEBUG("new block since last time!"); - m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1); - } - m_light_wallet_connected = true; - MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height); - MDEBUG("lw blockchain height: " << m_light_wallet_blockchain_height); - MDEBUG(m_light_wallet_blockchain_height-m_light_wallet_scanned_block_height << " blocks behind"); - // TODO: add wallet created block info - - light_wallet_get_address_txs(); - } else - m_light_wallet_connected = false; - - // Lighwallet refresh done - return; - } received_money = false; blocks_fetched = 0; uint64_t added_blocks = 0; @@ -5553,16 +5706,6 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout, b return false; } - // TODO: Add light wallet version check. - if(m_light_wallet) { - m_rpc_version = 0; - if (version) - *version = 0; - if (ssl) - *ssl = m_light_wallet_connected; // light wallet is always SSL - return m_light_wallet_connected; - } - { boost::lock_guard lock(m_daemon_rpc_mutex); if(!m_http_client->is_connected(ssl)) @@ -6095,8 +6238,6 @@ boost::optional wallet2::get_cache_file_data(const epe uint64_t wallet2::balance(uint32_t index_major, bool strict) const { uint64_t amount = 0; - if(m_light_wallet) - return m_light_wallet_balance; for (const auto& i : balance_per_subaddress(index_major, strict)) amount += i.second; return amount; @@ -6109,8 +6250,6 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * *blocks_to_unlock = 0; if (time_to_unlock) *time_to_unlock = 0; - if(m_light_wallet) - return m_light_wallet_unlocked_balance; for (const auto& i : unlocked_balance_per_subaddress(index_major, strict)) { amount += i.second.first; @@ -6654,22 +6793,6 @@ void wallet2::commit_tx(pending_tx& ptx) bool r = cryptonote::get_tx_asset_types(ptx.tx, ptx.tx.hash, source_asset, dest_asset, false); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "failed to get TX asset types"); - if(m_light_wallet) - { - cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::request oreq; - cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::response ores; - oreq.address = get_account().get_public_address_str(m_nettype); - oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); - { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, *m_http_client, rpc_timeout, "POST"); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx"); - // MyMonero and OpenMonero use different status strings - THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error); - } - } - else { // Normal submit COMMAND_RPC_SEND_RAW_TX::request req; @@ -7022,7 +7145,8 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector(&i), sizeof(size_t)); + if (generate_key_image_helper(keys, m_subaddresses, output_public_key, tx_pub_key, additional_tx_pub_keys, i, uniqueness, in_ephemeral, ki, hwdev)) signed_txes.tx_key_images[output_public_key] = ki; else MERROR("Failed to calculate key image"); @@ -7656,13 +7780,6 @@ uint64_t wallet2::get_dynamic_base_fee_estimate() //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_base_fee() { - if(m_light_wallet) - { - if (use_fork_rules(HF_VERSION_PER_BYTE_FEE)) - return m_light_wallet_per_kb_fee / 1024; - else - return m_light_wallet_per_kb_fee; - } bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -30 * 1); if (!use_dyn_fee) return FEE_PER_KB; @@ -7706,14 +7823,6 @@ uint64_t wallet2::get_base_fee(uint32_t priority) //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_fee_quantization_mask() { - if(m_light_wallet) - { - return 1; // TODO - } - bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0); - if (!use_per_byte_fee) - return 1; - uint64_t fee_quantization_mask; boost::optional result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask); if (result) @@ -7723,14 +7832,7 @@ uint64_t wallet2::get_fee_quantization_mask() //---------------------------------------------------------------------------------------------------- int wallet2::get_fee_algorithm() { - // changes at v3, v5, v8 - if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0)) - return 3; - if (use_fork_rules(5, 0)) - return 2; - if (use_fork_rules(3, -30 * 14)) - return 1; - return 0; + return 3; } //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::get_min_ring_size() @@ -8176,113 +8278,6 @@ bool wallet2::tx_add_fake_output(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count) { - - MDEBUG("LIGHTWALLET - Getting random outs"); - - tools::COMMAND_RPC_GET_RANDOM_OUTS::request oreq; - tools::COMMAND_RPC_GET_RANDOM_OUTS::response ores; - - size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1); - - // Amounts to ask for - // MyMonero api handle amounts and fees as strings - for(size_t idx: selected_transfers) { - const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount(); - std::ostringstream amount_ss; - amount_ss << ask_amount; - oreq.amounts.push_back(amount_ss.str()); - } - - oreq.count = light_wallet_requested_outputs_count; - - { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, *m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs"); - THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error); - } - - // Check if we got enough outputs for each amount - for(auto& out: ores.amount_outs) { - THROW_WALLET_EXCEPTION_IF(out.outputs.size() < light_wallet_requested_outputs_count , error::wallet_internal_error, "Not enough outputs for amount: " + boost::lexical_cast(out.amount)); - MDEBUG(out.outputs.size() << " outputs for amount "+ boost::lexical_cast(out.amount) + " received from light wallet node"); - } - - MDEBUG("selected transfers size: " << selected_transfers.size()); - - std::unordered_set valid_public_keys_cache; - for(size_t idx: selected_transfers) - { - // Create new index - outs.push_back(std::vector()); - outs.back().reserve(fake_outputs_count + 1); - - // add real output first - const transfer_details &td = m_transfers[idx]; - const uint64_t amount = td.is_rct() ? 0 : td.amount(); - outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), rct::commit(td.amount(), td.m_mask))); - MDEBUG("added real output " << string_tools::pod_to_hex(td.get_public_key())); - - // Even if the lightwallet server returns random outputs, we pick them randomly. - std::vector order; - order.resize(light_wallet_requested_outputs_count); - for (size_t n = 0; n < order.size(); ++n) - order[n] = n; - std::shuffle(order.begin(), order.end(), crypto::random_device{}); - - - LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_money(td.is_rct() ? 0 : td.amount())); - MDEBUG("OUTS SIZE: " << outs.back().size()); - for (size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) - { - // Random pick - size_t i = order[o]; - - // Find which random output key to use - bool found_amount = false; - size_t amount_key; - for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key) - { - if(boost::lexical_cast(ores.amount_outs[amount_key].amount) == amount) { - found_amount = true; - break; - } - } - THROW_WALLET_EXCEPTION_IF(!found_amount , error::wallet_internal_error, "Outputs for amount " + boost::lexical_cast(ores.amount_outs[amount_key].amount) + " not found" ); - - LOG_PRINT_L2("Index " << i << "/" << light_wallet_requested_outputs_count << ": idx " << ores.amount_outs[amount_key].outputs[i].global_index << " (real " << td.m_global_output_index << "), unlocked " << "(always in light)" << ", key " << ores.amount_outs[0].outputs[i].public_key); - - // Convert light wallet string data to proper data structures - crypto::public_key tx_public_key; - rct::key mask = AUTO_VAL_INIT(mask); // decrypted mask - not used here - rct::key rct_commit = AUTO_VAL_INIT(rct_commit); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ores.amount_outs[amount_key].outputs[i].public_key), error::wallet_internal_error, "Invalid public_key"); - string_tools::hex_to_pod(ores.amount_outs[amount_key].outputs[i].public_key, tx_public_key); - const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index; - if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false)) - rct_commit = rct::zeroCommit(td.amount()); - - if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true, valid_public_keys_cache)) { - MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key); - MDEBUG("index " << global_index); - } - } - - THROW_WALLET_EXCEPTION_IF(outs.back().size() < fake_outputs_count + 1 , error::wallet_internal_error, "Not enough fake outputs found" ); - - // Real output is the first. Shuffle outputs - MTRACE(outs.back().size() << " outputs added. Sorting outputs by index:"); - std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); }); - - // Print output order - for(auto added_out: outs.back()) - MTRACE(std::get<0>(added_out)); - - } -} - std::pair, size_t> outs_unique(const std::vector> &outs) { std::set unique; @@ -9312,6 +9307,7 @@ void wallet2::transfer_selected_rct(std::vector splitted_dsts = dsts; cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts); change_dts.amount = found_money - needed_money; + change_dts.is_change = true; if (change_dts.amount == 0) { if (splitted_dsts.size() == 1) @@ -9643,476 +9639,6 @@ static uint32_t get_count_above(const std::vector &tr return count; } -bool wallet2::light_wallet_login(bool &new_address) -{ - MDEBUG("Light wallet login request"); - m_light_wallet_connected = false; - tools::COMMAND_RPC_LOGIN::request request; - tools::COMMAND_RPC_LOGIN::response response; - request.address = get_account().get_public_address_str(m_nettype); - request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - // Always create account if it doesn't exist. - request.create_account = true; - m_daemon_rpc_mutex.lock(); - bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - // MyMonero doesn't send any status message. OpenMonero does. - m_light_wallet_connected = connected && (response.status.empty() || response.status == "success"); - new_address = response.new_address; - MDEBUG("Status: " << response.status); - MDEBUG("Reason: " << response.reason); - MDEBUG("New wallet: " << response.new_address); - if(m_light_wallet_connected) - { - // Clear old data on successful login. - // m_transfers.clear(); - // m_payments.clear(); - // m_unconfirmed_payments.clear(); - } - return m_light_wallet_connected; -} - -bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response) -{ - MDEBUG("Light wallet import wallet request"); - tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq; - oreq.address = get_account().get_public_address_str(m_nettype); - oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - m_daemon_rpc_mutex.lock(); - bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request"); - - - return true; -} - -void wallet2::light_wallet_get_unspent_outs() -{ - MDEBUG("Getting unspent outs"); - - tools::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq; - tools::COMMAND_RPC_GET_UNSPENT_OUTS::response ores; - - oreq.amount = "0"; - oreq.address = get_account().get_public_address_str(m_nettype); - oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - // openMonero specific - oreq.dust_threshold = boost::lexical_cast(::config::DEFAULT_DUST_THRESHOLD); - // below are required by openMonero api - but are not used. - oreq.mixin = 0; - oreq.use_dust = true; - - - m_daemon_rpc_mutex.lock(); - bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_unspent_outs"); - THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason); - - m_light_wallet_per_kb_fee = ores.per_kb_fee; - - std::unordered_map transfers_txs; - for(const auto &t: m_transfers) - transfers_txs.emplace(t.m_txid,t.m_spent); - - MDEBUG("FOUND " << ores.outputs.size() <<" outputs"); - - // return if no outputs found - if(ores.outputs.empty()) - return; - - // Clear old outputs - m_transfers.clear(); - - for (const auto &o: ores.outputs) { - bool spent = false; - bool add_transfer = true; - crypto::key_image unspent_key_image; - crypto::public_key tx_public_key = AUTO_VAL_INIT(tx_public_key); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field"); - string_tools::hex_to_pod(o.tx_pub_key, tx_public_key); - - for (const std::string &ski: o.spend_key_images) { - spent = false; - - // Check if key image is ours - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ski), error::wallet_internal_error, "Invalid key image"); - string_tools::hex_to_pod(ski, unspent_key_image); - if(light_wallet_key_image_is_ours(unspent_key_image, tx_public_key, o.index)){ - MTRACE("Output " << o.public_key << " is spent. Key image: " << ski); - spent = true; - break; - } { - MTRACE("Unspent output found. " << o.public_key); - } - } - - // Check if tx already exists in m_transfers. - crypto::hash txid; - crypto::public_key tx_pub_key; - crypto::public_key public_key; - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_hash), error::wallet_internal_error, "Invalid tx_hash field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.public_key), error::wallet_internal_error, "Invalid public_key field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field"); - string_tools::hex_to_pod(o.tx_hash, txid); - string_tools::hex_to_pod(o.public_key, public_key); - string_tools::hex_to_pod(o.tx_pub_key, tx_pub_key); - - for(auto &t: m_transfers){ - if(t.get_public_key() == public_key) { - t.m_spent = spent; - add_transfer = false; - break; - } - } - - if(!add_transfer) - continue; - - m_transfers.push_back(transfer_details{}); - transfer_details& td = m_transfers.back(); - - td.m_block_height = o.height; - td.m_global_output_index = o.global_index; - td.m_txid = txid; - - // Add to extra - add_tx_pub_key_to_extra(td.m_tx, tx_pub_key); - - td.m_key_image = unspent_key_image; - td.m_key_image_known = !m_watch_only && !m_multisig; - td.m_key_image_request = false; - td.m_key_image_partial = m_multisig; - td.m_amount = o.amount; - td.m_pk_index = 0; - td.m_internal_output_index = o.index; - td.m_spent = spent; - td.m_frozen = false; - - tx_out txout; - txout.target = txout_to_key(public_key); - txout.amount = td.m_amount; - - td.m_tx.vout.resize(td.m_internal_output_index + 1); - td.m_tx.vout[td.m_internal_output_index] = txout; - - // Add unlock time and coinbase bool got from get_address_txs api call - std::unordered_map::const_iterator found = m_light_wallet_address_txs.find(txid); - THROW_WALLET_EXCEPTION_IF(found == m_light_wallet_address_txs.end(), error::wallet_internal_error, "Lightwallet: tx not found in m_light_wallet_address_txs"); - bool miner_tx = found->second.m_coinbase; - td.m_tx.unlock_time = found->second.m_unlock_time; - - if (!o.rct.empty()) - { - // Coinbase tx's - if(miner_tx) - { - td.m_mask = rct::identity(); - } - else - { - // rct txs - // decrypt rct mask, calculate commit hash and compare against blockchain commit hash - rct::key rct_commit; - light_wallet_parse_rct_str(o.rct, tx_pub_key, td.m_internal_output_index, td.m_mask, rct_commit, true); - bool valid_commit = (rct_commit == rct::commit(td.amount(), td.m_mask)); - if(!valid_commit) - { - MDEBUG("output index: " << o.global_index); - MDEBUG("mask: " + string_tools::pod_to_hex(td.m_mask)); - MDEBUG("calculated commit: " + string_tools::pod_to_hex(rct::commit(td.amount(), td.m_mask))); - MDEBUG("expected commit: " + string_tools::pod_to_hex(rct_commit)); - MDEBUG("amount: " << td.amount()); - } - THROW_WALLET_EXCEPTION_IF(!valid_commit, error::wallet_internal_error, "Lightwallet: rct commit hash mismatch!"); - } - td.m_rct = true; - } - else - { - td.m_mask = rct::identity(); - td.m_rct = false; - } - if(!spent) - set_unspent(m_transfers.size()-1); - m_key_images[td.m_key_image] = m_transfers.size()-1; - m_pub_keys[td.get_public_key()] = m_transfers.size()-1; - } -} - -bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response) -{ - MTRACE(__FUNCTION__); - - tools::COMMAND_RPC_GET_ADDRESS_INFO::request request; - - request.address = get_account().get_public_address_str(m_nettype); - request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - m_daemon_rpc_mutex.lock(); - bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_info"); - // TODO: Validate result - return true; -} - -void wallet2::light_wallet_get_address_txs() -{ - MDEBUG("Refreshing light wallet"); - - tools::COMMAND_RPC_GET_ADDRESS_TXS::request ireq; - tools::COMMAND_RPC_GET_ADDRESS_TXS::response ires; - - ireq.address = get_account().get_public_address_str(m_nettype); - ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - m_daemon_rpc_mutex.lock(); - bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_txs"); - //OpenMonero sends status=success, Mymonero doesn't. - THROW_WALLET_EXCEPTION_IF((!ires.status.empty() && ires.status != "success"), error::no_connection_to_daemon, "get_address_txs"); - - - // Abort if no transactions - if(ires.transactions.empty()) - return; - - // Create searchable vectors - std::vector payments_txs; - for(const auto &p: m_payments) - payments_txs.push_back(p.second.m_tx_hash); - std::vector unconfirmed_payments_txs; - for(const auto &up: m_unconfirmed_payments) - unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash); - - // for balance calculation - uint64_t wallet_total_sent = 0; - // txs in pool - std::vector pool_txs; - - for (const auto &t: ires.transactions) { - const uint64_t total_received = t.total_received; - uint64_t total_sent = t.total_sent; - - // Check key images - subtract fake outputs from total_sent - for(const auto &so: t.spent_outputs) - { - crypto::public_key tx_public_key; - crypto::key_image key_image; - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.key_image), error::wallet_internal_error, "Invalid key_image field"); - string_tools::hex_to_pod(so.tx_pub_key, tx_public_key); - string_tools::hex_to_pod(so.key_image, key_image); - - if(!light_wallet_key_image_is_ours(key_image, tx_public_key, so.out_index)) { - THROW_WALLET_EXCEPTION_IF(so.amount > t.total_sent, error::wallet_internal_error, "Lightwallet: total sent is negative!"); - total_sent -= so.amount; - } - } - - // Do not add tx if empty. - if(total_sent == 0 && total_received == 0) - continue; - - crypto::hash payment_id = null_hash; - crypto::hash tx_hash; - - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.payment_id), error::wallet_internal_error, "Invalid payment_id field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.hash), error::wallet_internal_error, "Invalid hash field"); - string_tools::hex_to_pod(t.payment_id, payment_id); - string_tools::hex_to_pod(t.hash, tx_hash); - - // lightwallet specific info - bool incoming = (total_received > total_sent); - address_tx address_tx; - address_tx.m_tx_hash = tx_hash; - address_tx.m_incoming = incoming; - address_tx.m_amount = incoming ? total_received - total_sent : total_sent - total_received; - address_tx.m_fee = 0; // TODO - address_tx.m_block_height = t.height; - address_tx.m_unlock_time = t.unlock_time; - address_tx.m_timestamp = t.timestamp; - address_tx.m_coinbase = t.coinbase; - address_tx.m_mempool = t.mempool; - m_light_wallet_address_txs.emplace(tx_hash,address_tx); - - // populate data needed for history (m_payments, m_unconfirmed_payments, m_confirmed_txs) - // INCOMING transfers - if(total_received > total_sent) { - payment_details payment; - payment.m_tx_hash = tx_hash; - payment.m_amount = total_received - total_sent; - payment.m_fee = 0; // TODO - payment.m_block_height = t.height; - payment.m_unlock_time = t.unlock_time; - payment.m_timestamp = t.timestamp; - payment.m_coinbase = t.coinbase; - - if (t.mempool) { - if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) { - pool_txs.push_back(tx_hash); - // assume false as we don't get that info from the light wallet server - crypto::hash payment_id; - THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id), - error::wallet_internal_error, "Failed to parse payment id"); - emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false}); - if (0 != m_callback) { - m_callback->on_lw_unconfirmed_money_received(t.height, payment.m_tx_hash, payment.m_amount); - } - } - } else { - if (std::find(payments_txs.begin(), payments_txs.end(), tx_hash) == payments_txs.end()) { - m_payments.emplace(tx_hash, payment); - if (0 != m_callback) { - m_callback->on_lw_money_received(t.height, payment.m_tx_hash, payment.m_amount); - } - } - } - // Outgoing transfers - } else { - uint64_t amount_sent = total_sent - total_received; - cryptonote::transaction dummy_tx; // not used by light wallet - // increase wallet total sent - wallet_total_sent += total_sent; - if (t.mempool) - { - // Handled by add_unconfirmed_tx in commit_tx - // If sent from another wallet instance we need to add it - if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end()) - { - unconfirmed_transfer_details utd; - utd.m_amount_in = amount_sent; - utd.m_amount_out = amount_sent; - utd.m_change = 0; - utd.m_payment_id = payment_id; - utd.m_timestamp = t.timestamp; - utd.m_state = wallet2::unconfirmed_transfer_details::pending; - m_unconfirmed_txs.emplace(tx_hash,utd); - } - } - else - { - // Only add if new - auto confirmed_tx = m_confirmed_txs.find(tx_hash); - if(confirmed_tx == m_confirmed_txs.end()) { - // tx is added to m_unconfirmed_txs - move to confirmed - if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end()) - { - process_unconfirmed(tx_hash, dummy_tx, t.height); - } - // Tx sent by another wallet instance - else - { - confirmed_transfer_details ctd; - ctd.m_amount_in = amount_sent; - ctd.m_amount_out = amount_sent; - ctd.m_change = 0; - ctd.m_payment_id = payment_id; - ctd.m_block_height = t.height; - ctd.m_timestamp = t.timestamp; - m_confirmed_txs.emplace(tx_hash,ctd); - } - if (0 != m_callback) - { - m_callback->on_lw_money_spent(t.height, tx_hash, amount_sent); - } - } - // If not new - check the amount and update if necessary. - // when sending a tx to same wallet the receiving amount has to be credited - else - { - if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent) - { - MDEBUG("Adjusting amount sent/received for tx: <" + t.hash + ">. Is tx sent to own wallet? " << print_money(amount_sent) << " != " << print_money(confirmed_tx->second.m_amount_in)); - confirmed_tx->second.m_amount_in = amount_sent; - confirmed_tx->second.m_amount_out = amount_sent; - confirmed_tx->second.m_change = 0; - } - } - } - } - } - // TODO: purge old unconfirmed_txs - remove_obsolete_pool_txs(pool_txs); - - // Calculate wallet balance - m_light_wallet_balance = ires.total_received-wallet_total_sent; - // MyMonero doesn't send unlocked balance - if(ires.total_received_unlocked > 0) - m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent; - else - m_light_wallet_unlocked_balance = m_light_wallet_balance; -} - -bool wallet2::light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const -{ - // rct string is empty if output is non RCT - if (rct_string.empty()) - return false; - // rct_string is a string with length 64+64+64 ( + + ) - rct::key encrypted_mask; - std::string rct_commit_str = rct_string.substr(0,64); - std::string encrypted_mask_str = rct_string.substr(64,64); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, rct_commit_str), error::wallet_internal_error, "Invalid rct commit hash: " + rct_commit_str); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, encrypted_mask_str), error::wallet_internal_error, "Invalid rct mask: " + encrypted_mask_str); - string_tools::hex_to_pod(rct_commit_str, rct_commit); - string_tools::hex_to_pod(encrypted_mask_str, encrypted_mask); - if (decrypt) { - // Decrypt the mask - crypto::key_derivation derivation; - bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); - crypto::secret_key scalar; - crypto::derivation_to_scalar(derivation, internal_output_index, scalar); - sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes); - } - return true; -} - -bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index) -{ - // Lookup key image from cache - serializable_map index_keyimage_map; - serializable_unordered_map >::const_iterator found_pub_key = m_key_image_cache.find(tx_public_key); - if(found_pub_key != m_key_image_cache.end()) { - // pub key found. key image for index cached? - index_keyimage_map = found_pub_key->second; - std::map::const_iterator index_found = index_keyimage_map.find(out_index); - if(index_found != index_keyimage_map.end()) - return key_image == index_found->second; - } - - // Not in cache - calculate key image - crypto::key_image calculated_key_image; - cryptonote::keypair in_ephemeral; - - // Subaddresses aren't supported in mymonero/openmonero yet. Roll out the original scheme: - // compute D = a*R - // compute P = Hs(D || i)*G + B - // compute x = Hs(D || i) + b (and check if P==x*G) - // compute I = x*Hp(P) - const account_keys& ack = get_account().get_keys(); - crypto::key_derivation derivation; - bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); - - r = crypto::derive_public_key(derivation, out_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); - CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key (" << derivation << ", " << out_index << ", " << ack.m_account_address.m_spend_public_key << ")"); - - crypto::derive_secret_key(derivation, out_index, ack.m_spend_secret_key, in_ephemeral.sec); - crypto::public_key out_pkey_test; - r = crypto::secret_key_to_public_key(in_ephemeral.sec, out_pkey_test); - CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key(" << in_ephemeral.sec << ")"); - CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_pkey_test, false, "derived secret key doesn't match derived public key"); - - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, calculated_key_image); - - index_keyimage_map.emplace(out_index, calculated_key_image); - m_key_image_cache.emplace(tx_public_key, index_keyimage_map); - return key_image == calculated_key_image; -} - // Another implementation of transaction creation that is hopefully better // While there is anything left to pay, it goes through random outputs and tries // to fill the next destination/amount. If it fully fills it, it will use the @@ -10147,10 +9673,6 @@ std::vector wallet2::create_transactions_2( auto original_dsts = dsts; - if(m_light_wallet) { - // Populate m_transfers - light_wallet_get_unspent_outs(); - } std::vector>> unused_transfers_indices_per_subaddr; std::vector>> unused_dust_indices_per_subaddr; uint64_t needed_money; @@ -10244,6 +9766,9 @@ std::vector wallet2::create_transactions_2( // Get the circulating supply data THROW_WALLET_EXCEPTION_IF(!get_circulating_supply(circ_amounts), error::wallet_internal_error, "Failed to get circulating supply"); break; + case transaction_type::YIELD: + THROW_WALLET_EXCEPTION_IF(dest_asset != "FULM", error::wallet_internal_error, "Yield TX must specify 'FULM' destination asset type"); + break; default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Invalid tx type specified: " + static_cast(tx_type)); break; @@ -11222,9 +10747,6 @@ void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) //---------------------------------------------------------------------------------------------------- bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) { - // TODO: How to get fork rule info from light wallet node? - if(m_light_wallet) - return true; uint64_t height, earliest_height; boost::optional result = m_node_rpc_proxy.get_height(height); THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get height"); @@ -11613,6 +11135,8 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string for(size_t i = 0; i < tx.vin.size(); ++i) { + if (tx.vin[i].type() not_eq typeid(cryptonote::txin_to_key)) + continue; const txin_to_key* const in_key = boost::get(std::addressof(tx.vin[i])); if (in_key == nullptr) continue; @@ -11625,14 +11149,18 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "This tx wasn't generated by this wallet!"); } + // SRCG: We KNOW it's a txin_to_key, so it's an RCT transaction, NOT a PROTOCOL transaction. + // The uniqueness value will therefore ALWAYS be a hash of the output_index + crypto::hash uniqueness = cn_fast_hash(reinterpret_cast(&i), sizeof(size_t)); + // derive the real output keypair const transfer_details& in_td = m_transfers[found->second]; crypto::public_key in_tx_out_pkey = in_td.get_public_key(); const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index); - const std::vector in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx); + const std::vector in_additional_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()), + THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey, in_tx_pub_key, in_additional_tx_pub_keys, in_td.m_internal_output_index, uniqueness, 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"); @@ -12362,10 +11890,19 @@ std::string wallet2::get_reserve_proof(const boost::optional(&output_index_wrapper), sizeof(size_t)); + // derive ephemeral secret key crypto::key_image ki; cryptonote::keypair ephemeral; - const bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, td.get_public_key(), tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, ephemeral, ki, m_account.get_device()); + const bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, td.get_public_key(), tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, uniqueness, ephemeral, ki, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); THROW_WALLET_EXCEPTION_IF(ephemeral.pub != td.get_public_key(), error::wallet_internal_error, "Derived public key doesn't agree with the stored one"); @@ -12992,10 +12529,28 @@ std::pair> crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); const std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); + // SRCG: Calculate the correct uniqueness hash + crypto::hash uniqueness = crypto::null_hash; + if (td.m_tx.type == cryptonote::MINER) { + size_t uniqueness_wrapper = td.m_block_height; + uniqueness = cn_fast_hash(reinterpret_cast(&uniqueness_wrapper), sizeof(size_t)); + } else if (td.m_tx.type == cryptonote::PROTOCOL) { + /** + * SRCG: we now need to find the following, in the given order: + * 1. the CONVERT or YIELD tx that spawned this payout + * 2. the key_image of vin[0] from that particular TX + * 3. the tx_pub_key from that particular TX - because otherwise the following code will FAIL + */ + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Failed to generate uniqueness - wallet2::export_key_images()"); + } else { + size_t output_index_wrapper = td.m_internal_output_index; + uniqueness = cn_fast_hash(reinterpret_cast(&output_index_wrapper), sizeof(size_t)); + } + // 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()); + 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, uniqueness, 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, @@ -13276,7 +12831,7 @@ uint64_t wallet2::import_key_images(const std::vectorderivation, output_index, mask, hwdev); + tx_scan_info.money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_scan_info.received->derivation, output_index, tx_scan_info.uniqueness, mask, hwdev); } THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[asset_type] >= std::numeric_limits::max() - tx_scan_info.money_transfered, error::wallet_internal_error, "Overflow in received amounts"); @@ -13579,6 +13134,24 @@ size_t wallet2::import_outputs(const std::tuple(&uniqueness_wrapper), sizeof(size_t)); + } else if (td.m_tx.type == cryptonote::PROTOCOL) { + /** + * SRCG: we now need to find the following, in the given order: + * 1. the CONVERT or YIELD tx that spawned this payout + * 2. the key_image of vin[0] from that particular TX + * 3. the tx_pub_key from that particular TX - because otherwise the following code will FAIL + */ + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Failed to generate uniqueness - wallet2::import_outputs()"); + } else { + size_t output_index_wrapper = td.m_internal_output_index; + uniqueness = cn_fast_hash(reinterpret_cast(&output_index_wrapper), sizeof(size_t)); + } + // the hot wallet wouldn't have known about key images (except if we already exported them) cryptonote::keypair in_ephemeral; @@ -13591,7 +13164,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()); + 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, uniqueness, 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); @@ -13687,6 +13260,24 @@ size_t wallet2::import_outputs(const std::tuple(&uniqueness_wrapper), sizeof(size_t)); + } else if (td.m_tx.type == cryptonote::PROTOCOL) { + /** + * SRCG: we now need to find the following, in the given order: + * 1. the CONVERT or YIELD tx that spawned this payout + * 2. the key_image of vin[0] from that particular TX + * 3. the tx_pub_key from that particular TX - because otherwise the following code will FAIL + */ + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Failed to generate uniqueness - wallet2::import_outputs()"); + } else { + size_t output_index_wrapper = td.m_internal_output_index; + uniqueness = cn_fast_hash(reinterpret_cast(&output_index_wrapper), sizeof(size_t)); + } + // the hot wallet wouldn't have known about key images (except if we already exported them) cryptonote::keypair in_ephemeral; @@ -13695,7 +13286,7 @@ size_t wallet2::import_outputs(const std::tuple on_get_password(const char *reason) { return boost::none; } - // Light wallet callbacks - virtual void on_lw_new_block(uint64_t height) {} - virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} - virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} - virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {} // Device callbacks virtual void on_device_button_request(uint64_t code) {} virtual void on_device_button_pressed() {} @@ -320,10 +315,11 @@ private: uint64_t amount; uint64_t money_transfered; std::string asset_type; + crypto::hash uniqueness; bool error; boost::optional received; - tx_scan_info_t(): amount(0), asset_type(""), money_transfered(0), error(true) {} + tx_scan_info_t(): amount(0), asset_type(""), money_transfered(0), uniqueness(crypto::null_hash), error(true) {} }; struct transfer_details @@ -992,14 +988,6 @@ private: bool is_deterministic() const; bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; - /*! - * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. - */ - bool light_wallet() const { return m_light_wallet; } - void set_light_wallet(bool light_wallet) { m_light_wallet = light_wallet; } - uint64_t get_light_wallet_scanned_block_height() const { return m_light_wallet_scanned_block_height; } - uint64_t get_light_wallet_blockchain_height() const { return m_light_wallet_blockchain_height; } - /*! * \brief Gets the seed language */ @@ -1128,7 +1116,7 @@ private: void get_unconfirmed_payments_out(std::list>& unconfirmed_payments, const boost::optional& subaddr_account = boost::none, const std::set& subaddr_indices = {}) const; void get_unconfirmed_payments(std::list>& unconfirmed_payments, const boost::optional& subaddr_account = boost::none, const std::set& subaddr_indices = {}) const; - uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); } + uint64_t get_blockchain_current_height() const { return m_blockchain.size(); } void rescan_spent(); void rescan_blockchain(bool hard, bool refresh = true, bool keep_key_images = false); bool is_transfer_unlocked(const transfer_details& td); @@ -1582,24 +1570,6 @@ private: } template void handle_payment_changes(const T &res, std::false_type) {} - // Light wallet specific functions - // fetch unspent outs from lw node and store in m_transfers - void light_wallet_get_unspent_outs(); - // fetch txs and store in m_payments - void light_wallet_get_address_txs(); - // get_address_info - bool light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response); - // Login. new_address is true if address hasn't been used on lw node before. - bool light_wallet_login(bool &new_address); - // Send an import request to lw node. returns info about import fee, address and payment_id - bool light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response); - // get random outputs from light wallet server - void light_wallet_get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count); - // Parse rct string - bool light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const; - // check if key image is ours - bool light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index); - /* * "attributes" are a mechanism to store an arbitrary number of string values * on the level of the wallet as a whole, identified by keys. Their introduction, @@ -1767,6 +1737,7 @@ private: bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector &unused_transfers_indices, const std::vector &unused_dust_indices) const; std::vector get_only_rct(const std::vector &unused_dust_indices, const std::vector &unused_transfers_indices) const; void scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map> &tx_money_got_in_outs, std::vector &outs, bool pool); + void scan_protocol_tx_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map> &tx_money_got_in_outs, std::vector &outs, bool pool); void trim_hashchain(); crypto::key_image get_multisig_composite_key_image(size_t n) const; rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const std::unordered_set &ignore_set, std::unordered_set &used_L, std::unordered_set &new_used_L) const; @@ -1913,20 +1884,6 @@ private: // Aux transaction data from device serializable_unordered_map m_tx_device; - // Light wallet - bool m_light_wallet; /* sends view key to daemon for scanning */ - uint64_t m_light_wallet_scanned_block_height; - uint64_t m_light_wallet_blockchain_height; - uint64_t m_light_wallet_per_kb_fee = FEE_PER_KB; - bool m_light_wallet_connected; - uint64_t m_light_wallet_balance; - uint64_t m_light_wallet_unlocked_balance; - // Light wallet info needed to populate m_payment requires 2 separate api calls (get_address_txs and get_unspent_outs) - // We save the info from the first call in m_light_wallet_address_txs for easier lookup. - std::unordered_map m_light_wallet_address_txs; - // store calculated key image for faster lookup - serializable_unordered_map > m_key_image_cache; - std::string m_ring_database; bool m_ring_history_saved; std::unique_ptr m_ringdb; @@ -1956,6 +1913,8 @@ private: static boost::mutex default_daemon_address_lock; static std::string default_daemon_address; + + serializable_map m_protocol_txs; }; } BOOST_CLASS_VERSION(tools::wallet2, 30)