#include "UniversalIdentifier.hpp" namespace xmreg { void Output::identify(transaction const& tx, public_key const& tx_pub_key, vector 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)) { throw std::runtime_error("generate_key_derivation failed"); } // since introduction of subaddresses, a tx can // have extra public keys, thus we need additional // derivations vector 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])) { throw std::runtime_error("additional generate_key_derivation failed"); } } } 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(tx.vout[i].target); uint64_t amount = tx.vout[i].amount; // get the tx output public key // that normally would be generated for us, // if someone had sent us some xmr. public_key generated_tx_pubkey; derive_public_key(derivation, i, get_address()->address.m_spend_public_key, generated_tx_pubkey); // cout << pod_to_hex(derivation) << ", " << i << ", " // << pod_to_hex(get_address()->address.m_spend_public_key) << ", " // << pod_to_hex(txout_key.key) << " == " // << pod_to_hex(generated_tx_pubkey) << '\n' << '\n'; // check if generated public key matches the current output's key bool mine_output = (txout_key.key == generated_tx_pubkey); auto with_additional = false; if (!mine_output && !additional_tx_pub_keys.empty()) { // check for output using additional tx public keys derive_public_key(additional_derivations[i], i, get_address()->address.m_spend_public_key, generated_tx_pubkey); mine_output = (txout_key.key == generated_tx_pubkey); 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; auto r = decode_ringct(tx.rct_signatures, !with_additional ? derivation : additional_derivations[i], i, mask, rct_amount_val); 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, rtc_outpk, rtc_mask, rtc_amount }); total_xmr += amount; } // if (mine_output) } // for (uint64_t i = 0; i < tx.vout.size(); ++i) } void Input::identify(transaction const& tx, public_key const& tx_pub_key, vector 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(tx.vin[i]); // get absolute offsets of mixins vector 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 mixin_outputs; // this can THROW if no outputs are found 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) } void RealInput::identify(transaction const& tx, public_key const& tx_pub_key, vector 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(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:: vector 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 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 auto identifier = make_identifier( mixin_tx, make_unique(get_address(), get_viewkey())); identifier.identify(); for (auto const& found_output: identifier.get()->get()) { // 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 (!xmreg::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)); } // 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()->get_outputs() // 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) } }