2018-12-27 18:50:45 +03:00
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Boolberry developers
// 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 "mixin_attr.h"
using namespace epee ;
using namespace currency ;
mix_attr_tests : : mix_attr_tests ( )
{
REGISTER_CALLBACK_METHOD ( mix_attr_tests , check_balances_1 ) ;
REGISTER_CALLBACK_METHOD ( mix_attr_tests , remember_last_block ) ;
REGISTER_CALLBACK_METHOD ( mix_attr_tests , check_last_not_changed ) ;
REGISTER_CALLBACK_METHOD ( mix_attr_tests , check_last2_and_balance ) ;
REGISTER_CALLBACK_METHOD ( mix_attr_tests , check_last_changed ) ;
}
bool mix_attr_tests : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
//TODO events
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ; // 0
MAKE_ACCOUNT ( events , bob_account ) ; // 1
MAKE_ACCOUNT ( events , alice_account ) ; // 2
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner_account ) ; // 3
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_account ) ; // 4
MAKE_NEXT_BLOCK ( events , blk_3 , blk_2 , miner_account ) ; // 5
REWIND_BLOCKS ( events , blk_3r , blk_3 , miner_account ) ; // <N blocks>
MAKE_TX_LIST_START_MIX_ATTR ( events , txs_blk_4 , miner_account ,
miner_account ,
MK_TEST_COINS ( 5 ) ,
blk_3r ,
CURRENCY_TO_KEY_OUT_RELAXED , std : : vector < currency : : attachment_v > ( ) ) ; // 6 + N
MAKE_TX_LIST_MIX_ATTR ( events , txs_blk_4 , miner_account , miner_account , MK_TEST_COINS ( 5 ) , blk_3r ,
CURRENCY_TO_KEY_OUT_RELAXED ) ; // 7 + N
MAKE_TX_LIST_MIX_ATTR ( events , txs_blk_4 , miner_account , miner_account , MK_TEST_COINS ( 5 ) , blk_3r ,
CURRENCY_TO_KEY_OUT_RELAXED ) ; // 8 + N
MAKE_TX_LIST_MIX_ATTR ( events , txs_blk_4 , miner_account , bob_account , MK_TEST_COINS ( 5 ) , blk_3r ,
CURRENCY_TO_KEY_OUT_FORCED_NO_MIX ) ; // 9 + N
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_4 , blk_3r , miner_account , txs_blk_4 ) ; // 10 + N
DO_CALLBACK ( events , " check_balances_1 " ) ; // 11 + N
REWIND_BLOCKS ( events , blk_4r , blk_4 , miner_account ) ; // 11 + 2N
// let's try to spend Bob's no-mix output mixing in 1 fake outputs -- should fail
DO_CALLBACK ( events , " remember_last_block " ) ; // 12 + 2N
DO_CALLBACK ( events , " mark_invalid_tx " ) ; // 13 + 2N
MAKE_TX_MIX ( events , tx_0 , bob_account , alice_account , MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , 1 , blk_4 ) ; // 14 + 2N
DO_CALLBACK ( events , " mark_invalid_block " ) ; // 15 + 2N
MAKE_NEXT_BLOCK_TX1 ( events , blk_5_f , blk_4r , miner_account , tx_0 ) ; // 16 + 2N
DO_CALLBACK ( events , " check_last_not_changed " ) ; // 17 + 2N
// then try to spend Bob's no-mix output with direct spend -- should go well
MAKE_TX_MIX_ATTR ( events , tx_1 , bob_account , alice_account , MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , 0 , blk_4 , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_5 , blk_4r , miner_account , tx_1 ) ;
DO_CALLBACK ( events , " check_last2_and_balance " ) ;
//check mixin
MAKE_ACCOUNT ( events , bob_account2 ) ;
MAKE_ACCOUNT ( events , alice_account2 ) ;
MAKE_TX_LIST_START_MIX_ATTR ( events , txs_blk_6 , miner_account ,
miner_account ,
MK_TEST_COINS ( 5 ) ,
blk_5 ,
CURRENCY_TO_KEY_OUT_RELAXED ,
std : : vector < currency : : attachment_v > ( ) ) ;
//MAKE_TX_LIST_MIX_ATTR(events, txs_blk_6, miner_account, miner_account, MK_COINS(5), blk_5, 4);
MAKE_TX_LIST_MIX_ATTR ( events , txs_blk_6 , miner_account , bob_account2 , MK_TEST_COINS ( 5 ) , blk_5 , 4 ) ; // mix_attr == 4
MAKE_TX_LIST_MIX_ATTR ( events , txs_blk_6 , miner_account , bob_account2 , MK_TEST_COINS ( 5 ) , blk_5 , 4 ) ; // mix_attr == 4
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_6 , blk_5 , miner_account , txs_blk_6 ) ;
//
// 7 outputs with amount == MK_TEST_COINS(5) having these mix_attrs: 0, 0, 0, 1 (miner -> miner, txs_blk_4), 0 (miner -> miner, txs_blk_6), 4, 4 (miner -> bob2, txs_blk_6)
//
DO_CALLBACK ( events , " remember_last_block " ) ;
bool r = false ;
try
{
// should fail to construct as there are not enough outputs to mix (regardless of mix_attr)
MAKE_TX_MIX_ATTR ( events , tx_3 , bob_account2 , alice_account2 , MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , 7 , blk_6 , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
}
catch ( std : : runtime_error & )
{
r = true ;
}
CHECK_AND_ASSERT_MES ( r , false , " tx_3 passed ok, but expected to fail " ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ; // should fail because Bob2 outputs may only be used in set of 4 and more outputs (there's only 2 + 1 = 3)
MAKE_TX_MIX_ATTR ( events , tx_4 , bob_account2 , alice_account2 , MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , 2 , blk_6 , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ; // the same
MAKE_TX_MIX_ATTR ( events , tx_5 , bob_account2 , alice_account2 , MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , 1 , blk_6 , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ; // the same
MAKE_TX_MIX_ATTR ( events , tx_6 , bob_account2 , alice_account2 , MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , 0 , blk_6 , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
MAKE_TX_MIX_ATTR ( events , tx_7 , bob_account2 , alice_account2 , MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , 3 , blk_6 , CURRENCY_TO_KEY_OUT_RELAXED , false ) ; // should pass
MAKE_NEXT_BLOCK_TX1 ( events , blk_7 , blk_6 , miner_account , tx_7 ) ;
DO_CALLBACK ( events , " check_last_changed " ) ;
return true ;
}
bool mix_attr_tests : : check_balances_1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
currency : : account_base m_bob_account = boost : : get < account_base > ( events [ 1 ] ) ;
currency : : account_base m_alice_account = boost : : get < account_base > ( events [ 2 ] ) ;
std : : list < block > blocks ;
bool r = c . get_blocks ( 0 , 100 + 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW , blocks ) ;
CHECK_TEST_CONDITION ( r ) ;
std : : vector < currency : : block > chain ;
map_hash2tx_t mtx ;
r = find_block_chain ( events , chain , mtx , get_block_hash ( blocks . back ( ) ) ) ;
CHECK_TEST_CONDITION ( r ) ;
CHECK_EQ ( MK_TEST_COINS ( 5 ) , get_balance ( m_bob_account , chain , mtx ) ) ;
CHECK_EQ ( 0 , get_balance ( m_alice_account , chain , mtx ) ) ;
return true ;
}
bool mix_attr_tests : : remember_last_block ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
top_id_befor_split = c . get_tail_id ( ) ;
return true ;
}
bool mix_attr_tests : : check_last_not_changed ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
CHECK_EQ ( top_id_befor_split , c . get_tail_id ( ) ) ;
return true ;
}
bool mix_attr_tests : : check_last2_and_balance ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
CHECK_NOT_EQ ( top_id_befor_split , c . get_tail_id ( ) ) ;
currency : : account_base m_bob_account = boost : : get < account_base > ( events [ 1 ] ) ;
currency : : account_base m_alice_account = boost : : get < account_base > ( events [ 2 ] ) ;
std : : list < block > blocks ;
bool r = c . get_blocks ( 0 , 100 + 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW , blocks ) ;
CHECK_TEST_CONDITION ( r ) ;
std : : vector < currency : : block > chain ;
map_hash2tx_t mtx ;
r = find_block_chain ( events , chain , mtx , get_block_hash ( blocks . back ( ) ) ) ;
CHECK_TEST_CONDITION ( r ) ;
CHECK_EQ ( 0 , get_balance ( m_bob_account , chain , mtx ) ) ;
CHECK_EQ ( MK_TEST_COINS ( 5 ) - TESTS_DEFAULT_FEE , get_balance ( m_alice_account , chain , mtx ) ) ;
return true ;
}
bool mix_attr_tests : : check_last_changed ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
CHECK_NOT_EQ ( top_id_befor_split , c . get_tail_id ( ) ) ;
return true ;
}
//--------------------------------------------------------------------------
struct check_outs_count_params
{
check_outs_count_params ( ) : amount ( 0 ) , count ( 0 ) { }
check_outs_count_params ( uint64_t amount , size_t count ) : amount ( amount ) , count ( count ) { }
uint64_t amount ;
size_t count ;
BEGIN_KV_SERIALIZE_MAP ( )
KV_SERIALIZE_POD_AS_HEX_STRING ( amount )
KV_SERIALIZE_POD_AS_HEX_STRING ( count )
END_KV_SERIALIZE_MAP ( )
} ;
mix_in_spent_outs : : mix_in_spent_outs ( )
{
REGISTER_CALLBACK_METHOD ( mix_in_spent_outs , c1 ) ;
REGISTER_CALLBACK_METHOD ( mix_in_spent_outs , check_outs_count ) ;
}
bool mix_in_spent_outs : : generate ( std : : vector < test_event_entry > & events ) const
{
// test idea: use obviously spent outputs as fake ones for mixing in. Such outputs MUST NOT be used for mixing in.
m_accounts . resize ( TOTAL_ACCS_COUNT ) ;
auto & miner_acc = m_accounts [ MINER_ACC_IDX ] ; miner_acc . generate ( ) ;
auto & alice_acc = m_accounts [ ALICE_ACC_IDX ] ; alice_acc . generate ( ) ;
auto & bob_acc = m_accounts [ BOB_ACC_IDX ] ; bob_acc . generate ( ) ;
GENERATE_ACCOUNT ( preminer_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 6 ) ;
m_test_amount = MK_TEST_COINS ( 6 ) ;
for ( auto & o : blk_0 . miner_tx . vout )
{
2022-05-25 22:31:23 +02:00
CHECK_AND_ASSERT_MES ( boost : : get < tx_out_bare > ( o ) . amount ! = m_test_amount , false , " Premine surprisingly has test amount output, change m_test_amount " ) ;
2018-12-27 18:50:45 +03:00
}
MAKE_TX_LIST_START ( events , txs , miner_acc , alice_acc , m_test_amount , blk_0r ) ;
MAKE_TX_LIST ( events , txs , miner_acc , alice_acc , m_test_amount , blk_0r ) ;
MAKE_TX_LIST ( events , txs , miner_acc , alice_acc , m_test_amount , blk_0r ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_1 , blk_0r , miner_acc , txs ) ;
DO_CALLBACK_PARAMS_STR ( events , " check_outs_count " , epee : : serialization : : store_t_to_json ( check_outs_count_params ( m_test_amount , 3 ) ) ) ;
MAKE_TX_MIX ( events , tx_0 , miner_acc , bob_acc , m_test_amount , 0 , blk_1 ) ; // mix_attr == 0
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx_0 ) ;
DO_CALLBACK_PARAMS_STR ( events , " check_outs_count " , epee : : serialization : : store_t_to_json ( check_outs_count_params ( m_test_amount , 4 ) ) ) ;
// spend 2 of 3 Alice's outputs with direct transfer (not mixins)
2019-04-25 23:37:43 +02:00
MAKE_TX ( events , tx_1 , alice_acc , miner_acc , m_test_amount * 2 - TESTS_DEFAULT_FEE , blk_2 ) ; // mix_attr == 0
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_1 ) ;
// despite the fact all Alice's outputs are spent now, total number of outputs with such amount should not changed
DO_CALLBACK_PARAMS_STR ( events , " check_outs_count " , epee : : serialization : : store_t_to_json ( check_outs_count_params ( m_test_amount , 4 ) ) ) ;
REWIND_BLOCKS_N ( events , blk_3r , blk_3 , miner_acc , WALLET_DEFAULT_TX_SPENDABLE_AGE ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool mix_in_spent_outs : : check_outs_count ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
// makes sure there are params.count outputs with params.amount in the blockchain
check_outs_count_params params ;
bool r = epee : : serialization : : load_t_from_json ( params , boost : : get < callback_entry > ( events [ ev_index ] ) . callback_params ) ;
CHECK_AND_ASSERT_MES ( r , false , " Can't load params struct " ) ;
auto & bcs = c . get_blockchain_storage ( ) ;
uint64_t such_amount_outs_count = bcs . get_outputs_container ( ) . get_item_size ( params . amount ) ;
CHECK_AND_ASSERT_MES ( such_amount_outs_count = = params . count , false , " invalid global outputs with amount == " < < print_money ( params . amount ) < < " : " < < such_amount_outs_count < < " , expected: " < < params . count ) ;
return true ;
}
bool mix_in_spent_outs : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
bool r = false ;
std : : shared_ptr < tools : : wallet2 > bob_wlt = init_playtime_test_wallet ( events , c , m_accounts [ BOB_ACC_IDX ] ) ;
bob_wlt - > refresh ( ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " There are txs in the pool " ) ;
2019-04-25 23:37:43 +02:00
std : : vector < tx_destination_entry > destinations ( { tx_destination_entry ( m_test_amount - TESTS_DEFAULT_FEE , m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) ) } ) ;
2018-12-27 18:50:45 +03:00
// check that mixing in 3 or 2 fake outputs fails because of using already spend outputs
for ( size_t fake_outputs_to_mixin = 2 ; fake_outputs_to_mixin > = 2 ; fake_outputs_to_mixin - - )
{
r = false ;
try
{
2019-04-25 23:37:43 +02:00
bob_wlt - > transfer ( destinations , fake_outputs_to_mixin , 0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment ) ;
2018-12-27 18:50:45 +03:00
}
catch ( tools : : error : : not_enough_outs_to_mix & e )
{
r = true ;
LOG_PRINT_GREEN ( " Caught not_enough_outs_to_mix as expected, what() = " < < e . what ( ) , LOG_LEVEL_0 ) ;
}
CHECK_AND_ASSERT_MES ( r , false , " Expected exception was not caught for fake_outputs_to_mixin == " < < fake_outputs_to_mixin ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " There are txs in the pool " ) ;
}
transaction tx = AUTO_VAL_INIT ( tx ) ;
2019-04-25 23:37:43 +02:00
bob_wlt - > transfer ( destinations , 1 , 0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment , tx ) ; // mixing in 1 fake output should be okay as Alice did not spend it
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( tx . vin . size ( ) = = 1 , false , " tx vin.size() is not 1 " ) ;
CHECK_AND_ASSERT_MES ( tx . vin [ 0 ] . type ( ) = = typeid ( txin_to_key ) & & boost : : get < txin_to_key > ( tx . vin [ 0 ] ) . key_offsets . size ( ) = = 2 , false , " Incorrect vin[0] type of key_offsets " ) ; // make sure 1 fake output was used
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect number of txs in the pool " ) ;
2019-03-25 01:30:20 +01:00
r = mine_next_pow_block_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c ) ;
2018-12-27 18:50:45 +03:00
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 ;
}