PaymentSearcher.hpp added

new_rpc
moneroexamples 6 years ago
parent 3639989ffe
commit 3551ab3c80

@ -3,6 +3,7 @@
//
#include "CurrentBlockchainStatus.h"
#include "PaymentSearcher.hpp"
#undef MONERO_DEFAULT_LOG_CATEGORY
@ -451,8 +452,8 @@ CurrentBlockchainStatus::search_if_payment_made(
uint64_t current_blockchain_height = get_current_blockchain_height();
cout << "current_blockchain_height: "
<< current_blockchain_height << '\n';
// cout << "current_blockchain_height: "
// << current_blockchain_height << '\n';
vector<transaction> txs_to_check;
@ -489,171 +490,47 @@ CurrentBlockchainStatus::search_if_payment_made(
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))
{
OMERROR << "Failed parsing hex to pod for "
"encrypted_payment_id8";
continue;
}
// 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,
bc_setup.import_payment_viewkey,
derivation))
{
OMERROR << "Cant get derived key for: " << "\n"
<< "pub_tx_key: " << tx_pub_key << " and "
<< "prv_view_key" << bc_setup.import_payment_viewkey;
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 (!mcore->decrypt_payment_id(
decrypted_payment_id8, tx_pub_key,
bc_setup.import_payment_viewkey))
{
OMERROR << "Cant decrypt decrypted_payment_id8: "
<< pod_to_hex(decrypted_payment_id8);
continue;
}
}
string decrypted_tx_payment_id_str
= pod_to_hex(decrypted_payment_id8);
crypto::hash8 expected_payment_id;
// 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,
bc_setup.import_payment_address
.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,
bc_setup.import_payment_viewkey,
output_idx_in_tx,
tx.rct_signatures
.ecdhInfo[output_idx_in_tx].mask,
rct_amount);
if (!hex_to_pod(payment_id_str, expected_payment_id))
{
OMERROR << "Cant convert payment id to pod: " << payment_id_str;
return false;
}
if (!r)
{
OMERROR << "Cant decode ringCT!";
return false;
}
PaymentSearcher<crypto::hash8> tx_searcher {
bc_setup.import_payment_address,
bc_setup.import_payment_viewkey,
mcore.get()};
amount = rct_amount;
}
} // if (mine_output && tx.version == 2)
auto found_amount_pair = std::make_pair(0ull, std::cend(txs_to_check));
try
{
found_amount_pair
= tx_searcher.search(expected_payment_id, txs_to_check);
}
catch (PaymentSearcherException const& e)
{
OMERROR << e.what();
return false;
}
if (mine_output)
total_received += amount;
}
if (found_amount_pair.first >= desired_amount)
{
string tx_hash_str = pod_to_hex(
get_transaction_hash(*found_amount_pair.second));
OMINFO << " Payment id check in tx: "
<< tx_hash_str
<< " found: " << total_received;
<< " found: " << found_amount_pair.first;
if (total_received >= desired_amount)
{
// the payment has been made.
tx_hash_with_payment = tx_hash_str;
OMINFO << "Import payment done";
return true;
}
// the payment has been made.
tx_hash_with_payment = tx_hash_str;
OMINFO << "Import payment done";
return true;
}
return false;

@ -189,7 +189,7 @@ public:
get_tx(crypto::hash const& tx_hash, transaction& tx) const;
virtual bool
decrypt_payment_id(crypto::hash8 &payment_id,
decrypt_payment_id(crypto::hash8& payment_id,
public_key const& public_key,
secret_key const& secret_key)
{

@ -13,9 +13,7 @@ OutputInputIdentification::OutputInputIdentification(
const secret_key* _v,
const transaction* _tx,
crypto::hash const& _tx_hash,
bool is_coinbase,
std::shared_ptr<CurrentBlockchainStatus> _current_bc_status)
: current_bc_status {_current_bc_status}
bool is_coinbase)
{
address_info = _a;
viewkey = _v;
@ -158,7 +156,8 @@ OutputInputIdentification::identify_outputs()
void
OutputInputIdentification::identify_inputs(
unordered_map<public_key, uint64_t> const& known_outputs_keys)
unordered_map<public_key, uint64_t> const& known_outputs_keys,
CurrentBlockchainStatus* current_bc_status)
{
vector<txin_to_key> input_key_imgs = xmreg::get_key_images(*tx);

@ -100,8 +100,6 @@ public:
vector<output_info> identified_outputs;
vector<input_info> identified_inputs;
std::shared_ptr<CurrentBlockchainStatus> current_bc_status;
// default constructor. Useful for unit tests
OutputInputIdentification() = default;
@ -109,9 +107,7 @@ public:
const secret_key* _v,
const transaction* _tx,
crypto::hash const& _tx_hash,
bool is_coinbase,
std::shared_ptr<CurrentBlockchainStatus>
_current_bc_status);
bool is_coinbase);
/**
* FIRST step. search for the incoming xmr using address, viewkey and
@ -139,8 +135,9 @@ public:
*
*/
virtual void
identify_inputs(unordered_map<public_key, uint64_t> const&
known_outputs_keys);
identify_inputs(
unordered_map<public_key, uint64_t> const& known_outputs_keys,
CurrentBlockchainStatus* current_bc_status);
virtual string const&
get_tx_hash_str();

