2018-12-27 18:50:45 +03:00
// 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 "multisig_wallet_tests.h"
# include "pos_block_builder.h"
# include "tx_builder.h"
using namespace epee ;
using namespace crypto ;
using namespace currency ;
# define TMP_LOG_FLOOD_STOP // <- comment this when debugging a broken test to skip log filtering
# if defined(TMP_LOG_FLOOD_STOP)
# define TMP_LOG_SILENT log_level = log_space::get_set_log_detalisation_level(); \
log_space : : get_set_log_detalisation_level ( true , LOG_LEVEL_SILENT )
# define TMP_LOG_RESTORE log_space::get_set_log_detalisation_level(true, log_level)
# else
# define TMP_LOG_SILENT
# define TMP_LOG_RESTORE
# endif
void exception_handler ( ) { }
//==============================================================================================================================
// helper routine: creates multisig-spending tx using a wallet and keys of other ms-participants, then sends it to the core proxy
void transfer_multisig ( tools : : wallet2 & w ,
const std : : list < currency : : account_keys > & owner_keys ,
2019-04-03 17:53:49 +03:00
const crypto : : hash & multisig_id ,
2018-12-27 18:50:45 +03:00
const std : : vector < currency : : tx_destination_entry > & dsts ,
uint64_t unlock_time ,
uint64_t fee ,
const std : : vector < currency : : extra_v > & extra ,
2019-04-03 17:53:49 +03:00
const std : : vector < currency : : attachment_v > & attachments ,
tools : : detail : : split_strategy_id_t split_strategy_id ,
2018-12-27 18:50:45 +03:00
const tools : : tx_dust_policy & dust_policy ,
currency : : transaction & tx ,
2022-05-10 23:49:20 +02:00
uint64_t tx_version ,
2018-12-27 18:50:45 +03:00
uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED ,
uint64_t flags = 0 ,
bool shuffle = true ,
bool send_to_network = true )
{
currency : : account_public_address crypt_address = get_crypt_address_from_destinations ( w . get_account ( ) . get_keys ( ) , dsts ) ;
// prepare transaction will sign ms input partially with wallet's keys - it needed to be signed fully with the others
2019-04-03 17:53:49 +03:00
tools : : construct_tx_param ctp = AUTO_VAL_INIT ( ctp ) ;
2021-02-04 21:28:53 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2019-04-03 17:53:49 +03:00
ctp . attachments = attachments ;
ctp . crypt_address = crypt_address ;
ctp . dsts = dsts ;
ctp . dust_policy = dust_policy ;
ctp . extra = extra ;
ctp . fake_outputs_count = 0 ;
ctp . fee = fee ;
ctp . flags = flags ;
ctp . mark_tx_as_complete = false ;
ctp . multisig_id = multisig_id ;
ctp . shuffle = shuffle ;
ctp . split_strategy_id = split_strategy_id ;
ctp . tx_outs_attr = tx_outs_attr ;
ctp . unlock_time = unlock_time ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = tx_version ;
2023-04-18 16:55:00 +02:00
tools : : mode_separate_context emode_separate = AUTO_VAL_INIT ( emode_separate ) ;
emode_separate . tx_for_mode_separate = tx ;
w . prepare_transaction ( ctp , ftp , emode_separate ) ;
2019-04-30 19:36:53 +02:00
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
w . finalize_transaction ( ftp , tx , sk , false ) ;
2018-12-27 18:50:45 +03:00
// sign ms input with all other non-wallet keys
auto it = w . get_multisig_transfers ( ) . find ( multisig_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( it ! = w . get_multisig_transfers ( ) . end ( ) , " can't find multisig_id: " < < multisig_id ) ;
const currency : : transaction & ms_source_tx = it - > second . m_ptx_wallet_info - > m_tx ;
bool is_tx_input_fully_signed = false ;
bool r = false ;
for ( const currency : : account_keys & keys : owner_keys )
{
//if (keys == w.get_account().get_keys())
// continue;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! is_tx_input_fully_signed , " is_tx_input_fully_signed == true, expected: false " ) ;
r = sign_multisig_input_in_tx_custom ( tx , 0 , keys , ms_source_tx , & is_tx_input_fully_signed , true ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " sign_multisig_input_in_tx failed " ) ;
}
THROW_IF_FALSE_WALLET_INT_ERR_EX ( is_tx_input_fully_signed , " is_tx_input_fully_signed == false, expected: true " ) ;
// send to network
if ( send_to_network )
{
COMMAND_RPC_SEND_RAW_TX : : request req ;
req . tx_as_hex = epee : : string_tools : : buff_to_hex_nodelimer ( tx_to_blob ( tx ) ) ;
COMMAND_RPC_SEND_RAW_TX : : response daemon_send_resp ;
bool r = w . get_core_proxy ( ) - > call_COMMAND_RPC_SEND_RAW_TX ( req , daemon_send_resp ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , tools : : error : : no_connection_to_daemon , " sendrawtransaction " ) ;
2020-05-07 23:26:41 +02:00
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status = = API_RETURN_CODE_BUSY , tools : : error : : daemon_busy , " sendrawtransaction " ) ;
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status = = API_RETURN_CODE_DISCONNECTED , tools : : error : : wallet_internal_error , " Transfer attempt while daemon offline " ) ;
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status ! = API_RETURN_CODE_OK , tools : : error : : tx_rejected , tx , daemon_send_resp . status ) ;
2018-12-27 18:50:45 +03:00
}
}
//==============================================================================================================================
multisig_wallet_test : : multisig_wallet_test ( )
{
REGISTER_CALLBACK_METHOD ( multisig_wallet_test , c1 ) ;
}
bool multisig_wallet_test : : generate ( std : : vector < test_event_entry > & events ) const
{
currency : : account_base genesis_acc ;
genesis_acc . generate ( ) ;
m_mining_accunt . generate ( ) ;
m_accunt_a . generate ( ) ;
m_accunt_b . generate ( ) ;
m_accunt_c . generate ( ) ;
block blk_0 = AUTO_VAL_INIT ( blk_0 ) ;
generator . construct_genesis_block ( blk_0 , genesis_acc , test_core_time : : get_time ( ) ) ;
events . push_back ( blk_0 ) ;
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , m_mining_accunt , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool multisig_wallet_test : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
# define AMOUNT_TO_TRANSFER_MULTISIG MK_TEST_COINS(11)
// Test outline:
// 1. Generate multisig and send it to blockchain
// 2. Transfer from multisig
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , m_mining_accunt ) ;
size_t blocks_fetched = 0 ;
bool received_money ;
std : : atomic < bool > atomic_false = ATOMIC_VAR_INIT ( false ) ;
miner_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW , false , " Incorrect numbers of blocks fetched " ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Incorrect txs count in the pool " ) ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
std : : vector < tx_destination_entry > dst ;
dst . resize ( 1 ) ;
//multisig
dst . back ( ) . addr . push_back ( m_accunt_a . get_public_address ( ) ) ;
dst . back ( ) . addr . push_back ( m_accunt_b . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
dst . back ( ) . amount = AMOUNT_TO_TRANSFER_MULTISIG + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
dst . back ( ) . minimum_sigs = dst . back ( ) . addr . size ( ) ;
transaction result_tx = AUTO_VAL_INIT ( result_tx ) ;
2019-04-30 18:45:12 +02:00
miner_wlt - > transfer ( dst , 0 , 0 , TESTS_DEFAULT_FEE , extra , attachments , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , result_tx ) ;
2018-12-27 18:50:45 +03:00
2019-03-25 01:30:20 +01:00
bool r = mine_next_pow_blocks_in_playtime ( m_mining_accunt . get_public_address ( ) , c , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
//findout multisig out intex
size_t i = 0 ;
for ( ; i ! = result_tx . vout . size ( ) ; i + + )
{
2022-05-20 21:32:27 +02:00
if ( boost : : get < currency : : tx_out_bare > ( result_tx . vout [ i ] ) . target . type ( ) = = typeid ( txout_multisig ) )
2018-12-27 18:50:45 +03:00
break ;
}
CHECK_AND_ASSERT_MES ( i ! = result_tx . vout . size ( ) , false , " Incorrect txs outs " ) ;
crypto : : hash multisig_id = get_multisig_out_id ( result_tx , i ) ;
CHECK_AND_ASSERT_MES ( multisig_id ! = null_hash , false , " Multisig failed: failed to get get_multisig_out_id " ) ;
2019-03-25 01:30:20 +01:00
r = mine_next_pow_blocks_in_playtime ( m_mining_accunt . get_public_address ( ) , c , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
std : : shared_ptr < tools : : wallet2 > wallet_a = init_playtime_test_wallet ( events , c , m_accunt_a ) ;
std : : shared_ptr < tools : : wallet2 > wallet_b = init_playtime_test_wallet ( events , c , m_accunt_b ) ;
wallet_a - > refresh ( ) ;
wallet_b - > refresh ( ) ;
tools : : wallet2 : : multisig_transfer_container ms_a , ms_b ;
wallet_a - > get_multisig_transfers ( ms_a ) ;
wallet_b - > get_multisig_transfers ( ms_b ) ;
CHECK_AND_ASSERT_MES ( ms_a . size ( ) = = 1 & & ms_b . size ( ) = = 1 , false , " Multisig failed: ms_a.size() == 1 && ms_b.size() == 1 " ) ;
2022-05-10 23:49:20 +02:00
uint64_t tx_version = currency : : get_tx_version ( c . get_current_blockchain_size ( ) , c . get_blockchain_storage ( ) . get_core_runtime_config ( ) . hard_forks ) ;
2018-12-27 18:50:45 +03:00
std : : vector < tx_destination_entry > dst2 ( 1 ) ;
dst2 . back ( ) . addr . resize ( 1 ) ;
dst2 . back ( ) . addr . back ( ) = m_accunt_c . get_public_address ( ) ;
dst2 . back ( ) . amount = AMOUNT_TO_TRANSFER_MULTISIG ;
dst2 . back ( ) . minimum_sigs = dst2 . back ( ) . addr . size ( ) ;
std : : list < currency : : account_keys > acc_keys ;
//acc_keys.push_back(m_accunt_a.get_keys());
acc_keys . push_back ( m_accunt_b . get_keys ( ) ) ;
transfer_multisig ( * wallet_a . get ( ) ,
acc_keys ,
multisig_id ,
dst2 ,
0 ,
2019-04-25 23:37:43 +02:00
TESTS_DEFAULT_FEE ,
2018-12-27 18:50:45 +03:00
extra ,
attachments ,
2019-04-03 17:53:49 +03:00
tools : : detail : : ssi_digit ,
2018-12-27 18:50:45 +03:00
tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) ,
2022-05-10 23:49:20 +02:00
result_tx ,
tx_version ) ;
2018-12-27 18:50:45 +03:00
2019-03-25 01:30:20 +01:00
r = mine_next_pow_blocks_in_playtime ( m_mining_accunt . get_public_address ( ) , c , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
wallet_a - > refresh ( ) ;
wallet_b - > refresh ( ) ;
ms_a . clear ( ) ; ms_b . clear ( ) ;
wallet_b - > get_multisig_transfers ( ms_a ) ;
wallet_b - > get_multisig_transfers ( ms_b ) ;
CHECK_AND_ASSERT_MES ( ms_a . size ( ) = = 1 & & ms_b . size ( ) = = 1 , false , " Multisig failed: ms_a.size() == 1 && ms_b.size() == 1 " ) ;
CHECK_AND_ASSERT_MES ( ms_a . begin ( ) - > second . is_spent ( ) = = true & & ms_b . begin ( ) - > second . is_spent ( ) = = true , false , " ms_a.begin()->second.m_spent == false || ms_b.begin()->second.m_spent == false " ) ;
std : : shared_ptr < tools : : wallet2 > wallet_c = init_playtime_test_wallet ( events , c , m_accunt_c ) ;
wallet_c - > refresh ( ) ;
uint64_t balance = wallet_c - > balance ( ) ;
CHECK_AND_ASSERT_MES ( balance = = AMOUNT_TO_TRANSFER_MULTISIG , false , " Multisig failed: balance missmatch " ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_wallet_test_many_dst : : multisig_wallet_test_many_dst ( )
{
REGISTER_CALLBACK_METHOD ( multisig_wallet_test_many_dst , c1 ) ;
}
bool multisig_wallet_test_many_dst : : 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 ) ;
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool multisig_wallet_test_many_dst : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
// Test outline:
// 1. Generate few PoW blocks.
// 2. Transfer multisig tx to enormous amount of receivers (addresses_count)
// 3. Receive the transfer, check it's ok.
// 4. Send the money to Alice and make sure she successfully receives them.
static const size_t addresses_count = 700 ;
// !!!
// NOTE: When debugging this test undefine TMP_LOG_FLOOD_STOP to skip log filtering!
// !!!
int log_level = 0 ;
bool r = false ;
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , m_accounts [ MINER_ACC_IDX ] ) ;
size_t blocks_fetched = 0 ;
bool received_money ;
std : : atomic < bool > atomic_false = ATOMIC_VAR_INIT ( false ) ;
miner_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW , false , " Incorrect numbers of blocks fetched " ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Incorrect txs count in the pool " ) ;
uint64_t amount = 0 ;
miner_wlt - > balance ( amount ) ;
CHECK_AND_ASSERT_MES ( amount > TESTS_DEFAULT_FEE , false , " miner has unexpected balance " ) ;
amount - = TESTS_DEFAULT_FEE ;
std : : vector < currency : : account_base > addresses ( addresses_count ) ;
tx_destination_entry de = AUTO_VAL_INIT ( de ) ;
for ( size_t i = 0 ; i < addresses_count ; + + i )
{
addresses [ i ] . generate ( ) ;
de . addr . push_back ( addresses [ i ] . get_public_address ( ) ) ;
}
de . amount = amount ;
de . minimum_sigs = de . addr . size ( ) ;
transaction result_tx = AUTO_VAL_INIT ( result_tx ) ;
TMP_LOG_SILENT ;
2019-04-03 17:53:49 +03:00
miner_wlt - > transfer ( std : : vector < tx_destination_entry > ( { de } ) , 0 , 0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , result_tx ) ;
2018-12-27 18:50:45 +03:00
TMP_LOG_RESTORE ;
2022-05-25 22:31:23 +02:00
auto it = std : : find_if ( result_tx . vout . begin ( ) , result_tx . vout . end ( ) , [ ] ( tx_out_v & o )
{
return boost : : get < tx_out_bare > ( o ) . target . type ( ) = = typeid ( txout_multisig ) ;
} ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( it ! = result_tx . vout . end ( ) , false , " Can't find output txout_multisig " ) ;
size_t multisig_index = it - result_tx . vout . begin ( ) ;
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 " ) ;
std : : shared_ptr < tools : : wallet2 > w = init_playtime_test_wallet ( events , c , addresses [ 0 ] ) ;
w - > refresh ( ) ;
std : : list < currency : : account_keys > owner_keys ;
for ( auto a : addresses )
owner_keys . push_back ( a . get_keys ( ) ) ;
owner_keys . pop_front ( ) ; // the first one was used to initialize the wallet 'w'
tx_destination_entry de2 ( amount - TESTS_DEFAULT_FEE , m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) ) ;
transaction tx = AUTO_VAL_INIT ( tx ) ;
TMP_LOG_SILENT ;
2022-05-10 23:49:20 +02:00
transfer_multisig ( * w . get ( ) , owner_keys , get_multisig_out_id ( result_tx , multisig_index ) , std : : vector < tx_destination_entry > ( { de2 } ) , 0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
TMP_LOG_RESTORE ;
2019-03-25 01:30:20 +01:00
r = mine_next_pow_blocks_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , m_accounts [ ALICE_ACC_IDX ] ) ;
alice_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 1 , false , " Incorrect numbers of blocks fetched " ) ;
r = check_balance_via_wallet ( * alice_wlt . get ( ) , " alice_wlt " , amount - TESTS_DEFAULT_FEE ) ;
CHECK_AND_ASSERT_MES ( r , false , " invalid balance " ) ;
tools : : wallet2 : : multisig_transfer_container mstc ;
alice_wlt - > get_multisig_transfers ( mstc ) ;
CHECK_AND_ASSERT_MES ( mstc . empty ( ) , false , " Got invalid multisig transfer " ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_wallet_heterogenous_dst : : multisig_wallet_heterogenous_dst ( )
{
REGISTER_CALLBACK_METHOD ( multisig_wallet_heterogenous_dst , c1 ) ;
}
bool multisig_wallet_heterogenous_dst : : 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 ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ;
GENERATE_ACCOUNT ( carol_acc ) ;
m_accounts . push_back ( carol_acc ) ;
GENERATE_ACCOUNT ( dan_acc ) ;
m_accounts . push_back ( dan_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 ) ;
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool multisig_wallet_heterogenous_dst : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , m_accounts [ MINER_ACC_IDX ] ) ;
size_t blocks_fetched = 0 ;
bool received_money = false ;
bool r = false ;
std : : atomic < bool > atomic_false ( false ) ;
miner_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW , false , " Incorrect numbers of blocks fetched " ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Incorrect txs count in the pool " ) ;
2019-04-25 23:37:43 +02:00
uint64_t amount1 = TESTS_DEFAULT_FEE * 13 + 1 ;
uint64_t amount2 = TESTS_DEFAULT_FEE * 11 + 2 ;
uint64_t amount3 = TESTS_DEFAULT_FEE * 7 + 3 ;
uint64_t amount4 = TESTS_DEFAULT_FEE * 5 + 4 ;
uint64_t amount5 = TESTS_DEFAULT_FEE * 21 ;
uint64_t amount6 = TESTS_DEFAULT_FEE * 23 ;
2018-12-27 18:50:45 +03:00
tx_destination_entry de1 = AUTO_VAL_INIT ( de1 ) ;
de1 . addr . push_back ( m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) ) ;
de1 . addr . push_back ( m_accounts [ BOB_ACC_IDX ] . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
de1 . amount = amount1 + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de1 . minimum_sigs = de1 . addr . size ( ) ;
tx_destination_entry de2 = AUTO_VAL_INIT ( de2 ) ;
de2 . addr . push_back ( m_accounts [ CAROL_ACC_IDX ] . get_public_address ( ) ) ;
de2 . addr . push_back ( m_accounts [ DAN_ACC_IDX ] . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
de2 . amount = amount2 + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de2 . minimum_sigs = de2 . addr . size ( ) ;
tx_destination_entry de3 = AUTO_VAL_INIT ( de3 ) ;
de3 . addr . push_back ( m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) ) ;
de3 . addr . push_back ( m_accounts [ DAN_ACC_IDX ] . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
de3 . amount = amount3 + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de3 . minimum_sigs = de3 . addr . size ( ) ;
tx_destination_entry de4 = AUTO_VAL_INIT ( de4 ) ;
de4 . addr . push_back ( m_accounts [ CAROL_ACC_IDX ] . get_public_address ( ) ) ;
de4 . addr . push_back ( m_accounts [ BOB_ACC_IDX ] . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
de4 . amount = amount4 + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de4 . minimum_sigs = de4 . addr . size ( ) ;
tx_destination_entry de5 = AUTO_VAL_INIT ( de5 ) ;
de5 . addr . push_back ( m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
de5 . amount = amount5 + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de5 . minimum_sigs = de5 . addr . size ( ) ;
tx_destination_entry de6 = AUTO_VAL_INIT ( de6 ) ;
de6 . addr . push_back ( m_accounts [ DAN_ACC_IDX ] . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
de6 . amount = amount6 + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de6 . minimum_sigs = de6 . addr . size ( ) ;
// Send multisig tx
transaction ms_tx = AUTO_VAL_INIT ( ms_tx ) ;
miner_wlt - > transfer ( std : : vector < tx_destination_entry > ( { de1 , de2 , de3 , de4 , de5 , de6 } ) ,
2019-04-30 18:45:12 +02:00
0 , 0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , ms_tx ) ;
2018-12-27 18:50:45 +03:00
// calculate multisig hashes for further usage
crypto : : hash ms1_hash = currency : : get_multisig_out_id ( ms_tx , get_tx_out_index_by_amount ( ms_tx , de1 . amount ) ) ;
crypto : : hash ms2_hash = currency : : get_multisig_out_id ( ms_tx , get_tx_out_index_by_amount ( ms_tx , de2 . amount ) ) ;
crypto : : hash ms3_hash = currency : : get_multisig_out_id ( ms_tx , get_tx_out_index_by_amount ( ms_tx , de3 . amount ) ) ;
crypto : : hash ms4_hash = currency : : get_multisig_out_id ( ms_tx , get_tx_out_index_by_amount ( ms_tx , de4 . amount ) ) ;
CHECK_AND_ASSERT_MES ( ms1_hash ! = null_hash & & ms2_hash ! = null_hash & & ms3_hash ! = null_hash & & ms4_hash ! = null_hash , false , " Can't get multisig out id " ) ;
// Mine a block and make sure tx was put into it
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect txs count 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 , " Incorrect txs count in the pool " ) ;
// Check Alice's wallet
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , m_accounts [ ALICE_ACC_IDX ] ) ;
alice_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 , false , " Incorrect numbers of blocks fetched " ) ;
2019-04-25 23:37:43 +02:00
r = check_balance_via_wallet ( * alice_wlt . get ( ) , " alice_wlt " , amount5 + TESTS_DEFAULT_FEE ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " Invalid wallet balance " ) ;
tools : : wallet2 : : multisig_transfer_container alice_mstc ;
alice_wlt - > get_multisig_transfers ( alice_mstc ) ;
CHECK_AND_ASSERT_MES ( alice_mstc . size ( ) = = 2 & & alice_mstc . count ( ms1_hash ) = = 1 & & alice_mstc . count ( ms3_hash ) = = 1 , false , " Alice has incorrect multisig transfers " ) ;
// Check Bob's wallet
std : : shared_ptr < tools : : wallet2 > bob_wlt = init_playtime_test_wallet ( events , c , m_accounts [ BOB_ACC_IDX ] ) ;
bob_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 , false , " Incorrect numbers of blocks fetched " ) ;
r = check_balance_via_wallet ( * bob_wlt . get ( ) , " bob_wlt " , 0 ) ;
CHECK_AND_ASSERT_MES ( r , false , " Invalid wallet balance " ) ;
tools : : wallet2 : : multisig_transfer_container bob_mstc ;
bob_wlt - > get_multisig_transfers ( bob_mstc ) ;
CHECK_AND_ASSERT_MES ( bob_mstc . size ( ) = = 2 & & bob_mstc . count ( ms1_hash ) = = 1 & & bob_mstc . count ( ms4_hash ) = = 1 , false , " Bob has incorrect multisig transfers " ) ;
// Check Carol's wallet
std : : shared_ptr < tools : : wallet2 > carol_wlt = init_playtime_test_wallet ( events , c , m_accounts [ CAROL_ACC_IDX ] ) ;
carol_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 , false , " Incorrect numbers of blocks fetched " ) ;
r = check_balance_via_wallet ( * carol_wlt . get ( ) , " carol_wlt " , 0 ) ;
CHECK_AND_ASSERT_MES ( r , false , " Invalid wallet balance " ) ;
tools : : wallet2 : : multisig_transfer_container carol_mstc ;
carol_wlt - > get_multisig_transfers ( carol_mstc ) ;
CHECK_AND_ASSERT_MES ( carol_mstc . size ( ) = = 2 & & carol_mstc . count ( ms2_hash ) = = 1 & & carol_mstc . count ( ms4_hash ) = = 1 , false , " Carol has incorrect multisig transfers " ) ;
// Check Dan's wallet
std : : shared_ptr < tools : : wallet2 > dan_wlt = init_playtime_test_wallet ( events , c , m_accounts [ DAN_ACC_IDX ] ) ;
dan_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 , false , " Incorrect numbers of blocks fetched " ) ;
2019-04-25 23:37:43 +02:00
r = check_balance_via_wallet ( * dan_wlt . get ( ) , " dan_wlt " , amount6 + TESTS_DEFAULT_FEE ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " Invalid wallet balance " ) ;
tools : : wallet2 : : multisig_transfer_container dan_mstc ;
dan_wlt - > get_multisig_transfers ( dan_mstc ) ;
CHECK_AND_ASSERT_MES ( dan_mstc . size ( ) = = 2 & & dan_mstc . count ( ms2_hash ) = = 1 & & dan_mstc . count ( ms3_hash ) = = 1 , false , " Dan has incorrect multisig transfers " ) ;
// Spending multisig 1: Alice & Bob
// 1. Alice spends multisig out - send tx to the pool, the Miner add it to a block (so tx become confirmed)
// 2. Bob refreshes his wallet ans tries to do the same
// 3. Make sure Bob can't create multisig tx
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Incorrect txs count in the pool " ) ;
GENERATE_ACCOUNT ( receiver_acc ) ;
currency : : transaction tx = AUTO_VAL_INIT ( tx ) ;
transfer_multisig ( * alice_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ BOB_ACC_IDX ] . get_keys ( ) } ) , ms1_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount1 , receiver_acc . get_public_address ( ) ) } ) ,
2022-05-10 23:49:20 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect txs count 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 " ) ;
// Once Bob refreshes his wallet he should see that Alice has already spent they shared multisig
bob_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = 1 , false , " Incorrect numbers of blocks fetched " ) ;
bool caught = false ;
try
{
// as 'false' means don't send to network. This should fail during preparation, not during sending/processing
transfer_multisig ( * bob_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ ALICE_ACC_IDX ] . get_keys ( ) } ) , ms1_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount1 , receiver_acc . get_public_address ( ) ) } ) ,
2019-04-30 18:45:12 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , 0 , 0 , true , false ) ;
2018-12-27 18:50:45 +03:00
}
catch ( tools : : error : : wallet_internal_error & )
{
caught = true ;
}
CHECK_AND_ASSERT_MES ( caught , false , " Bob was able to make multisig tx for alreadly spent output " ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Incorrect txs count in the pool " ) ;
// Spending multisig 2: Carol & Dan
// 1. Carol spends multisig out - send tx to the pool (it's unconfirmed)
// 2. Dan refreshes wallet and tries to do the same
// 3. Make sure Dan can't do this
// 4. Clear the pool
// 5. Make sure Dan now is able to send tx after it refreshes his wallet
tx = AUTO_VAL_INIT ( tx ) ;
transfer_multisig ( * carol_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ DAN_ACC_IDX ] . get_keys ( ) } ) , ms2_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount2 , receiver_acc . get_public_address ( ) ) } ) ,
2022-05-10 23:49:20 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect txs count in the pool " ) ;
bool stub ;
dan_wlt - > scan_tx_pool ( stub ) ;
caught = false ;
try
{
// as 'false' means don't send to network. This should fail during preparation, not during sending/processing
transfer_multisig ( * dan_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ CAROL_ACC_IDX ] . get_keys ( ) } ) , ms2_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount2 , receiver_acc . get_public_address ( ) ) } ) ,
2019-04-30 18:45:12 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , 0 , 0 , true , false ) ;
2018-12-27 18:50:45 +03:00
}
catch ( tools : : error : : wallet_internal_error & )
{
caught = true ;
}
CHECK_AND_ASSERT_MES ( caught , false , " Dan was able to make multisig tx for alreadly spent output " ) ;
// Clear tx pool
c . get_tx_pool ( ) . purge_transactions ( ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " There are txs in the pool. " ) ;
dan_wlt - > scan_tx_pool ( stub ) ;
// Re-try spending Carol-Dan multisig out on behalf of Dan. It should be OK now
transfer_multisig ( * dan_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ CAROL_ACC_IDX ] . get_keys ( ) } ) , ms2_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount2 , receiver_acc . get_public_address ( ) ) } ) ,
2022-05-10 23:49:20 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect txs count 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. " ) ;
// Spending multisig 3: Alice & Dan
// 1. Alice spends multisig out - send tx to the pool (it's unconfirmed)
// 2. Dan refreshes wallet and tries to do the same
// 3. Make sure Dan can't do this
// 4. Miner mines a PoW block confirming Alice's tx.
// 5. Make sure Dan's wallet correctly handle it.
// 6. Make sure Dan is not able to spend multisig out.
tx = AUTO_VAL_INIT ( tx ) ;
transfer_multisig ( * alice_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ DAN_ACC_IDX ] . get_keys ( ) } ) , ms3_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount3 , receiver_acc . get_public_address ( ) ) } ) ,
2022-05-10 23:49:20 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect txs count in the pool " ) ;
dan_wlt - > scan_tx_pool ( stub ) ;
caught = false ;
try
{
// last 'false' means don't send to network. This should fail during preparation, not during sending/processing
transfer_multisig ( * dan_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ ALICE_ACC_IDX ] . get_keys ( ) } ) , ms3_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount3 , receiver_acc . get_public_address ( ) ) } ) ,
2019-04-30 18:45:12 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , 0 , 0 , true , false ) ;
2018-12-27 18:50:45 +03:00
}
catch ( tools : : error : : wallet_internal_error & )
{
caught = true ;
}
CHECK_AND_ASSERT_MES ( caught , false , " Dan was able to make multisig tx for alreadly spent output " ) ;
// Miner mines the next PoW block, confirming Alice's transaction.
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. " ) ;
// Refresh Dan's wallet. Unconfirmed multisig out should be correctly processed in the wallet during refresh.
dan_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = 3 , false , " Incorrect numbers of blocks fetched " ) ;
// This should correctly removed the transfer from wallet's unconfirmed list.
dan_wlt - > scan_tx_pool ( stub ) ;
// Both Dan's ms transfers should be marked as 'spent' now.
dan_mstc . clear ( ) ;
dan_wlt - > get_multisig_transfers ( dan_mstc ) ;
CHECK_AND_ASSERT_MES ( dan_mstc . size ( ) = = 2 & & dan_mstc . count ( ms2_hash ) = = 1 & & dan_mstc . count ( ms3_hash ) = = 1 & & dan_mstc [ ms2_hash ] . is_spent ( ) & & dan_mstc [ ms3_hash ] . is_spent ( ) , false , " Dan has incorrect multisig transfers " ) ;
// Re-try spending Carol-Dan multisig out on behalf of Dan. It should fail
caught = false ;
try
{
transfer_multisig ( * dan_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ ALICE_ACC_IDX ] . get_keys ( ) } ) , ms3_hash , std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount3 , receiver_acc . get_public_address ( ) ) } ) ,
2019-04-30 18:45:12 +02:00
0 , TESTS_DEFAULT_FEE , std : : vector < currency : : extra_v > ( ) , std : : vector < currency : : attachment_v > ( ) , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , 0 , 0 , true , false ) ;
2018-12-27 18:50:45 +03:00
}
catch ( tools : : error : : wallet_internal_error & )
{
caught = true ;
}
CHECK_AND_ASSERT_MES ( caught , false , " Dan was able to make multisig tx for alreadly spent output " ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " There are txs in the pool. " ) ;
// Spending multisig 4: Carol & Bob
// Reset Carol wallet and check it's correctness
carol_mstc . clear ( ) ;
carol_wlt - > reset_history ( ) ;
carol_wlt - > get_multisig_transfers ( carol_mstc ) ;
CHECK_AND_ASSERT_MES ( carol_mstc . empty ( ) , false , " Carol has multisig transfers " ) ;
carol_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4 , false , " Incorrect numbers of blocks fetched " ) ;
r = check_balance_via_wallet ( * carol_wlt . get ( ) , " carol_wlt " , 0 ) ;
CHECK_AND_ASSERT_MES ( r , false , " Invalid wallet balance " ) ;
carol_wlt - > get_multisig_transfers ( carol_mstc ) ;
CHECK_AND_ASSERT_MES ( carol_mstc . size ( ) = = 2 & & carol_mstc . count ( ms2_hash ) = = 1 & & carol_mstc . count ( ms4_hash ) = = 1 & & carol_mstc [ ms2_hash ] . is_spent ( ) & & ! carol_mstc [ ms4_hash ] . is_spent ( ) , false , " Carol has incorrect multisig transfers " ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_wallet_same_dst_addr : : multisig_wallet_same_dst_addr ( )
{
REGISTER_CALLBACK_METHOD ( multisig_wallet_same_dst_addr , c1 ) ;
}
bool multisig_wallet_same_dst_addr : : 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 ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_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 ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool multisig_wallet_same_dst_addr : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
// Test outline:
// 1. Miner mines few blocks.
// 2. Miner transfers 'amount' to Alice with multisig out (mentioning Alice's account _twice_).
// 3. Alice refreshes her wallet and transfers to Bob the money received by multisig tx.
// 4. Bob checks his wallet and balance.
size_t blocks_fetched = 0 ;
bool received_money = false , r = false ;
std : : atomic < bool > atomic_false ( false ) ;
2019-04-25 23:37:43 +02:00
static const uint64_t amount = TESTS_DEFAULT_FEE * 13 ;
2018-12-27 18:50:45 +03:00
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , m_accounts [ MINER_ACC_IDX ] ) ;
miner_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW , false , " Incorrect number of blocks fetched. " ) ;
tx_destination_entry de = AUTO_VAL_INIT ( de ) ;
de . addr . push_back ( m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) ) ;
de . addr . push_back ( m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) ) ; // multisig to the same address
2019-04-25 23:37:43 +02:00
de . amount = amount + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de . minimum_sigs = de . addr . size ( ) ;
// transfer multisig to Alice
transaction ms_tx = AUTO_VAL_INIT ( ms_tx ) ;
2019-04-25 23:37:43 +02:00
miner_wlt - > transfer ( std : : vector < tx_destination_entry > ( { de } ) , 0 , 0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment , ms_tx ) ;
2018-12-27 18:50:45 +03:00
// mine the next PoW and make sure everythig is allright
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect number of tx 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 transactions in the pool " ) ;
crypto : : hash ms_hash = currency : : get_multisig_out_id ( ms_tx , get_tx_out_index_by_amount ( ms_tx , de . amount ) ) ;
// refresh Alice wallet
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , m_accounts [ ALICE_ACC_IDX ] ) ;
alice_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 , false , " Incorrect number of blocks fetched. " ) ;
// transfers money, Alice has just received with multitsig tx, to Bob
transaction tx = AUTO_VAL_INIT ( tx ) ;
transfer_multisig ( * alice_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ ALICE_ACC_IDX ] . get_keys ( ) } ) , ms_hash ,
std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount , m_accounts [ BOB_ACC_IDX ] . get_public_address ( ) ) } ) ,
2022-05-10 23:49:20 +02:00
0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect number of tx 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 transactions in the pool " ) ;
// make sure Bob gets them
std : : shared_ptr < tools : : wallet2 > bob_wlt = init_playtime_test_wallet ( events , c , m_accounts [ BOB_ACC_IDX ] ) ;
bob_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 , false , " Incorrect number of blocks fetched. " ) ;
r = check_balance_via_wallet ( * bob_wlt . get ( ) , " bob_wlt " , amount ) ;
CHECK_AND_ASSERT_MES ( r , false , " check_balance_via_wallet failed " ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_wallet_ms_to_ms : : multisig_wallet_ms_to_ms ( )
{
REGISTER_CALLBACK_METHOD ( multisig_wallet_ms_to_ms , c1 ) ;
}
bool multisig_wallet_ms_to_ms : : 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 ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_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 ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool multisig_wallet_ms_to_ms : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
// Test outline:
// 1. Miner mines few blocks and sends multisig(Alice, Bob) with 'amount' of coins.
// 2. Alice receives this multisig tx and spends it by sending the next multisig(Miner, Bob). This makes a tx, having one ms input and one ms output.
// 3. Miner receives that multisig tx and spends it by sending all the money to Bob.
// 4. Make sure Bob gets the money.
size_t blocks_fetched = 0 ;
bool received_money = false , r = false ;
std : : atomic < bool > atomic_false ( false ) ;
2019-04-25 23:37:43 +02:00
static const uint64_t amount = TESTS_DEFAULT_FEE * 13 ;
2018-12-27 18:50:45 +03:00
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , m_accounts [ MINER_ACC_IDX ] ) ;
miner_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW , false , " Incorrect number of blocks fetched. " ) ;
tx_destination_entry de = AUTO_VAL_INIT ( de ) ;
de . addr . push_back ( m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) ) ;
de . addr . push_back ( m_accounts [ BOB_ACC_IDX ] . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
de . amount = amount + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
de . minimum_sigs = de . addr . size ( ) ;
// transfer multisig to Alice + Bob
transaction ms_tx = AUTO_VAL_INIT ( ms_tx ) ;
2019-04-25 23:37:43 +02:00
miner_wlt - > transfer ( std : : vector < tx_destination_entry > ( { de } ) , 0 , 0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment , ms_tx ) ;
2018-12-27 18:50:45 +03:00
// mine the next PoW and make sure everythig is allright
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect number of tx 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 transactions in the pool " ) ;
crypto : : hash ms_hash = currency : : get_multisig_out_id ( ms_tx , get_tx_out_index_by_amount ( ms_tx , de . amount ) ) ;
// refresh Alice wallet
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , m_accounts [ ALICE_ACC_IDX ] ) ;
alice_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 , false , " Incorrect number of blocks fetched. " ) ;
// multisig(Alice, Bob) to multisig(Miner, Bob) transfer
de = AUTO_VAL_INIT ( de ) ;
de . addr . push_back ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) ) ;
de . addr . push_back ( m_accounts [ BOB_ACC_IDX ] . get_public_address ( ) ) ;
de . amount = amount ;
de . minimum_sigs = de . addr . size ( ) ;
transaction ms_tx2 = AUTO_VAL_INIT ( ms_tx2 ) ;
transfer_multisig ( * alice_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ BOB_ACC_IDX ] . get_keys ( ) } ) , ms_hash ,
std : : vector < tx_destination_entry > ( { de } ) ,
2022-05-10 23:49:20 +02:00
0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , ms_tx2 , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
// check the pool and mine a block
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect number of tx 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 transactions in the pool " ) ;
crypto : : hash ms_hash2 = currency : : get_multisig_out_id ( ms_tx2 , get_tx_out_index_by_amount ( ms_tx2 , de . amount ) ) ;
miner_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = 2 , false , " Incorrect number of blocks fetched. " ) ;
// spend the last multisig(Miner, Bob) and transfer to Bob
transaction tx = AUTO_VAL_INIT ( tx ) ;
transfer_multisig ( * miner_wlt . get ( ) , std : : list < account_keys > ( { m_accounts [ BOB_ACC_IDX ] . get_keys ( ) } ) , ms_hash2 ,
2019-04-25 23:37:43 +02:00
std : : vector < tx_destination_entry > ( { tx_destination_entry ( amount - TESTS_DEFAULT_FEE , m_accounts [ BOB_ACC_IDX ] . get_public_address ( ) ) } ) ,
2022-05-10 23:49:20 +02:00
0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect number of tx 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 transactions in the pool " ) ;
// make sure Bob gets coins
std : : shared_ptr < tools : : wallet2 > bob_wlt = init_playtime_test_wallet ( events , c , m_accounts [ BOB_ACC_IDX ] ) ;
bob_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3 , false , " Incorrect number of blocks fetched. " ) ;
2019-04-25 23:37:43 +02:00
r = check_balance_via_wallet ( * bob_wlt . get ( ) , " bob_wlt " , amount - TESTS_DEFAULT_FEE ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " check_balance_via_wallet failed " ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_minimum_sigs : : multisig_minimum_sigs ( )
{
}
bool multisig_minimum_sigs : : generate ( std : : vector < test_event_entry > & events ) const
{
// Checks minimum_sigs feature. Doesn't use wallet2. Gentime only.
// Test outline:
// Case 1. Create multisig for three participants (Miner, Alice, Bob) and specify minimum_sigs == 4 (should fail on construction)
// Case 2. Create multisig for three participants (Miner, Alice, Bob) and specify minimum_sigs == 2
// 2.1. Then try to spend it using only 1 key (should fail)
// 2.2. Spend using 2 keys (Alice, Bob) by sending money to Bob
// Case 3. Try to spend already spent multisig output
// Case 4. Use on spending multisig output more keys than required
// Case 5. Spending multisig output using more keys than required and, moreover, add some fake keys
// Case 6. Spending multisig using too many redundant keys (which is computationally expensive)
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
bool r = false ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 17 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > ms_addr_list ( { miner_acc . get_public_address ( ) , alice_acc . get_public_address ( ) , bob_acc . get_public_address ( ) } ) ;
// Case 1. Create multisig for three participants (Miner, Alice, Bob) and specify minimum_sigs == 4 (should fail on construction)
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 4 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx = AUTO_VAL_INIT ( tx ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r = = false , false , " construct_tx was expected to fail, but successed " ) ;
// Case 2. Create multisig for three participants (Miner, Alice, Bob) and specify minimum_sigs == 2
// 2.1. Then try to spend it using only 1 key (should fail)
// 2.2. Spend using 2 keys (Alice, Bob) by sending money to Bob
sources . clear ( ) ;
destinations . clear ( ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 2 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_1 ) ;
// store multisig output index and id for further reference
size_t ms_out_idx = get_tx_out_index_by_amount ( tx_1 , amount ) ;
crypto : : hash ms_id = get_multisig_out_id ( tx_1 , ms_out_idx ) ;
// Put tx_1 into a block
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
// Subcase 2.1 Try to spend multisig out using only one key
tx_source_entry se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = ms_id ;
se . real_output_in_tx_index = ms_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_1 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 2 ;
2019-04-25 23:37:43 +02:00
tx_destination_entry de ( se . amount - TESTS_DEFAULT_FEE , bob_acc . get_public_address ( ) ) ;
2018-12-27 18:50:45 +03:00
// Transaction should be successfully created, but rejected by the core
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { de } ) , empty_attachment , tx_2 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
bool tx_fully_signed = false ;
r = sign_multisig_input_in_tx ( tx_2 , 0 , bob_acc . get_keys ( ) , tx_1 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_2 ) ;
// Subcase 2.2 Add second participant and send. Should be okay.
r = sign_multisig_input_in_tx ( tx_2 , 0 , alice_acc . get_keys ( ) , tx_1 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ; // should be fully signed now
events . push_back ( tx_2 ) ;
// Put the tx into a block
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx_2 ) ;
// Make sure Bob's balance is correct
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , de . amount ) ) ;
// Case 3. Try to spend already spent multisig output
se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = ms_id ;
se . real_output_in_tx_index = ms_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_1 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 2 ;
transaction tx_3 = AUTO_VAL_INIT ( tx_3 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { tx_destination_entry ( se . amount - TESTS_DEFAULT_FEE , bob_acc . get_public_address ( ) ) } ) , empty_attachment , tx_3 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
r = sign_multisig_input_in_tx ( tx_3 , 0 , bob_acc . get_keys ( ) , tx_1 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
r = sign_multisig_input_in_tx ( tx_3 , 0 , alice_acc . get_keys ( ) , tx_1 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ; // should be fully signed now
DO_CALLBACK ( events , " mark_invalid_tx " ) ; // already spent
events . push_back ( tx_3 ) ;
// Case 4. Use on spending multisig output more keys than required
// create source tx: tx_4
sources . clear ( ) ;
destinations . clear ( ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_2 , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_4 = AUTO_VAL_INIT ( tx_4 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_4 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_4 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_4 ) ;
size_t ms_4_out_idx = get_tx_out_index_by_amount ( tx_4 , amount ) ;
crypto : : hash ms_4_id = get_multisig_out_id ( tx_4 , ms_4_out_idx ) ;
se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = ms_4_id ;
se . real_output_in_tx_index = ms_4_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_4 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_4 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 3 ;
transaction tx_5 = AUTO_VAL_INIT ( tx_5 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { tx_destination_entry ( se . amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) } ) , empty_attachment , tx_5 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
// use sign_multisig_input_in_tx_custom to create tx with more signatures (3) than minimum_sigs (1)
r = sign_multisig_input_in_tx_custom ( tx_5 , ms_4_out_idx , bob_acc . get_keys ( ) , tx_4 , & tx_fully_signed , false ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
r = sign_multisig_input_in_tx_custom ( tx_5 , ms_4_out_idx , alice_acc . get_keys ( ) , tx_4 , & tx_fully_signed , false ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
r = sign_multisig_input_in_tx_custom ( tx_5 , ms_4_out_idx , miner_acc . get_keys ( ) , tx_4 , & tx_fully_signed , true ) ;
CHECK_AND_ASSERT_MES ( r & & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ; // should be fully signed now
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_5 ) ;
MAKE_NEXT_BLOCK ( events , blk_4 , blk_3 , miner_acc ) ;
//DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, amount - TX_POOL_MINIMUM_FEE));
// Case 5. Spending multisig output using more keys than required and, moreover, add some fake keys
sources . clear ( ) ;
destinations . clear ( ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_4 , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_6 = AUTO_VAL_INIT ( tx_6 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_6 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_6 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_5 , blk_4 , miner_acc , tx_6 ) ;
// store multisig output index and id for further reference
size_t ms_6_out_idx = get_tx_out_index_by_amount ( tx_6 , amount ) ;
crypto : : hash ms_6_id = get_multisig_out_id ( tx_6 , ms_6_out_idx ) ;
se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = ms_6_id ;
se . real_output_in_tx_index = ms_6_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_6 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_6 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 4 ;
transaction tx_7 = AUTO_VAL_INIT ( tx_7 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { tx_destination_entry ( se . amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) } ) , empty_attachment , tx_7 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
// use sign_multisig_input_in_tx_custom to create tx with more signatures (4) than minimum_sigs (1)
r = sign_multisig_input_in_tx_custom ( tx_7 , ms_6_out_idx , bob_acc . get_keys ( ) , tx_6 , & tx_fully_signed , false ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
r = sign_multisig_input_in_tx_custom ( tx_7 , ms_6_out_idx , alice_acc . get_keys ( ) , tx_6 , & tx_fully_signed , false ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
r = sign_multisig_input_in_tx_custom ( tx_7 , ms_6_out_idx , miner_acc . get_keys ( ) , tx_6 , & tx_fully_signed , false ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
2022-06-19 19:47:43 +02:00
boost : : get < currency : : NLSAG_sig > ( tx_7 . signatures [ 0 ] ) . s . push_back ( invalid_signature ) ; // instead of 4th sig just add invalid sig
2018-12-27 18:50:45 +03:00
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_7 ) ;
// Case 6. Spending multisig using too many redundant keys
sources . clear ( ) ;
destinations . clear ( ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_5 , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_8 = AUTO_VAL_INIT ( tx_8 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_8 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_8 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_6 , blk_5 , miner_acc , tx_8 ) ;
// store multisig output index and id for further reference
size_t ms_8_out_idx = get_tx_out_index_by_amount ( tx_8 , amount ) ;
crypto : : hash ms_8_id = get_multisig_out_id ( tx_8 , ms_8_out_idx ) ;
se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = ms_8_id ;
se . real_output_in_tx_index = ms_8_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_8 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_8 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
static const size_t redundant_keys_count = 7000 ;
se . ms_sigs_count = redundant_keys_count ;
transaction tx_9 = AUTO_VAL_INIT ( tx_9 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { tx_destination_entry ( se . amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) } ) , empty_attachment , tx_9 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
2022-06-19 19:47:43 +02:00
boost : : get < currency : : NLSAG_sig > ( tx_9 . signatures [ 0 ] ) . s . resize ( redundant_keys_count , invalid_signature ) ;
2018-12-27 18:50:45 +03:00
r = sign_multisig_input_in_tx_custom ( tx_9 , ms_8_out_idx , bob_acc . get_keys ( ) , tx_8 , & tx_fully_signed , false ) ;
CHECK_AND_ASSERT_MES ( r & & ! tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed : " < < tx_fully_signed ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_9 ) ;
//MAKE_NEXT_BLOCK_TX1(events, blk_7, blk_6, miner_acc, tx_9);
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 ) ) ;
return true ;
}
//------------------------------------------------------------------------------
bool multisig_and_fake_outputs : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: multisig outs are incompartible with faked mix-ins.
// Here we try to mix multisig and faked inputs/outputs in one transaction to see if smth goes wrong.
// Setup accounts
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
// Mine starter blocks
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3 ) ;
// Calculate amount that many outs already have to simplify debugging (such amount won't be splitted into digits)
bool r = false ;
uint64_t amount = 0 , stub ;
r = calculate_amounts_many_outs_have_and_no_outs_have ( get_outs_money_amount ( blk_0r . miner_tx ) , amount , stub ) ;
CHECK_AND_ASSERT_MES ( r , false , " calculate_amounts_many_outs_have_and_no_outs_have failed " ) ;
// Use two account as multisig target
std : : list < account_public_address > ms_addr_list ( { miner_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ;
// tx_1: 1 real + 2 fake inputs => 1 multisig output (Miner + Alice, minimum_sigs = 1)
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 2 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
// Make sure tx_1 is successfully created
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_1 ) ;
// Add it to blockchain
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
size_t tx_1_ms_out_idx = get_tx_out_index_by_amount ( tx_1 , amount ) ;
crypto : : hash tx_1_ms_out_id = get_multisig_out_id ( tx_1 , tx_1_ms_out_idx ) ;
// Try to create tx_source_entry with a mix of multisig and to_key outputs
size_t nmix = 2 ; // mix-in 1 fake output
sources . clear ( ) ;
// First, prepare as normal source entry with some fake inputs
r = fill_tx_sources ( sources , events , blk_1 , miner_acc . get_keys ( ) , amount , nmix , true , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources failed " ) ;
CHECK_AND_ASSERT_MES ( sources . size ( ) = = 1 & & sources [ 0 ] . amount = = amount & & sources [ 0 ] . outputs . size ( ) = = nmix + 1 , false , " fill_tx_sources returned unexpected results " ) ;
// Second, correctly set up multisig part
tx_source_entry & se = sources . back ( ) ;
se . multisig_id = tx_1_ms_out_id ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ tx_1_ms_out_idx ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
se . real_output_in_tx_index = tx_1_ms_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_1 ) ;
destinations . clear ( ) ;
2019-04-25 23:37:43 +02:00
destinations . push_back ( tx_destination_entry ( amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) ) ;
2018-12-27 18:50:45 +03:00
// Make sure tx is successfully created
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_2 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
bool tx_fully_signed = false ;
r = sign_multisig_input_in_tx ( tx_2 , 0 , alice_acc . get_keys ( ) , tx_1 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & & tx_fully_signed , false , " sign_multisig_input_in_tx failed, sign_multisig_input_in_tx: " < < r ) ;
events . push_back ( tx_2 ) ;
// ...and put into a block
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx_2 ) ;
// Finally, check the balance of Alice's wallet
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , amount - TESTS_DEFAULT_FEE ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------------------
bool multisig_and_unlock_time : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: check how multisig-containing tx with unlock_time and expiration_time are processed
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
// Mine starter blocks
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3 ) ;
test_core_time : : adjust ( blk_0r . timestamp ) ; // adjust gentime time to blockchain timestamps
bool r = false ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 9 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > ms_addr_list ( { miner_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ;
// noramal input -> multisig output with unlock time
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2022-10-27 23:58:52 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 1 /*nmix*/ , sources , destinations , true , true , 1 /* minimum sigs */ ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
uint64_t unlock_time = blk_0r . timestamp + DIFFICULTY_TOTAL_TARGET * 3 + CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS ;
uint64_t unlock_time_2 = blk_0r . timestamp + DIFFICULTY_TOTAL_TARGET * 6 + CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2022-05-13 23:35:56 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , get_tx_version_from_events ( events ) , unlock_time , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
2019-07-26 18:53:42 +02:00
CHECK_AND_ASSERT_MES ( get_tx_max_unlock_time ( tx_1 ) = = unlock_time , false , " Unlock time was not correctly set " ) ;
2018-12-27 18:50:45 +03:00
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
CHECK_AND_ASSERT_MES ( blk_1 . timestamp < unlock_time , false , " Block timestamp went far beyond expected limits, consider adjusting 'unlock_time' " ) ;
test_core_time : : adjust ( blk_1 . timestamp ) ; // adjust gentime time to blockchain timestamps
events . push_back ( event_core_time ( blk_1 . timestamp ) ) ; // adjust playtime time to blockchain timestamps
// Then try to spend multisig out of tx_1 earlier than it's unlock_time
size_t tx_1_ms_out_idx = get_tx_out_index_by_amount ( tx_1 , amount ) ;
crypto : : hash tx_1_ms_out_id = get_multisig_out_id ( tx_1 , tx_1_ms_out_idx ) ;
tx_source_entry se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = tx_1_ms_out_id ;
se . real_output_in_tx_index = tx_1_ms_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_1 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ tx_1_ms_out_idx ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
sources . assign ( { se } ) ;
2019-04-25 23:37:43 +02:00
destinations . assign ( { tx_destination_entry ( amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) } ) ;
2018-12-27 18:50:45 +03:00
ADJUST_TEST_CORE_TIME ( unlock_time - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS - 1 ) ;
// tx_2 should be created ok, but rejected by the core, as one of input refers to a locked tx
// Note: tx_2 has unlock_time_2 specified
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
2022-05-14 19:47:47 +02:00
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_2 , get_tx_version_from_events ( events ) , unlock_time_2 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
bool tx_fully_signed = false ;
r = sign_multisig_input_in_tx ( tx_2 , tx_1_ms_out_idx , miner_acc . get_keys ( ) , tx_1 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed: " < < tx_fully_signed ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_2 ) ;
// rewind the time a little, now tx_2 should be accepted
ADJUST_TEST_CORE_TIME ( unlock_time - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS + 1 ) ;
events . push_back ( tx_2 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx_2 ) ;
// Next, check tx_2 unlock time by spending all coins
ADJUST_TEST_CORE_TIME ( unlock_time_2 - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS - 1 ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
2022-10-27 23:58:52 +02:00
// instead of MAKE_TX use manual construction to set check_for_unlocktime = false, old: MAKE_TX(events, tx_3, alice_acc, bob_acc, amount - TESTS_DEFAULT_FEE * 2, blk_2);
r = fill_tx_sources_and_destinations ( events , blk_2 , alice_acc , bob_acc , amount - TESTS_DEFAULT_FEE * 2 , TESTS_DEFAULT_FEE , 0 /*nmix*/ , sources , destinations , true , false /* check_for_unlocktime */ ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_3 { } ;
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_3 , get_tx_version_from_events ( events ) , 0 ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
ADD_CUSTOM_EVENT ( events , tx_3 ) ;
2018-12-27 18:50:45 +03:00
ADJUST_TEST_CORE_TIME ( unlock_time_2 - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS + 1 ) ;
2022-10-27 23:58:52 +02:00
ADD_CUSTOM_EVENT ( events , tx_3 ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_3 ) ;
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - TESTS_DEFAULT_FEE * 2 ) ) ;
2018-12-27 18:50:45 +03:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 ) ) ;
// Expiration time
// 1. txin_to_key -> multisig output with expiration time
uint64_t expiration_time = generator . get_timestamps_median ( get_block_hash ( blk_3 ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) - DIFFICULTY_TOTAL_TARGET * 3 ;
sources . clear ( ) ;
destinations . clear ( ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_3 , miner_acc . get_keys ( ) , ms_addr_list , amount , TESTS_DEFAULT_FEE , 1 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_4 = AUTO_VAL_INIT ( tx_4 ) ;
crypto : : secret_key stub_key = AUTO_VAL_INIT ( stub_key ) ;
etc_tx_details_expiration_time extra_expiration_time = AUTO_VAL_INIT ( extra_expiration_time ) ;
extra_expiration_time . v = expiration_time ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , std : : vector < extra_v > ( { extra_expiration_time } ) , empty_attachment , tx_4 , get_tx_version_from_events ( events ) , stub_key , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
CHECK_AND_ASSERT_MES ( get_tx_expiration_time ( tx_4 ) = = expiration_time , false , " Expiration time was not correctly set " ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_4 ) ;
// add similar tx (same sources and destinations) with no expiration_time - should be accepted by the core
transaction tx_5 = AUTO_VAL_INIT ( tx_5 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_extra , empty_attachment , tx_5 , get_tx_version_from_events ( events ) , stub_key , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_5 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_4 , blk_3 , miner_acc , tx_5 ) ;
ADJUST_TEST_CORE_TIME ( blk_4 . timestamp ) ;
// 2. Multisig output -> txout_to_key with expiration time
size_t tx_5_ms_out_idx = get_tx_out_index_by_amount ( tx_5 , amount ) ;
crypto : : hash tx_5_ms_out_id = get_multisig_out_id ( tx_5 , tx_5_ms_out_idx ) ;
se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = tx_5_ms_out_id ;
se . real_output_in_tx_index = tx_5_ms_out_idx ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_5 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_5 . vout [ tx_5_ms_out_idx ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
sources . assign ( { se } ) ;
2019-04-25 23:37:43 +02:00
destinations . assign ( { tx_destination_entry ( amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) } ) ;
2018-12-27 18:50:45 +03:00
expiration_time = generator . get_timestamps_median ( get_block_hash ( blk_4 ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) - DIFFICULTY_TOTAL_TARGET * 3 ;
extra_expiration_time = AUTO_VAL_INIT ( extra_expiration_time ) ;
extra_expiration_time . v = expiration_time ;
transaction tx_6 = AUTO_VAL_INIT ( tx_6 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , std : : vector < extra_v > ( { extra_expiration_time } ) , empty_attachment , tx_6 , get_tx_version_from_events ( events ) , stub_key , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
CHECK_AND_ASSERT_MES ( get_tx_expiration_time ( tx_6 ) = = expiration_time , false , " Expiration time was not correctly set " ) ;
r = sign_multisig_input_in_tx ( tx_6 , tx_5_ms_out_idx , miner_acc . get_keys ( ) , tx_5 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed: " < < tx_fully_signed ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_6 ) ;
return true ;
}
//------------------------------------------------------------------------------
bool multisig_and_coinbase : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: creat a block with multisig output in its miner tx, then spend it to make sure everything is ok.
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
bool r = false ;
// Mine starter blocks
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ; // to lower PoS minimum height
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
//
// Part 1. PoS block
//
const block & prev_block = blk_0r ;
crypto : : hash prev_id = get_block_hash ( prev_block ) ;
size_t height = get_block_height ( prev_block ) + 1 ;
currency : : wide_difficulty_type diff = generator . get_difficulty_for_next_block ( prev_id , false ) ;
block blk_1 = AUTO_VAL_INIT ( blk_1 ) ;
{
const transaction & stake = blk_0 . miner_tx ;
crypto : : public_key stake_tx_pub_key = get_tx_pub_key_from_extra ( stake ) ;
size_t stake_output_idx = 0 ;
size_t stake_output_gidx = 0 ;
2022-05-20 21:32:27 +02:00
uint64_t stake_output_amount = boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . amount ;
2018-12-27 18:50:45 +03:00
crypto : : key_image stake_output_key_image ;
keypair kp ;
generate_key_image_helper ( miner_acc . get_keys ( ) , stake_tx_pub_key , stake_output_idx , kp , stake_output_key_image ) ;
2022-05-20 21:32:27 +02:00
crypto : : public_key stake_output_pubkey = boost : : get < txout_to_key > ( boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . target ) . key ;
2018-12-27 18:50:45 +03:00
keypair tx_key = keypair : : generate ( ) ;
pos_block_builder pb ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( ) ) ;
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , prev_block . timestamp ) ;
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( prev_block ) , miner_acc . get_public_address ( ) ,
2022-11-08 00:06:52 +01:00
blobdata ( ) , CURRENCY_MINER_TX_MAX_OUTS , & tx_key ) ;
2018-12-27 18:50:45 +03:00
// The builder creates PoS miner tx with normal outputs.
// Replace all miner_tx outputs with one multisig output and re-sign it.
{
std : : vector < account_public_address > participants ( { alice_acc . get_public_address ( ) , bob_acc . get_public_address ( ) } ) ;
transaction & miner_tx = pb . m_block . miner_tx ;
txout_multisig ms_out_target = AUTO_VAL_INIT ( ms_out_target ) ;
ms_out_target . minimum_sigs = 1 ;
size_t multisig_out_idx = 0 ; // because it's the only output in vout
for ( auto & p : participants )
{
crypto : : key_derivation der = AUTO_VAL_INIT ( der ) ;
2020-04-23 15:41:40 +03:00
r = crypto : : generate_key_derivation ( p . view_public_key , tx_key . sec , der ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " generate_key_derivation failed " ) ;
crypto : : public_key key = AUTO_VAL_INIT ( key ) ;
2020-04-23 15:41:40 +03:00
r = crypto : : derive_public_key ( der , multisig_out_idx , p . spend_public_key , key ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " derive_public_key failed " ) ;
ms_out_target . keys . push_back ( key ) ;
}
2022-05-17 17:32:53 +02:00
tx_out_bare ms_out = AUTO_VAL_INIT ( ms_out ) ;
2018-12-27 18:50:45 +03:00
ms_out . amount = get_outs_money_amount ( miner_tx ) ; // get amount from vout before clearing
miner_tx . vout . clear ( ) ;
ms_out . target = ms_out_target ;
miner_tx . vout . push_back ( ms_out ) ;
}
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner_acc ) ;
// add block info to generator only to be able to make the next block in gentime
generator . add_block_info ( test_generator : : block_info ( pb . m_block , generator . get_already_generated_coins ( prev_block ) + get_outs_money_amount ( pb . m_block . miner_tx ) - stake_output_amount ,
pb . m_txs_total_size + get_object_blobsize ( pb . m_block ) , generator . get_cumul_difficulty_for_next_block ( prev_id ) , std : : list < transaction > ( ) , null_hash ) ) ;
blk_1 = pb . m_block ;
}
events . push_back ( blk_1 ) ;
// In order to spend coinbase tx we must wait for CURRENCY_MINED_MONEY_UNLOCK_WINDOW blocks being added to the blockchain
REWIND_BLOCKS_N_WITH_TIME ( events , blk_1r , blk_1 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// spend multisig output to make sure everything is okay
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
tx_source_entry se = AUTO_VAL_INIT ( se ) ;
se . amount = get_outs_money_amount ( blk_1 . miner_tx ) ;
se . real_output_in_tx_index = 0 ;
se . multisig_id = get_multisig_out_id ( blk_1 . miner_tx , se . real_output_in_tx_index ) ;
//se.participants.push_back(alice_acc.get_keys());
se . real_out_tx_key = get_tx_pub_key_from_extra ( blk_1 . miner_tx ) ;
2022-05-25 22:31:23 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( blk_1 . miner_tx . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
sources . assign ( { se } ) ;
2019-04-25 23:37:43 +02:00
destinations . assign ( { tx_destination_entry ( se . amount - TESTS_DEFAULT_FEE , bob_acc . get_public_address ( ) ) } ) ;
2018-12-27 18:50:45 +03:00
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
bool fully_signed_tx = false ;
r = sign_multisig_input_in_tx ( tx_1 , 0 , alice_acc . get_keys ( ) , blk_1 . miner_tx , & fully_signed_tx ) ;
CHECK_AND_ASSERT_MES ( r & & fully_signed_tx , false , " sign_multisig_input_in_tx failed, fully_signed_tx: " < < fully_signed_tx ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1r , miner_acc , tx_1 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 ) ) ;
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , se . amount - TESTS_DEFAULT_FEE ) ) ;
2018-12-27 18:50:45 +03:00
//
// Part 2. The same for PoW
//
uint64_t blk_2_reward = generator . get_base_reward_for_next_block ( get_block_hash ( blk_1 ) , true ) ;
block blk_3 = AUTO_VAL_INIT ( blk_3 ) ;
{
block & b = blk_3 ;
const block & prev_block = blk_2 ;
size_t height = get_block_height ( prev_block ) + 1 ;
// manually create a transaction with multisig out
std : : list < account_public_address > ms_addr_list ( { miner_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , prev_block , miner_acc . get_keys ( ) , ms_addr_list , blk_2_reward , TESTS_DEFAULT_FEE , 0 , sources , destinations , false , false , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction miner_tx = AUTO_VAL_INIT ( miner_tx ) ;
2022-05-13 23:35:56 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , miner_tx , get_tx_version_from_events ( events ) , height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
// replace vin with coinbase input
txin_gen in_gen = AUTO_VAL_INIT ( in_gen ) ;
in_gen . height = height ;
miner_tx . vin . assign ( { in_gen } ) ;
// remove all outputs except the multisig
2022-05-25 22:31:23 +02:00
auto it = std : : find_if ( miner_tx . vout . begin ( ) , miner_tx . vout . end ( ) , [ ] ( const tx_out_v & o ) { return boost : : get < tx_out_bare > ( o ) . target . type ( ) = = typeid ( txout_multisig ) ; } ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( it ! = miner_tx . vout . end ( ) , false , " construct_tx didn't create multisig output as expected " ) ;
2022-05-25 22:31:23 +02:00
tx_out_bare ms_out = boost : : get < tx_out_bare > ( * it ) ;
2018-12-27 18:50:45 +03:00
miner_tx . vout . assign ( { ms_out } ) ;
CHECK_AND_ASSERT_MES ( ms_out . amount = = blk_2_reward , false , " unexpected amount for found ms output " ) ;
r = generator . construct_block_manually ( b , prev_block , miner_acc , test_generator : : bf_miner_tx , 0 , 0 , 0 , null_hash , 1 , miner_tx ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_block_manually failed " ) ;
}
events . push_back ( blk_3 ) ;
// rewind blocks to be able to spend mined money
REWIND_BLOCKS_N ( events , blk_3r , blk_3 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// spend coinbase in blk_3 to make sure everything is okay
se = AUTO_VAL_INIT ( se ) ;
se . amount = blk_2_reward ;
se . multisig_id = get_multisig_out_id ( blk_3 . miner_tx , 0 ) ;
se . real_output_in_tx_index = 0 ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( blk_3 . miner_tx ) ;
2022-05-25 22:31:23 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( blk_3 . miner_tx . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
2019-04-25 23:37:43 +02:00
r = construct_tx ( alice_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { tx_destination_entry ( se . amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) } ) ,
2022-05-11 23:52:33 +02:00
empty_attachment , tx_2 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
r = sign_multisig_input_in_tx ( tx_2 , 0 , alice_acc . get_keys ( ) , blk_3 . miner_tx , & fully_signed_tx ) ;
CHECK_AND_ASSERT_MES ( r & & fully_signed_tx , false , " sign_multisig_input_in_tx failed, fully_signed_tx: " < < fully_signed_tx ) ;
events . push_back ( tx_2 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_4 , blk_3r , miner_acc , tx_2 ) ;
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , se . amount - TESTS_DEFAULT_FEE ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------------------
bool multisig_with_same_id_in_pool : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: create two txs having the same multisig id in their outputs and see how the core handles it.
// Then put two txs referring to the ms out id in their inputs.
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 ) ;
bool r = false ;
uint64_t amount_many_out_have = 0 , stub = 0 ;
r = calculate_amounts_many_outs_have_and_no_outs_have ( generator . get_base_reward_for_next_block ( get_block_hash ( blk_0r ) ) , amount_many_out_have , stub ) ; // this amount corresponds to many outs, so no changeback will be generated
CHECK_AND_ASSERT_MES ( r , false , " calculate_amounts_many_outs_have_and_no_outs_have failed " ) ;
2019-04-25 23:37:43 +02:00
uint64_t amount = amount_many_out_have - TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > to_addrs ( { alice_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ; // multisig to same account, mentioned twice
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
// tx_1: normal input -> multisig output
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r & & sources . size ( ) = = 1 & & destinations . size ( ) = = 1 , false , " fill_tx_sources_and_destinations failed " ) ; // we selected such good amount so sources.size() and destinations.size() should be eq 1
// create tx_1 manually in order to have access to its one-time key
tx_builder txb_1 ;
txb_1 . step1_init ( ) ;
txb_1 . step2_fill_inputs ( miner_acc . get_keys ( ) , sources ) ;
txb_1 . step3_fill_outputs ( destinations , 0 , 1 ) ;
txb_1 . step4_calc_hash ( ) ;
txb_1 . step5_sign ( sources ) ;
const transaction & tx_1 = txb_1 . m_tx ;
// tx_1 should be accepted, no surprize here
events . push_back ( tx_1 ) ;
// prepare sources and destinations for tx_2, using the same amount
std : : vector < tx_source_entry > sources2 ;
std : : vector < tx_destination_entry > destinations2 ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources2 , destinations2 , true , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r & & sources2 . size ( ) = = 1 & & destinations2 . size ( ) = = 1 , false , " fill_tx_sources_and_destinations failed " ) ; // we selected such good amount so sources.size() and destinations.size() should be eq 1
// tx_2 differs in inputs but should have the same outputs
tx_builder txb_2 ;
txb_2 . step1_init ( ) ;
txb_2 . m_tx_key = txb_1 . m_tx_key ; // use the same one-time tx key as tx_1
// clear extra and put corresponding pub key to the extra
txb_2 . m_tx . extra . clear ( ) ;
add_tx_pub_key_to_extra ( txb_2 . m_tx , txb_2 . m_tx_key . pub ) ;
txb_2 . step2_fill_inputs ( miner_acc . get_keys ( ) , sources2 ) ; // use other sources
txb_2 . step3_fill_outputs ( destinations , 0 , 1 ) ; // ...but the same destinations, thus vout should be the same (while one-time tx key is the same)
txb_2 . step4_calc_hash ( ) ;
txb_2 . step5_sign ( sources2 ) ;
const transaction & tx_2 = txb_2 . m_tx ;
// Well done! Now multisig output id in both txs should be the same.
crypto : : hash tx_1_ms_id = get_multisig_out_id ( tx_1 , get_tx_out_index_by_amount ( tx_1 , amount ) ) ;
crypto : : hash tx_2_ms_id = get_multisig_out_id ( tx_2 , get_tx_out_index_by_amount ( tx_2 , amount ) ) ;
CHECK_AND_ASSERT_MES ( tx_1_ms_id = = tx_2_ms_id , false , " Multisig ids didn't match as expected " ) ;
// tx_2 should be rejected from being added to the pool as it contains the same multisig id as tx_1 which is still in the poll
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_2 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
// now tx_2 should still be rejected by the pool as the same multisig id is in then blockchain
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_2 ) ;
// Create and add two txs referring to the same ms out id in their inputs
tx_source_entry se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . multisig_id = tx_1_ms_id ;
se . real_output_in_tx_index = get_tx_out_index_by_amount ( tx_1 , amount ) ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_1 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
2019-04-25 23:37:43 +02:00
tx_destination_entry de ( amount - TESTS_DEFAULT_FEE , bob_acc . get_public_address ( ) ) ;
2018-12-27 18:50:45 +03:00
transaction tx_3 = AUTO_VAL_INIT ( tx_3 ) ;
r = construct_tx ( alice_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { de } ) , empty_attachment , tx_3 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
bool fully_signed_tx = false ;
r = sign_multisig_input_in_tx_custom ( tx_3 , 0 , alice_acc . get_keys ( ) , tx_1 , & fully_signed_tx ) ;
CHECK_AND_ASSERT_MES ( r & & fully_signed_tx , false , " sign_multisig_input_in_tx failed, fully_signed_tx: " < < fully_signed_tx ) ;
events . push_back ( tx_3 ) ;
transaction tx_4 = AUTO_VAL_INIT ( tx_4 ) ;
r = construct_tx ( alice_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { de } ) , empty_attachment , tx_4 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
// tx_4 should be rejected as containing multisig input already mentioned in the pool (by tx_3)
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_4 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_1 , miner_acc , tx_3 ) ;
// tx_4 should still be rejected as spending already spent (by tx_3) multisig output
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_4 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , de . amount ) ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_and_checkpoints : : multisig_and_checkpoints ( )
{
// NOTE: This test is made deterministic to be able to correctly set up checkpoint.
random_state_test_restorer : : reset_random ( ) ; // random generator's state was previously stored, will be restore on dtor (see also m_random_state_test_restorer)
REGISTER_CALLBACK_METHOD ( multisig_and_checkpoints , set_cp ) ;
}
bool multisig_and_checkpoints : : set_cp ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
currency : : checkpoints checkpoints ;
2023-05-20 21:21:45 +02:00
checkpoints . add_checkpoint ( 15 , " 6f9194c144afd73077478e7f04e947c50160b5673558e6f696a4f662a3ca261e " ) ;
2018-12-27 18:50:45 +03:00
c . set_checkpoints ( std : : move ( checkpoints ) ) ;
return true ;
}
bool multisig_and_checkpoints : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test outline:
// 1. Set checkpoint (on height 0 for height 15).
// 2. Make few block to unlock mined money.
// 3. tx_1: normal input -> multisig output
// 4. tx_2: multisig input -> normal output (spends tx_1 ms out)
// 5. tx_3: normal input -> multisig output
// 6. (here blockchain passes checkpoint)
// 7. tx_4 multisig input -> normal output (spends tx_3 ms out before CP)
// 8. tx_5: normal input -> multisig output
// 9. tx_6 multisig input -> normal output (spends tx_5 ms out)
// 10. Check balances.
uint64_t ts = 1450000000 ;
test_core_time : : adjust ( ts ) ;
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
miner_acc . set_createtime ( ts ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
alice_acc . set_createtime ( ts ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
bob_acc . set_createtime ( ts ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
bool r = false ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , ts ) ;
2023-06-05 18:19:39 +02:00
DO_CALLBACK ( events , " configure_core " ) ;
2018-12-27 18:50:45 +03:00
// set checkpoint
DO_CALLBACK ( events , " set_cp " ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 ) ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 99 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > to_addrs ( { alice_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ; // multisig to same account, mentioned twice
// tx_1: normal input -> multisig output
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2023-05-20 21:21:45 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , currency : : get_tx_version ( get_block_height ( blk_0r ) , m_hardforks ) , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx " ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ; // tx_1 goes with the block blk_1
2018-12-27 18:50:45 +03:00
events . push_back ( tx_1 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
// tx_2: multisig input -> normal output (spends tx_1 ms out)
tx_source_entry se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . real_output_in_tx_index = get_tx_out_index_by_amount ( tx_1 , amount ) ;
se . multisig_id = get_multisig_out_id ( tx_1 , se . real_output_in_tx_index ) ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_1 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
2019-04-25 23:37:43 +02:00
tx_destination_entry de ( amount - TESTS_DEFAULT_FEE , bob_acc . get_public_address ( ) ) ;
2018-12-27 18:50:45 +03:00
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
2023-05-20 21:21:45 +02:00
r = construct_tx ( alice_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { de } ) , empty_attachment , tx_2 , currency : : get_tx_version ( get_block_height ( blk_1 ) , m_hardforks ) , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
bool tx_fully_signed = false ;
r = sign_multisig_input_in_tx ( tx_2 , 0 , alice_acc . get_keys ( ) , tx_1 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed: " < < tx_fully_signed ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ; // tx_2 goes with the block blk_2
2018-12-27 18:50:45 +03:00
events . push_back ( tx_2 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx_2 ) ;
ADJUST_TEST_CORE_TIME ( blk_2 . timestamp ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , de . amount ) ) ;
// tx_3: normal input -> multisig output
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_2 , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_3 = AUTO_VAL_INIT ( tx_3 ) ;
2023-05-20 21:21:45 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_3 , currency : : get_tx_version ( get_block_height ( blk_2 ) , m_hardforks ) , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx " ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ; // tx_3 goes with the block blk_3
2018-12-27 18:50:45 +03:00
events . push_back ( tx_3 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_3 ) ; // <-- CP
//
// test multisig after CP
//
// tx_4 multisig input -> normal output (spends tx_3 ms out)
se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . real_output_in_tx_index = get_tx_out_index_by_amount ( tx_3 , amount ) ;
se . multisig_id = get_multisig_out_id ( tx_3 , se . real_output_in_tx_index ) ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_3 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_3 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
2019-04-25 23:37:43 +02:00
de = tx_destination_entry ( amount - TESTS_DEFAULT_FEE , bob_acc . get_public_address ( ) ) ;
2018-12-27 18:50:45 +03:00
transaction tx_4 = AUTO_VAL_INIT ( tx_4 ) ;
2023-05-20 21:21:45 +02:00
r = construct_tx ( alice_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { de } ) , empty_attachment , tx_4 , currency : : get_tx_version ( get_block_height ( blk_3 ) , m_hardforks ) , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
r = sign_multisig_input_in_tx ( tx_4 , 0 , alice_acc . get_keys ( ) , tx_3 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed: " < < tx_fully_signed ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ; // tx_4 goes with the block blk_4
2018-12-27 18:50:45 +03:00
events . push_back ( tx_4 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_4 , blk_3 , miner_acc , tx_4 ) ;
// tx_5: normal input -> multisig output
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_4 , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_5 = AUTO_VAL_INIT ( tx_5 ) ;
2023-05-20 21:21:45 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_5 , currency : : get_tx_version ( get_block_height ( blk_4 ) , m_hardforks ) , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx " ) ;
events . push_back ( tx_5 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_5 , blk_4 , miner_acc , tx_5 ) ;
// tx_6 multisig input -> normal output (spends tx_5 ms out)
se = AUTO_VAL_INIT ( se ) ;
se . amount = amount ;
se . real_output_in_tx_index = get_tx_out_index_by_amount ( tx_5 , amount ) ;
se . multisig_id = get_multisig_out_id ( tx_5 , se . real_output_in_tx_index ) ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_5 ) ;
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_5 . vout [ se . real_output_in_tx_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
se . ms_sigs_count = 1 ;
2019-04-25 23:37:43 +02:00
de = tx_destination_entry ( amount - TESTS_DEFAULT_FEE , bob_acc . get_public_address ( ) ) ;
2018-12-27 18:50:45 +03:00
transaction tx_6 = AUTO_VAL_INIT ( tx_6 ) ;
2023-05-20 21:21:45 +02:00
r = construct_tx ( alice_acc . get_keys ( ) , std : : vector < tx_source_entry > ( { se } ) , std : : vector < tx_destination_entry > ( { de } ) , empty_attachment , tx_6 , currency : : get_tx_version ( get_block_height ( blk_5 ) , m_hardforks ) , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
r = sign_multisig_input_in_tx ( tx_6 , 0 , alice_acc . get_keys ( ) , tx_5 , & tx_fully_signed ) ;
CHECK_AND_ASSERT_MES ( r & tx_fully_signed , false , " sign_multisig_input_in_tx failed, tx_fully_signed: " < < tx_fully_signed ) ;
events . push_back ( tx_6 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_6 , blk_5 , miner_acc , tx_6 ) ;
ADJUST_TEST_CORE_TIME ( blk_6 . timestamp ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , de . amount * 3 ) ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_and_checkpoints_bad_txs : : multisig_and_checkpoints_bad_txs ( )
{
REGISTER_CALLBACK_METHOD ( multisig_and_checkpoints_bad_txs , set_cp ) ;
}
bool multisig_and_checkpoints_bad_txs : : set_cp ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
// set checkpoint far enough - it won't be reached in the test
currency : : checkpoints checkpoints ;
checkpoints . add_checkpoint ( 100 , " 0000000000000000000000000000000000000000000000000000000000000001 " ) ;
c . set_checkpoints ( std : : move ( checkpoints ) ) ;
return true ;
}
bool multisig_and_checkpoints_bad_txs : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: check few hypotheses about handling incorrect multisig inputs and outputs within checkpoint zone.
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " set_cp " ) ; // set checkpoint
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 ) ;
bool r = false ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 99 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > to_addrs ( { alice_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ; // multisig to same account, mentioned twice
// tx_1: normal key to multisig transaction
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2023-05-19 19:21:02 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , TRANSACTION_VERSION_PRE_HF4 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx " ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ; // tx_1 goes with the block blk_1
2018-12-27 18:50:45 +03:00
events . push_back ( tx_1 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 , 0 , 0 , 0 , 0 ) ) ;
// tx_2: invalid ms input sigs_count (should fail even in CP zone)
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
r = make_tx_multisig_to_key ( tx_1 , get_tx_out_index_by_amount ( tx_1 , amount ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) } ) , bob_acc . get_public_address ( ) , tx_2 ) ;
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
2022-06-19 19:47:43 +02:00
tx_2 . signatures . resize ( 10 ) ;
2018-12-27 18:50:45 +03:00
boost : : get < txin_multisig > ( tx_2 . vin [ 0 ] ) . sigs_count = 10 ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
2018-12-27 18:50:45 +03:00
events . push_back ( tx_2 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_acc ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , 0 , 0 , 0 , 0 , 0 ) ) ;
2020-02-12 15:59:51 +03:00
// tx_3: no signatures, zero sigs_count in ms input (should pass under CP zone)
2018-12-27 18:50:45 +03:00
transaction tx_3 = AUTO_VAL_INIT ( tx_3 ) ;
r = make_tx_multisig_to_key ( tx_1 , get_tx_out_index_by_amount ( tx_1 , amount ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) } ) , bob_acc . get_public_address ( ) , tx_3 ) ;
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
2022-06-19 19:47:43 +02:00
tx_3 . signatures . clear ( ) ;
2018-12-27 18:50:45 +03:00
boost : : get < txin_multisig > ( tx_3 . vin [ 0 ] ) . sigs_count = 0 ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
2018-12-27 18:50:45 +03:00
events . push_back ( tx_3 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
// tx_4: zero ms out keys (should FAIL)
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
tx_builder txb = AUTO_VAL_INIT ( txb ) ;
txb . step1_init ( ) ;
txb . step2_fill_inputs ( miner_acc . get_keys ( ) , sources ) ;
txb . step3_fill_outputs ( destinations , 0 , 1 ) ;
2022-05-25 22:31:23 +02:00
boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( txb . m_tx . vout [ 0 ] ) . target ) . keys . clear ( ) ; // zero keys
2018-12-27 18:50:45 +03:00
txb . step4_calc_hash ( ) ;
txb . step5_sign ( sources ) ;
transaction tx_4 = txb . m_tx ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
2018-12-27 18:50:45 +03:00
events . push_back ( tx_4 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
// tx_6: many ms out keys + no sigs (should pass due to CP zone)
txb = AUTO_VAL_INIT ( txb ) ;
txb . step1_init ( ) ;
txb . step2_fill_inputs ( miner_acc . get_keys ( ) , sources ) ;
txb . step3_fill_outputs ( destinations , 0 , 1 ) ;
2022-05-25 22:31:23 +02:00
crypto : : public_key k = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( txb . m_tx . vout [ 0 ] ) . target ) . keys [ 0 ] ;
boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( txb . m_tx . vout [ 0 ] ) . target ) . keys . resize ( 1500 , k ) ;
2018-12-27 18:50:45 +03:00
txb . step4_calc_hash ( ) ;
txb . step5_sign ( sources ) ;
2022-06-19 19:47:43 +02:00
txb . m_tx . signatures . clear ( ) ;
2018-12-27 18:50:45 +03:00
transaction tx_6 = txb . m_tx ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
2018-12-27 18:50:45 +03:00
events . push_back ( tx_6 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_6 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 , 0 , 0 , 0 , 0 ) ) ; // Alice got the money within ms-input in tx_6, but they don't show up in the balance
// tx_7: just spending tx_6 ms out, sending to key (Alice -> Bob)
transaction tx_7 = AUTO_VAL_INIT ( tx_7 ) ;
r = make_tx_multisig_to_key ( tx_6 , get_tx_out_index_by_amount ( tx_6 , amount ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) } ) , bob_acc . get_public_address ( ) , tx_7 ) ;
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
2018-12-27 18:50:45 +03:00
events . push_back ( tx_7 ) ;
2020-02-12 15:59:51 +03:00
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_4 , blk_3 , miner_acc , tx_7 ) ;
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - TESTS_DEFAULT_FEE ) ) ; // Bob finally got the money
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------------------
multisig_and_altchains : : multisig_and_altchains ( )
{
REGISTER_CALLBACK_METHOD ( multisig_and_altchains , check_tx_pool ) ;
}
bool multisig_and_altchains : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: check multisig output handling in case of multiple altchain switching
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
GENERATE_ACCOUNT ( carol_acc ) ;
m_accounts . push_back ( carol_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = CAROL_ACC_IDX ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
bool r = false ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 99 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > to_addrs ( { alice_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ; // multisig to same account, mentioned twice
// tx_1: normal key to multisig transaction
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , 1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx " ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
MAKE_NEXT_BLOCK ( events , blk_1b , blk_0r , miner_acc ) ;
// 0 .. 10 11 12 13 14 15 <- height
// (0 ).. (0r)- (1 )- <- main chain
// | tx_1
// \- (1b)-
// tx_2 and tx_3 both spend the SAME multisig output in tx_1, transferring coins to Bob (tx_2) or Carol (tx_3)
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
r = make_tx_multisig_to_key ( tx_1 , get_tx_out_index_by_amount ( tx_1 , amount ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) } ) , bob_acc . get_public_address ( ) , tx_2 ) ;
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
transaction tx_3 = AUTO_VAL_INIT ( tx_3 ) ;
r = make_tx_multisig_to_key ( tx_1 , get_tx_out_index_by_amount ( tx_1 , amount ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) } ) , carol_acc . get_public_address ( ) , tx_3 ) ;
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
events . push_back ( tx_2 ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2b , blk_1b , miner_acc , tx_2 ) ;
// 0 .. 10 11 12 13 14 15 <- height
// (0 ).. (0r)- (1 )- <- main chain
// | tx_1
// \- (1b)- #2b# <- 2b is rejected as tx_2 refers to non-existing multisig out id (in tx_1)
// | tx_2
// make sure tx_2 is still in the pool
DO_CALLBACK_PARAMS ( events , " check_tx_pool " , params_tx_pool ( 1 ) ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool " , params_tx_pool ( tx_2 ) ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1c , blk_0r , miner_acc , tx_1 ) ;
MAKE_NEXT_BLOCK ( events , blk_2c , blk_1c , miner_acc ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ; // tx_3 should be rejected here by the pool, as it's tx_2 in the pool which is referring to the same ms id in tx_1
events . push_back ( tx_3 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3c , blk_2c , miner_acc , tx_2 ) ;
// 0 .. 10 11 12 13 14 15 <- height
// (0 ).. (0r)- (1 )-
// | tx_1
// \- (1b)- #2b#
// | tx_2
// |- (1c)- (2c)- (3c)- <- becomes main chain
// tx_1 tx_2
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - TESTS_DEFAULT_FEE ) ) ;
2018-12-27 18:50:45 +03:00
DO_CALLBACK ( events , " mark_invalid_tx " ) ; // tx_3 should be rejected by the pool, as spending already spent ms output in tx_2
events . push_back ( tx_3 ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool " , params_tx_pool ( 0 ) ) ;
// add blocks to the first chain and trigger chain switching
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_3 , blk_2 , miner_acc ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_4 , blk_3 , miner_acc , tx_2 ) ;
// 0 .. 10 11 12 13 14 15 <- height
// (0 ).. (0r)- (1 )- (2 )- (3 )- (4 )- <- becomes main chain
// | tx_1 tx_2
// \- (1b)- #2b#
// | tx_2
// |- (1c)- (2c)- (3c)-
// tx_1 tx_2
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( CAROL_ACC_IDX , 0 ) ) ; // tx_3 was rejected, thus zero
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - TESTS_DEFAULT_FEE ) ) ;
2018-12-27 18:50:45 +03:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool " , params_tx_pool ( 0 ) ) ;
// add blocks to new subchain and trigger chain switching
MAKE_NEXT_BLOCK ( events , blk_3d , blk_2 , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_4d , blk_3d , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_5d , blk_4d , miner_acc ) ;
// make sure tx_2 was moved out to the pool
DO_CALLBACK_PARAMS ( events , " check_tx_pool " , params_tx_pool ( 1 ) ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool " , params_tx_pool ( tx_2 ) ) ;
// 0 .. 10 11 12 13 14 15 <- height
// (0 ).. (0r)- (1 )- (2 )- (3 )- (4 )- <- becomes main chain
// | tx_1 tx_2
// \- (3d)- (4d)- (5d)-
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - TESTS_DEFAULT_FEE , 0 , 0 , amount - TESTS_DEFAULT_FEE ) ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_6d , blk_5d , miner_acc , tx_2 ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool " , params_tx_pool ( 0 ) ) ;
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - TESTS_DEFAULT_FEE , 0 , 0 , 0 , 0 ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
bool multisig_and_altchains : : check_tx_pool ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
params_tx_pool p = AUTO_VAL_INIT ( p ) ;
bool r = epee : : string_tools : : hex_to_pod ( boost : : get < callback_entry > ( events [ ev_index ] ) . callback_params , p ) ;
CHECK_AND_ASSERT_MES ( r , false , " Can't obtain event params. Forgot to pass them? " ) ;
if ( p . counter ! = SIZE_MAX )
{
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = p . counter , false , " Tx pool has " < < c . get_pool_transactions_count ( ) < < " transactions, NOT " < < p . counter < < " as expected " ) ;
}
else
{
std : : list < transaction > txs ;
bool r = c . get_pool_transactions ( txs ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_pool_transactions failed " ) ;
r = false ;
for ( const auto & t : txs )
{
if ( get_transaction_hash ( t ) = = p . tx_hash )
{
r = true ;
break ;
}
}
CHECK_AND_ASSERT_MES ( r , false , " tx " < < p . tx_hash < < " is NOT in the pool as expected " ) ;
}
return true ;
}
//------------------------------------------------------------------------------
bool ref_by_id_basics : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: transfer some coins (Miner->Alicee->Bob) using only ref_by_id inputs to make sure they are handled okay.
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4 ) ;
bool r = false ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 90 ;
2018-12-27 18:50:45 +03:00
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc , alice_acc , amount , TESTS_DEFAULT_FEE , 4 , sources , destinations , true , true , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx " ) ;
2022-06-19 19:47:43 +02:00
r = check_ring_signature_at_gen_time ( events , get_block_hash ( blk_0r ) , boost : : get < txin_to_key > ( tx_1 . vin [ 0 ] ) , get_transaction_hash ( tx_1 ) , boost : : get < currency : : NLSAG_sig > ( tx_1 . signatures [ 0 ] ) . s ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " check_ring_signature_at_gen_time failed " ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , amount , 0 , 0 , 0 , 0 ) ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_1 , alice_acc , bob_acc , amount - TESTS_DEFAULT_FEE , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , true ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_2 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx " ) ;
events . push_back ( tx_2 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx_2 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 , 0 , 0 , 0 , 0 ) ) ;
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - TESTS_DEFAULT_FEE , 0 , 0 , 0 , 0 ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------------------
bool ref_by_id_mixed_inputs_types : : generate ( std : : vector < test_event_entry > & events ) const
{
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = MINER_ACC_IDX ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . push_back ( alice_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = ALICE_ACC_IDX ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_accounts . push_back ( bob_acc ) ; LOCAL_ASSERT ( m_accounts . size ( ) - 1 = = BOB_ACC_IDX ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 ) ;
bool r = false ;
std : : vector < tx_source_entry > sources , sources2 ;
std : : vector < tx_destination_entry > destinations , destinations2 ;
// obtain such amount no output ever has
uint64_t amount = 0 , stub = 0 ;
r = calculate_amounts_many_outs_have_and_no_outs_have ( get_outs_money_amount ( blk_0r . miner_tx ) , stub , amount ) ;
CHECK_AND_ASSERT_MES ( r , false , " calculate_amounts_many_outs_have_and_no_outs_have failed " ) ;
MAKE_TX_LIST_START ( events , tx_list_1 , miner_acc , alice_acc , amount , blk_0r ) ;
MAKE_TX_LIST ( events , tx_list_1 , miner_acc , alice_acc , amount , blk_0r ) ;
MAKE_TX_LIST ( events , tx_list_1 , miner_acc , alice_acc , amount , blk_0r ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_1 , blk_0r , miner_acc , tx_list_1 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , amount * 3 , 0 , 0 , 0 , 0 ) ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_1 , alice_acc , bob_acc , amount * 3 - TESTS_DEFAULT_FEE , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , false ) ; // normal inputs
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_1 , alice_acc , bob_acc , amount * 3 - TESTS_DEFAULT_FEE , TESTS_DEFAULT_FEE , 0 , sources2 , destinations2 , true , true , true ) ; // ref_by_id
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
// mix sources to achive mixed normal and ref_by_id inputs
sources [ 1 ] = sources2 [ 1 ] ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
// make sure we created correct transaction
size_t normal_outs_count = 0 , ref_by_id_outs_count = 0 ;
for ( auto & in : tx_1 . vin )
{
CHECKED_GET_SPECIFIC_VARIANT ( in , const txin_to_key , in2key , false ) ;
CHECK_AND_ASSERT_MES ( in2key . amount = = amount , false , " Invalid amount in source entries " ) ;
for ( auto & ko : in2key . key_offsets )
{
if ( ko . type ( ) . hash_code ( ) = = typeid ( ref_by_id ) . hash_code ( ) )
+ + ref_by_id_outs_count ;
else if ( ko . type ( ) . hash_code ( ) = = typeid ( uint64_t ) . hash_code ( ) )
+ + normal_outs_count ;
}
}
CHECK_AND_ASSERT_MES ( normal_outs_count = = 2 & & ref_by_id_outs_count = = 1 , false , " Incorrect tx created " ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx_1 ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 , 0 , 0 , 0 , 0 ) ) ;
2019-04-25 23:37:43 +02:00
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount * 3 - TESTS_DEFAULT_FEE , 0 , 0 , 0 , 0 ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------------------
multisig_n_participants_seq_signing : : multisig_n_participants_seq_signing ( )
: m_participants_count ( 10 )
, m_minimum_signs_to_spend ( 8 )
{
m_participants . resize ( m_participants_count ) ;
for ( auto & p : m_participants )
p . generate ( ) ;
}
bool multisig_n_participants_seq_signing : : generate ( std : : vector < test_event_entry > & events ) const
{
// test idea: tx with ms input is being signed by N participants _sequentially_ and _separately_ (real-life approximation: they don't know privates keys of each other)
bool r = false ;
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 ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// prepare ms data: amount and list of participants' adresses (belived to be publicly available)
2019-04-25 23:37:43 +02:00
uint64_t ms_amount = TESTS_DEFAULT_FEE * 17 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > ms_addr_list ;
for ( auto & p : m_participants )
ms_addr_list . push_back ( p . get_public_address ( ) ) ;
// Miner makes a tx with ms output to all the participants (with minimum_sigs == m_minimum_signs_to_spend)
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , ms_addr_list , ms_amount , TESTS_DEFAULT_FEE , 0 , sources , destinations , true , true , m_minimum_signs_to_spend ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
//REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
// Alice (a very third person) make a tx, spending ms output
// THEN all the participants one-by-one sign it
sources . clear ( ) ;
destinations . clear ( ) ;
size_t ms_out_index = get_multisig_out_index ( tx_1 . vout ) ;
CHECK_AND_ASSERT_MES ( ms_out_index ! = tx_1 . vout . size ( ) , false , " Can't find ms out index in tx_1 " ) ;
tx_source_entry se = AUTO_VAL_INIT ( se ) ;
2022-05-20 21:32:27 +02:00
se . amount = boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ ms_out_index ] ) . amount ;
2018-12-27 18:50:45 +03:00
se . multisig_id = get_multisig_out_id ( tx_1 , ms_out_index ) ;
se . ms_sigs_count = m_minimum_signs_to_spend ;
// se.outputs -- not used for ms-outs
// se.real_output -- not used for ms-outs
se . real_output_in_tx_index = ms_out_index ;
se . real_out_tx_key = get_tx_pub_key_from_extra ( tx_1 ) ;
// se.separately_signed_tx_complete -- not a separately-signed tx
2022-05-20 21:32:27 +02:00
se . ms_keys_count = boost : : get < txout_multisig > ( boost : : get < currency : : tx_out_bare > ( tx_1 . vout [ ms_out_index ] ) . target ) . keys . size ( ) ;
2018-12-27 18:50:45 +03:00
sources . push_back ( se ) ;
2019-04-25 23:37:43 +02:00
tx_destination_entry de ( ms_amount - TESTS_DEFAULT_FEE , alice_acc . get_public_address ( ) ) ;
2018-12-27 18:50:45 +03:00
destinations . push_back ( de ) ;
// construct a transaction (no participants keys are provided, thus no signs for ms input are created)
transaction tx = AUTO_VAL_INIT ( tx ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , empty_attachment , tx , get_tx_version_from_events ( events ) , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
// sign the transaction by (m_minimum_signs_to_spend) participants in random order
std : : vector < account_base > shuffled_participants ( m_participants ) ;
std : : shuffle ( shuffled_participants . begin ( ) , shuffled_participants . end ( ) , crypto : : uniform_random_bit_generator ( ) ) ;
for ( size_t i = 0 ; i < m_minimum_signs_to_spend ; + + i )
{
r = sign_multisig_input_in_tx ( tx , 0 , shuffled_participants [ i ] . get_keys ( ) , tx_1 ) ;
CHECK_AND_ASSERT_MES ( r , false , " sign_ms_input_by_a_participant failed " ) ;
}
events . push_back ( tx ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1 , miner_acc , tx ) ;
std : : shared_ptr < tools : : wallet2 > alice_wlt ;
r = generator . init_test_wallet ( alice_acc , get_block_hash ( blk_0 ) , alice_wlt ) ;
CHECK_AND_ASSERT_MES ( r , false , " " ) ;
r = generator . refresh_test_wallet ( events , alice_wlt . get ( ) , get_block_hash ( blk_2 ) , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 ) ;
CHECK_AND_ASSERT_MES ( r , false , " refresh_test_wallet failed " ) ;
2019-04-25 23:37:43 +02:00
r = check_balance_via_wallet ( * alice_wlt . get ( ) , " alice " , ms_amount - TESTS_DEFAULT_FEE , 0 , 0 , 0 , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " invalid balance " ) ;
return true ;
}
bool multisig_n_participants_seq_signing : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
return true ;
}
//------------------------------------------------------------------------------
bool multisig_out_make_and_spent_in_altchain : : generate ( std : : vector < test_event_entry > & events ) const
{
// test idea: one tx has a multisig out in an altchain, and the second tx in the same altchain spents it
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 a few blocks, this is main chain
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0r , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_3 , blk_2 , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_4 , blk_3 , miner_acc ) ;
bool r = false ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 33 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > to_addrs ( { alice_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ; // multisig to same account, mentioned twice
// tx_1: normal key to multisig transaction
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE * 7 , 0 , sources , destinations , true , true , 2 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1a , blk_0r , miner_acc , tx_1 ) ;
MAKE_NEXT_BLOCK ( events , blk_2a , blk_1a , miner_acc ) ;
// 0 .. 10 11 12 13 14 15 <- height
// (0 ).. (0r)- (1 )- (2 )- (3 )- (4 )- <- main chain
// |
// \- (1a)- (2a)- (3a)- (4a)-
// tx_1 tx_2 tx_3 <- tx_2 spents multisig out in tx_1
// ^ | |
// +<----<----<---+ <----+
// atm tx_2 can't be added to the pool, as it contains reference to an ms out (in it's inputs) that is already referenced to tx_1 (in it's outs) still waiting in the pool
// to workaround this we simulate alt block tx pushing, when txs are pushed to the core right before the block and with "kept_by_block" flag
// tx_2
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
2019-04-25 23:37:43 +02:00
r = make_tx_multisig_to_key ( tx_1 , get_multisig_out_index ( tx_1 . vout ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) , alice_acc . get_keys ( ) } ) , bob_acc . get_public_address ( ) , tx_2 , TESTS_DEFAULT_FEE * 13 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ; // simulate
events . push_back ( tx_2 ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3a , blk_2a , miner_acc , tx_2 ) ;
// tx_3 spends the same ms out
transaction tx_3 = AUTO_VAL_INIT ( tx_3 ) ;
2019-04-25 23:37:43 +02:00
r = make_tx_multisig_to_key ( tx_1 , get_multisig_out_index ( tx_1 . vout ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) , alice_acc . get_keys ( ) } ) , bob_acc . get_public_address ( ) , tx_3 , TESTS_DEFAULT_FEE * 13 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ; // simulate
events . push_back ( tx_3 ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ; // alt block 4a should be rejected as it has invalid tx_3 which spends already spent ms output of tx_1
MAKE_NEXT_BLOCK_TX1 ( events , blk_4a , blk_3a , miner_acc , tx_3 ) ;
MAKE_NEXT_BLOCK ( events , blk_4b , blk_3a , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_5b , blk_4b , miner_acc ) ;
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( get_block_height ( blk_5b ) , get_block_hash ( blk_5b ) ) ) ;
2019-11-20 23:03:27 +01:00
size_t tx_count = 0 ;
2018-12-27 18:50:45 +03:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , tx_count ) ;
DO_CALLBACK ( events , " clear_tx_pool " ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( ALICE_ACC_IDX , 0 ) ) ;
DO_CALLBACK_PARAMS ( events , " check_balance " , params_check_balance ( BOB_ACC_IDX , amount - get_tx_fee ( tx_2 ) ) ) ;
return true ;
}
//------------------------------------------------------------------------------
bool multisig_out_spent_in_altchain_case_b4 : : generate ( std : : vector < test_event_entry > & events ) const
{
// test idea: multisig output is spent in altchain, while output's source tx is in the main chain atfer split height (case b4)
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 ) ;
bool r = false ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 22 ;
2018-12-27 18:50:45 +03:00
std : : list < account_public_address > to_addrs ( { alice_acc . get_public_address ( ) , alice_acc . get_public_address ( ) } ) ; // multisig to same account, mentioned twice
// tx_1: normal key to multisig transaction
2019-04-25 23:37:43 +02:00
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , to_addrs , amount , TESTS_DEFAULT_FEE * 5 , 0 , sources , destinations , true , true , 2 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_1 , 0 , CURRENCY_TO_KEY_OUT_RELAXED , true ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_acc ) ;
// 0 .. 10 11 12 13 14 15 <- height
// (0 ).. (0r)- (1 )- (2 )- (3 )- (4 )- <- main chain
// | tx_1 tx_2 <- tx_2 spents multisig out in tx_1
// | ^ |
// | +<----<----<---+
// |
// \- (1a)- (2a)- (3a)- (4a)- <- alt chain
// tx_2 <- tx_2 refers to unknown ms out (tx_1 won't be in this chain event if switching occurs)
// tx_2
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
2019-04-25 23:37:43 +02:00
r = make_tx_multisig_to_key ( tx_1 , get_multisig_out_index ( tx_1 . vout ) , std : : list < account_keys > ( { alice_acc . get_keys ( ) , alice_acc . get_keys ( ) } ) , bob_acc . get_public_address ( ) , tx_2 , TESTS_DEFAULT_FEE * 13 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " make_tx_multisig_to_key failed " ) ;
events . push_back ( tx_2 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_2 ) ;
MAKE_NEXT_BLOCK ( events , blk_4 , blk_3 , miner_acc ) ;
// alt chain
// block blk_1a is invalid as it contains tx_2 which refers to unknown ms output
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1a , blk_0r , miner_acc , tx_2 ) ;
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( get_block_height ( blk_4 ) , get_block_hash ( blk_4 ) ) ) ;
size_t tx_count = 0 ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , tx_count ) ;
return true ;
}
//------------------------------------------------------------------------------
multisig_unconfirmed_transfer_and_multiple_scan_pool_calls : : multisig_unconfirmed_transfer_and_multiple_scan_pool_calls ( )
{
REGISTER_CALLBACK_METHOD ( multisig_unconfirmed_transfer_and_multiple_scan_pool_calls , c1 ) ;
}
bool multisig_unconfirmed_transfer_and_multiple_scan_pool_calls : : generate ( std : : vector < test_event_entry > & events ) const
{
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 ( ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool multisig_unconfirmed_transfer_and_multiple_scan_pool_calls : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
auto & miner_acc = m_accounts [ MINER_ACC_IDX ] ;
auto & alice_acc = m_accounts [ ALICE_ACC_IDX ] ;
auto & bob_acc = m_accounts [ BOB_ACC_IDX ] ;
2019-04-25 23:37:43 +02:00
uint64_t ms_amount = TESTS_DEFAULT_FEE * 13 ;
2018-12-27 18:50:45 +03:00
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , m_accounts [ MINER_ACC_IDX ] ) ;
size_t blocks_fetched = 0 ;
bool received_money ;
std : : atomic < bool > atomic_false = ATOMIC_VAR_INIT ( false ) ;
miner_wlt - > refresh ( blocks_fetched , received_money , atomic_false ) ;
CHECK_AND_ASSERT_MES ( blocks_fetched = = CURRENCY_MINED_MONEY_UNLOCK_WINDOW , false , " Incorrect numbers of blocks fetched " ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Incorrect txs count in the pool " ) ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
std : : vector < tx_destination_entry > dst ;
dst . resize ( 1 ) ;
// to_key => multisig
dst . back ( ) . addr . push_back ( miner_acc . get_public_address ( ) ) ;
dst . back ( ) . addr . push_back ( alice_acc . get_public_address ( ) ) ;
2019-04-25 23:37:43 +02:00
dst . back ( ) . amount = ms_amount + TESTS_DEFAULT_FEE ;
2018-12-27 18:50:45 +03:00
dst . back ( ) . minimum_sigs = dst . back ( ) . addr . size ( ) ;
transaction key_to_ms_tx = AUTO_VAL_INIT ( key_to_ms_tx ) ;
2019-04-30 18:45:12 +02:00
miner_wlt - > transfer ( dst , 0 , 0 , TESTS_DEFAULT_FEE , extra , attachments , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , key_to_ms_tx ) ;
2018-12-27 18:50:45 +03:00
2019-03-25 01:30:20 +01:00
bool r = mine_next_pow_blocks_in_playtime ( miner_acc . get_public_address ( ) , c , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
size_t ms_out_idx = get_multisig_out_index ( key_to_ms_tx . vout ) ;
crypto : : hash multisig_id = get_multisig_out_id ( key_to_ms_tx , ms_out_idx ) ;
CHECK_AND_ASSERT_MES ( multisig_id ! = null_hash , false , " Multisig failed: failed to get get_multisig_out_id " ) ;
2019-03-25 01:30:20 +01:00
//r = mine_next_pow_blocks_in_playtime(miner_acc.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
2018-12-27 18:50:45 +03:00
//CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , alice_acc ) ;
miner_wlt - > refresh ( ) ;
alice_wlt - > refresh ( ) ;
tools : : wallet2 : : multisig_transfer_container ms_m , ms_a ;
miner_wlt - > get_multisig_transfers ( ms_m ) ;
alice_wlt - > get_multisig_transfers ( ms_a ) ;
CHECK_AND_ASSERT_MES ( ms_m . size ( ) = = 1 & & ms_a . size ( ) = = 1 , false , " Multisig failed: ms_m.size() == 1 && ms_a.size() == 1 " ) ;
LOG_PRINT_YELLOW ( " %%%%% generating multisig spending tx... " , LOG_LEVEL_0 ) ;
std : : vector < tx_destination_entry > dst2 ( 1 ) ;
dst2 . back ( ) . addr . resize ( 1 ) ;
dst2 . back ( ) . addr . back ( ) = bob_acc . get_public_address ( ) ;
dst2 . back ( ) . amount = ms_amount ;
dst2 . back ( ) . minimum_sigs = dst2 . back ( ) . addr . size ( ) ;
std : : list < currency : : account_keys > acc_keys ;
//acc_keys.push_back(miner_acc.get_keys()); will be signed as we pass miner_wlt
acc_keys . push_back ( alice_acc . get_keys ( ) ) ;
transaction tx = AUTO_VAL_INIT ( tx ) ;
transfer_multisig ( * miner_wlt . get ( ) ,
acc_keys ,
multisig_id ,
dst2 ,
0 ,
2019-04-25 23:37:43 +02:00
TESTS_DEFAULT_FEE ,
2018-12-27 18:50:45 +03:00
extra ,
attachments ,
2019-04-03 17:53:49 +03:00
tools : : detail : : ssi_digit ,
2018-12-27 18:50:45 +03:00
tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) ,
2022-05-10 23:49:20 +02:00
tx , c . get_current_tx_version ( ) ) ;
2018-12-27 18:50:45 +03:00
LOG_PRINT_YELLOW ( " %%%%% tx " < < get_transaction_hash ( tx ) < < " is spending multisig output " < < multisig_id , LOG_LEVEL_0 ) ;
bool stub ;
std : : deque < tools : : wallet2 : : transfer_details > transfers ;
2019-08-27 17:36:53 +02:00
std : : vector < tools : : wallet_public : : wallet_transfer_info > unconfirmed_transfers ;
2018-12-27 18:50:45 +03:00
alice_wlt - > scan_tx_pool ( stub ) ;
alice_wlt - > get_transfers ( transfers ) ;
CHECK_AND_ASSERT_MES ( transfers . size ( ) = = 0 , false , " incorrect transfers size for Alice: " < < transfers . size ( ) < < " \n " < < alice_wlt - > dump_trunsfers ( ) ) ;
alice_wlt - > get_unconfirmed_transfers ( unconfirmed_transfers ) ;
CHECK_AND_ASSERT_MES ( unconfirmed_transfers . size ( ) = = 1 , false , " incorrect unconfirmed transfers size for Alice: " < < unconfirmed_transfers . size ( ) ) ;
CHECK_AND_ASSERT_MES ( alice_wlt - > get_multisig_transfers ( ) . size ( ) = = 1 , false , " incorrect multisig transfers size for Alice: " < < alice_wlt - > get_multisig_transfers ( ) . size ( ) ) ;
uint32_t flags = alice_wlt - > get_multisig_transfers ( ) . begin ( ) - > second . m_flags ;
CHECK_AND_ASSERT_MES ( flags = = WALLET_TRANSFER_DETAIL_FLAG_SPENT , false , " incorrect ms transfer flags for Alice: " < < flags ) ;
// scan the pool second time (nothing happened neither in the pool, nor in blockchain)
alice_wlt - > scan_tx_pool ( stub ) ;
transfers . clear ( ) ;
unconfirmed_transfers . clear ( ) ;
alice_wlt - > get_transfers ( transfers ) ;
CHECK_AND_ASSERT_MES ( transfers . size ( ) = = 0 , false , " incorrect transfers size for Alice: " < < transfers . size ( ) < < " \n " < < alice_wlt - > dump_trunsfers ( ) ) ;
alice_wlt - > get_unconfirmed_transfers ( unconfirmed_transfers ) ;
CHECK_AND_ASSERT_MES ( unconfirmed_transfers . size ( ) = = 1 , false , " incorrect unconfirmed transfers size for Alice: " < < unconfirmed_transfers . size ( ) ) ;
CHECK_AND_ASSERT_MES ( alice_wlt - > get_multisig_transfers ( ) . size ( ) = = 1 , false , " incorrect multisig transfers size for Alice: " < < alice_wlt - > get_multisig_transfers ( ) . size ( ) ) ;
// flags should not change
flags = alice_wlt - > get_multisig_transfers ( ) . begin ( ) - > second . m_flags ;
CHECK_AND_ASSERT_MES ( flags = = WALLET_TRANSFER_DETAIL_FLAG_SPENT , false , " incorrect ms transfer flags for Alice: " < < flags ) ;
return true ;
}