Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify non-standard received legacy enotes #43

Merged
merged 3 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ringct/rctTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extern "C" {
#include "serialization/debug_archive.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
#include "common/variant.h"


//Define this flag when debugging to get additional info on the console
Expand Down Expand Up @@ -88,6 +89,7 @@ namespace rct {
};
typedef std::vector<key> keyV; //vector of keys
typedef std::vector<keyV> keyM; //matrix of keys (indexed by column first)
using key_keyV_variant = tools::variant<key, keyV>;

//containers For CT operations
//if it's representing a private ctkey then "dest" contains the secret key of the address
Expand Down
28 changes: 20 additions & 8 deletions src/seraphis_core/legacy_core_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,8 @@ bool try_append_legacy_enote_ephemeral_pubkeys_to_tx_extra(const std::vector<rct
}
//-------------------------------------------------------------------------------------------------------------------
void extract_legacy_enote_ephemeral_pubkeys_from_tx_extra(const TxExtra &tx_extra,
crypto::public_key &legacy_main_enote_ephemeral_pubkey_out,
std::vector<crypto::public_key> &legacy_additional_enote_ephemeral_pubkeys)
rct::key_keyV_variant &legacy_main_enote_ephemeral_pubkeys_out,
std::vector<crypto::public_key> &legacy_additional_enote_ephemeral_pubkeys_out)
{
// 1. parse field
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
Expand All @@ -385,17 +385,29 @@ void extract_legacy_enote_ephemeral_pubkeys_from_tx_extra(const TxExtra &tx_extr
// main enote ephemeral pubkey for key derivations
cryptonote::tx_extra_pub_key pub_key_field;

if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, pub_key_field))
legacy_main_enote_ephemeral_pubkey_out = pub_key_field.pub_key;
else
legacy_main_enote_ephemeral_pubkey_out = rct::rct2pk(rct::I);
size_t pk_index = 0;
while (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++))
{
if (pk_index == 1) // first iteration
{
legacy_main_enote_ephemeral_pubkeys_out = rct::pk2rct(pub_key_field.pub_key);
continue;
}
else if (pk_index == 2) // second iteration
legacy_main_enote_ephemeral_pubkeys_out = rct::keyV{legacy_main_enote_ephemeral_pubkeys_out.unwrap<rct::key>()};
legacy_main_enote_ephemeral_pubkeys_out.unwrap<rct::keyV>().push_back(rct::pk2rct(pub_key_field.pub_key));
}

if (legacy_main_enote_ephemeral_pubkeys_out.is_type<rct::key>() &&
legacy_main_enote_ephemeral_pubkeys_out.unwrap<rct::key>() == rct::key{})
legacy_main_enote_ephemeral_pubkeys_out = rct::I;

// 3. try to get 'additional' enote ephemeral pubkeys (one per output): r_t K^v_t
cryptonote::tx_extra_additional_pub_keys additional_pub_keys_field;
legacy_additional_enote_ephemeral_pubkeys.clear();
legacy_additional_enote_ephemeral_pubkeys_out.clear();