@ -0,0 +1,164 @@
#pragma once
#include "om_log.h"
#include "OutputInputIdentification.h"
#include <map>
#include <utility>
namespace xmreg
{
class PaymentSearcherException: public std::runtime_error
{
using std::runtime_error::runtime_error;
};
/**
* Class for searching transactions having given payment_id
* This is primarly used to check if a payment was made
* for importing wallet
*/
template<typename Hash_T>
class PaymentSearcher
{
// null hash value for hash8 or regular hash
using null_hash_T = boost::value_initialized<Hash_T>;
public:
PaymentSearcher(
address_parse_info const& _address_info,
secret_key const& _view_key,
MicroCore* const _mcore):
address_info {_address_info},
view_key {_view_key},
mcore {_mcore}
{}
template<template<typename T,
typename A = std::allocator<T>>
class Cont_T>
std::pair<uint64_t, typename Cont_T<transaction>::const_iterator>
search(Hash_T const& expected_payment_id,
Cont_T<transaction> const& txs,
bool skip_coinbase = true)
{
//auto null_hash = null_hash_T();
uint64_t found_amount {0};
Hash_T null_hash {};
// iterator to last txs that we found containing
// our payment
typename Cont_T<transaction>::const_iterator found_tx_it
= std::cend(txs);
for (auto it = std::cbegin(txs);
it != std::cend(txs); ++it)
{
auto const& tx = *it;
bool is_coinbase_tx = is_coinbase(tx);
if (skip_coinbase && is_coinbase_tx)
continue;
// get payment id. by default we are intrested
// in short ids from integrated addresses
auto payment_id_tuple = xmreg::get_payment_id(tx);
auto pay_id = std::get<Hash_T>(payment_id_tuple);
if (pay_id == null_hash)
continue;
public_key tx_pub_key
= xmreg::get_tx_pub_key_from_received_outs(tx);
// we have pay_id. it can be crypto::hash8 or crypto:hash
// so naw we need to perform comparison of the pay_id found
// with the expected value of payment_id
// for hash8 we need to do decoding of the pay_id, as it is
// encoded
if (!payment_id_decryptor(pay_id, tx_pub_key))
{
throw PaymentSearcherException("Cant decrypt pay_id: "
+ pod_to_hex(pay_id));
}
if (pay_id != expected_payment_id)
continue;
// if everything ok with payment id, we proceed with
// checking how much xmr we got in that tx.
// for each output, in a tx, check if it belongs
// to the given account of specific address and viewkey
auto tx_hash = get_transaction_hash(tx);
OutputInputIdentification oi_identification {
&address_info, &view_key, &tx,
tx_hash, is_coinbase_tx};
oi_identification.identify_outputs();
if (!oi_identification.identified_outputs.empty())
{ // nothing was found
make_pair(found_amount, found_tx_it);
}
// now add the found outputs into Outputs tables
for (auto& out_info: oi_identification.identified_outputs)
found_amount += out_info.amount;
if (found_amount > 0)
found_tx_it = it;
}
return make_pair(found_amount, found_tx_it);
}
virtual ~PaymentSearcher() = default;
private:
address_parse_info const& address_info;
secret_key const& view_key;
Hash_T payment_id;
MicroCore* const mcore;
inline bool
payment_id_decryptor(
crypto::hash& payment_id,
public_key const& tx_pub_key)
{
// don't need to do anything for legacy payment ids
return true;
}
inline bool
payment_id_decryptor(
crypto::hash8& payment_id,
public_key const& tx_pub_key)
{
// overload for short payment id,
// the we are going to decrypt
return mcore->decrypt_payment_id(
payment_id, tx_pub_key, view_key);
}
};
}

