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) }
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; }