TxUnlockChecker class added and tested

pull/93/merge
moneroexamples 6 years ago
parent 4d8b631e1b
commit 118e4ef1b0

4
.gitignore vendored

@ -2,8 +2,12 @@
.sass-cache .sass-cache
*.*~ *.*~
*.user *.user
*.user.*
.idea/ .idea/
cmake-build-debug/ cmake-build-debug/
ext/restbed/dependency/catch/ ext/restbed/dependency/catch/
build build
.cproject
.project
.settings/

@ -17,7 +17,8 @@ set(SOURCE_FILES
version.h.in version.h.in
BlockchainSetup.cpp BlockchainSetup.cpp
ThreadRAII.cpp ThreadRAII.cpp
MysqlPing.cpp) MysqlPing.cpp
TxUnlockChecker)
# make static library called libmyxrm # make static library called libmyxrm
# that we are going to link to # that we are going to link to

@ -31,7 +31,8 @@ CurrentBlockchainStatus::start_monitor_blockchain_thread()
{ {
network_type net_type = bc_setup.net_type; network_type net_type = bc_setup.net_type;
TxSearch::set_search_thread_life(bc_setup.search_thread_life_in_seconds); TxSearch::set_search_thread_life(
bc_setup.search_thread_life_in_seconds);
if (!is_running) if (!is_running)
{ {
@ -42,12 +43,13 @@ CurrentBlockchainStatus::start_monitor_blockchain_thread()
update_current_blockchain_height(); update_current_blockchain_height();
read_mempool(); read_mempool();
cout << "Check block height: " << current_height cout << "Check block height: " << current_height
<< " no of mempool txs: " << mempool_txs.size(); << " no of mempool txs: " << mempool_txs.size()
cout << endl; << '\n';
clean_search_thread_map(); clean_search_thread_map();
std::this_thread::sleep_for( std::this_thread::sleep_for(
std::chrono::seconds( std::chrono::seconds(
bc_setup.refresh_block_status_every_seconds)); bc_setup
.refresh_block_status_every_seconds));
} }
}}; }};
@ -59,14 +61,14 @@ CurrentBlockchainStatus::start_monitor_blockchain_thread()
uint64_t uint64_t
CurrentBlockchainStatus::get_current_blockchain_height() CurrentBlockchainStatus::get_current_blockchain_height()
{ {
return mcore->get_current_blockchain_height() - 1; return current_height;
} }
void void
CurrentBlockchainStatus::update_current_blockchain_height() CurrentBlockchainStatus::update_current_blockchain_height()
{ {
current_height = get_current_blockchain_height(); current_height = mcore->get_current_blockchain_height() - 1;
} }
bool bool
@ -79,57 +81,22 @@ CurrentBlockchainStatus::init_monero_blockchain()
// initialize the core using the blockchain path // initialize the core using the blockchain path
return mcore->init(bc_setup.blockchain_path, bc_setup.net_type);} return mcore->init(bc_setup.blockchain_path, bc_setup.net_type);}
// taken from
// bool wallet2::is_transfer_unlocked(uint64_t unlock_time,
//uint64_t block_height) const
bool bool
CurrentBlockchainStatus::is_tx_unlocked( CurrentBlockchainStatus::is_tx_unlocked(
uint64_t unlock_time, uint64_t unlock_time,
uint64_t block_height) uint64_t block_height,
TxUnlockChecker const& tx_unlock_checker)
{ {
if(!is_tx_spendtime_unlocked(unlock_time, block_height)) return tx_unlock_checker.is_unlocked(bc_setup.net_type,
return false; current_height,
unlock_time,
if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > current_height + 1) block_height);
return false;
return true;
} }
bool
CurrentBlockchainStatus::is_tx_spendtime_unlocked(
uint64_t unlock_time,
uint64_t block_height)
{
if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
//interpret as block index
if(current_height + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
return true;
else
return false;
}
else
{
//interpret as time
uint64_t current_time = static_cast<uint64_t>(time(NULL));
// XXX: this needs to be fast, so we'd need to get the starting heights
// from the daemon to be correct once voting kicks in
uint64_t v2height = bc_setup.net_type == TESTNET ? 624634 : bc_setup.net_type == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827;
uint64_t leeway = block_height < v2height
? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1
: CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2;
if(current_time + leeway >= unlock_time)
return true;
else
return false;
}
return false;
}
bool bool
CurrentBlockchainStatus::get_block(uint64_t height, block &blk) CurrentBlockchainStatus::get_block(uint64_t height, block &blk)
{ {
@ -165,7 +132,9 @@ CurrentBlockchainStatus::get_block_txs(
if (!mcore->get_transactions(blk.tx_hashes, blk_txs, missed_txs)) if (!mcore->get_transactions(blk.tx_hashes, blk_txs, missed_txs))
{ {
cerr << "Cant get transactions in block: " << get_block_hash(blk) << endl; cerr << "Cant get transactions in block: "
<< get_block_hash(blk) << endl;
return false; return false;
} }
@ -180,7 +149,8 @@ CurrentBlockchainStatus::get_txs(
{ {
if (!mcore->get_transactions(txs_to_get, txs, missed_txs)) if (!mcore->get_transactions(txs_to_get, txs, missed_txs))
{ {
cerr << "CurrentBlockchainStatus::get_txs: cant get transactions!\n"; cerr << "CurrentBlockchainStatus::get_txs: "
"cant get transactions!\n";
return false; return false;
} }
@ -194,14 +164,18 @@ CurrentBlockchainStatus::tx_exist(const crypto::hash& tx_hash)
} }
bool bool
CurrentBlockchainStatus::tx_exist(const crypto::hash& tx_hash, uint64_t& tx_index) CurrentBlockchainStatus::tx_exist(
const crypto::hash& tx_hash,
uint64_t& tx_index)
{ {
return mcore->tx_exists(tx_hash, tx_index); return mcore->tx_exists(tx_hash, tx_index);
} }
bool bool
CurrentBlockchainStatus::tx_exist(const string& tx_hash_str, uint64_t& tx_index) CurrentBlockchainStatus::tx_exist(
const string& tx_hash_str,
uint64_t& tx_index)
{ {
crypto::hash tx_hash; crypto::hash tx_hash;
@ -293,8 +267,9 @@ CurrentBlockchainStatus::get_account_integrated_address_as_str(
} }
bool bool
CurrentBlockchainStatus::get_amount_specific_indices(const crypto::hash& tx_hash, CurrentBlockchainStatus::get_amount_specific_indices(
vector<uint64_t>& out_indices) const crypto::hash& tx_hash,
vector<uint64_t>& out_indices)
{ {
try try
{ {
@ -320,13 +295,15 @@ bool
CurrentBlockchainStatus::get_random_outputs( CurrentBlockchainStatus::get_random_outputs(
const vector<uint64_t>& amounts, const vector<uint64_t>& amounts,
const uint64_t& outs_count, const uint64_t& outs_count,
vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& found_outputs) vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS
::outs_for_amount>& found_outputs)
{ {
rpccalls rpc {bc_setup.deamon_url}; rpccalls rpc {bc_setup.deamon_url};
string error_msg; string error_msg;
if (!rpc.get_random_outs_for_amounts(amounts, outs_count, found_outputs, error_msg)) if (!rpc.get_random_outs_for_amounts(
amounts, outs_count, found_outputs, error_msg))
{ {
cerr << "rpc.get_random_outs_for_amounts failed" << endl; cerr << "rpc.get_random_outs_for_amounts failed" << endl;
return false; return false;
@ -357,7 +334,8 @@ CurrentBlockchainStatus::get_output(
bool bool
CurrentBlockchainStatus::get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated) CurrentBlockchainStatus::get_dynamic_per_kb_fee_estimate(
uint64_t& fee_estimated)
{ {
rpccalls rpc {bc_setup.deamon_url}; rpccalls rpc {bc_setup.deamon_url};
@ -376,7 +354,10 @@ CurrentBlockchainStatus::get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated
bool bool
CurrentBlockchainStatus::commit_tx(const string& tx_blob, string& error_msg, bool do_not_relay) CurrentBlockchainStatus::commit_tx(
const string& tx_blob,
string& error_msg,
bool do_not_relay)
{ {
rpccalls rpc {bc_setup.deamon_url}; rpccalls rpc {bc_setup.deamon_url};
@ -455,7 +436,8 @@ CurrentBlockchainStatus::search_if_payment_made(
string& tx_hash_with_payment) string& tx_hash_with_payment)
{ {
vector<pair<uint64_t, transaction>> mempool_transactions = get_mempool_txs(); vector<pair<uint64_t, transaction>> mempool_transactions
= get_mempool_txs();
uint64_t current_blockchain_height = current_height; uint64_t current_blockchain_height = current_height;
@ -475,7 +457,8 @@ CurrentBlockchainStatus::search_if_payment_made(
block blk; block blk;
if (!get_block(blk_i, blk)) { if (!get_block(blk_i, blk)) {
cerr << "Cant get block of height: " + to_string(blk_i) << endl; cerr << "Cant get block of height: "
+ to_string(blk_i) << endl;
return false; return false;
} }
@ -484,13 +467,15 @@ CurrentBlockchainStatus::search_if_payment_made(
if (!get_block_txs(blk, blk_txs, missed_txs)) if (!get_block_txs(blk, blk_txs, missed_txs))
{ {
cerr << "Cant get transactions in block: " << to_string(blk_i) << endl; cerr << "Cant get transactions in block: "
<< to_string(blk_i) << endl;
return false; return false;
} }
// combine mempool txs and txs from given number of // combine mempool txs and txs from given number of
// last blocks // last blocks
txs_to_check.insert(txs_to_check.end(), blk_txs.begin(), blk_txs.end()); txs_to_check.insert(txs_to_check.end(),
blk_txs.begin(), blk_txs.end());
} }
for (transaction& tx: txs_to_check) for (transaction& tx: txs_to_check)
@ -520,23 +505,28 @@ CurrentBlockchainStatus::search_if_payment_made(
if (!hex_to_pod(tx_payment_id_str, 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'; cerr << "failed parsing hex to pod for encrypted_payment_id8"
<< '\n';
} }
// decrypt the encrypted_payment_id8 // decrypt the encrypted_payment_id8
public_key tx_pub_key = xmreg::get_tx_pub_key_from_received_outs(tx); public_key tx_pub_key
= xmreg::get_tx_pub_key_from_received_outs(tx);
// public transaction key is combined with our viewkey // public transaction key is combined with our viewkey
// to create, so called, derived key. // to create, so called, derived key.
key_derivation derivation; key_derivation derivation;
if (!generate_key_derivation(tx_pub_key, bc_setup.import_payment_viewkey, derivation)) if (!generate_key_derivation(tx_pub_key,
bc_setup.import_payment_viewkey,
derivation))
{ {
cerr << "Cant get derived key for: " << "\n" cerr << "Cant get derived key for: " << "\n"
<< "pub_tx_key: " << tx_pub_key << " and " << "pub_tx_key: " << tx_pub_key << " and "
<< "prv_view_key" << bc_setup.import_payment_viewkey << endl; << "prv_view_key" << bc_setup.import_payment_viewkey
<< endl;
return false; return false;
} }
@ -547,14 +537,16 @@ CurrentBlockchainStatus::search_if_payment_made(
if (decrypted_payment_id8 != null_hash8) if (decrypted_payment_id8 != null_hash8)
{ {
if (!mcore->get_device()->decrypt_payment_id( if (!mcore->get_device()->decrypt_payment_id(
decrypted_payment_id8, tx_pub_key, bc_setup.import_payment_viewkey)) decrypted_payment_id8, tx_pub_key,
bc_setup.import_payment_viewkey))
{ {
cerr << "Cant decrypt decrypted_payment_id8: " cerr << "Cant decrypt decrypted_payment_id8: "
<< pod_to_hex(decrypted_payment_id8) << "\n"; << pod_to_hex(decrypted_payment_id8) << "\n";
} }
} }
string decrypted_tx_payment_id_str = pod_to_hex(decrypted_payment_id8); string decrypted_tx_payment_id_str
= pod_to_hex(decrypted_payment_id8);
// check if decrypted payment id matches what we have stored // check if decrypted payment id matches what we have stored
// in mysql. // in mysql.
@ -594,10 +586,12 @@ CurrentBlockchainStatus::search_if_payment_made(
derive_public_key(derivation, derive_public_key(derivation,
output_idx_in_tx, output_idx_in_tx,
bc_setup.import_payment_address.address.m_spend_public_key, bc_setup.import_payment_address
.address.m_spend_public_key,
generated_tx_pubkey); generated_tx_pubkey);
// check if generated public key matches the current output's key // check if generated public key matches the current
// output's key
bool mine_output = (txout_k.key == generated_tx_pubkey); bool mine_output = (txout_k.key == generated_tx_pubkey);
// if mine output has RingCT, i.e., tx version is 2 // if mine output has RingCT, i.e., tx version is 2
@ -617,7 +611,8 @@ CurrentBlockchainStatus::search_if_payment_made(
tx_pub_key, tx_pub_key,
bc_setup.import_payment_viewkey, bc_setup.import_payment_viewkey,
output_idx_in_tx, output_idx_in_tx,
tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask, tx.rct_signatures
.ecdhInfo[output_idx_in_tx].mask,
rct_amount); rct_amount);
if (!r) if (!r)
@ -679,7 +674,9 @@ CurrentBlockchainStatus::get_payment_id_as_string(const transaction& tx)
output_data_t output_data_t
CurrentBlockchainStatus::get_output_key(uint64_t amount, uint64_t global_amount_index) CurrentBlockchainStatus::get_output_key(
uint64_t amount,
uint64_t global_amount_index)
{ {
return mcore->get_output_key(amount, global_amount_index); return mcore->get_output_key(amount, global_amount_index);
} }
@ -699,9 +696,11 @@ CurrentBlockchainStatus::start_tx_search_thread(XmrAccount acc)
try try
{ {
// make a tx_search object for the given xmr account // make a tx_search object for the given xmr account
//searching_threads.emplace(acc.address, new TxSearch(acc)); // does not work on older gcc //searching_threads.emplace(acc.address, new TxSearch(acc));
// such as the one in ubuntu 16.04 // does not work on older gcc
searching_threads[acc.address] = make_unique<TxSearch>(acc, shared_from_this()); // such as the one in ubuntu 16.04
searching_threads[acc.address]
= make_unique<TxSearch>(acc, shared_from_this());
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -710,7 +709,10 @@ CurrentBlockchainStatus::start_tx_search_thread(XmrAccount acc)
} }
// start the thread for the created object // start the thread for the created object
std::thread t1 {&TxSearch::search, searching_threads[acc.address].get()}; std::thread t1 {
&TxSearch::search,
searching_threads[acc.address].get()};
t1.detach(); t1.detach();
cout << "Search thread created\n"; cout << "Search thread created\n";
@ -728,7 +730,7 @@ CurrentBlockchainStatus::ping_search_thread(const string& address)
if (!search_thread_exist(address)) if (!search_thread_exist(address))
{ {
// thread does not exist // thread does not exist
cout << "thread for " << address << " does not exist" << endl; cout << "thread for " << address << " does not exist\n";
return false; return false;
} }
@ -746,11 +748,12 @@ CurrentBlockchainStatus::get_searched_blk_no(const string& address,
if (!search_thread_exist(address)) if (!search_thread_exist(address))
{ {
// thread does not exist // thread does not exist
cout << "thread for " << address << " does not exist" << endl; cout << "thread for " << address << " does not exist\n";
return false; return false;
} }
searched_blk_no = searching_threads[address].get()->get_searched_blk_no(); searched_blk_no = searching_threads[address].get()
->get_searched_blk_no();
return true; return true;
} }
@ -765,7 +768,7 @@ CurrentBlockchainStatus::get_known_outputs_keys(
if (!search_thread_exist(address)) if (!search_thread_exist(address))
{ {
// thread does not exist // thread does not exist
cout << "thread for " << address << " does not exist" << endl; cout << "thread for " << address << " does not exist\n";
return false; return false;
} }
@ -796,12 +799,14 @@ CurrentBlockchainStatus::get_xmr_address_viewkey(
if (!search_thread_exist(address_str)) if (!search_thread_exist(address_str))
{ {
// thread does not exist // thread does not exist
cout << "thread for " << address_str << " does not exist" << endl; cout << "thread for " << address_str << " does not exist\n";
return false; return false;
} }
address = searching_threads[address_str].get()->get_xmr_address_viewkey().first; address = searching_threads[address_str].get()
viewkey = searching_threads[address_str].get()->get_xmr_address_viewkey().second; ->get_xmr_address_viewkey().first;
viewkey = searching_threads[address_str].get()
->get_xmr_address_viewkey().second;
return true; return true;
}; };
@ -816,7 +821,7 @@ CurrentBlockchainStatus::find_txs_in_mempool(
if (searching_threads.count(address_str) == 0) if (searching_threads.count(address_str) == 0)
{ {
// thread does not exist // thread does not exist
cout << "thread for " << address_str << " does not exist" << endl; cout << "thread for " << address_str << " does not exist\n";
return false; return false;
} }
@ -849,7 +854,8 @@ CurrentBlockchainStatus::find_tx_in_mempool(
} }
bool bool
CurrentBlockchainStatus::find_key_images_in_mempool(std::vector<txin_v> const& vin) CurrentBlockchainStatus::find_key_images_in_mempool(
std::vector<txin_v> const& vin)
{ {
mempool_txs_t mempool_tx_cpy; mempool_txs_t mempool_tx_cpy;
@ -861,7 +867,8 @@ CurrentBlockchainStatus::find_key_images_in_mempool(std::vector<txin_v> const& v
} }
// perform exhostive search to check if any key image in vin vector // perform exhostive search to check if any key image in vin vector
// is in the mempool. This is used to check if a tx generated by the frontend // is in the mempool. This is used to check if a tx generated
// by the frontend
// is using any key images that area already in the mempool. // is using any key images that area already in the mempool.
for (auto const& kin: vin) for (auto const& kin: vin)
{ {
@ -876,7 +883,8 @@ CurrentBlockchainStatus::find_key_images_in_mempool(std::vector<txin_v> const& v
{ {
const transaction &m_tx = mtx.second; const transaction &m_tx = mtx.second;
vector<txin_to_key> input_key_imgs = xmreg::get_key_images(m_tx); vector<txin_to_key> input_key_imgs
= xmreg::get_key_images(m_tx);
for (auto const& mki: input_key_imgs) for (auto const& mki: input_key_imgs)
{ {
@ -892,21 +900,26 @@ CurrentBlockchainStatus::find_key_images_in_mempool(std::vector<txin_v> const& v
bool bool
CurrentBlockchainStatus::find_key_images_in_mempool(transaction const& tx) CurrentBlockchainStatus::find_key_images_in_mempool(
transaction const& tx)
{ {
return find_key_images_in_mempool(tx.vin); return find_key_images_in_mempool(tx.vin);
} }
bool bool
CurrentBlockchainStatus::get_tx(crypto::hash const& tx_hash, transaction& tx) CurrentBlockchainStatus::get_tx(
crypto::hash const& tx_hash,
transaction& tx)
{ {
return mcore->get_tx(tx_hash, tx); return mcore->get_tx(tx_hash, tx);
} }
bool bool
CurrentBlockchainStatus::get_tx(string const& tx_hash_str, transaction& tx) CurrentBlockchainStatus::get_tx(
string const& tx_hash_str,
transaction& tx)
{ {
crypto::hash tx_hash; crypto::hash tx_hash;
@ -919,7 +932,9 @@ CurrentBlockchainStatus::get_tx(string const& tx_hash_str, transaction& tx)
} }
bool bool
CurrentBlockchainStatus::get_tx_block_height(crypto::hash const& tx_hash, int64_t& tx_height) CurrentBlockchainStatus::get_tx_block_height(
crypto::hash const& tx_hash,
int64_t& tx_height)
{ {
if (!tx_exist(tx_hash)) if (!tx_exist(tx_hash))
return false; return false;
@ -930,7 +945,8 @@ CurrentBlockchainStatus::get_tx_block_height(crypto::hash const& tx_hash, int64_
} }
bool bool
CurrentBlockchainStatus::set_new_searched_blk_no(const string& address, uint64_t new_value) CurrentBlockchainStatus::set_new_searched_blk_no(
const string& address, uint64_t new_value)
{ {
std::lock_guard<std::mutex> lck (searching_threads_map_mtx); std::lock_guard<std::mutex> lck (searching_threads_map_mtx);
@ -954,9 +970,11 @@ CurrentBlockchainStatus::clean_search_thread_map()
for (const auto& st: searching_threads) for (const auto& st: searching_threads)
{ {
if (search_thread_exist(st.first) && st.second->still_searching() == false) if (search_thread_exist(st.first)
&& st.second->still_searching() == false)
{ {
cout << st.first << " still searching: " << st.second->still_searching() << endl; cout << st.first << " still searching: "
<< st.second->still_searching() << endl;
searching_threads.erase(st.first); searching_threads.erase(st.first);
} }
} }
@ -996,23 +1014,29 @@ CurrentBlockchainStatus::construct_output_rct_field(
if (random_output_tx.version > 1 && !is_coinbase(random_output_tx)) 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_outpk = pod_to_hex(random_output_tx.rct_signatures
rtc_mask = pod_to_hex(random_output_tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask); .outPk[output_idx_in_tx].mask);
rtc_amount = pod_to_hex(random_output_tx.rct_signatures.ecdhInfo[output_idx_in_tx].amount); 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 else
{ {
// for non ringct txs, we need to take it rct amount commitment // for non ringct txs, we need to take it rct amount commitment
// and sent to the frontend. the mask is zero mask for those, // and sent to the frontend. the mask is zero mask for those,
// as frontend will produce identy mask autmatically for non-ringct outputs // as frontend will produce identy mask autmatically
//for non-ringct outputs
output_data_t od = get_output_key(out_amount, global_amount_index); output_data_t od = get_output_key(out_amount, global_amount_index);
rtc_outpk = pod_to_hex(od.commitment); rtc_outpk = pod_to_hex(od.commitment);
if (is_coinbase(random_output_tx)) // commenting this out. think its not needed. if (is_coinbase(random_output_tx))
{ // as this function provides keys for mixin outputs {
// not the ones we actually spend. // 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. // ringct coinbase txs are special. they have identity mask.
// as suggested by this code: // 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/eacf2124b6822d088199179b18d4587404408e0f/src/wallet/wallet2.cpp#L893
@ -1042,26 +1066,30 @@ CurrentBlockchainStatus::get_txs_in_blocks(
// first insert miner_tx // first insert miner_tx
txs_data.emplace_back(get_transaction_hash(blk.miner_tx), txs_data.emplace_back(get_transaction_hash(blk.miner_tx),
transaction{}, blk_height, blk.timestamp, true); transaction{}, blk_height,
blk.timestamp, true);
// now insert hashes of regular txs to be fatched later // now insert hashes of regular txs to be fatched later
// so for now, theys txs are null pointers // so for now, theys txs are null pointers
for (auto& tx_hash: blk.tx_hashes) for (auto& tx_hash: blk.tx_hashes)
txs_data.emplace_back(tx_hash, transaction{}, blk_height, blk.timestamp, false); txs_data.emplace_back(tx_hash, transaction{},
blk_height, blk.timestamp, false);
} }
// prepare vector<tx_hash> for CurrentBlockchainStatus::get_txs to fetch // prepare vector<tx_hash> for CurrentBlockchainStatus::get_txs
// the correspoding transactions // to fetch the correspoding transactions
std::vector<crypto::hash> txs_to_get; std::vector<crypto::hash> txs_to_get;
for (auto const& tx_tuple: txs_data) for (auto const& tx_tuple: txs_data)
txs_to_get.push_back(std::get<0>(tx_tuple)); txs_to_get.push_back(std::get<0>(tx_tuple));
// fetch all txs from the blocks that we are analyzing in this iteration // fetch all txs from the blocks that we are
// analyzing in this iteration
vector<cryptonote::transaction> txs; vector<cryptonote::transaction> txs;
vector<crypto::hash> missed_txs; vector<crypto::hash> missed_txs;
if (!CurrentBlockchainStatus::get_txs(txs_to_get, txs, missed_txs) || !missed_txs.empty()) if (!CurrentBlockchainStatus::get_txs(txs_to_get, txs, missed_txs)
|| !missed_txs.empty())
{ {
cerr << "Cant get transactions in blocks from : " << h1 << '\n'; cerr << "Cant get transactions in blocks from : " << h1 << '\n';
return false; return false;

@ -9,6 +9,7 @@
#include "MicroCore.h" #include "MicroCore.h"
#include "ssqlses.h" #include "ssqlses.h"
#include "TxUnlockChecker.h"
#include "BlockchainSetup.h" #include "BlockchainSetup.h"
#include "TxSearch.h" #include "TxSearch.h"
@ -68,11 +69,28 @@ public:
virtual bool virtual bool
init_monero_blockchain(); init_monero_blockchain();
virtual bool // inject TxUnlockChecker object
is_tx_unlocked(uint64_t unlock_time, uint64_t block_height); // its simplifies mocking its behavior in our
// tests, as we just inject mock version of
// TxUnlockChecker class
// template <typename T = xmreg::TxUnlockChecker>
// virtual bool
// is_tx_unlocked(uint64_t unlock_time,
// uint64_t block_height,
// T tx_unlock_checker = T())
// {
// tx_unlock_checker.init(bc_setup.net_type);
// return tx_unlock_checker.is_unlocked(current_height,
// unlock_time,
// block_height);
// }
virtual bool virtual bool
is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height); is_tx_unlocked(uint64_t unlock_time,
uint64_t block_height,
TxUnlockChecker const& tx_unlock_checker
= TxUnlockChecker());
virtual bool virtual bool
get_block(uint64_t height, block &blk); get_block(uint64_t height, block &blk);
@ -109,10 +127,12 @@ public:
vector<cryptonote::output_data_t>& outputs); vector<cryptonote::output_data_t>& outputs);
virtual string virtual string
get_account_integrated_address_as_str(crypto::hash8 const& payment_id8); get_account_integrated_address_as_str(
crypto::hash8 const& payment_id8);
virtual string virtual string
get_account_integrated_address_as_str(string const& payment_id8_str); get_account_integrated_address_as_str(
string const& payment_id8_str);
virtual bool virtual bool
get_output(const uint64_t amount, get_output(const uint64_t amount,
@ -126,7 +146,8 @@ public:
virtual bool virtual bool
get_random_outputs(const vector<uint64_t>& amounts, get_random_outputs(const vector<uint64_t>& amounts,
const uint64_t& outs_count, const uint64_t& outs_count,
vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount>& found_outputs); vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS
::outs_for_amount>& found_outputs);
virtual bool virtual bool
get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated); get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated);

@ -104,10 +104,10 @@ public:
return core_storage.get_db().tx_exists(tx_hash, tx_id); return core_storage.get_db().tx_exists(tx_hash, tx_id);
} }
template<typename... T> virtual tx_out_index
auto get_output_tx_and_index(T&&... args) const get_output_tx_and_index(uint64_t const& amount, uint64_t const& index) const
{ {
return core_storage.get_db().get_output_tx_and_index(std::forward<T>(args)...); return core_storage.get_db().get_output_tx_and_index(amount, index);
} }
template<typename... T> template<typename... T>

@ -0,0 +1,70 @@
#include "TxUnlockChecker.h"
namespace xmreg
{
uint64_t
TxUnlockChecker::get_current_time() const
{
return static_cast<uint64_t>(time(NULL));
}
uint64_t
TxUnlockChecker::get_v2height(network_type net_type) const
{
return net_type == TESTNET ?
624634 : net_type == STAGENET ?
(uint64_t)-1 : 1009827;
}
uint64_t
TxUnlockChecker::get_leeway(
uint64_t tx_block_height,
network_type net_type) const
{
return tx_block_height < get_v2height(net_type)
? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1
: CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2;
}
bool
TxUnlockChecker::is_unlocked(
network_type net_type,
uint64_t current_blockchain_height,
uint64_t tx_unlock_time,
uint64_t tx_block_height) const
{
if(tx_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
//interpret as block index
if(current_blockchain_height
+ CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
>= tx_unlock_time)
return true;
else
return false;
}
else
{
//interpret unlock_time as timestamp
if(get_current_time() + get_leeway(tx_block_height, net_type)
>= tx_unlock_time)
return true;
else
return false;
}
// this part will never execute. dont need it
// if(tx_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
// > current_blockchain_height + 1)
// return false;
return true;
}
}

@ -0,0 +1,47 @@
#ifndef TXUNLOCKCHECKER_H
#define TXUNLOCKCHECKER_H
#include "monero_headers.h"
namespace xmreg
{
using namespace cryptonote;
using namespace crypto;
using namespace std;
// class based on
// bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const
// and
// bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const
// from monero.
// we make it separate class as its easier to mock it
// later on in our tests
class TxUnlockChecker
{
public:
TxUnlockChecker() = default;
virtual uint64_t
get_current_time() const;
virtual uint64_t
get_v2height(network_type net_type) const;
virtual uint64_t
get_leeway(uint64_t tx_block_height, network_type net_type) const;
virtual bool
is_unlocked(network_type net_type,
uint64_t current_blockchain_height,
uint64_t tx_unlock_time,
uint64_t tx_block_height) const;
virtual ~TxUnlockChecker() = default;
};
}
#endif // TXUNLOCKCHECKER_H

@ -3,7 +3,7 @@
macro(add_om_test _TEST_NAME) macro(add_om_test _TEST_NAME)
add_executable(${_TEST_NAME}_tests add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests.cpp ${_TEST_NAME}_tests.cpp
#${MONERO_DIR}/tests/core_tests/chaingen.cpp #${MONERO_DIR}/tests/core_tests/chaingen.cpp
#${MONERO_DIR}/tests/core_tests/chaingen001.cpp #${MONERO_DIR}/tests/core_tests/chaingen001.cpp
) )

@ -9,7 +9,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "helpers.h"
namespace namespace
{ {
@ -36,8 +36,9 @@ using ::testing::internal::FilePath;
class MockMicroCore : public xmreg::MicroCore class MockMicroCore : public xmreg::MicroCore
{ {
public: public:
MOCK_METHOD2(init, bool(const string& _blockchain_path, network_type nt)); MOCK_METHOD2(init, bool(const string& _blockchain_path, network_type nt));
MOCK_CONST_METHOD0(get_current_blockchain_height, uint64_t());
MOCK_CONST_METHOD2(get_block_from_height, bool(uint64_t height, block& blk)); MOCK_CONST_METHOD2(get_block_from_height, bool(uint64_t height, block& blk));
MOCK_CONST_METHOD2(get_blocks_range, std::vector<block>(const uint64_t& h1, const uint64_t& h2)); MOCK_CONST_METHOD2(get_blocks_range, std::vector<block>(const uint64_t& h1, const uint64_t& h2));
MOCK_CONST_METHOD3(get_transactions, bool(const std::vector<crypto::hash>& txs_ids, MOCK_CONST_METHOD3(get_transactions, bool(const std::vector<crypto::hash>& txs_ids,
@ -45,6 +46,9 @@ public:
std::vector<crypto::hash>& missed_txs)); std::vector<crypto::hash>& missed_txs));
MOCK_CONST_METHOD1(have_tx, bool(crypto::hash const& tx_hash)); MOCK_CONST_METHOD1(have_tx, bool(crypto::hash const& tx_hash));
MOCK_CONST_METHOD2(tx_exists, bool(crypto::hash const& tx_hash, uint64_t& tx_id)); MOCK_CONST_METHOD2(tx_exists, bool(crypto::hash const& tx_hash, uint64_t& tx_id));
MOCK_CONST_METHOD2(get_output_tx_and_index, tx_out_index(uint64_t const& amount, uint64_t const& index));
MOCK_CONST_METHOD2(get_tx, bool(crypto::hash const& tx_hash, transaction& tx));
}; };
@ -64,10 +68,12 @@ protected:
virtual void virtual void
SetUp() SetUp()
{ {
bc_setup = xmreg::BlockchainSetup{net_type, do_not_relay, config_json}; bc_setup = xmreg::BlockchainSetup {
net_type, do_not_relay, config_json};
mcore = std::make_unique<MockMicroCore>(); mcore = std::make_unique<MockMicroCore>();
mcore_ptr = mcore.get(); mcore_ptr = mcore.get();
bcs = std::make_unique<xmreg::CurrentBlockchainStatus>(bc_setup, std::move(mcore)); bcs = std::make_unique<xmreg::CurrentBlockchainStatus>(
bc_setup, std::move(mcore));
} }
network_type net_type {network_type::STAGENET}; network_type net_type {network_type::STAGENET};
@ -184,7 +190,8 @@ TEST_F(BCSTATUS_TEST, TxExist)
// return true and set tx_index (ret by ref) to mock_tx_index_to_return // return true and set tx_index (ret by ref) to mock_tx_index_to_return
EXPECT_CALL(*mcore_ptr, tx_exists(_, _)) EXPECT_CALL(*mcore_ptr, tx_exists(_, _))
.WillOnce(DoAll(SetArgReferee<1>(mock_tx_index_to_return), Return(true))); .WillOnce(DoAll(SetArgReferee<1>(mock_tx_index_to_return),
Return(true)));
uint64_t tx_index {0}; uint64_t tx_index {0};
@ -192,12 +199,14 @@ TEST_F(BCSTATUS_TEST, TxExist)
EXPECT_EQ(tx_index, mock_tx_index_to_return); EXPECT_EQ(tx_index, mock_tx_index_to_return);
// just some dummy hash // just some dummy hash
string tx_hash_str {"fc4b8d5956b30dc4a353b171b4d974697dfc32730778f138a8e7f16c11907691"}; string tx_hash_str
{"fc4b8d5956b30dc4a353b171b4d974697dfc32730778f138a8e7f16c11907691"};
tx_index = 0; tx_index = 0;
EXPECT_CALL(*mcore_ptr, tx_exists(_, _)) EXPECT_CALL(*mcore_ptr, tx_exists(_, _))
.WillOnce(DoAll(SetArgReferee<1>(mock_tx_index_to_return), Return(true))); .WillOnce(DoAll(SetArgReferee<1>(mock_tx_index_to_return),
Return(true)));
EXPECT_TRUE(bcs->tx_exist(tx_hash_str, tx_index)); EXPECT_TRUE(bcs->tx_exist(tx_hash_str, tx_index));
EXPECT_EQ(tx_index, mock_tx_index_to_return); EXPECT_EQ(tx_index, mock_tx_index_to_return);
@ -208,5 +217,178 @@ TEST_F(BCSTATUS_TEST, TxExist)
} }
TEST_F(BCSTATUS_TEST, GetTxWithOutput)
{
// some dummy tx hash
RAND_TX_HASH();
const tx_out_index tx_idx_to_return = make_pair(tx_hash, 6);
EXPECT_CALL(*mcore_ptr, get_output_tx_and_index(_, _))
.WillOnce(Return(tx_idx_to_return));
EXPECT_CALL(*mcore_ptr, get_tx(_, _))
.WillOnce(Return(true));
const uint64_t mock_output_idx {4};
const uint64_t mock_amount {11110};
transaction tx_returned;
uint64_t out_idx_returned;
EXPECT_TRUE(bcs->get_tx_with_output(mock_output_idx, mock_amount,
tx_returned, out_idx_returned));
}
ACTION(ThrowOutputDNE)
{
throw OUTPUT_DNE("Mock Throw: Output does not exist!");
}
TEST_F(BCSTATUS_TEST, GetTxWithOutputFailure)
{
// some dummy tx hash
RAND_TX_HASH();
const tx_out_index tx_idx_to_return = make_pair(tx_hash, 6);
EXPECT_CALL(*mcore_ptr, get_output_tx_and_index(_, _))
.WillOnce(Return(tx_idx_to_return));
EXPECT_CALL(*mcore_ptr, get_tx(_, _))
.WillOnce(Return(false));
const uint64_t mock_output_idx {4};
const uint64_t mock_amount {11110};
transaction tx_returned;
uint64_t out_idx_returned;
EXPECT_FALSE(bcs->get_tx_with_output(mock_output_idx, mock_amount,
tx_returned, out_idx_returned));
// or
EXPECT_CALL(*mcore_ptr, get_output_tx_and_index(_, _))
.WillOnce(ThrowOutputDNE());
EXPECT_FALSE(bcs->get_tx_with_output(mock_output_idx, mock_amount,
tx_returned, out_idx_returned));
}
TEST_F(BCSTATUS_TEST, GetCurrentHeight)
{
uint64_t mock_current_height {1619148};
EXPECT_CALL(*mcore_ptr, get_current_blockchain_height())
.WillOnce(Return(mock_current_height));
bcs->update_current_blockchain_height();
EXPECT_EQ(bcs->get_current_blockchain_height(),
mock_current_height - 1);
}
TEST_F(BCSTATUS_TEST, IsTxSpendtimeUnlockedScenario1)
{
// there are two main scenerious here.
// Scenerio 1: tx_unlock_time is block height
// Scenerio 2: tx_unlock_time is timestamp.
const uint64_t mock_current_height {100};
EXPECT_CALL(*mcore_ptr, get_current_blockchain_height())
.WillOnce(Return(mock_current_height));
bcs->update_current_blockchain_height();
// SCENARIO 1: tx_unlock_time is block height
// expected unlock time is in future, thus a tx is still locked
uint64_t tx_unlock_time {mock_current_height
+ CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE};
uint64_t not_used_block_height {0}; // not used in the first
// part of the test case
EXPECT_FALSE(bcs->is_tx_unlocked(
tx_unlock_time, not_used_block_height));
// expected unlock time is in the future
// (1 blocks from now), thus a tx is locked
tx_unlock_time = mock_current_height + 1;
EXPECT_FALSE(bcs->is_tx_unlocked(
tx_unlock_time, not_used_block_height));
// expected unlock time is in the past
// (10 blocks behind), thus a tx is unlocked
tx_unlock_time = mock_current_height
- CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
EXPECT_TRUE(bcs->is_tx_unlocked(tx_unlock_time,
not_used_block_height));
// expected unlock time is same as as current height
// thus a tx is unlocked
tx_unlock_time = mock_current_height;
EXPECT_TRUE(bcs->is_tx_unlocked(tx_unlock_time,
not_used_block_height));
}
class MockTxUnlockChecker : public xmreg::TxUnlockChecker
{
public:
// mock system call to get current timestamp
MOCK_CONST_METHOD0(get_current_time, uint64_t());
//MOCK_CONST_METHOD1(get_leeway, uint64_t(uint64_t tx_block_height));
};
TEST_F(BCSTATUS_TEST, IsTxSpendtimeUnlockedScenario2)
{
// there are two main scenerious here.
// Scenerio 1: tx_unlock_time is block height
// Scenerio 2: tx_unlock_time is timestamp.
const uint64_t mock_current_height {100};
EXPECT_CALL(*mcore_ptr, get_current_blockchain_height())
.WillOnce(Return(mock_current_height));
bcs->update_current_blockchain_height();
// SCENARIO 2: tx_unlock_time is timestamp.
MockTxUnlockChecker mock_tx_unlock_checker;
const uint64_t current_timestamp {1000000000};
EXPECT_CALL(mock_tx_unlock_checker, get_current_time())
.WillRepeatedly(Return(1000000000));
uint64_t block_height = mock_current_height;
// tx unlock time is now
uint64_t tx_unlock_time {current_timestamp}; // mock timestamp
EXPECT_TRUE(bcs->is_tx_unlocked(tx_unlock_time, block_height,
mock_tx_unlock_checker));
// unlock time is 1 second into the future
tx_unlock_time = current_timestamp
+ mock_tx_unlock_checker.get_leeway(
block_height, bcs->get_bc_setup().net_type) + 1;
EXPECT_FALSE(bcs->is_tx_unlocked(tx_unlock_time, block_height,
mock_tx_unlock_checker));
}
} }

@ -0,0 +1,26 @@
#ifndef HELPERS_H
#define HELPERS_H
#define RAND_TX_HASH() \
crypto::hash tx_hash = crypto::rand<crypto::hash>(); \
string tx_hash_str = pod_to_hex(tx_hash); \
#define TX_FROM_HEX(hex_string) \
transaction tx; \
crypto::hash tx_hash; \
crypto::hash tx_prefix_hash; \
ASSERT_TRUE(xmreg::hex_to_tx(hex_string, tx, tx_hash, tx_prefix_hash)); \
string tx_hash_str = pod_to_hex(tx_hash); \
string tx_prefix_hash_str = pod_to_hex(tx_prefix_hash);
#define ACC_FROM_HEX(hex_address) \
xmreg::XmrAccount acc; \
ASSERT_TRUE(this->xmr_accounts->select(hex_address, acc));
#define TX_AND_ACC_FROM_HEX(hex_tx, hex_address) \
TX_FROM_HEX(hex_tx); \
ACC_FROM_HEX(hex_address);
#endif // HELPERS_H

@ -13,6 +13,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "../src/ThreadRAII.h" #include "../src/ThreadRAII.h"
#include "helpers.h"
namespace namespace
{ {
@ -29,23 +30,6 @@ using ::testing::AllOf;
using ::testing::Ge; using ::testing::Ge;
using ::testing::Le; using ::testing::Le;
#define TX_FROM_HEX(hex_string) \
transaction tx; \
crypto::hash tx_hash; \
crypto::hash tx_prefix_hash; \
ASSERT_TRUE(xmreg::hex_to_tx(hex_string, tx, tx_hash, tx_prefix_hash)); \
string tx_hash_str = pod_to_hex(tx_hash); \
string tx_prefix_hash_str = pod_to_hex(tx_prefix_hash);
#define ACC_FROM_HEX(hex_address) \
xmreg::XmrAccount acc; \
ASSERT_TRUE(this->xmr_accounts->select(hex_address, acc));
#define TX_AND_ACC_FROM_HEX(hex_tx, hex_address) \
TX_FROM_HEX(hex_tx); \
ACC_FROM_HEX(hex_address);
json json
readin_config() readin_config()
@ -1211,7 +1195,10 @@ public:
// all txs in the blockchain are unlocked // all txs in the blockchain are unlocked
virtual bool virtual bool
is_tx_unlocked(uint64_t unlock_time, uint64_t block_height) override is_tx_unlocked(uint64_t unlock_time,
uint64_t block_height,
xmreg::TxUnlockChecker const& tx_unlock_checker
= xmreg::TxUnlockChecker()) override
{ {
return tx_unlock_state; return tx_unlock_state;
} }

Loading…
Cancel
Save