@ -62,6 +62,9 @@ TxSearch::operator()()
uint64_t blocks_lookahead
= current_bc_status->get_bc_setup().blocks_search_lookahead;
auto current_bc_status_ptr = current_bc_status.get();
// we put everything in massive catch, as there are plenty ways in which
// an exceptions can be thrown here. Mostly from mysql.
// but because this is detatch thread, we cant catch them in main thread.
@ -170,7 +173,7 @@ TxSearch::operator()()
// and inputs in a given tx.
OutputInputIdentification oi_identification {
&address, &viewkey, &tx, tx_hash,
is_coinbase, current_bc_status};
is_coinbase};
// flag indicating whether the txs in the given block are spendable.
// this is true when block number is more than 10 blocks from current
@ -350,7 +353,9 @@ TxSearch::operator()()
// no need mutex here, as this will be exectued only after
// the above. there is no threads here.
oi_identification.identify_inputs(known_outputs_keys);
oi_identification.identify_inputs(
known_outputs_keys,
current_bc_status_ptr);
if (!oi_identification.identified_inputs.empty())
@ -659,6 +664,8 @@ TxSearch::find_txs_in_mempool(
uint64_t current_height = current_bc_status
->get_current_blockchain_height();
auto current_bc_status_ptr = current_bc_status.get();
known_outputs_t known_outputs_keys_copy = get_known_outputs_keys();
// since find_txs_in_mempool can be called outside of this thread,
@ -683,8 +690,7 @@ TxSearch::find_txs_in_mempool(
// Class that is resposnible for idenficitaction of our outputs
// and inputs in a given tx.
OutputInputIdentification oi_identification
{&address, &viewkey, &tx, tx_hash, coinbase,
current_bc_status};
{&address, &viewkey, &tx, tx_hash, coinbase};
// FIRSt step. to search for the incoming xmr, we use address,
// viewkey and
@ -730,7 +736,8 @@ TxSearch::find_txs_in_mempool(
// no need mutex here, as this will be exectued only after
// the above. there is no threads here.
oi_identification.identify_inputs(known_outputs_keys_copy);
oi_identification.identify_inputs(known_outputs_keys_copy,
current_bc_status_ptr);
if (!oi_identification.identified_inputs.empty())
{

@ -1531,7 +1531,7 @@ YourMoneroRequests::get_tx(
{
OutputInputIdentification oi_identification {
&address_info, &viewkey, &tx, tx_hash,
coinbase, current_bc_status};
coinbase};
oi_identification.identify_outputs();
@ -1643,11 +1643,12 @@ YourMoneroRequests::get_tx(
// and inputs in a given tx.
OutputInputIdentification oi_identification
{&address_info, &viewkey, &tx, tx_hash,
coinbase, current_bc_status};
coinbase};
// no need mutex here, as this will be exectued only
// after the above. there is no threads here.
oi_identification.identify_inputs(known_outputs_keys);
oi_identification.identify_inputs(known_outputs_keys,
current_bc_status.get());
json j_spent_outputs = json::array();

@ -710,15 +710,6 @@ get_payment_id(const vector<uint8_t>& extra,
}
bool
get_payment_id(const transaction& tx,
crypto::hash& payment_id,
crypto::hash8& payment_id8)
{
return get_payment_id(tx.extra, payment_id, payment_id8);
}
array<size_t, 5>
timestamp_difference(uint64_t t1, uint64_t t2)
{

@ -158,10 +158,26 @@ get_payment_id(const vector<uint8_t>& extra,
crypto::hash& payment_id,
crypto::hash8& payment_id8);
bool
inline bool
get_payment_id(const transaction& tx,
crypto::hash& payment_id,
crypto::hash8& payment_id8);
crypto::hash8& payment_id8)
{
return get_payment_id(tx.extra, payment_id, payment_id8);
}
inline tuple<crypto::hash, crypto::hash8>
get_payment_id(transaction const& tx)
{
crypto::hash payment_id;
crypto::hash8 payment_id8;
get_payment_id(tx.extra, payment_id, payment_id8);
return make_tuple(payment_id, payment_id8);
}
inline double
get_xmr(uint64_t core_amount)

@ -39,3 +39,9 @@ SETUP_TARGET_FOR_COVERAGE(
SETUP_TARGET_FOR_COVERAGE(
NAME txsearch_cov
EXECUTABLE txsearch_tests)
add_custom_target(cov
COMMAND ;
DEPENDS mysql_cov microcore_cov bcstatus_cov txsearch_cov
COMMENT "Runs all coverage tests"
)

@ -69,8 +69,7 @@ TEST_F(OUTPUTIDENT_TEST, NonDefaultConstruction)
bool is_coinbase {false};
xmreg::OutputInputIdentification oi {&address, &viewkey, &tx,
tx_hash, is_coinbase,
cbs_mock};
tx_hash, is_coinbase};
EXPECT_TRUE(true);
}
@ -87,8 +86,7 @@ TEST_F(OUTPUTIDENT_TEST, IncomingPreRingctTransaction)
ADDR_VIEWKEY_FROM_STRING(addr_9wq79, viewkey_9wq79, net_type);
xmreg::OutputInputIdentification oi {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
oi.identify_outputs();
EXPECT_EQ(oi.identified_outputs.size(), 1);
@ -157,8 +155,7 @@ TEST_F(OUTPUTIDENT_TEST, OutgingPreRingctTransaction)
::get_output_keys));
xmreg::OutputInputIdentification oi {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
xmreg::TxSearch::known_outputs_t known_outputs;
@ -168,7 +165,8 @@ TEST_F(OUTPUTIDENT_TEST, OutgingPreRingctTransaction)
known_outputs_csv_9wq792k, known_outputs));
oi.identify_outputs();
oi.identify_inputs(known_outputs);
oi.identify_inputs(known_outputs,
cbs_mock.get());
EXPECT_EQ(oi.identified_inputs.size(), 2);
@ -228,8 +226,7 @@ TEST_F(OUTPUTIDENT_TEST, SweepUnmixableTransaction)
::get_output_keys));
xmreg::OutputInputIdentification oi {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
xmreg::TxSearch::known_outputs_t known_outputs;
@ -253,7 +250,8 @@ TEST_F(OUTPUTIDENT_TEST, SweepUnmixableTransaction)
"ac89c9e71c93e6c292bc22185f3de25fc925f37119a4ec8d9980082072f40889");
oi.identify_inputs(known_outputs);
oi.identify_inputs(known_outputs,
cbs_mock.get());
EXPECT_EQ(oi.identified_inputs.size(), 3);
@ -302,8 +300,7 @@ TEST_F(OUTPUTIDENT_TEST, OutgingMixRingctTransaction)
::get_output_keys));
xmreg::OutputInputIdentification oi {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
xmreg::TxSearch::known_outputs_t known_outputs;
@ -320,7 +317,7 @@ TEST_F(OUTPUTIDENT_TEST, OutgingMixRingctTransaction)
EXPECT_EQ(oi.total_received, 42433713670000);
EXPECT_TRUE(oi.is_rct);
oi.identify_inputs(known_outputs);
oi.identify_inputs(known_outputs, cbs_mock.get());
// numer of inputs is basically number
// of all possible maches of our mixins.
@ -345,10 +342,10 @@ TEST_F(OUTPUTIDENT_TEST, OutgingMixRingctTransaction)
::get_output_keys));
xmreg::OutputInputIdentification oi2 {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
oi2.identify_inputs(empty_known_outputs);
oi2.identify_inputs(empty_known_outputs,
cbs_mock.get());
EXPECT_EQ(oi2.identified_inputs.size(), 0);
}
@ -387,8 +384,7 @@ TEST_F(OUTPUTIDENT_TEST, OutgingRingctTransaction)
::get_output_keys));
xmreg::OutputInputIdentification oi {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
xmreg::TxSearch::known_outputs_t known_outputs;
@ -405,7 +401,8 @@ TEST_F(OUTPUTIDENT_TEST, OutgingRingctTransaction)
EXPECT_EQ(oi.total_received, 3296300490000ull);
EXPECT_TRUE(oi.is_rct);
oi.identify_inputs(known_outputs);
oi.identify_inputs(known_outputs,
cbs_mock.get());
// numer of inputs is basically number
// of all possible maches of our mixins.
@ -427,10 +424,10 @@ TEST_F(OUTPUTIDENT_TEST, OutgingRingctTransaction)
.WillRepeatedly(Return(false));
xmreg::OutputInputIdentification oi2 {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
oi2.identify_inputs(known_outputs);
oi2.identify_inputs(known_outputs,
cbs_mock.get());
EXPECT_EQ(oi2.identified_inputs.size(), 0);
}
@ -448,8 +445,7 @@ TEST_F(OUTPUTIDENT_TEST, IncomingPreRingctCoinbaseTransaction)
ADDR_VIEWKEY_FROM_STRING(addr_9wq79, viewkey_9wq79, net_type);
xmreg::OutputInputIdentification oi {&address, &viewkey, &tx,
tx_hash, is_tx_coinbase,
cbs_mock};
tx_hash, is_tx_coinbase};
oi.identify_outputs();
EXPECT_EQ(oi.identified_outputs.size(), 6);

Loading…
Cancel
Save