bool CurrentBlockchainStatus::read_mempool() { rpccalls rpc {deamon_url}; string error_msg; std::lock_guard<std::mutex> lck (getting_mempool_txs); // clear current mempool txs vector // repopulate it with each execution of read_mempool() // not very efficient but good enough for now. mempool_txs.clear(); // get txs in the mempool std::vector<tx_info> mempool_tx_info; if (!rpc.get_mempool(mempool_tx_info)) { cerr << "Getting mempool failed " << endl; return false; } // if dont have tx_blob member, construct tx // from json obtained from the rpc call for (size_t i = 0; i < mempool_tx_info.size(); ++i) { // get transaction info of the tx in the mempool tx_info _tx_info = mempool_tx_info.at(i); crypto::hash mem_tx_hash = null_hash; if (hex_to_pod(_tx_info.id_hash, mem_tx_hash)) { transaction tx; if (!xmreg::make_tx_from_json(_tx_info.tx_json, tx)) { cerr << "Cant make tx from _tx_info.tx_json" << endl; return false; } if (_tx_info.id_hash != pod_to_hex(get_transaction_hash(tx))) { cerr << "Hash of reconstructed tx from json does not match " "what we should get!" << endl; return false; } mempool_txs.emplace_back(_tx_info.receive_time, tx); } // if (hex_to_pod(_tx_info.id_hash, mem_tx_hash)) } // for (size_t i = 0; i < mempool_tx_info.size(); ++i) return true; }
string const& OutputInputIdentification::get_tx_pub_key_str() { if (tx_pub_key_str.empty()) tx_pub_key_str = pod_to_hex(tx_pub_key); return tx_pub_key_str; }
string const& OutputInputIdentification::get_tx_prefix_hash_str() { if (tx_prefix_hash_str.empty()) { tx_prefix_hash = get_transaction_prefix_hash(*tx); tx_prefix_hash_str = pod_to_hex(tx_prefix_hash); } return tx_prefix_hash_str; }
string const& OutputInputIdentification::get_tx_hash_str() { if (tx_hash_str.empty()) { tx_hash_str = pod_to_hex(tx_hash); } return tx_hash_str; }
string CurrentBlockchainStatus::get_payment_id_as_string(const transaction& tx) { crypto::hash payment_id = null_hash; crypto::hash8 payment_id8 = null_hash8; get_payment_id(tx, payment_id, payment_id8); string payment_id_str{""}; if (payment_id != null_hash) { payment_id_str = pod_to_hex(payment_id); } else if (payment_id8 != null_hash8) { payment_id_str = pod_to_hex(payment_id8); } return payment_id_str; }
void OutputInputIdentification::identify_outputs() { // <public_key , amount , out idx> vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs; outputs = get_ouputs_tuple(*tx); for (auto& out: outputs) { txout_to_key txout_k = std::get<0>(out); uint64_t amount = std::get<1>(out); uint64_t output_idx_in_tx = std::get<2>(out); // 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, output_idx_in_tx, address_info->address.m_spend_public_key, generated_tx_pubkey); // check if generated public key matches the current output's key bool mine_output = (txout_k.key == generated_tx_pubkey); //cout << "Chekcing output: " << pod_to_hex(txout_k.key) << " " // << "mine_output: " << mine_output << endl; // placeholder variable for ringct outputs info // that we need to save in database string rtc_outpk; string rtc_mask; string rtc_amount; // 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) { bool r; // for ringct non-coinbase txs, these values are provided 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 = pod_to_hex(tx->rct_signatures.outPk[output_idx_in_tx].mask); rtc_mask = pod_to_hex(tx->rct_signatures.ecdhInfo[output_idx_in_tx].mask); rtc_amount = pod_to_hex(tx->rct_signatures.ecdhInfo[output_idx_in_tx].amount); rct::key mask = tx->rct_signatures.ecdhInfo[output_idx_in_tx].mask; r = decode_ringct(tx->rct_signatures, tx_pub_key, *viewkey, output_idx_in_tx, mask, rct_amount_val); if (!r) { cerr << "Cant decode ringCT!" << endl; throw OutputInputIdentificationException("Cant decode ringCT!"); } amount = rct_amount_val; } } // if (mine_output && tx.version == 2) if (mine_output) { string out_key_str = pod_to_hex(txout_k.key); // found an output associated with the given address and viewkey string msg = fmt::format("tx_hash: {:s}, output_pub_key: {:s}\n", get_tx_hash_str(), out_key_str); cout << msg << endl; total_received += amount; identified_outputs.emplace_back( output_info{ txout_k.key, amount, output_idx_in_tx, rtc_outpk, rtc_mask, rtc_amount }); } // if (mine_output) } // for (const auto& out: outputs) }
void OutputInputIdentification::identify_inputs( const vector<pair<public_key, uint64_t>>& known_outputs_keys) { vector<txin_to_key> input_key_imgs = xmreg::get_key_images(*tx); size_t search_misses = {0}; // make timescale maps for mixins in input for (const txin_to_key& in_key: input_key_imgs) { // get absolute offsets of mixins std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute( in_key.key_offsets); // get public keys of outputs used in the mixins that match to the offests std::vector<cryptonote::output_data_t> mixin_outputs; if (!CurrentBlockchainStatus::get_output_keys(in_key.amount, absolute_offsets, mixin_outputs)) { cerr << "Mixins key images not found" << endl; continue; } // mixin counter size_t count = 0; // indicates whether we found any matching mixin in the current input bool found_a_match {false}; // for each found output public key check if its ours or not for (const uint64_t& abs_offset: absolute_offsets) { // get basic information about mixn's output cryptonote::output_data_t output_data = mixin_outputs[count]; //string output_public_key_str = pod_to_hex(output_data.pubkey); //cout << " - output_public_key_str: " << output_public_key_str << endl; // 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 = std::find_if( known_outputs_keys.begin(), known_outputs_keys.end(), [&output_data](pair<public_key, uint64_t> const& known_output) { return output_data.pubkey == known_output.first; }); if (it == known_outputs_keys.end()) { // this mixins's output is unknown. ++count; continue; } // this seems to be our mixin. // save it into identified_inputs vector identified_inputs.push_back(input_info { pod_to_hex(in_key.k_image), (*it).second, // amount output_data.pubkey}); found_a_match = true; ++count; } // 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 (const txin_to_key& in_key: input_key_imgs) }
tuple<string, string, string> CurrentBlockchainStatus::construct_output_rct_field( const uint64_t global_amount_index, const uint64_t out_amount) { transaction random_output_tx; uint64_t output_idx_in_tx; // we got random outputs, but now we need to get rct data of those // outputs, because by default frontend created ringct txs. if (!CurrentBlockchainStatus::get_tx_with_output( global_amount_index, out_amount, random_output_tx, output_idx_in_tx)) { cerr << "cant get random output transaction" << endl; return make_tuple(string {}, string {}, string {}); } //cout << pod_to_hex(out.out_key) << endl; //cout << pod_to_hex(get_transaction_hash(random_output_tx)) << endl; //cout << output_idx_in_tx << endl; // placeholder variable for ringct outputs info // that we need to save in database string rtc_outpk; string rtc_mask(64, '0'); string rtc_amount(64, '0'); if (random_output_tx.version > 1 && !is_coinbase(random_output_tx)) { rtc_outpk = pod_to_hex(random_output_tx.rct_signatures.outPk[output_idx_in_tx].mask); rtc_mask = pod_to_hex(random_output_tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask); rtc_amount = pod_to_hex(random_output_tx.rct_signatures.ecdhInfo[output_idx_in_tx].amount); } else { // for non ringct txs, we need to take it rct amount commitment // and sent to the frontend. the mask is zero mask for those, // as frontend will produce identy mask autmatically for non-ringct outputs output_data_t od = get_output_key(out_amount, global_amount_index); rtc_outpk = pod_to_hex(od.commitment); if (is_coinbase(random_output_tx)) // commenting this out. think its not needed. { // as this function provides keys for mixin outputs // not the ones we actually spend. // ringct coinbase txs are special. they have identity mask. // as suggested by this code: // https://github.com/monero-project/monero/blob/eacf2124b6822d088199179b18d4587404408e0f/src/wallet/wallet2.cpp#L893 // https://github.com/monero-project/monero/blob/master/src/blockchain_db/blockchain_db.cpp#L100 // rtc_mask = pod_to_hex(rct::identity()); } } return make_tuple(rtc_outpk, rtc_mask, rtc_amount); };
bool CurrentBlockchainStatus::search_if_payment_made( const string& payment_id_str, const uint64_t& desired_amount, string& tx_hash_with_payment) { vector<pair<uint64_t, transaction>> mempool_transactions = get_mempool_txs(); uint64_t current_blockchain_height = current_height; vector<transaction> txs_to_check; for (auto& mtx: mempool_transactions) { txs_to_check.push_back(mtx.second); } // apend txs in last to blocks into the txs_to_check vector for (uint64_t blk_i = current_blockchain_height - 10; blk_i <= current_blockchain_height; ++blk_i) { // get block cointaining this tx block blk; if (!get_block(blk_i, blk)) { cerr << "Cant get block of height: " + to_string(blk_i) << endl; return false; } list <cryptonote::transaction> blk_txs; if (!get_block_txs(blk, blk_txs)) { cerr << "Cant get transactions in block: " << to_string(blk_i) << endl; return false; } // combine mempool txs and txs from given number of // last blocks txs_to_check.insert(txs_to_check.end(), blk_txs.begin(), blk_txs.end()); } for (transaction& tx: txs_to_check) { if (is_coinbase(tx)) { // not interested in coinbase txs continue; } string tx_payment_id_str = get_payment_id_as_string(tx); // we are interested only in txs with encrypted payments id8 // they have length of 16 characters. if (tx_payment_id_str.length() != 16) { continue; } // we have some tx with encrypted payment_id8 // need to decode it using tx public key, and our // private view key, before we can comapre it is // what we are after. crypto::hash8 encrypted_payment_id8; if (!hex_to_pod(tx_payment_id_str, encrypted_payment_id8)) { cerr << "failed parsing hex to pod for encrypted_payment_id8" << '\n'; } // decrypt the encrypted_payment_id8 public_key tx_pub_key = xmreg::get_tx_pub_key_from_received_outs(tx); // public transaction key is combined with our viewkey // to create, so called, derived key. key_derivation derivation; if (!generate_key_derivation(tx_pub_key, import_payment_viewkey, derivation)) { cerr << "Cant get derived key for: " << "\n" << "pub_tx_key: " << tx_pub_key << " and " << "prv_view_key" << import_payment_viewkey << endl; return false; } // decrypt encrypted payment id, as used in integreated addresses crypto::hash8 decrypted_payment_id8 = encrypted_payment_id8; if (decrypted_payment_id8 != null_hash8) { if (!decrypt_payment_id(decrypted_payment_id8, tx_pub_key, import_payment_viewkey)) { cerr << "Cant decrypt decrypted_payment_id8: " << pod_to_hex(decrypted_payment_id8) << "\n"; } } string decrypted_tx_payment_id_str = pod_to_hex(decrypted_payment_id8); // check if decrypted payment id matches what we have stored // in mysql. if (payment_id_str != decrypted_tx_payment_id_str) { // check tx having specific payment id only continue; } // if everything ok with payment id, we proceed with // checking if the amount transfered is correct. // for each output, in a tx, check if it belongs // to the given account of specific address and viewkey // <public_key , amount , out idx> vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs; outputs = get_ouputs_tuple(tx); string tx_hash_str = pod_to_hex(get_transaction_hash(tx)); uint64_t total_received {0}; for (auto& out: outputs) { txout_to_key txout_k = std::get<0>(out); uint64_t amount = std::get<1>(out); uint64_t output_idx_in_tx = std::get<2>(out); // 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, output_idx_in_tx, import_payment_address.m_spend_public_key, generated_tx_pubkey); // check if generated public key matches the current output's key bool mine_output = (txout_k.key == generated_tx_pubkey); // 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 uint64_t rct_amount = amount; // cointbase txs have amounts in plain sight. // so use amount from ringct, only for non-coinbase txs if (!is_coinbase(tx)) { bool r; r = decode_ringct(tx.rct_signatures, tx_pub_key, import_payment_viewkey, output_idx_in_tx, tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask, rct_amount); if (!r) { cerr << "Cant decode ringCT!" << endl; throw TxSearchException("Cant decode ringCT!"); } amount = rct_amount; } } // if (mine_output && tx.version == 2) if (mine_output) { total_received += amount; } } cout << " - payment id check in tx: " << tx_hash_str << " found: " << total_received << endl; if (total_received >= desired_amount) { // the payment has been made. tx_hash_with_payment = tx_hash_str; return true; } } return false; }