From 118e4ef1b0efc24a101788018741c306844fe3d4 Mon Sep 17 00:00:00 2001 From: moneroexamples Date: Thu, 19 Jul 2018 11:43:26 +0800 Subject: [PATCH] TxUnlockChecker class added and tested --- .gitignore | 4 + src/CMakeLists.txt | 3 +- src/CurrentBlockchainStatus.cpp | 246 ++++++++++++++++++-------------- src/CurrentBlockchainStatus.h | 33 ++++- src/MicroCore.h | 6 +- src/TxUnlockChecker.cpp | 70 +++++++++ src/TxUnlockChecker.h | 47 ++++++ tests/CMakeLists.txt | 2 +- tests/bcstatus_tests.cpp | 196 ++++++++++++++++++++++++- tests/helpers.h | 26 ++++ tests/mysql_tests.cpp | 23 +-- 11 files changed, 511 insertions(+), 145 deletions(-) create mode 100644 src/TxUnlockChecker.cpp create mode 100644 src/TxUnlockChecker.h create mode 100644 tests/helpers.h diff --git a/.gitignore b/.gitignore index f2e7ced..a408a51 100755 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,12 @@ .sass-cache *.*~ *.user +*.user.* .idea/ cmake-build-debug/ ext/restbed/dependency/catch/ build +.cproject +.project +.settings/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ba646e..d7af6de 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,7 +17,8 @@ set(SOURCE_FILES version.h.in BlockchainSetup.cpp ThreadRAII.cpp - MysqlPing.cpp) + MysqlPing.cpp + TxUnlockChecker) # make static library called libmyxrm # that we are going to link to diff --git a/src/CurrentBlockchainStatus.cpp b/src/CurrentBlockchainStatus.cpp index 8c0bb2a..a36d68e 100755 --- a/src/CurrentBlockchainStatus.cpp +++ b/src/CurrentBlockchainStatus.cpp @@ -31,7 +31,8 @@ CurrentBlockchainStatus::start_monitor_blockchain_thread() { 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) { @@ -42,12 +43,13 @@ CurrentBlockchainStatus::start_monitor_blockchain_thread() update_current_blockchain_height(); read_mempool(); cout << "Check block height: " << current_height - << " no of mempool txs: " << mempool_txs.size(); - cout << endl; + << " no of mempool txs: " << mempool_txs.size() + << '\n'; clean_search_thread_map(); std::this_thread::sleep_for( 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 CurrentBlockchainStatus::get_current_blockchain_height() { - return mcore->get_current_blockchain_height() - 1; + return current_height; } void CurrentBlockchainStatus::update_current_blockchain_height() { - current_height = get_current_blockchain_height(); + current_height = mcore->get_current_blockchain_height() - 1; } bool @@ -79,57 +81,22 @@ CurrentBlockchainStatus::init_monero_blockchain() // initialize the core using the blockchain path 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 CurrentBlockchainStatus::is_tx_unlocked( 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 false; - - if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > current_height + 1) - return false; - - return true; + return tx_unlock_checker.is_unlocked(bc_setup.net_type, + current_height, + unlock_time, + block_height); } -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(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 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)) { - cerr << "Cant get transactions in block: " << get_block_hash(blk) << endl; + cerr << "Cant get transactions in block: " + << get_block_hash(blk) << endl; + return false; } @@ -180,7 +149,8 @@ CurrentBlockchainStatus::get_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; } @@ -194,14 +164,18 @@ CurrentBlockchainStatus::tx_exist(const crypto::hash& tx_hash) } 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); } 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; @@ -293,8 +267,9 @@ CurrentBlockchainStatus::get_account_integrated_address_as_str( } bool -CurrentBlockchainStatus::get_amount_specific_indices(const crypto::hash& tx_hash, - vector& out_indices) +CurrentBlockchainStatus::get_amount_specific_indices( + const crypto::hash& tx_hash, + vector& out_indices) { try { @@ -320,13 +295,15 @@ bool CurrentBlockchainStatus::get_random_outputs( const vector& amounts, const uint64_t& outs_count, - vector& found_outputs) + vector& found_outputs) { rpccalls rpc {bc_setup.deamon_url}; 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; return false; @@ -357,7 +334,8 @@ CurrentBlockchainStatus::get_output( 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}; @@ -376,7 +354,10 @@ CurrentBlockchainStatus::get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated 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}; @@ -455,7 +436,8 @@ CurrentBlockchainStatus::search_if_payment_made( string& tx_hash_with_payment) { - vector> mempool_transactions = get_mempool_txs(); + vector> mempool_transactions + = get_mempool_txs(); uint64_t current_blockchain_height = current_height; @@ -475,7 +457,8 @@ CurrentBlockchainStatus::search_if_payment_made( block 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; } @@ -484,13 +467,15 @@ CurrentBlockchainStatus::search_if_payment_made( 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; } // 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()); + txs_to_check.insert(txs_to_check.end(), + blk_txs.begin(), blk_txs.end()); } 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)) { - 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 - 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 // to create, so called, derived key. 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" << "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; } @@ -547,14 +537,16 @@ CurrentBlockchainStatus::search_if_payment_made( if (decrypted_payment_id8 != null_hash8) { 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: " << 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 // in mysql. @@ -594,10 +586,12 @@ CurrentBlockchainStatus::search_if_payment_made( derive_public_key(derivation, 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); - // 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); // if mine output has RingCT, i.e., tx version is 2 @@ -617,7 +611,8 @@ CurrentBlockchainStatus::search_if_payment_made( tx_pub_key, bc_setup.import_payment_viewkey, output_idx_in_tx, - tx.rct_signatures.ecdhInfo[output_idx_in_tx].mask, + tx.rct_signatures + .ecdhInfo[output_idx_in_tx].mask, rct_amount); if (!r) @@ -679,7 +674,9 @@ CurrentBlockchainStatus::get_payment_id_as_string(const transaction& tx) 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); } @@ -699,9 +696,11 @@ CurrentBlockchainStatus::start_tx_search_thread(XmrAccount acc) try { // make a tx_search object for the given xmr account - //searching_threads.emplace(acc.address, new TxSearch(acc)); // does not work on older gcc - // such as the one in ubuntu 16.04 - searching_threads[acc.address] = make_unique(acc, shared_from_this()); + //searching_threads.emplace(acc.address, new TxSearch(acc)); + // does not work on older gcc + // such as the one in ubuntu 16.04 + searching_threads[acc.address] + = make_unique(acc, shared_from_this()); } catch (const std::exception& e) { @@ -710,7 +709,10 @@ CurrentBlockchainStatus::start_tx_search_thread(XmrAccount acc) } // 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(); cout << "Search thread created\n"; @@ -728,7 +730,7 @@ CurrentBlockchainStatus::ping_search_thread(const string& address) if (!search_thread_exist(address)) { // thread does not exist - cout << "thread for " << address << " does not exist" << endl; + cout << "thread for " << address << " does not exist\n"; return false; } @@ -746,11 +748,12 @@ CurrentBlockchainStatus::get_searched_blk_no(const string& address, if (!search_thread_exist(address)) { // thread does not exist - cout << "thread for " << address << " does not exist" << endl; + cout << "thread for " << address << " does not exist\n"; 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; } @@ -765,7 +768,7 @@ CurrentBlockchainStatus::get_known_outputs_keys( if (!search_thread_exist(address)) { // thread does not exist - cout << "thread for " << address << " does not exist" << endl; + cout << "thread for " << address << " does not exist\n"; return false; } @@ -796,12 +799,14 @@ CurrentBlockchainStatus::get_xmr_address_viewkey( if (!search_thread_exist(address_str)) { // thread does not exist - cout << "thread for " << address_str << " does not exist" << endl; + cout << "thread for " << address_str << " does not exist\n"; return false; } - address = searching_threads[address_str].get()->get_xmr_address_viewkey().first; - viewkey = searching_threads[address_str].get()->get_xmr_address_viewkey().second; + address = searching_threads[address_str].get() + ->get_xmr_address_viewkey().first; + viewkey = searching_threads[address_str].get() + ->get_xmr_address_viewkey().second; return true; }; @@ -816,7 +821,7 @@ CurrentBlockchainStatus::find_txs_in_mempool( if (searching_threads.count(address_str) == 0) { // thread does not exist - cout << "thread for " << address_str << " does not exist" << endl; + cout << "thread for " << address_str << " does not exist\n"; return false; } @@ -849,7 +854,8 @@ CurrentBlockchainStatus::find_tx_in_mempool( } bool -CurrentBlockchainStatus::find_key_images_in_mempool(std::vector const& vin) +CurrentBlockchainStatus::find_key_images_in_mempool( + std::vector const& vin) { mempool_txs_t mempool_tx_cpy; @@ -861,7 +867,8 @@ CurrentBlockchainStatus::find_key_images_in_mempool(std::vector const& v } // 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. for (auto const& kin: vin) { @@ -876,7 +883,8 @@ CurrentBlockchainStatus::find_key_images_in_mempool(std::vector const& v { const transaction &m_tx = mtx.second; - vector input_key_imgs = xmreg::get_key_images(m_tx); + vector input_key_imgs + = xmreg::get_key_images(m_tx); for (auto const& mki: input_key_imgs) { @@ -892,21 +900,26 @@ CurrentBlockchainStatus::find_key_images_in_mempool(std::vector const& v 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); } 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); } 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; @@ -919,7 +932,9 @@ CurrentBlockchainStatus::get_tx(string const& tx_hash_str, transaction& tx) } 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)) return false; @@ -930,7 +945,8 @@ CurrentBlockchainStatus::get_tx_block_height(crypto::hash const& tx_hash, int64_ } 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 lck (searching_threads_map_mtx); @@ -954,9 +970,11 @@ CurrentBlockchainStatus::clean_search_thread_map() 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); } } @@ -996,23 +1014,29 @@ CurrentBlockchainStatus::construct_output_rct_field( 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); + 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 + // 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. + 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 @@ -1042,26 +1066,30 @@ CurrentBlockchainStatus::get_txs_in_blocks( // first insert 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 // so for now, theys txs are null pointers 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 for CurrentBlockchainStatus::get_txs to fetch - // the correspoding transactions + // prepare vector for CurrentBlockchainStatus::get_txs + // to fetch the correspoding transactions std::vector txs_to_get; for (auto const& tx_tuple: txs_data) 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 txs; vector 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'; return false; diff --git a/src/CurrentBlockchainStatus.h b/src/CurrentBlockchainStatus.h index 71ba05c..9a0ae5b 100755 --- a/src/CurrentBlockchainStatus.h +++ b/src/CurrentBlockchainStatus.h @@ -9,6 +9,7 @@ #include "MicroCore.h" #include "ssqlses.h" +#include "TxUnlockChecker.h" #include "BlockchainSetup.h" #include "TxSearch.h" @@ -68,11 +69,28 @@ public: virtual bool init_monero_blockchain(); - virtual bool - is_tx_unlocked(uint64_t unlock_time, uint64_t block_height); + // inject TxUnlockChecker object + // its simplifies mocking its behavior in our + // tests, as we just inject mock version of + // TxUnlockChecker class +// template +// 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 - 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 get_block(uint64_t height, block &blk); @@ -109,10 +127,12 @@ public: vector& outputs); 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 - get_account_integrated_address_as_str(string const& payment_id8_str); + get_account_integrated_address_as_str( + string const& payment_id8_str); virtual bool get_output(const uint64_t amount, @@ -126,7 +146,8 @@ public: virtual bool get_random_outputs(const vector& amounts, const uint64_t& outs_count, - vector& found_outputs); + vector& found_outputs); virtual bool get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated); diff --git a/src/MicroCore.h b/src/MicroCore.h index 5662ac3..aaa7aef 100755 --- a/src/MicroCore.h +++ b/src/MicroCore.h @@ -104,10 +104,10 @@ public: return core_storage.get_db().tx_exists(tx_hash, tx_id); } - template - auto get_output_tx_and_index(T&&... args) const + virtual tx_out_index + 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(args)...); + return core_storage.get_db().get_output_tx_and_index(amount, index); } template diff --git a/src/TxUnlockChecker.cpp b/src/TxUnlockChecker.cpp new file mode 100644 index 0000000..3057111 --- /dev/null +++ b/src/TxUnlockChecker.cpp @@ -0,0 +1,70 @@ +#include "TxUnlockChecker.h" + +namespace xmreg +{ + +uint64_t +TxUnlockChecker::get_current_time() const +{ + return static_cast(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; +} + + + +} diff --git a/src/TxUnlockChecker.h b/src/TxUnlockChecker.h new file mode 100644 index 0000000..7c6daa2 --- /dev/null +++ b/src/TxUnlockChecker.h @@ -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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8031a11..524b115 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,7 +3,7 @@ macro(add_om_test _TEST_NAME) 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/chaingen001.cpp ) diff --git a/tests/bcstatus_tests.cpp b/tests/bcstatus_tests.cpp index 093efc7..a892e28 100644 --- a/tests/bcstatus_tests.cpp +++ b/tests/bcstatus_tests.cpp @@ -9,7 +9,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" - +#include "helpers.h" namespace { @@ -36,8 +36,9 @@ using ::testing::internal::FilePath; class MockMicroCore : public xmreg::MicroCore { -public: +public: 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_blocks_range, std::vector(const uint64_t& h1, const uint64_t& h2)); MOCK_CONST_METHOD3(get_transactions, bool(const std::vector& txs_ids, @@ -45,6 +46,9 @@ public: std::vector& missed_txs)); 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(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 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(); mcore_ptr = mcore.get(); - bcs = std::make_unique(bc_setup, std::move(mcore)); + bcs = std::make_unique( + bc_setup, std::move(mcore)); } 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 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}; @@ -192,12 +199,14 @@ TEST_F(BCSTATUS_TEST, TxExist) EXPECT_EQ(tx_index, mock_tx_index_to_return); // just some dummy hash - string tx_hash_str {"fc4b8d5956b30dc4a353b171b4d974697dfc32730778f138a8e7f16c11907691"}; + string tx_hash_str + {"fc4b8d5956b30dc4a353b171b4d974697dfc32730778f138a8e7f16c11907691"}; tx_index = 0; 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_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)); + +} } diff --git a/tests/helpers.h b/tests/helpers.h new file mode 100644 index 0000000..21639df --- /dev/null +++ b/tests/helpers.h @@ -0,0 +1,26 @@ +#ifndef HELPERS_H +#define HELPERS_H + +#define RAND_TX_HASH() \ + crypto::hash tx_hash = crypto::rand(); \ + 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 diff --git a/tests/mysql_tests.cpp b/tests/mysql_tests.cpp index 2f65c55..66cd6b3 100644 --- a/tests/mysql_tests.cpp +++ b/tests/mysql_tests.cpp @@ -13,6 +13,7 @@ #include "gtest/gtest.h" #include "../src/ThreadRAII.h" +#include "helpers.h" namespace { @@ -29,23 +30,6 @@ using ::testing::AllOf; using ::testing::Ge; 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 readin_config() @@ -1211,7 +1195,10 @@ public: // all txs in the blockchain are unlocked 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; }