You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
872 lines
28 KiB
872 lines
28 KiB
#include "UniversalIdentifier.hpp"
|
|
|
|
namespace xmreg
|
|
{
|
|
|
|
using epee::string_tools::pod_to_hex;
|
|
using epee::string_tools::hex_to_pod;
|
|
|
|
public_key
|
|
get_tx_pub_key_from_received_outs(transaction const& tx)
|
|
{
|
|
std::vector<tx_extra_field> tx_extra_fields;
|
|
|
|
if(!parse_tx_extra(tx.extra, tx_extra_fields))
|
|
{
|
|
// Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
|
|
}
|
|
|
|
// Due to a previous bug, there might be more than one tx pubkey in extra, one being
|
|
// the result of a previously discarded signature.
|
|
// For speed, since scanning for outputs is a slow process, we check whether extra
|
|
// contains more than one pubkey. If not, the first one is returned. If yes, they're
|
|
// checked for whether they yield at least one output
|
|
tx_extra_pub_key pub_key_field;
|
|
|
|
if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0))
|
|
{
|
|
return null_pkey;
|
|
}
|
|
|
|
public_key tx_pub_key = pub_key_field.pub_key;
|
|
|
|
bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1);
|
|
|
|
if (!two_found)
|
|
{
|
|
// easy case, just one found
|
|
return tx_pub_key;
|
|
}
|
|
else
|
|
{
|
|
// just return second one if there are two.
|
|
// this does not require private view key, as
|
|
// its not needed for my use case.
|
|
return pub_key_field.pub_key;
|
|
}
|
|
|
|
return null_pkey;
|
|
}
|
|
|
|
|
|
void
|
|
Output::identify(transaction const& tx,
|
|
public_key const& tx_pub_key,
|
|
vector<public_key> const& additional_tx_pub_keys)
|
|
{
|
|
auto tx_is_coinbase = is_coinbase(tx);
|
|
|
|
key_derivation derivation;
|
|
|
|
if (!generate_key_derivation(tx_pub_key,
|
|
*get_viewkey(), derivation))
|
|
{
|
|
static_assert(sizeof(derivation) == sizeof(rct::key),
|
|
"Mismatched sizes of key_derivation and rct::key");
|
|
|
|
// use identity derivation instead
|
|
// solution based on that found in wallet2.cpp in monero
|
|
// this will cause the tx output to be effectively skipped
|
|
memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
|
|
}
|
|
|
|
// since introduction of subaddresses, a tx can
|
|
// have extra public keys, thus we need additional
|
|
// derivations
|
|
|
|
vector<key_derivation> additional_derivations;
|
|
|
|
if (!additional_tx_pub_keys.empty())
|
|
{
|
|
additional_derivations.resize(additional_tx_pub_keys.size());
|
|
|
|
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
|
|
{
|
|
if (!generate_key_derivation(additional_tx_pub_keys[i],
|
|
*get_viewkey(),
|
|
additional_derivations[i]))
|
|
{
|
|
static_assert(sizeof(derivation) == sizeof(rct::key),
|
|
"Mismatched sizes of key_derivation and rct::key");
|
|
|
|
// use identity derivation instead
|
|
// solution based on that found in wallet2.cpp in monero
|
|
// this will cause the tx output to be effectively skipped
|
|
memcpy(&additional_derivations[i],
|
|
rct::identity().bytes,
|
|
sizeof(additional_derivations[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
auto const& pub_spend_key
|
|
= get_address()->address.m_spend_public_key;
|
|
|
|
// if we have PrimaryAccount we can check
|
|
// if a given output belongs to any of its
|
|
// its subaddresses
|
|
PrimaryAccount* pacc {nullptr};
|
|
|
|
if (acc && !acc->is_subaddress())
|
|
{
|
|
// so we have primary address
|
|
pacc = static_cast<PrimaryAccount*>(acc);
|
|
}
|
|
|
|
|
|
for (auto i = 0u; i < tx.vout.size(); ++i)
|
|
{
|
|
// i will act as output indxes in the tx
|
|
|
|
if (tx.vout[i].target.type() != typeid(txout_to_key))
|
|
continue;
|
|
|
|
// get tx input key
|
|
txout_to_key const& txout_key
|
|
= boost::get<txout_to_key>(tx.vout[i].target);
|
|
|
|
uint64_t amount = tx.vout[i].amount;
|
|
|
|
// calculate public spendkey using derivation
|
|
// and tx output key. If this is our output
|
|
// the caulcualted public spendkey should match
|
|
// our actuall public spend key avaiable in our
|
|
// public monero address. Primary address is
|
|
// a special case of subaddress.
|
|
|
|
// we are always going to have the subaddress_spend
|
|
// key if an output is ours
|
|
crypto::public_key subaddress_spendkey;
|
|
|
|
// however we might not have its index, in case
|
|
// we are not using primary addresses directly
|
|
// but instead use a subaddress for searching
|
|
// outputs
|
|
std::unique_ptr<subaddress_index> subaddr_idx;
|
|
|
|
hwdev.derive_subaddress_public_key(
|
|
txout_key.key, derivation, i,
|
|
subaddress_spendkey);
|
|
|
|
// this derivation is going to be saved
|
|
// it can be one of addiitnal derivations
|
|
// if we are dealing with multiouput tx
|
|
// which cointains subaddress
|
|
auto derivation_to_save = derivation;
|
|
|
|
bool mine_output {false};
|
|
|
|
if (!pacc)
|
|
{
|
|
// if pacc is not given, we check generated
|
|
// subaddress_spendkey against the spendkey
|
|
// of the address for which the Output identifier
|
|
// was instantiated
|
|
mine_output = (pub_spend_key == subaddress_spendkey);
|
|
}
|
|
else
|
|
{
|
|
// if pacc is given, we are going to use its
|
|
// subaddress unordered map to check if generated
|
|
// subaddress_spendkey is one of its keys. this is
|
|
// because the map can contain spendkeys of subaddreses
|
|
// assiciated with primary address. primary address's
|
|
// spendkey will be one of the keys as a special case
|
|
|
|
subaddr_idx = pacc->has_subaddress(subaddress_spendkey);
|
|
|
|
mine_output = bool {subaddr_idx};
|
|
}
|
|
|
|
auto with_additional = false;
|
|
|
|
if (!mine_output && !additional_tx_pub_keys.empty())
|
|
{
|
|
// check for output using additional tx public keys
|
|
hwdev.derive_subaddress_public_key(
|
|
txout_key.key, additional_derivations[i],
|
|
i,
|
|
subaddress_spendkey);
|
|
|
|
// do same comparison as above depending of the
|
|
// avaliabity of the PrimaryAddress Account
|
|
if (!pacc)
|
|
{
|
|
mine_output = (pub_spend_key == subaddress_spendkey);
|
|
}
|
|
else
|
|
{
|
|
subaddr_idx = pacc->has_subaddress(subaddress_spendkey);
|
|
mine_output = bool {subaddr_idx};
|
|
}
|
|
|
|
with_additional = true;
|
|
}
|
|
|
|
// placeholder variable for ringct outputs info
|
|
// that we need to save in database
|
|
|
|
rct::key rtc_outpk {0};
|
|
rct::key rtc_mask {0};
|
|
rct::key rtc_amount {0};
|
|
|
|
// if mine output has RingCT, i.e., tx version is 2
|
|
// need to decode its amount. otherwise its zero.
|
|
if (mine_output && tx.version == 2)
|
|
{
|
|
// initialize with regular amount value
|
|
// for ringct, except coinbase, it will be 0
|
|
uint64_t rct_amount_val = amount;
|
|
|
|
// cointbase txs have amounts in plain sight.
|
|
// so use amount from ringct, only for non-coinbase txs
|
|
if (!tx_is_coinbase)
|
|
{
|
|
// for ringct non-coinbase txs, these values are given
|
|
// with txs.
|
|
// coinbase ringctx dont have this information. we will provide
|
|
// them only when needed, in get_unspent_outputs. So go there
|
|
// to see how we deal with ringct coinbase txs when we spent
|
|
// them
|
|
// go to CurrentBlockchainStatus::construct_output_rct_field
|
|
// to see how we deal with coinbase ringct that are used
|
|
// as mixins
|
|
|
|
rtc_outpk = tx.rct_signatures.outPk[i].mask;
|
|
rtc_mask = tx.rct_signatures.ecdhInfo[i].mask;
|
|
rtc_amount = tx.rct_signatures.ecdhInfo[i].amount;
|
|
|
|
rct::key mask = tx.rct_signatures.ecdhInfo[i].mask;
|
|
|
|
derivation_to_save = !with_additional ? derivation
|
|
: additional_derivations[i];
|
|
|
|
auto r = decode_ringct(tx.rct_signatures,
|
|
derivation_to_save,
|
|
i,
|
|
mask,
|
|
rct_amount_val);
|
|
|
|
(void) mask;
|
|
|
|
if (!r)
|
|
{
|
|
throw std::runtime_error(
|
|
"Cant decode ringCT!");
|
|
}
|
|
|
|
amount = rct_amount_val;
|
|
|
|
} // if (!tx_is_coinbase)
|
|
|
|
} // if (mine_output && tx.version == 2)
|
|
|
|
if (mine_output)
|
|
{
|
|
total_received += amount;
|
|
|
|
identified_outputs.emplace_back(
|
|
info{
|
|
txout_key.key, amount, i,
|
|
derivation_to_save,
|
|
rtc_outpk, rtc_mask, rtc_amount,
|
|
subaddress_spendkey
|
|
});
|
|
|
|
if (subaddr_idx)
|
|
{
|
|
auto& out = identified_outputs.back();
|
|
out.subaddr_idx = *subaddr_idx;
|
|
|
|
// now need to check if we need to expand
|
|
// list of initial 10'000 of subaddresses.
|
|
// we do this only if account id (subaddress_idx.major)
|
|
// is greater than 0.
|
|
|
|
auto next_subaddr_acc_id
|
|
= pacc->get_next_subbaddress_acc_id();
|
|
|
|
auto no_of_new_accounts = std::min<int>(
|
|
static_cast<int>(out.subaddr_idx.major
|
|
+ PrimaryAccount::SUBADDRESS_LOOKAHEAD_MAJOR)
|
|
- next_subaddr_acc_id
|
|
, 50);
|
|
|
|
if (no_of_new_accounts > 0)
|
|
{
|
|
auto new_last_acc_id
|
|
= pacc->get_next_subbaddress_acc_id()
|
|
+ no_of_new_accounts;
|
|
pacc->expand_subaddresses(new_last_acc_id);
|
|
}
|
|
}
|
|
|
|
total_xmr += amount;
|
|
} // if (mine_output)
|
|
|
|
} // for (uint64_t i = 0; i < tx.vout.size(); ++i)
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
Output::decode_ringct(rct::rctSig const& rv,
|
|
crypto::key_derivation const& derivation,
|
|
unsigned int i,
|
|
rct::key& mask,
|
|
uint64_t& amount) const
|
|
{
|
|
try
|
|
{
|
|
crypto::secret_key scalar1;
|
|
|
|
hwdev.derivation_to_scalar(derivation, i, scalar1);
|
|
|
|
switch (rv.type)
|
|
{
|
|
case rct::RCTTypeSimple:
|
|
case rct::RCTTypeBulletproof:
|
|
case rct::RCTTypeBulletproof2:
|
|
amount = rct::decodeRctSimple(rv,
|
|
rct::sk2rct(scalar1),
|
|
i,
|
|
mask,
|
|
hwdev);
|
|
break;
|
|
case rct::RCTTypeFull:
|
|
amount = rct::decodeRct(rv,
|
|
rct::sk2rct(scalar1),
|
|
i,
|
|
mask,
|
|
hwdev);
|
|
break;
|
|
default:
|
|
cerr << "Unsupported rct type: " << rv.type << '\n';
|
|
return false;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
cerr << "Failed to decode input " << i << '\n';
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void Input::identify(transaction const& tx,
|
|
public_key const& tx_pub_key,
|
|
vector<public_key> const& additional_tx_pub_keys)
|
|
{
|
|
|
|
// if known_outputs is null do nothing
|
|
if (!known_outputs)
|
|
return;
|
|
|
|
auto search_misses {0};
|
|
|
|
auto input_no = tx.vin.size();
|
|
|
|
for (auto i = 0u; i < input_no; ++i)
|
|
{
|
|
if(tx.vin[i].type() != typeid(txin_to_key))
|
|
continue;
|
|
|
|
// get tx input key
|
|
txin_to_key const& in_key
|
|
= boost::get<txin_to_key>(tx.vin[i]);
|
|
|
|
// get absolute offsets of mixins
|
|
vector<uint64_t> absolute_offsets
|
|
= relative_output_offsets_to_absolute(
|
|
in_key.key_offsets);
|
|
|
|
// get public keys of outputs used in the mixins that
|
|
// match to the offests
|
|
vector<output_data_t> mixin_outputs;
|
|
|
|
// before we procced to fetch the outputs from lmdb
|
|
// check if we are not trying to get the outputs
|
|
// with non-existing offsets
|
|
|
|
auto num_outputs = mcore->get_num_outputs(in_key.amount);
|
|
|
|
if (absolute_offsets.back() >= num_outputs)
|
|
{
|
|
//cerr << "skipping offset" << endl;
|
|
// we try to get output with offset
|
|
// greater what is storred in lmdb
|
|
continue;
|
|
}
|
|
|
|
// this can THROW if no outputs are found
|
|
// but previous check should prevent this
|
|
mcore->get_output_key(in_key.amount,
|
|
absolute_offsets,
|
|
mixin_outputs);
|
|
|
|
// indicates whether we found any matching mixin in the current input
|
|
auto found_a_match {false};
|
|
|
|
// for each found output public key check if its ours or not
|
|
for (auto count = 0u; count < absolute_offsets.size(); ++count)
|
|
{
|
|
// get basic information about mixn's output
|
|
output_data_t const& output_data
|
|
= mixin_outputs.at(count);
|
|
|
|
// before going to the mysql, check our known outputs cash
|
|
// if the key exists. Its much faster than going to mysql
|
|
// for this.
|
|
|
|
auto it = known_outputs->find(output_data.pubkey);
|
|
|
|
if (it != known_outputs->end())
|
|
{
|
|
// this seems to be our mixin.
|
|
// save it into identified_inputs vector
|
|
|
|
identified_inputs.push_back(info {
|
|
in_key.k_image,
|
|
it->second, // amount
|
|
output_data.pubkey});
|
|
|
|
total_xmr += it->second;
|
|
|
|
found_a_match = true;
|
|
}
|
|
|
|
} // for (const cryptonote::output_data_t& output_data: outputs)
|
|
|
|
if (found_a_match == false)
|
|
{
|
|
// if we didnt find any match, break of the look.
|
|
// there is no reason to check remaining key images
|
|
// as when we spent something, our outputs should be
|
|
// in all inputs in a given txs. Thus, if a single input
|
|
// is without our output, we can assume this tx does
|
|
// not contain any of our spendings.
|
|
|
|
// just to be sure before we break out of this loop,
|
|
// do it only after two misses
|
|
|
|
if (++search_misses > 2)
|
|
break;
|
|
}
|
|
|
|
} // for (auto i = 0u; i < input_no; ++i)
|
|
}
|
|
|
|
|
|
/*
|
|
* Generate key_image of foran ith output
|
|
*/
|
|
bool
|
|
Input::generate_key_image(const crypto::key_derivation& derivation,
|
|
const std::size_t i,
|
|
const crypto::secret_key& sec_key,
|
|
const crypto::public_key& pub_key,
|
|
crypto::key_image& key_img) const
|
|
{
|
|
|
|
cryptonote::keypair in_ephemeral;
|
|
|
|
if (!crypto::derive_public_key(derivation, i,
|
|
pub_key,
|
|
in_ephemeral.pub))
|
|
{
|
|
cerr << "Error generating publick key " << pub_key << endl;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
crypto::derive_secret_key(derivation, i,
|
|
sec_key,
|
|
in_ephemeral.sec);
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
cerr << "Error generate secret image: " << e.what() << endl;
|
|
return false;
|
|
}
|
|
|
|
|
|
try
|
|
{
|
|
crypto::generate_key_image(in_ephemeral.pub,
|
|
in_ephemeral.sec,
|
|
key_img);
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
cerr << "Error generate key image: " << e.what() << endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
GuessInput::identify(transaction const& tx,
|
|
public_key const& tx_pub_key,
|
|
vector<public_key> const& additional_tx_pub_keys)
|
|
{
|
|
// to implement this method, we are just going
|
|
// to generate known_outputs_t = unordered_map<public_key, uint64_t>;
|
|
// based on ring members in each key image, and then
|
|
// we will call identify method of the Input base class.
|
|
|
|
// this will store guessed inputs
|
|
vector<info> local_identified_inputs;
|
|
|
|
// will keep output public key and amount
|
|
// of mixins in the given key image which
|
|
// are ours.
|
|
known_outputs_t known_outputs_map;
|
|
|
|
auto input_no = tx.vin.size();
|
|
|
|
for (auto i = 0u; i < input_no; ++i)
|
|
{
|
|
if(tx.vin[i].type() != typeid(txin_to_key))
|
|
continue;
|
|
|
|
// get tx input key
|
|
txin_to_key const& in_key
|
|
= boost::get<cryptonote::txin_to_key>(tx.vin[i]);
|
|
|
|
// get absolute offsets of mixins
|
|
auto absolute_offsets
|
|
= relative_output_offsets_to_absolute(
|
|
in_key.key_offsets);
|
|
|
|
//tx_out_index is pair::<transaction hash, output index>
|
|
vector<tx_out_index> indices;
|
|
|
|
// get tx hashes and indices in the txs for the
|
|
// given outputs of mixins
|
|
// this cant THROW DB_EXCEPTION
|
|
mcore->get_output_tx_and_index(
|
|
in_key.amount, absolute_offsets, indices);
|
|
|
|
// for each found mixin tx, check if any key image
|
|
// generated using our outputs in the mixin tx
|
|
// matches the given key image in the current tx
|
|
for (auto const& txi : indices)
|
|
{
|
|
auto const& mixin_tx_hash = txi.first;
|
|
auto const& output_index_in_tx = txi.second;
|
|
|
|
transaction mixin_tx;
|
|
|
|
if (!mcore->get_tx(mixin_tx_hash, mixin_tx))
|
|
{
|
|
throw std::runtime_error("Cant get tx: "
|
|
+ pod_to_hex(mixin_tx_hash));
|
|
}
|
|
|
|
// use Output universal identifier to identify our outputs
|
|
// in a mixin tx
|
|
|
|
std::unique_ptr<Output> output_identifier;
|
|
|
|
if (acc)
|
|
{
|
|
output_identifier = make_unique<Output>(acc);
|
|
}
|
|
else
|
|
{
|
|
output_identifier = make_unique<Output>(
|
|
get_address(), get_viewkey());
|
|
}
|
|
|
|
auto identifier = make_identifier(
|
|
mixin_tx, std::move(output_identifier));
|
|
|
|
identifier.identify();
|
|
|
|
for (auto const& found_output: identifier.get<Output>()->get())
|
|
{
|
|
// add found output into the map of known ouputs
|
|
known_outputs_map.insert(
|
|
{found_output.pub_key, found_output.amount});
|
|
}
|
|
|
|
} // for (auto const& txi : indices)
|
|
|
|
// so hopefully we found some of the mixins that are
|
|
// ours. So now, lets try use that information to
|
|
// guess which of them was used in the current
|
|
// key image.
|
|
|
|
} // for (auto i = 0u; i < input_no; ++i)
|
|
|
|
// to do this, set known_outputs to the known_outputs_map
|
|
known_outputs = &known_outputs_map;
|
|
|
|
// and now execute baseclasses (i.e. Input) identify
|
|
// method. The method will use known_outputs as
|
|
// its list of outputs
|
|
Input::identify(tx, tx_pub_key, additional_tx_pub_keys);
|
|
|
|
// copy what was identified using Input::identify
|
|
// into local_identified_inputs
|
|
local_identified_inputs.insert(local_identified_inputs.end(),
|
|
identified_inputs.begin(),
|
|
identified_inputs.end());
|
|
|
|
|
|
// once all key images have been scanned,
|
|
// copy local_identified_inputs into identified_inputs
|
|
// so that we can return them to the end user
|
|
identified_inputs = std::move(local_identified_inputs);
|
|
}
|
|
|
|
|
|
void RealInput::identify(transaction const& tx,
|
|
public_key const& tx_pub_key,
|
|
vector<public_key> const& additional_tx_pub_keys)
|
|
{
|
|
auto search_misses {0};
|
|
|
|
auto input_no = tx.vin.size();
|
|
|
|
for (auto i = 0u; i < input_no; ++i)
|
|
{
|
|
if(tx.vin[i].type() != typeid(txin_to_key))
|
|
continue;
|
|
|
|
// get tx input key
|
|
txin_to_key const& in_key
|
|
= boost::get<cryptonote::txin_to_key>(tx.vin[i]);
|
|
|
|
// get absolute offsets of mixins
|
|
auto absolute_offsets
|
|
= relative_output_offsets_to_absolute(
|
|
in_key.key_offsets);
|
|
|
|
|
|
//tx_out_index is pair::<transaction hash, output index>
|
|
vector<tx_out_index> indices;
|
|
|
|
// get tx hashes and indices in the txs for the
|
|
// given outputs of mixins
|
|
// this cant THROW DB_EXCEPTION
|
|
mcore->get_output_tx_and_index(
|
|
in_key.amount, absolute_offsets, indices);
|
|
|
|
// placeholder for information about key image that
|
|
// we will find as ours
|
|
unique_ptr<info> key_image_info {nullptr};
|
|
|
|
// for each found mixin tx, check if any key image
|
|
// generated using our outputs in the mixin tx
|
|
// matches the given key image in the current tx
|
|
for (auto const& txi : indices)
|
|
{
|
|
auto const& mixin_tx_hash = txi.first;
|
|
auto const& output_index_in_tx = txi.second;
|
|
|
|
transaction mixin_tx;
|
|
|
|
if (!mcore->get_tx(mixin_tx_hash, mixin_tx))
|
|
{
|
|
throw std::runtime_error("Cant get tx: "
|
|
+ pod_to_hex(mixin_tx_hash));
|
|
}
|
|
|
|
// use Output universal identifier to identify our outputs
|
|
// in a mixin tx
|
|
|
|
std::unique_ptr<Output> output_identifier;
|
|
|
|
if (acc)
|
|
{
|
|
output_identifier = make_unique<Output>(acc);
|
|
}
|
|
else
|
|
{
|
|
output_identifier = make_unique<Output>(
|
|
get_address(), get_viewkey());
|
|
}
|
|
|
|
auto identifier = make_identifier(
|
|
mixin_tx, std::move(output_identifier));
|
|
|
|
identifier.identify();
|
|
|
|
//cout << "mixin tx hash: " << get_transaction_hash(mixin_tx) << '\n';
|
|
|
|
for (auto const& found_output: identifier.get<Output>()->get())
|
|
{
|
|
//cout << "found_output: " << found_output << endl;
|
|
|
|
// generate key_image using this output
|
|
// to check for sure if the given key image is ours
|
|
// or not
|
|
crypto::key_image key_img_generated;
|
|
|
|
if (acc)
|
|
{
|
|
// if we have primary account, as we should when
|
|
// we want to include
|
|
// for spendings from subaddresses, use the below procedure
|
|
// to calcualted key_img_generated
|
|
|
|
cryptonote::keypair in_ephemeral;
|
|
|
|
if (!generate_key_image_helper_precomp(*acc->keys(),
|
|
found_output.pub_key,
|
|
found_output.derivation,
|
|
found_output.idx_in_tx,
|
|
found_output.subaddr_idx,
|
|
in_ephemeral,
|
|
key_img_generated,
|
|
hwdev))
|
|
{
|
|
throw std::runtime_error("Cant get key_img_generated");
|
|
}
|
|
|
|
(void) in_ephemeral;
|
|
}
|
|
else
|
|
{
|
|
// if we don't have acc, i.e., dont have info about subaddresses
|
|
// then use the simpler way
|
|
// to calcualate key_img_generated
|
|
|
|
if (!generate_key_image(found_output.derivation,
|
|
found_output.idx_in_tx, /* position in the tx */
|
|
*spendkey,
|
|
get_address()->address.m_spend_public_key,
|
|
key_img_generated))
|
|
{
|
|
throw std::runtime_error("Cant generate "
|
|
"key image for output: "
|
|
+ pod_to_hex(found_output.pub_key));
|
|
}
|
|
}
|
|
|
|
//cout << pod_to_hex(in_key.k_image) << " == "
|
|
//<< pod_to_hex(key_img_generated) << '\n';
|
|
|
|
// now check if current key image in the tx which we
|
|
// analyze matches generated key image
|
|
if (in_key.k_image == key_img_generated)
|
|
{
|
|
// this is our key image if they are equal!
|
|
key_image_info.reset(new info {key_img_generated,
|
|
found_output.amount,
|
|
found_output.pub_key});
|
|
|
|
break;
|
|
}
|
|
|
|
} // auto const& found_output: identifier.get<
|
|
|
|
// if key_image_info has been populated, there is no
|
|
// reason to keep check remaning outputs in the mixin tx
|
|
// instead add its info to identified_inputs and move on
|
|
// to the next key image
|
|
if (key_image_info)
|
|
{
|
|
identified_inputs.push_back(*key_image_info);
|
|
total_xmr += key_image_info->amount;
|
|
break;
|
|
}
|
|
|
|
} // for (auto const& txi : indices)
|
|
|
|
} // for (auto i = 0u; i < input_no; ++i)
|
|
}
|
|
|
|
|
|
|
|
// just a copy from bool
|
|
// device_default::encrypt_payment_id(crypto::hash8
|
|
// &payment_id, const crypto::public_key &public_key,
|
|
// const crypto::secret_key &secret_key)
|
|
template <typename HashT>
|
|
bool
|
|
PaymentID<HashT>::encrypt_payment_id(
|
|
crypto::hash8& payment_id,
|
|
crypto::public_key const& public_key,
|
|
crypto::secret_key const& secret_key) const
|
|
{
|
|
#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
|
|
|
|
crypto::key_derivation derivation;
|
|
crypto::hash hash;
|
|
char data[33]; /* A hash, and an extra byte */
|
|
|
|
if (!generate_key_derivation(public_key, secret_key, derivation))
|
|
return false;
|
|
|
|
memcpy(data, &derivation, 32);
|
|
data[32] = ENCRYPTED_PAYMENT_ID_TAIL;
|
|
cn_fast_hash(data, 33, hash);
|
|
|
|
for (size_t b = 0; b < 8; ++b)
|
|
payment_id.data[b] ^= hash.data[b];
|
|
|
|
return true;
|
|
}
|
|
|
|
template bool
|
|
PaymentID<crypto::hash8>::encrypt_payment_id(
|
|
crypto::hash8& payment_id,
|
|
crypto::public_key const& public_key,
|
|
crypto::secret_key const& secret_key) const;
|
|
|
|
template bool
|
|
PaymentID<crypto::hash>::encrypt_payment_id(
|
|
crypto::hash8& payment_id,
|
|
crypto::public_key const& public_key,
|
|
crypto::secret_key const& secret_key) const;
|
|
|
|
template <typename HashT>
|
|
typename PaymentID<HashT>::payments_t
|
|
PaymentID<HashT>::get_payment_id(
|
|
transaction const& tx) const
|
|
{
|
|
crypto::hash payment_id {0};
|
|
crypto::hash8 payment_id8 {0};
|
|
|
|
std::vector<tx_extra_field> tx_extra_fields;
|
|
|
|
if(!parse_tx_extra(tx.extra, tx_extra_fields))
|
|
{
|
|
return make_tuple(payment_id, payment_id8);
|
|
}
|
|
|
|
tx_extra_nonce extra_nonce;
|
|
|
|
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
|
|
{
|
|
// first check for encrypted id and then for normal one
|
|
if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce,
|
|
payment_id8))
|
|
{
|
|
return make_tuple(payment_id, payment_id8);
|
|
}
|
|
else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce,
|
|
payment_id))
|
|
{
|
|
return make_tuple(payment_id, payment_id8);
|
|
}
|
|
}
|
|
|
|
return make_tuple(payment_id, payment_id8);
|
|
}
|
|
|
|
template tuple<crypto::hash, crypto::hash8>
|
|
PaymentID<crypto::hash8>::get_payment_id(transaction const& tx) const;
|
|
template tuple<crypto::hash, crypto::hash8>
|
|
PaymentID<crypto::hash>::get_payment_id(transaction const& tx) const;
|
|
|
|
|
|
}
|