if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, additional_pub_keys_field))
legacy_additional_enote_ephemeral_pubkeys = additional_pub_keys_field.data;
legacy_additional_enote_ephemeral_pubkeys_out = additional_pub_keys_field.data;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace sp
4 changes: 2 additions & 2 deletions src/seraphis_core/legacy_core_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,12 @@ bool try_append_legacy_enote_ephemeral_pubkeys_to_tx_extra(const std::vector<rct
/**
* brief: extract_legacy_enote_ephemeral_pubkeys_from_tx_extra - find legacy enote ephemeral pubkeys in a tx extra field
* param: tx_extra - memo field (byte vector)
* outparam: legacy_main_enote_ephemeral_pubkey_out - r G (this should always be present, because yucky legacy tx gen code)
* outparam: legacy_main_enote_ephemeral_pubkeys_out - r G (this should always be present, because yucky legacy tx gen code)
* outparam: legacy_additional_enote_ephemeral_pubkeys_out - [empty if no subaddress destinations]
* [otherwise r_0 K^v_0, ..., r_n K^v_n]
*/
void extract_legacy_enote_ephemeral_pubkeys_from_tx_extra(const TxExtra &tx_extra,
crypto::public_key &legacy_main_enote_ephemeral_pubkey_out,
rct::key_keyV_variant &legacy_main_enote_ephemeral_pubkeys_out,
std::vector<crypto::public_key> &legacy_additional_enote_ephemeral_pubkeys_out);

} //namespace sp
121 changes: 84 additions & 37 deletions src/seraphis_main/scan_balance_recovery_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,30 @@ static std::unordered_set<rct::key> process_chunk_sp_selfsend_pass(
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static rct::key& key_by_index_ref(rct::key_keyV_variant &variant, size_t index)
{
struct visitor final : public tools::variant_static_visitor<rct::key&>
{
visitor(size_t index) : id{index} {}
size_t id;

using variant_static_visitor::operator(); //for blank overload
rct::key& operator()(rct::key &key_variant) const
{
CHECK_AND_ASSERT_THROW_MES(id == 0, "invalid index for rct::key.");
return key_variant;
}
rct::key& operator()(rct::keyV &key_variant) const
{
CHECK_AND_ASSERT_THROW_MES(id < key_variant.size(), "index greater than vector size.");
return key_variant[id];
}
};

return variant.visit(visitor{index});
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey,
const std::unordered_map<rct::key, cryptonote::subaddress_index> &legacy_subaddress_map,
const crypto::secret_key &legacy_view_privkey,
Expand All @@ -371,25 +395,22 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey,
basic_records_in_tx_out.clear();

// 1. extract enote ephemeral pubkeys from the memo
crypto::public_key legacy_main_enote_ephemeral_pubkey;
rct::key_keyV_variant legacy_main_enote_ephemeral_pubkeys = rct::key{};
std::vector<crypto::public_key> legacy_additional_enote_ephemeral_pubkeys;

extract_legacy_enote_ephemeral_pubkeys_from_tx_extra(tx_memo,
legacy_main_enote_ephemeral_pubkey,
legacy_main_enote_ephemeral_pubkeys,
legacy_additional_enote_ephemeral_pubkeys);

// 2. check if there are a valid number of additional enote ephemeral pubkeys
if (legacy_additional_enote_ephemeral_pubkeys.size() > 0 &&
legacy_additional_enote_ephemeral_pubkeys.size() != enotes_in_tx.size())
return false;

// 3. scan each enote in the tx using the 'additional enote ephemeral pubkeys'
// 2. scan each enote in the tx using the 'additional enote ephemeral pubkeys'
// - this step is automatically skipped if legacy_additional_enote_ephemeral_pubkeys.size() == 0
crypto::key_derivation temp_DH_derivation;
LegacyContextualBasicEnoteRecordV1 temp_contextual_record{};
bool found_an_enote{false};

for (std::size_t enote_index{0}; enote_index < legacy_additional_enote_ephemeral_pubkeys.size(); ++enote_index)
for (std::size_t enote_index{0}; enote_index < legacy_additional_enote_ephemeral_pubkeys.size() &&
enote_index < enotes_in_tx.size();
++enote_index)
{
// a. compute the DH derivation for this enote ephemeral pubkey
hwdev.generate_key_derivation(legacy_additional_enote_ephemeral_pubkeys[enote_index],
Expand Down Expand Up @@ -423,41 +444,67 @@ bool try_find_legacy_enotes_in_tx(const rct::key &legacy_base_spend_pubkey,
found_an_enote = true;
}

// 4. check if there is a main enote ephemeral pubkey
if (legacy_main_enote_ephemeral_pubkey == rct::rct2pk(rct::I))
// 3. check if there is a main enote ephemeral pubkey
if (legacy_main_enote_ephemeral_pubkeys.is_type<rct::key>() &&
legacy_main_enote_ephemeral_pubkeys.unwrap<rct::key>() == rct::I)
return found_an_enote;

// 5. compute the key derivation for the main enote ephemeral pubkey
hwdev.generate_key_derivation(legacy_main_enote_ephemeral_pubkey, legacy_view_privkey, temp_DH_derivation);
// 4. compute the key derivations for all main enote ephemeral pubkeys
rct::key_keyV_variant temp_DH_derivations = rct::key{};
const auto pubkeys = legacy_main_enote_ephemeral_pubkeys.try_unwrap<rct::keyV>();
if (pubkeys)
{
rct::keyV temp{};
temp.reserve(pubkeys->size());
for (const rct::key &enote_ephemeral_pubkey : *pubkeys) {
hwdev.generate_key_derivation(rct::rct2pk(enote_ephemeral_pubkey), legacy_view_privkey, temp_DH_derivation);
temp.emplace_back((rct::key &) temp_DH_derivation);
}
temp_DH_derivations = temp;
}
else
{
hwdev.generate_key_derivation(rct::rct2pk(legacy_main_enote_ephemeral_pubkeys.unwrap<rct::key>()), legacy_view_privkey, temp_DH_derivation);
temp_DH_derivations = (rct::key &) temp_DH_derivation;
}

// 6. scan all enotes using the main key derivation
// 5. scan all enotes using key derivations for every ephemeral pub key
for (std::size_t enote_index{0}; enote_index < enotes_in_tx.size(); ++enote_index)
{
// a. try to recover a contextual basic record from the enote
if (!try_view_scan_legacy_enote_v1(legacy_base_spend_pubkey,
legacy_subaddress_map,
block_index,
block_timestamp,
transaction_id,
total_enotes_before_tx,
enote_index,
unlock_time,
tx_memo,
enotes_in_tx[enote_index],
legacy_main_enote_ephemeral_pubkey,
temp_DH_derivation,
origin_status,
hwdev,
temp_contextual_record))
continue;
// all ephemeral pub key - DH_derivation pairs
for (std::size_t pk_index{0};
(temp_DH_derivations.is_type<rct::keyV>() && pk_index < temp_DH_derivations.unwrap<rct::keyV>().size()) ||
(temp_DH_derivations.is_type<rct::key>() && pk_index < 1);
++pk_index)
{
crypto::public_key temp_legacy_main_enote_ephemeral_pubkey = rct2pk(key_by_index_ref(legacy_main_enote_ephemeral_pubkeys, pk_index));
temp_DH_derivation = (crypto::key_derivation &) key_by_index_ref(temp_DH_derivations, pk_index);
// a. try to recover a contextual basic record from the enote
if (!try_view_scan_legacy_enote_v1(legacy_base_spend_pubkey,
legacy_subaddress_map,
block_index,
block_timestamp,
transaction_id,
total_enotes_before_tx,
enote_index,
unlock_time,
tx_memo,
enotes_in_tx[enote_index],
temp_legacy_main_enote_ephemeral_pubkey,
temp_DH_derivation,
origin_status,
hwdev,
temp_contextual_record))
continue;

// b. save the contextual basic record
// note: it is possible for enotes with duplicate onetime addresses to be added here; it is assumed the
// upstream caller will be able to handle those without problems
basic_records_in_tx_out.emplace_back(temp_contextual_record);
// b. save the contextual basic record
// note: it is possible for enotes with duplicate onetime addresses to be added here; it is assumed the
// upstream caller will be able to handle those without problems
basic_records_in_tx_out.emplace_back(temp_contextual_record);

// c. record that an owned enote has been found
found_an_enote = true;
// c. record that an owned enote has been found
found_an_enote = true;
}
}

return found_an_enote;
Expand Down
Loading