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
*.*~
*.user
*.user.*
.idea/
cmake-build-debug/
ext/restbed/dependency/catch/
build
.cproject
.project
.settings/

@ -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

@ -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<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
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<uint64_t>& out_indices)
CurrentBlockchainStatus::get_amount_specific_indices(
const crypto::hash& tx_hash,
vector<uint64_t>& out_indices)
{
try
{
@ -320,13 +295,15 @@ bool
CurrentBlockchainStatus::get_random_outputs(
const vector<uint64_t>& amounts,
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};
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<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;
@ -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<TxSearch>(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<TxSearch>(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<txin_v> const& vin)
CurrentBlockchainStatus::find_key_images_in_mempool(
std::vector<txin_v> const& vin)
{
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
// 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<txin_v> const& v
{
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)
{
@ -892,21 +900,26 @@ CurrentBlockchainStatus::find_key_images_in_mempool(std::vector<txin_v> 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<std::mutex> 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<tx_hash> for CurrentBlockchainStatus::get_txs to fetch
// the correspoding transactions
// prepare vector<tx_hash> for CurrentBlockchainStatus::get_txs
// to fetch the correspoding transactions
std::vector<crypto::hash> 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<cryptonote::transaction> 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';
return false;

@ -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 <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
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<cryptonote::output_data_t>& 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<uint64_t>& amounts,
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
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);
}
template<typename... T>
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<T>(args)...);
return core_storage.get_db().get_output_tx_and_index(amount, index);
}
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)
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
)

@ -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<block>(const uint64_t& h1, const uint64_t& h2));
MOCK_CONST_METHOD3(get_transactions, bool(const std::vector<crypto::hash>& txs_ids,
@ -45,6 +46,9 @@ public:
std::vector<crypto::hash>& 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<MockMicroCore>();
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};
@ -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));
}
}

@ -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 "../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;
}

Loading…
Cancel
Save