1
0
Fork 0
forked from lthn/blockchain
blockchain/tests/core_tests/wallet_packing_tx.cpp
2019-11-29 21:43:17 +01:00

571 lines
25 KiB
C++

// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chaingen.h"
#include "wallet_tests.h"
#include "wallet_test_core_proxy.h"
#include "../../src/wallet/wallet_public_structs_defs.h"
#include "offers_helper.h"
#include "string_coding.h"
#include "random_helper.h"
#include "tx_builder.h"
using namespace epee;
using namespace crypto;
using namespace currency;
const uint64_t uint64_max = std::numeric_limits<uint64_t>::max();
const std::wstring g_wallet_filename = L"~coretests.wallet.file.tmp";
const std::string g_wallet_password = "dofatibmzibeziyekigo";
const currency::account_base null_account = AUTO_VAL_INIT(null_account);
POD_MAKE_COMPARABLE(currency, tx_out);
// Determines which output is real and actually spent in tx inputs, when there are fake outputs.
bool determine_tx_real_inputs(currency::core& c, const currency::transaction& tx, const currency::account_keys& keys, std::vector<size_t>& real_inputs)
{
struct local_visitor
{
local_visitor(const currency::account_keys& keys, const crypto::key_image key_image)
: m_keys(keys)
, m_txin_key_image(key_image)
, m_output_in_input_index(0)
, m_found(false)
{}
bool handle_output(const transaction& source_tx, const transaction& validated_tx, const tx_out& out, uint64_t out_i)
{
CHECK_AND_ASSERT_MES(!m_found, false, "Internal error: m_found is true but the visitor is still being applied");
auto it = std::find(validated_tx.vout.begin(), validated_tx.vout.end(), out);
if (it == validated_tx.vout.end())
return false;
size_t output_tx_index = it - validated_tx.vout.begin();
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(validated_tx);
crypto::key_derivation derivation;
bool r = generate_key_derivation(tx_pub_key, m_keys.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed");
crypto::secret_key ephemeral_secret_key;
derive_secret_key(derivation, output_tx_index, m_keys.m_spend_secret_key, ephemeral_secret_key);
crypto::public_key output_public_key = boost::get<txout_to_key>(out.target).key;
/*crypto::public_key ephemeral_public_key;
derive_public_key(derivation, output_tx_index, m_keys.m_account_address.m_spend_public_key, ephemeral_public_key);*/
crypto::key_image ki;
generate_key_image(output_public_key, ephemeral_secret_key, ki);
if (ki == m_txin_key_image)
{
m_found = true;
return false; // to break the loop in scan_outputkeys_for_indexes
}
++m_output_in_input_index;
return true;
}
currency::account_keys m_keys;
crypto::key_image m_txin_key_image;
size_t m_output_in_input_index;
bool m_found;
};
for (auto& txin : tx.vin)
{
const txin_to_key& in = boost::get<txin_to_key>(txin);
if (in.key_offsets.size() == 1)
{
real_inputs.push_back(0); // trivial case when no mixin is used
continue;
}
local_visitor vis(keys, in.k_image);
bool r = c.get_blockchain_storage().scan_outputkeys_for_indexes(tx, in, vis);
CHECK_AND_ASSERT_MES(r || vis.m_found, false, "scan_outputkeys_for_indexes failed");
if (!vis.m_found)
return false;
real_inputs.push_back(vis.m_output_in_input_index);
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
mined_balance_wallet_test::mined_balance_wallet_test()
{
REGISTER_CALLBACK_METHOD(mined_balance_wallet_test, c1);
REGISTER_CALLBACK_METHOD(mined_balance_wallet_test, set_core_config);
}
bool mined_balance_wallet_test::generate(std::vector<test_event_entry>& events) const
{
GENERATE_ACCOUNT(preminer_acc);
GENERATE_ACCOUNT(miner_acc);
m_accounts.push_back(miner_acc);
GENERATE_ACCOUNT(alice_acc);
m_accounts.push_back(alice_acc);
block blk_0 = AUTO_VAL_INIT(blk_0);
generator.construct_genesis_block(blk_0, preminer_acc, test_core_time::get_time());
events.push_back(blk_0);
DO_CALLBACK(events, "set_core_config");
DO_CALLBACK(events, "c1");
return true;
}
bool mined_balance_wallet_test::set_core_config(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
core_runtime_config crc = c.get_blockchain_storage().get_core_runtime_config();
crc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
crc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
c.get_blockchain_storage().set_core_runtime_config(crc);
return true;
}
bool mined_balance_wallet_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
blockchain_storage& bcs = c.get_blockchain_storage();
core_runtime_config crc = bcs.get_core_runtime_config();
crc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
bcs.set_core_runtime_config(crc);
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner", 0), false, "wrong balance");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice", 0), false, "wrong balance");
uint64_t miner_mined_money = 0;
bool r = false;
std::list<currency::block> blocks;
size_t n = CURRENCY_MINED_MONEY_UNLOCK_WINDOW;
r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, n);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
r = bcs.get_blocks(bcs.get_current_blockchain_size() - n, n, blocks);
CHECK_AND_ASSERT_MES(r, false, "get_blocks failed");
for (auto& b : blocks)
miner_mined_money += get_outs_money_amount(b.miner_tx);
miner_wlt->refresh();
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner", miner_mined_money, miner_mined_money), false, "wrong balance");
n = bcs.get_current_blockchain_size();
r = miner_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(r && bcs.get_current_blockchain_size() > n, false, "can't mint a PoS block");
block b = AUTO_VAL_INIT(b);
r = bcs.get_top_block(b);
CHECK_AND_ASSERT_MES(r, false, "get_top_block failed");
CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 2, false, "Invalid PoS coinbase tx");
uint64_t coinbase_outs_amount = get_outs_money_amount(b.miner_tx);
uint64_t stake_amount = boost::get<txin_to_key>(b.miner_tx.vin[1]).amount;
CHECK_AND_ASSERT_MES(coinbase_outs_amount > stake_amount, false, "coinbase_outs_amount = " << coinbase_outs_amount << ", stake_amount = " << stake_amount << " : invalid condition");
miner_mined_money += coinbase_outs_amount - stake_amount;
miner_wlt->refresh();
std::stringstream ss;
miner_wlt->dump_trunsfers(ss, false);
LOG_PRINT_CYAN("miner transfers: " << ENDL << ss.str(), LOG_LEVEL_0);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner", miner_mined_money, miner_mined_money), false, "wrong balance");
return true;
}
//------------------------------------------------------------------------------
wallet_outputs_with_same_key_image::wallet_outputs_with_same_key_image()
{
REGISTER_CALLBACK_METHOD(wallet_outputs_with_same_key_image, c1);
}
bool wallet_outputs_with_same_key_image::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure wallet does not take into account valid outputs having the same key image
// Only one such output is spendable thus only one output should be taken into account.
bool r = false;
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2);
uint64_t tx_amount = MK_TEST_COINS(3);
// tx_1
std::vector<tx_source_entry> sources_1;
r = fill_tx_sources(sources_1, events, blk_0r, miner_acc.get_keys(), tx_amount + TESTS_DEFAULT_FEE, 0);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
std::vector<tx_destination_entry> destinations{ tx_destination_entry(tx_amount, alice_acc.get_public_address()) };
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_acc.get_keys(), sources_1);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources_1);
transaction tx_1 = builder.m_tx;
events.push_back(tx_1);
// tx_2 with the same secret key
currency::keypair tmp_sec_key = builder.m_tx_key;
builder.step1_init();
builder.m_tx_key = tmp_sec_key;
builder.m_tx.extra.clear();
add_tx_pub_key_to_extra(builder.m_tx, builder.m_tx_key.pub);
std::vector<tx_source_entry> sources_2;
r = fill_tx_sources(sources_2, events, blk_0r, miner_acc.get_keys(), tx_amount + TESTS_DEFAULT_FEE, 0, sources_1);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
// keep destinations the same
builder.step2_fill_inputs(miner_acc.get_keys(), sources_2);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources_2);
transaction tx_2 = builder.m_tx;
events.push_back(tx_2);
// make sure tx_1 and tx_2 have been created with the same tx key
CHECK_AND_ASSERT_MES(get_tx_pub_key_from_extra(tx_1) == get_tx_pub_key_from_extra(tx_2), false, "internal error: tx_1 and tx_2 have different pub keys");
// now both txs are in the pool, make sure they are
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(2));
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_outputs_with_same_key_image::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]);
// check Alice has no unlocked coins
bool r = refresh_wallet_and_check_balance("before tx_1 and tx_2 added", "Alice", alice_wlt, MK_TEST_COINS(3) * 2, true, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2, 0);
CHECK_AND_ASSERT_MES(r, false, "");
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "there are txs in the pool!");
r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
// only one tx_1 output is counted as the tx_2 output has the very same key image
r = refresh_wallet_and_check_balance("after tx_1 and tx_2 added", "Alice", alice_wlt, MK_TEST_COINS(3), true, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1, MK_TEST_COINS(3));
CHECK_AND_ASSERT_MES(r, false, "");
// make sure Alice is able to transfer her coins to smbd
std::vector<tx_destination_entry> destinations{ tx_destination_entry(MK_TEST_COINS(3) - TESTS_DEFAULT_FEE, m_accounts[MINER_ACC_IDX].get_public_address()) };
try
{
alice_wlt->transfer(destinations, 0, 0, TESTS_DEFAULT_FEE, empty_extra, empty_attachment);
}
catch (...)
{
CHECK_AND_ASSERT_MES(false, false, "Alice failed to transfer all her funds");
}
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "wrong tx count in the pool: " << c.get_pool_transactions_count());
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "there are txs in the pool!");
return true;
}
//------------------------------------------------------------------------------
wallet_unconfirmed_tx_expiration::wallet_unconfirmed_tx_expiration()
{
REGISTER_CALLBACK_METHOD(wallet_unconfirmed_tx_expiration, c1);
}
bool wallet_unconfirmed_tx_expiration::generate(std::vector<test_event_entry>& events) const
{
// Test outline:
// 1. Alice sends tx with expiration.
// 2. Miner ignores Alice's tx, so tx expires in the pool.
// 3. Tx is being removed from the pool due to expiration.
// Make sure Alice eventually spent no coins and all her money is unlocked in the wallet.
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate();
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
bool r = false;
transaction tx_0 = AUTO_VAL_INIT(tx_0);
r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), alice_acc.get_public_address(), TESTS_DEFAULT_FEE * 20, 10, TESTS_DEFAULT_FEE, tx_0);
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs");
events.push_back(tx_0);
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), bob_acc.get_public_address(), TESTS_DEFAULT_FEE * 20, 10, TESTS_DEFAULT_FEE, tx_1);
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs");
events.push_back(tx_1);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({ tx_0, tx_1 }));
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_unconfirmed_tx_expiration::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
uint64_t alice_start_balance = TESTS_DEFAULT_FEE * 20;
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, alice_start_balance), false, "");
// Alice constructs and sends tx with expiration time
uint64_t expiration_time = c.get_blockchain_storage().get_tx_expiration_median() + TX_EXPIRATION_MEDIAN_SHIFT + 15;
etc_tx_details_expiration_time extra_entry = AUTO_VAL_INIT(extra_entry);
extra_entry.v = expiration_time;
std::vector<extra_v> extra({ extra_entry }); // extra with expiration time
std::vector<tx_destination_entry> destinations({ tx_destination_entry(TESTS_DEFAULT_FEE * 2, m_accounts[MINER_ACC_IDX].get_public_address()) });
transaction tx = AUTO_VAL_INIT(tx);
try
{
alice_wlt->transfer(destinations, 0, 0, TESTS_DEFAULT_FEE, extra, empty_attachment, tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx);
}
catch (std::exception &e)
{
CHECK_AND_ASSERT_MES(false, false, "alice_wlt->transfer() caused an exception: " << e.what());
}
CHECK_AND_ASSERT_MES(get_tx_expiration_time(tx) == expiration_time, false, "tx expiration time wasn't set");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid txs count in the pool: " << c.get_pool_transactions_count());
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("tx sent,", "Alice", alice_wlt, alice_start_balance - TESTS_DEFAULT_FEE * 2 - TESTS_DEFAULT_FEE, true, 0, UINT64_MAX, 0, 0, TESTS_DEFAULT_FEE * 2), false, "");
// mine a few block with no tx, so Alice's tx is expired in the pool
for (size_t i = 0; i < 5; ++i)
{
r = mine_next_pow_block_in_playtime_with_given_txs(m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>());
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed");
}
// tx is still there
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid txs count in the pool: " << c.get_pool_transactions_count());
// make sure expiration median was shifted enough
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().is_tx_expired(tx), false, "wrong expiration time condition");
LOG_PRINT_CYAN("%%%%% tx_pool::on_idle()", LOG_LEVEL_0);
c.get_tx_pool().on_idle();
// make sure tx was removed by the pool
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Invalid txs count in the pool: " << c.get_pool_transactions_count());
// mine one more block to trigger wallet's on_idle() and outdated tx clearing
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
// make sure all Alice's money are unlocked and no coins were actually spent
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("tx expired and removed from the pool,", "Alice", alice_wlt, alice_start_balance, true, 6, alice_start_balance, 0, 0, 0), false, "");
return true;
}
//------------------------------------------------------------------------------
wallet_chain_switch_with_spending_the_same_ki::wallet_chain_switch_with_spending_the_same_ki()
{
REGISTER_CALLBACK_METHOD(wallet_chain_switch_with_spending_the_same_ki, c1);
}
bool wallet_chain_switch_with_spending_the_same_ki::generate(std::vector<test_event_entry>& events) const
{
// Test outline
// 1. A wallet has one unspent output
// 2. wallet2::transfer() creates tx_0 that spends wallet's output
// 3. tx_0 is successfully put into the blockchain
// 4. Due to chain switch tx_0 is removed from the blockchain and get into the transaction pool
// 5. Make sure the wallet can't spend that output
// 6. After tx is expired make sure the wallet can spend that output
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate();
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
MAKE_TX(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(30), blk_0r);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
// rewind blocks to allow wallet be able to spend the coins
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_chain_switch_with_spending_the_same_ki::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(30), true, UINT64_MAX, MK_TEST_COINS(30)), false, "");
std::vector<tx_destination_entry> destinations { tx_destination_entry(MK_TEST_COINS(30) - TESTS_DEFAULT_FEE, m_accounts[BOB_ACC_IDX].get_public_address()) };
try
{
// create tx_1
alice_wlt->transfer(destinations, 0, 0, TESTS_DEFAULT_FEE, empty_extra, empty_attachment);
}
catch (std::exception &e)
{
CHECK_AND_ASSERT_MES(false, false, "alice_wlt->transfer() caused an exception: " << e.what());
}
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
// mine blk_2 on height 22
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c), false, "");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
// refresh wallet
alice_wlt->refresh();
// DO NOT scan_tx_pool here intentionally
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(0)), false, "");
uint64_t blk_1r_height = c.get_top_block_height() - 1;
crypto::hash blk_1r_id = c.get_block_id_by_height(blk_1r_height);
block blk_2a = AUTO_VAL_INIT(blk_2a);
r = mine_next_pow_block_in_playtime_with_given_txs(m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>(), blk_1r_id, blk_1r_height + 1, &blk_2a);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed");
// one more to trigger chain switch
block blk_3a = AUTO_VAL_INIT(blk_3a);
r = mine_next_pow_block_in_playtime_with_given_txs(m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>(), get_block_hash(blk_2a), get_block_height(blk_2a) + 1, &blk_3a);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed");
// make sure tx_1 has been moved back to the pool
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
CHECK_AND_ASSERT_MES(c.get_alternative_blocks_count() == 1, false, "Incorrect alt blocks count: " << c.get_alternative_blocks_count());
//const transaction& tx_1 = boost::get<transaction>(events[4 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3]);
// refresh wallet
alice_wlt->refresh();
// DO NOT scan_tx_pool here intentionally
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(0)), false, "");
return true;
}
//------------------------------------------------------------------------------
wallet_unconfimed_tx_balance::wallet_unconfimed_tx_balance()
{
REGISTER_CALLBACK_METHOD(wallet_unconfimed_tx_balance, c1);
}
bool wallet_unconfimed_tx_balance::generate(std::vector<test_event_entry>& events) const
{
// Test outline:
// 1. Miner sends 100 coins to Alice (50 + 50)
// 2. Alice sends 30 back to Miner (tx is unconfirmed)
// 3. Make sure Alice's wallet has correct balance, when it is checked from wallet's callback
// 4. Few blocks are mined so the tx is get confirmed
// 5. Make sure Alice's balance has changed correctly
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
MAKE_TX(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(50), blk_0r);
MAKE_TX(events, tx_1, miner_acc, alice_acc, MK_TEST_COINS(50), blk_0r);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({ tx_0, tx_1 }));
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_unconfimed_tx_balance::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(100), false, UINT64_MAX, MK_TEST_COINS(100)), false, "");
bool callback_is_ok = false;
// this callback will ba called from within wallet2::transfer() below
std::shared_ptr<wlt_lambda_on_transfer2_wrapper> l(new wlt_lambda_on_transfer2_wrapper(
[&callback_is_ok](const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) -> bool
{
CHECK_AND_ASSERT_MES(balance == MK_TEST_COINS(70), false, "invalid balance: " << print_money_brief(balance));
CHECK_AND_ASSERT_MES(unlocked_balance == MK_TEST_COINS(50), false, "invalid unlocked_balance: " << print_money_brief(unlocked_balance));
CHECK_AND_ASSERT_MES(total_mined == 0, false, "invalid total_mined: " << print_money_brief(total_mined));
callback_is_ok = true;
return true;
}
));
alice_wlt->callback(l);
uint64_t fee = TESTS_DEFAULT_FEE * 3;
std::vector<tx_destination_entry> destinations{ tx_destination_entry(MK_TEST_COINS(30) - fee, m_accounts[MINER_ACC_IDX].get_public_address()) };
try
{
alice_wlt->transfer(destinations, 0, 0, fee, empty_extra, empty_attachment);
}
catch (std::exception &e)
{
CHECK_AND_ASSERT_MES(false, false, "alice_wlt->transfer() caused an exception: " << e.what());
}
CHECK_AND_NO_ASSERT_MES(callback_is_ok, false, "callback failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
// 50 coins should be locked and 50 - unlocked
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(70), false, UINT64_MAX, MK_TEST_COINS(50), 0, 0, MK_TEST_COINS(30) - fee), false, "");
// mine WALLET_DEFAULT_TX_SPENDABLE_AGE blocks so the tx get confirmed and coins get unlocked
CHECK_AND_ASSERT_MES(mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, WALLET_DEFAULT_TX_SPENDABLE_AGE), false, "");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(70), false, UINT64_MAX, MK_TEST_COINS(70), 0, 0, 0), false, "");
return true;
}