2023-05-26 22:01:01 +02:00
// Copyright (c) 2014-2023 Zano Project
2018-12-27 18:50:45 +03:00
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <numeric>
# include <boost/archive/binary_oarchive.hpp>
# include <boost/archive/binary_iarchive.hpp>
2020-07-19 00:31:55 +02:00
# include <boost/iostreams/stream.hpp>
# include <boost/iostreams/filtering_stream.hpp>
2018-12-27 18:50:45 +03:00
# include <boost/filesystem/fstream.hpp>
# include <iostream>
# include <boost/utility/value_init.hpp>
# include "include_base_utils.h"
2022-01-08 16:36:33 +01:00
# include "net/levin_client.h"
2018-12-27 18:50:45 +03:00
using namespace epee ;
# include "string_coding.h"
2019-02-21 21:33:52 +03:00
# define KEEP_WALLET_LOG_MACROS
2018-12-27 18:50:45 +03:00
# include "wallet2.h"
# include "currency_core/currency_format_utils.h"
# include "currency_core/bc_offers_service_basic.h"
# include "rpc/core_rpc_server_commands_defs.h"
# include "misc_language.h"
# include "common/boost_serialization_helper.h"
# include "crypto/crypto.h"
# include "serialization/binary_utils.h"
# include "currency_core/bc_payments_id_service.h"
# include "version.h"
2020-07-19 00:31:55 +02:00
# include "common/encryption_filter.h"
2021-02-03 00:13:44 +01:00
# include "crypto/bitcoin/sha256_helper.h"
2023-05-29 14:31:48 +02:00
# define DISABLE_TOR
2022-04-20 17:17:11 +02:00
# ifndef DISABLE_TOR
# include "common/tor_helper.h"
# endif
2022-03-22 23:38:43 +02:00
# include "storages/levin_abstract_invoke2.h"
2022-05-25 22:31:23 +02:00
# include "common/variant_helper.h"
2022-08-29 23:00:34 +02:00
# include "currency_core/crypto_config.h"
2022-09-13 22:08:46 +02:00
# include "crypto/zarcanum.h"
2022-03-22 23:38:43 +02:00
2018-12-27 18:50:45 +03:00
using namespace currency ;
2023-04-25 00:16:13 +02:00
# define SET_CONTEXT_OBJ_FOR_SCOPE(name, obj) m_current_context.name = &obj; \
auto COMBINE ( auto_scope_var_ , __LINE__ ) = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { m_current_context . name = nullptr ; } ) ;
2022-01-08 16:36:33 +01:00
2019-10-09 16:01:33 +03:00
# define MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES (100*1024*1024) // 100 MB
2023-06-09 19:44:27 +02:00
# define WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX 10 // TODO @#@# change to default decoy set number
2023-06-15 21:02:22 +02:00
# define WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 3 // TODO: @#@# consider descreasing to mimic normal tx
# define WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 10 // TODO: @#@# consider descreasing to mimic normal tx
2023-06-09 01:10:16 +02:00
2018-12-27 18:50:45 +03:00
# undef LOG_DEFAULT_CHANNEL
# define LOG_DEFAULT_CHANNEL "wallet"
ENABLE_CHANNEL_BY_DEFAULT ( " wallet " )
namespace tools
{
2023-05-26 22:01:01 +02:00
wallet2 : : wallet2 ( )
: m_stop ( false )
, m_wcallback ( new i_wallet2_callback ( ) ) //stub
, m_core_proxy ( new default_http_core_proxy ( ) )
, m_upper_transaction_size_limit ( 0 )
, m_height_of_start_sync ( 0 )
, m_last_sync_percent ( 0 )
, m_fake_outputs_count ( 0 )
, m_do_rise_transfer ( false )
, m_log_prefix ( " ??? " )
, m_watch_only ( false )
, m_last_pow_block_h ( 0 )
, m_minimum_height ( WALLET_MINIMUM_HEIGHT_UNSET_CONST )
2023-06-09 01:10:16 +02:00
, m_min_utxo_count_for_defragmentation_tx ( WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX )
2023-06-09 19:44:27 +02:00
, m_max_utxo_count_for_defragmentation_tx ( WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX )
2023-06-09 01:10:16 +02:00
, m_decoys_count_for_defragmentation_tx ( WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX )
2023-05-26 22:01:01 +02:00
, m_current_wallet_file_size ( 0 )
, m_use_deffered_global_outputs ( false )
# ifdef DISABLE_TOR
, m_disable_tor_relay ( true )
# else
, m_disable_tor_relay ( false )
# endif
2020-06-14 02:17:10 +02:00
{
m_core_runtime_config = currency : : get_default_core_runtime_config ( ) ;
}
2019-07-24 19:14:35 +02:00
//---------------------------------------------------------------
2023-06-15 23:55:22 +02:00
uint64_t wallet2 : : get_max_unlock_time_from_receive_indices ( const currency : : transaction & tx , const wallet_public : : employed_tx_entries & td )
2019-07-24 19:14:35 +02:00
{
uint64_t max_unlock_time = 0 ;
// etc_tx_details_expiration_time have priority over etc_tx_details_expiration_time2
uint64_t major_unlock_time = get_tx_x_detail < etc_tx_details_unlock_time > ( tx ) ;
if ( major_unlock_time )
return major_unlock_time ;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT ( ut2 ) ;
get_type_in_variant_container ( tx . extra , ut2 ) ;
if ( ! ut2 . unlock_time_array . size ( ) )
return 0 ;
CHECK_AND_ASSERT_THROW_MES ( ut2 . unlock_time_array . size ( ) = = tx . vout . size ( ) , " Internal error: wrong tx transfer details: ut2.unlock_time_array.size() " < < ut2 . unlock_time_array . size ( ) < < " is not equal transaction outputs vector size= " < < tx . vout . size ( ) ) ;
2023-06-15 23:55:22 +02:00
for ( auto r : td . receive )
2019-07-24 19:14:35 +02:00
{
2023-06-15 23:55:22 +02:00
uint64_t ri = r . index ;
2019-07-24 19:14:35 +02:00
CHECK_AND_ASSERT_THROW_MES ( ri < tx . vout . size ( ) , " Internal error: wrong tx transfer details: reciev index= " < < ri < < " is greater than transaction outputs vector " < < tx . vout . size ( ) ) ;
2022-05-19 22:46:57 +02:00
VARIANT_SWITCH_BEGIN ( tx . vout [ ri ] ) ;
2022-05-25 22:31:23 +02:00
VARIANT_CASE_CONST ( tx_out_bare , o )
2022-05-19 22:46:57 +02:00
if ( o . target . type ( ) = = typeid ( currency : : txout_to_key ) )
{
//update unlock_time if needed
if ( ut2 . unlock_time_array [ ri ] > max_unlock_time )
max_unlock_time = ut2 . unlock_time_array [ ri ] ;
}
2022-05-25 22:31:23 +02:00
VARIANT_CASE_CONST ( tx_out_zarcanum , o ) ;
2022-05-19 22:46:57 +02:00
VARIANT_SWITCH_END ( ) ;
2019-07-24 19:14:35 +02:00
}
return max_unlock_time ;
}
2018-12-27 18:50:45 +03:00
//----------------------------------------------------------------------------------------------------
2019-04-12 09:48:33 +03:00
std : : string wallet2 : : transfer_flags_to_str ( uint32_t flags )
{
std : : string result ( 5 , ' ' ) ;
if ( flags & WALLET_TRANSFER_DETAIL_FLAG_SPENT )
result [ 0 ] = ' s ' ;
if ( flags & WALLET_TRANSFER_DETAIL_FLAG_BLOCKED )
result [ 1 ] = ' b ' ;
if ( flags & WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION )
result [ 2 ] = ' e ' ;
if ( flags & WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER )
result [ 3 ] = ' m ' ;
if ( flags & WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION )
result [ 4 ] = ' c ' ;
return result ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
std : : string wallet2 : : transform_tx_to_str ( const currency : : transaction & tx )
{
return currency : : obj_to_json_str ( tx ) ;
}
//----------------------------------------------------------------------------------------------------
currency : : transaction wallet2 : : transform_str_to_tx ( const std : : string & tx_str )
{
THROW_IF_TRUE_WALLET_INT_ERR_EX_NO_HANDLER ( false , " transform_str_to_tx shoruld never be called " ) ;
return currency : : transaction ( ) ;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : transfer_details_base_to_amount ( const transfer_details_base & tdb )
{
return tdb . amount ( ) ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : transfer_details_base_to_tx_hash ( const transfer_details_base & tdb )
{
return epee : : string_tools : : pod_to_hex ( currency : : get_transaction_hash ( tdb . m_ptx_wallet_info - > m_tx ) ) ;
}
//----------------------------------------------------------------------------------------------------
const wallet2 : : transaction_wallet_info & wallet2 : : transform_ptr_to_value ( const std : : shared_ptr < wallet2 : : transaction_wallet_info > & a )
{
return * a ;
}
//----------------------------------------------------------------------------------------------------
std : : shared_ptr < wallet2 : : transaction_wallet_info > wallet2 : : transform_value_to_ptr ( const wallet2 : : transaction_wallet_info & d )
{
THROW_IF_TRUE_WALLET_INT_ERR_EX_NO_HANDLER ( false , " transform_value_to_ptr shoruld never be called " ) ;
return std : : shared_ptr < wallet2 : : transaction_wallet_info > ( ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : init ( const std : : string & daemon_address )
{
m_miner_text_info = PROJECT_VERSION_LONG ;
m_core_proxy - > set_connection_addr ( daemon_address ) ;
2020-09-07 20:59:44 +02:00
m_core_proxy - > check_connection ( ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : set_core_proxy ( const std : : shared_ptr < i_core_proxy > & proxy )
{
THROW_IF_TRUE_WALLET_EX ( ! proxy , error : : wallet_internal_error , " Trying to set null core proxy. " ) ;
m_core_proxy = proxy ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2023-06-12 20:37:26 +02:00
void wallet2 : : set_pos_utxo_count_limits_for_defragmentation_tx ( uint64_t min_outs , uint64_t max_outs )
2019-11-29 21:43:17 +01:00
{
2023-06-12 20:37:26 +02:00
m_min_utxo_count_for_defragmentation_tx = min_outs ;
m_max_utxo_count_for_defragmentation_tx = max_outs ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : set_pos_decoys_count_for_defragmentation_tx ( size_t decoys_count )
2019-11-29 21:43:17 +01:00
{
2023-06-12 20:37:26 +02:00
m_decoys_count_for_defragmentation_tx = decoys_count ;
2019-11-29 21:43:17 +01:00
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
std : : shared_ptr < i_core_proxy > wallet2 : : get_core_proxy ( )
{
return m_core_proxy ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_transfer_info_by_key_image ( const crypto : : key_image & ki , transfer_details & td , size_t & i )
{
auto it = m_key_images . find ( ki ) ;
if ( it = = m_key_images . end ( ) )
{
return false ;
}
THROW_IF_FALSE_WALLET_EX ( it - > second < m_transfers . size ( ) , error : : wallet_internal_error , " wrong out in transaction: internal index " ) ;
td = m_transfers [ it - > second ] ;
i = it - > second ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_transfer_info_by_index ( size_t i , transfer_details & td )
{
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( i < m_transfers . size ( ) , false , " wrong out in transaction: internal index, m_transfers.size()= " < < m_transfers . size ( ) ) ;
2018-12-27 18:50:45 +03:00
td = m_transfers [ i ] ;
return true ;
}
//----------------------------------------------------------------------------------------------------
size_t wallet2 : : scan_for_collisions ( std : : unordered_map < crypto : : key_image , std : : list < size_t > > & key_images )
{
size_t count = 0 ;
for ( size_t i = 0 ; i ! = m_transfers . size ( ) ; i + + )
{
key_images [ m_transfers [ i ] . m_key_image ] . push_back ( i ) ;
if ( key_images [ m_transfers [ i ] . m_key_image ] . size ( ) > 1 )
count + + ;
}
return count ;
}
//----------------------------------------------------------------------------------------------------
size_t wallet2 : : fix_collisions ( )
{
std : : unordered_map < crypto : : key_image , std : : list < size_t > > key_images ;
scan_for_collisions ( key_images ) ;
size_t count = 0 ;
for ( auto & coll_entry : key_images )
{
if ( coll_entry . second . size ( ) < 2 )
continue ;
currency : : COMMAND_RPC_CHECK_KEYIMAGES : : request req_ki = AUTO_VAL_INIT ( req_ki ) ;
req_ki . images . push_back ( coll_entry . first ) ;
currency : : COMMAND_RPC_CHECK_KEYIMAGES : : response rsp_ki = AUTO_VAL_INIT ( rsp_ki ) ;
bool r = m_core_proxy - > call_COMMAND_RPC_COMMAND_RPC_CHECK_KEYIMAGES ( req_ki , rsp_ki ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " unable to get spent key image info for keyimage: " < < coll_entry . first ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( rsp_ki . images_stat . size ( ) = = 1 , " unable to get spent key image info for keyimage: " < < coll_entry . first < < " keyimages size()= " < < rsp_ki . images_stat . size ( ) ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( * rsp_ki . images_stat . begin ( ) ! = 0 , " unable to get spent key image info for keyimage: " < < coll_entry . first < < " keyimages [0]=0 " ) ;
for ( auto it = coll_entry . second . begin ( ) ; it ! = coll_entry . second . end ( ) ; it + + )
{
m_transfers [ * it ] . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ;
m_transfers [ * it ] . m_spent_height = * rsp_ki . images_stat . begin ( ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Fixed collision for key image " < < coll_entry . first < < " transfer " < < count ) ;
2018-12-27 18:50:45 +03:00
count + + ;
}
}
return count ;
}
//----------------------------------------------------------------------------------------------------
size_t wallet2 : : scan_for_transaction_entries ( const crypto : : hash & tx_id , const crypto : : key_image & ki , std : : list < transfer_details > & details )
{
bool check_ki = ki ! = currency : : null_ki ;
bool check_tx_id = tx_id ! = currency : : null_hash ;
for ( auto it = m_transfers . begin ( ) ; it ! = m_transfers . end ( ) ; it + + )
{
if ( check_ki & & it - > m_key_image = = ki )
{
details . push_back ( * it ) ;
}
if ( check_tx_id & & get_transaction_hash ( it - > m_ptx_wallet_info - > m_tx ) = = tx_id )
{
details . push_back ( * it ) ;
}
}
return details . size ( ) ;
}
//----------------------------------------------------------------------------------------------------
2020-06-12 00:32:08 +02:00
void wallet2 : : fetch_tx_global_indixes ( const currency : : transaction & tx , std : : vector < uint64_t > & goutputs_indexes )
2020-06-12 23:32:06 +02:00
{
std : : list < std : : reference_wrapper < const currency : : transaction > > txs ;
txs . push_back ( tx ) ;
std : : vector < std : : vector < uint64_t > > res ;
fetch_tx_global_indixes ( txs , res ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( res . size ( ) = = 1 , " fetch_tx_global_indixes for single entry returned wrong result: res.size()= " < < res . size ( ) ) ;
goutputs_indexes = res [ 0 ] ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : fetch_tx_global_indixes ( const std : : list < std : : reference_wrapper < const currency : : transaction > > & txs , std : : vector < std : : vector < uint64_t > > & goutputs_indexes )
2020-06-12 00:32:08 +02:00
{
currency : : COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES : : response res = AUTO_VAL_INIT ( res ) ;
2020-06-12 23:32:06 +02:00
for ( auto & tx : txs )
{
req . txids . push_back ( get_transaction_hash ( tx ) ) ;
}
2020-06-12 00:32:08 +02:00
bool r = m_core_proxy - > call_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES ( req , res ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : no_connection_to_daemon , " get_o_indexes.bin " ) ;
THROW_IF_TRUE_WALLET_EX ( res . status = = API_RETURN_CODE_BUSY , error : : daemon_busy , " get_o_indexes.bin " ) ;
THROW_IF_TRUE_WALLET_EX ( res . status ! = API_RETURN_CODE_OK , error : : get_out_indices_error , res . status ) ;
2020-06-12 23:32:06 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( res . tx_global_outs . size ( ) = = txs . size ( ) , " res.tx_global_outs.size()( " < < res . tx_global_outs . size ( )
< < " ) == txs.size()( " < < txs . size ( ) < < " ) " ) ;
goutputs_indexes . resize ( txs . size ( ) ) ;
auto it_resp = res . tx_global_outs . begin ( ) ;
auto it_txs = txs . begin ( ) ;
size_t i = 0 ;
for ( ; it_resp ! = res . tx_global_outs . end ( ) ; )
{
THROW_IF_FALSE_WALLET_INT_ERR_EX ( it_resp - > v . size ( ) = = it_txs - > get ( ) . vout . size ( ) ,
" transactions outputs size= " < < it_txs - > get ( ) . vout . size ( ) < <
" not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response[i] size= " < < it_resp - > v . size ( ) ) ;
2020-06-12 00:32:08 +02:00
2020-06-12 23:32:06 +02:00
goutputs_indexes [ i ] = it_resp - > v ;
it_resp + + ; it_txs + + ; i + + ;
}
2020-06-12 00:32:08 +02:00
}
2022-07-01 17:19:57 +02:00
bool out_is_to_key ( const currency : : tx_out_v & out_t )
2022-06-30 19:02:22 +02:00
{
2022-07-01 17:19:57 +02:00
if ( out_t . type ( ) = = typeid ( currency : : tx_out_bare ) )
2022-06-30 19:02:22 +02:00
{
2022-07-01 17:19:57 +02:00
return boost : : get < currency : : tx_out_bare > ( out_t ) . target . type ( ) = = typeid ( currency : : txout_to_key ) ;
2022-06-30 19:02:22 +02:00
}
return false ;
}
2022-07-01 17:19:57 +02:00
bool out_is_multisig ( const currency : : tx_out_v & out_t )
2022-06-30 21:47:59 +02:00
{
2022-07-01 17:19:57 +02:00
if ( out_t . type ( ) = = typeid ( currency : : tx_out_bare ) )
2022-06-30 21:47:59 +02:00
{
2022-07-01 17:19:57 +02:00
return boost : : get < currency : : tx_out_bare > ( out_t ) . target . type ( ) = = typeid ( currency : : txout_multisig ) ;
2022-06-30 21:47:59 +02:00
}
return false ;
}
2022-07-01 17:19:57 +02:00
bool out_is_to_htlc ( const currency : : tx_out_v & out_t )
2022-06-30 19:02:22 +02:00
{
if ( out_t . type ( ) = = typeid ( currency : : tx_out_bare ) )
{
return boost : : get < currency : : tx_out_bare > ( out_t ) . target . type ( ) = = typeid ( currency : : txout_htlc ) ;
}
return false ;
}
2022-09-12 18:47:38 +02:00
bool out_is_zc ( const currency : : tx_out_v & out_t )
{
return out_t . type ( ) = = typeid ( currency : : tx_out_zarcanum ) ;
}
2022-07-01 17:19:57 +02:00
const currency : : txout_htlc & out_get_htlc ( const currency : : tx_out_v & out_t )
2022-06-30 21:47:59 +02:00
{
return boost : : get < currency : : txout_htlc > ( boost : : get < currency : : tx_out_bare > ( out_t ) . target ) ;
}
2022-07-06 13:22:05 +02:00
uint8_t wallet2 : : out_get_mixin_attr ( const currency : : tx_out_v & out_t )
2022-06-30 21:47:59 +02:00
{
if ( out_t . type ( ) = = typeid ( currency : : tx_out_bare ) )
{
if ( boost : : get < currency : : tx_out_bare > ( out_t ) . target . type ( ) = = typeid ( currency : : txout_to_key ) )
{
return boost : : get < currency : : txout_to_key > ( boost : : get < currency : : tx_out_bare > ( out_t ) . target ) . mix_attr ;
}
else
{
2022-07-04 15:26:28 +02:00
THROW_WALLET_CMN_ERR_EX ( " Unexpected type in out_get_mixin_attr " ) ;
2022-06-30 21:47:59 +02:00
}
}
else if ( out_t . type ( ) = = typeid ( currency : : tx_out_zarcanum ) )
{
2022-07-01 17:20:55 +02:00
return boost : : get < currency : : tx_out_zarcanum > ( out_t ) . mix_attr ;
2022-06-30 21:47:59 +02:00
}
else
{
2022-07-04 15:26:28 +02:00
THROW_WALLET_CMN_ERR_EX ( " Unexpected type in out_get_mixin_attr " ) ;
2022-06-30 21:47:59 +02:00
}
2022-07-04 15:26:28 +02:00
THROW_WALLET_CMN_ERR_EX ( " Unexpected out type im wallet: " < < out_t . type ( ) . name ( ) ) ;
return false ;
2022-06-30 21:47:59 +02:00
}
2022-07-01 17:19:57 +02:00
const crypto : : public_key & wallet2 : : out_get_pub_key ( const currency : : tx_out_v & out_t , std : : list < currency : : htlc_info > & htlc_info_list )
2022-06-30 19:02:22 +02:00
{
if ( out_t . type ( ) = = typeid ( tx_out_bare ) )
{
2022-07-01 17:19:57 +02:00
const currency : : tx_out_bare & out = boost : : get < currency : : tx_out_bare > ( out_t ) ;
if ( out . target . type ( ) = = typeid ( currency : : txout_to_key ) )
2022-06-30 19:02:22 +02:00
{
return boost : : get < currency : : txout_to_key > ( out . target ) . key ;
}
2022-07-04 15:26:28 +02:00
else
2022-06-30 19:02:22 +02:00
{
2022-07-04 15:26:28 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( out . target . type ( ) = = typeid ( currency : : txout_htlc ) , " Unexpected out type in target wallet: " < < out . target . type ( ) . name ( ) ) ;
2022-06-30 19:02:22 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( htlc_info_list . size ( ) > 0 , " Found txout_htlc out but htlc_info_list is empty " ) ;
bool hltc_our_out_is_before_expiration = htlc_info_list . front ( ) . hltc_our_out_is_before_expiration ;
htlc_info_list . pop_front ( ) ;
if ( hltc_our_out_is_before_expiration )
{
return boost : : get < currency : : txout_htlc > ( out . target ) . pkey_redeem ;
}
else
{
return boost : : get < currency : : txout_htlc > ( out . target ) . pkey_refund ;
}
}
}
2022-07-01 17:19:57 +02:00
else
{
2022-07-04 15:26:28 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( out_t . type ( ) = = typeid ( currency : : tx_out_zarcanum ) , " Unexpected out type im wallet: " < < out_t . type ( ) . name ( ) ) ;
return boost : : get < currency : : tx_out_zarcanum > ( out_t ) . stealth_address ;
2022-06-30 19:02:22 +02:00
}
}
2020-06-12 00:32:08 +02:00
//----------------------------------------------------------------------------------------------------
2023-08-14 22:32:52 +02:00
void wallet2 : : process_ado_in_new_transaction ( const asset_descriptor_operation & ado , uint64_t height )
2023-08-05 20:27:46 +02:00
{
do
{
if ( ado . operation_type = = ASSET_DESCRIPTOR_OPERATION_REGISTER )
{
crypto : : public_key self_check = AUTO_VAL_INIT ( self_check ) ;
crypto : : secret_key asset_control_key = AUTO_VAL_INIT ( asset_control_key ) ;
bool r = derive_key_pair_from_key_pair ( tx_pub_key , m_account . get_keys ( ) . spend_secret_key , asset_control_key , self_check , CRYPTO_HDS_ASSET_CONTROL_KEY ) ;
if ( ! r )
{
//not critical error, continue to work
LOG_ERROR ( " Failed to derive_key_pair_from_key_pair for asset_descriptor_operation in tx " < < ptc . tx_hash ( ) ) ;
break ;
}
if ( self_check ! = ado . descriptor . owner )
{
//still not critical error
LOG_ERROR ( " Public key from asset_descriptor_operation( " < < ado . descriptor . owner < < " ) not much with derived public key( " < < self_check < < " ), for tx " < < ptc . tx_hash ( ) ) ;
break ;
}
crypto : : public_key asset_id { } ;
calculate_asset_id ( ado . descriptor . owner , nullptr , & asset_id ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( m_own_asset_descriptors . count ( asset_id ) = = 0 , " asset with asset_id " < < asset_id < < " has already been registered in the wallet as own asset " ) ;
wallet_own_asset_context & asset_context = m_own_asset_descriptors [ asset_id ] ;
asset_context . asset_descriptor = ado . descriptor ;
2023-08-14 22:32:52 +02:00
//asset_context.height = height;
2023-08-05 20:27:46 +02:00
std : : stringstream ss ;
ss < < " New Asset Registered: "
< < ENDL < < " asset id: " < < asset_id
< < ENDL < < " Name: " < < asset_context . asset_descriptor . full_name
< < ENDL < < " Ticker: " < < asset_context . asset_descriptor . ticker
< < ENDL < < " Total Max Supply: " < < print_asset_money ( asset_context . asset_descriptor . total_max_supply , asset_context . asset_descriptor . decimal_point )
< < ENDL < < " Current Supply: " < < print_asset_money ( asset_context . asset_descriptor . current_supply , asset_context . asset_descriptor . decimal_point )
< < ENDL < < " Decimal Point: " < < asset_context . asset_descriptor . decimal_point ;
2023-08-14 22:32:52 +02:00
add_rollback_event ( height , asset_register_event { asset_id } ) ;
2023-08-05 20:27:46 +02:00
WLT_LOG_MAGENTA ( ss . str ( ) , LOG_LEVEL_0 ) ;
if ( m_wcallback )
m_wcallback - > on_message ( i_wallet2_callback : : ms_yellow , ss . str ( ) ) ;
}
2023-08-14 22:32:52 +02:00
else if ( ado . operation_type = = ASSET_DESCRIPTOR_OPERATION_UPDATE | | ado . operation_type = = ASSET_DESCRIPTOR_OPERATION_EMMIT | | ado . operation_type = = ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN )
2023-08-05 20:27:46 +02:00
{
2023-08-14 22:32:52 +02:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ado . opt_asset_id , " ASSET_DESCRIPTOR_OPERATION_UPDATE with empty opt_asset_id " ) ;
auto it = m_own_asset_descriptors . find ( * ado . opt_asset_id ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( it ! = m_own_asset_descriptors . end ( ) , " asset with asset_id " < < * ado . opt_asset_id < < " not found during ASSET_DESCRIPTOR_OPERATION_UPDATE " ) ;
if ( it - > second . asset_descriptor . owner ! = ado . descriptor . owner )
{
//ownership of the asset had been transfered
add_rollback_event ( height , asset_unown_event { it - > first , it - > second } ) ;
m_own_asset_descriptors . erase ( it ) ;
}
else
{
//asset had been updated
add_rollback_event ( height , asset_update_event { it - > first , it - > second } ) ;
it - > second . asset_descriptor = ado . descriptor ;
}
2023-08-05 20:27:46 +02:00
}
} while ( false ) ;
}
2023-08-14 22:32:52 +02:00
//----------------------------------------------------------------------------------------------------
void wallet2 : : add_rollback_event ( uint64_t h , const wallet_event_t & ev )
{
m_rollback_events . emplace_back ( h , ev ) ;
}
//----------------------------------------------------------------------------------------------------
2020-06-12 00:32:08 +02:00
void wallet2 : : process_new_transaction ( const currency : : transaction & tx , uint64_t height , const currency : : block & b , const std : : vector < uint64_t > * pglobal_indexes )
2018-12-27 18:50:45 +03:00
{
//check for transaction spends
2023-06-06 23:05:53 +02:00
process_transaction_context ptc ( tx ) ;
process_unconfirmed ( tx , ptc . recipients , ptc . remote_aliases ) ;
2022-06-28 22:27:36 +02:00
2018-12-27 18:50:45 +03:00
// check all outputs for spending (compare key images)
2022-06-28 22:27:36 +02:00
ptc . coin_base_tx = is_coinbase ( tx , ptc . is_pos_coinbase ) ;
2018-12-27 18:50:45 +03:00
//PoW block don't have change, so all outs supposed to be marked as "mined"
2022-06-28 22:27:36 +02:00
ptc . is_derived_from_coinbase = ! ptc . is_pos_coinbase ;
ptc . height = height ;
2018-12-27 18:50:45 +03:00
2023-06-06 23:05:53 +02:00
2018-12-27 18:50:45 +03:00
for ( auto & in : tx . vin )
{
2022-06-28 22:27:36 +02:00
ptc . sub_i = 0 ;
VARIANT_SWITCH_BEGIN ( in ) ;
VARIANT_CASE_CONST ( currency : : txin_to_key , intk )
2018-12-27 18:50:45 +03:00
{
2022-06-28 22:27:36 +02:00
process_input_t ( intk , ptc , tx ) ;
}
2022-08-03 14:00:39 +02:00
VARIANT_CASE_CONST ( currency : : txin_zc_input , in_zc )
2022-06-28 22:27:36 +02:00
{
2022-08-03 14:00:39 +02:00
process_input_t ( in_zc , ptc , tx ) ;
//ptc.sub_i++;
2018-12-27 18:50:45 +03:00
}
2022-06-28 22:27:36 +02:00
VARIANT_CASE_CONST ( currency : : txin_multisig , inms )
2018-12-27 18:50:45 +03:00
{
2022-06-28 22:27:36 +02:00
crypto : : hash multisig_id = inms . multisig_out_id ;
2018-12-27 18:50:45 +03:00
auto it = m_multisig_transfers . find ( multisig_id ) ;
if ( it ! = m_multisig_transfers . end ( ) )
{
it - > second . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ;
it - > second . m_spent_height = height ;
2023-06-06 23:05:53 +02:00
WLT_LOG_L0 ( " Spent multisig out: " < < multisig_id < < " , amount: " < < print_money ( currency : : get_amount_from_variant ( in ) ) < < " , with tx: " < < ptc . tx_hash ( ) < < " , at height " < < height ) ;
2023-06-15 23:55:22 +02:00
ptc . employed_entries . spent . push_back ( wallet_public : : employed_tx_entry { ptc . i } ) ;
2018-12-27 18:50:45 +03:00
}
}
2022-06-28 22:27:36 +02:00
VARIANT_CASE_CONST ( currency : : txin_htlc , in_htlc )
2021-01-24 21:00:43 +01:00
{
if ( in_htlc . key_offsets . size ( ) ! = 1 )
{
LOG_ERROR ( " in_htlc.key_offsets.size() != 1, skip inout " ) ;
continue ;
}
if ( in_htlc . key_offsets [ 0 ] . type ( ) ! = typeid ( uint64_t ) )
{
LOG_ERROR ( " HTLC with ref_by_id is not supported by wallet yet " ) ;
continue ;
}
2021-01-31 19:42:02 +01:00
auto it = m_active_htlcs . find ( std : : make_pair ( in_htlc . amount , boost : : get < uint64_t > ( in_htlc . key_offsets [ 0 ] ) ) ) ;
2021-01-24 21:00:43 +01:00
if ( it ! = m_active_htlcs . end ( ) )
{
transfer_details & td = m_transfers [ it - > second ] ;
2022-05-01 21:07:17 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( td . m_ptx_wallet_info - > m_tx . vout . size ( ) > td . m_internal_output_index , " Internal error: wrong td.m_internal_output_index: " < < td . m_internal_output_index ) ;
2022-05-19 22:46:57 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . type ( ) = = typeid ( tx_out_bare ) , " Internal error: wrong output type: " < < td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . type ( ) . name ( ) ) ;
const boost : : typeindex : : type_info & ti = boost : : get < tx_out_bare > ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] ) . target . type ( ) ;
2022-05-01 21:07:17 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ti = = typeid ( txout_htlc ) , " Internal error: wrong type of output's target: " < < ti . name ( ) ) ;
2021-01-24 21:00:43 +01:00
//input spend active htlc
2021-02-14 00:28:39 +01:00
m_transfers [ it - > second ] . m_spent_height = height ;
2021-02-15 02:17:59 +01:00
transfer_details_extra_option_htlc_info & tdeohi = get_or_add_field_to_variant_vector < transfer_details_extra_option_htlc_info > ( td . varian_options ) ;
2021-02-14 00:28:39 +01:00
tdeohi . origin = in_htlc . hltc_origin ;
2023-06-06 23:05:53 +02:00
tdeohi . redeem_tx_id = ptc . tx_hash ( ) ;
2021-01-24 21:00:43 +01:00
}
}
2022-06-28 22:27:36 +02:00
VARIANT_SWITCH_END ( ) ;
ptc . i + + ;
2018-12-27 18:50:45 +03:00
}
2019-07-24 19:14:35 +02:00
/*
collect unlock_time from every output that transfered coins to this account and use maximum of
all values m_payments entry , use this strict policy is required to protect exchanges from being feeded with
useless outputs
*/
uint64_t max_out_unlock_time = 0 ;
2018-12-27 18:50:45 +03:00
2022-06-28 22:27:36 +02:00
std : : vector < wallet_out_info > outs ;
2023-06-06 23:05:53 +02:00
//uint64_t sum_of_native_outs = 0; // TODO: @#@# correctly calculate tx_money_got_in_outs for post-HF4
2022-06-28 22:27:36 +02:00
crypto : : public_key tx_pub_key = null_pkey ;
bool r = parse_and_validate_tx_extra ( tx , tx_pub_key ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : tx_extra_parse_error , tx ) ;
2019-07-24 19:14:35 +02:00
//check for transaction income
2018-12-27 18:50:45 +03:00
crypto : : key_derivation derivation = AUTO_VAL_INIT ( derivation ) ;
2021-01-24 21:00:43 +01:00
std : : list < htlc_info > htlc_info_list ;
2023-06-06 23:05:53 +02:00
r = lookup_acc_outs ( m_account . get_keys ( ) , tx , tx_pub_key , outs , derivation , htlc_info_list ) ;
2018-12-27 18:50:45 +03:00
THROW_IF_TRUE_WALLET_EX ( ! r , error : : acc_outs_lookup_error , tx , tx_pub_key , m_account . get_keys ( ) ) ;
2023-02-21 01:41:33 +01:00
if ( ! outs . empty ( ) )
2018-12-27 18:50:45 +03:00
{
2020-06-13 21:04:21 +02:00
//good news - got money! take care about it
//usually we have only one transfer for user in transaction
2018-12-27 18:50:45 +03:00
//create once instance of tx for all entries
std : : shared_ptr < transaction_wallet_info > pwallet_info ( new transaction_wallet_info ( ) ) ;
pwallet_info - > m_tx = tx ;
pwallet_info - > m_block_height = height ;
pwallet_info - > m_block_timestamp = b . timestamp ;
2020-06-12 00:32:08 +02:00
if ( is_auditable ( ) )
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( pglobal_indexes & & pglobal_indexes - > size ( ) = = tx . vout . size ( ) , " wrong pglobal_indexes = " < < pglobal_indexes < < " " ) ;
}
std : : vector < uint64_t > outputs_index_local ;
2020-06-14 02:17:10 +02:00
if ( ! pglobal_indexes | | ( pglobal_indexes - > size ( ) = = 0 & & tx . vout . size ( ) ! = 0 ) )
2020-06-12 00:32:08 +02:00
{
2021-01-24 21:00:43 +01:00
//if tx contain htlc_out, then we would need global_indexes anyway, to be able later detect redeem of htlc
if ( m_use_deffered_global_outputs & & htlc_info_list . size ( ) = = 0 )
2020-06-14 02:17:10 +02:00
{
pglobal_indexes = nullptr ;
}
else
2020-06-13 21:04:21 +02:00
{
fetch_tx_global_indixes ( tx , outputs_index_local ) ;
pglobal_indexes = & outputs_index_local ;
}
2020-06-12 00:32:08 +02:00
}
2020-06-13 21:04:21 +02:00
2018-12-27 18:50:45 +03:00
for ( size_t i_in_outs = 0 ; i_in_outs ! = outs . size ( ) ; i_in_outs + + )
{
2022-09-12 18:47:38 +02:00
const wallet_out_info & out = outs [ i_in_outs ] ;
size_t o = out . index ;
2023-02-21 01:41:33 +01:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( o < tx . vout . size ( ) , " wrong out in transaction: internal index: " < < o < < " , tx.vout.size(): " < < tx . vout . size ( ) ) ;
2018-12-27 18:50:45 +03:00
{
2022-07-01 17:19:57 +02:00
const currency : : tx_out_v & out_v = tx . vout [ o ] ;
2023-02-21 01:41:33 +01:00
bool out_type_zc = out_is_zc ( out_v ) ;
bool out_type_to_key = out_is_to_key ( out_v ) ;
bool out_type_htlc = out_is_to_htlc ( out_v ) ;
bool out_type_multisig = out_is_multisig ( out_v ) ;
2022-06-30 19:02:22 +02:00
2023-02-21 01:41:33 +01:00
if ( out_type_zc | | out_type_to_key | | out_type_htlc )
2021-01-24 21:00:43 +01:00
{
2023-02-21 01:41:33 +01:00
crypto : : public_key out_key = out_get_pub_key ( out_v , htlc_info_list ) ; // htlc_info_list contains information about which one, redeem or refund key is ours for an htlc output
2018-12-27 18:50:45 +03:00
2022-05-19 22:46:57 +02:00
// obtain key image for this output
crypto : : key_image ki = currency : : null_ki ;
if ( m_watch_only )
2019-04-08 14:16:11 +03:00
{
2022-05-19 22:46:57 +02:00
if ( ! is_auditable ( ) )
2020-05-25 15:05:25 +03:00
{
2022-05-19 22:46:57 +02:00
// don't have spend secret key, so we unable to calculate key image for an output
// look it up in special container instead
auto it = m_pending_key_images . find ( out_key ) ;
if ( it ! = m_pending_key_images . end ( ) )
{
ki = it - > second ;
WLT_LOG_L1 ( " pending key image " < < ki < < " was found by out pub key " < < out_key ) ;
}
else
{
ki = currency : : null_ki ;
WLT_LOG_L1 ( " can't find pending key image by out pub key: " < < out_key < < " , key image temporarily set to null " ) ;
}
2020-05-25 15:05:25 +03:00
}
2022-05-19 22:46:57 +02:00
}
else
{
// normal wallet, calculate and store key images for own outs
currency : : keypair in_ephemeral = AUTO_VAL_INIT ( in_ephemeral ) ;
currency : : generate_key_image_helper ( m_account . get_keys ( ) , tx_pub_key , o , in_ephemeral , ki ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( in_ephemeral . pub = = out_key , " key_image generated ephemeral public key that does not match with output_key " ) ;
}
if ( ki ! = currency : : null_ki )
{
// make sure calculated key image for this own output has not been seen before
auto it = m_key_images . find ( ki ) ;
if ( it ! = m_key_images . end ( ) )
2020-05-25 15:05:25 +03:00
{
2022-05-19 22:46:57 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( it - > second < m_transfers . size ( ) , " m_key_images entry has wrong m_transfers index, it->second: " < < it - > second < < " , m_transfers.size(): " < < m_transfers . size ( ) ) ;
const transfer_details & local_td = m_transfers [ it - > second ] ;
std : : stringstream ss ;
2023-06-06 23:05:53 +02:00
ss < < " tx " < < ptc . tx_hash ( ) < < " @ block " < < height < < " has output # " < < o < < " with key image " < < ki < < " that has already been seen in output # " < <
2022-05-19 22:46:57 +02:00
local_td . m_internal_output_index < < " in tx " < < get_transaction_hash ( local_td . m_ptx_wallet_info - > m_tx ) < < " @ block " < < local_td . m_spent_height < <
" . This output can't ever be spent and will be skipped. " ;
WLT_LOG_YELLOW ( ss . str ( ) , LOG_LEVEL_0 ) ;
if ( m_wcallback )
m_wcallback - > on_message ( i_wallet2_callback : : ms_yellow , ss . str ( ) ) ;
2023-06-06 23:05:53 +02:00
//if (out.is_native_coin())
//{
//WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(sum_of_native_outs >= out.amount, "sum_of_native_outs: " << sum_of_native_outs << ", out.amount:" << out.amount);
//sum_of_native_outs -= out.amount;
//}
2022-05-19 22:46:57 +02:00
continue ; // skip the output
2020-05-25 15:05:25 +03:00
}
2019-04-08 14:16:11 +03:00
}
2022-05-19 22:46:57 +02:00
2022-09-12 18:47:38 +02:00
if ( is_auditable ( ) & & ( out_type_to_key | | out_type_zc ) & &
2022-06-30 21:47:59 +02:00
out_get_mixin_attr ( out_v ) ! = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX )
2019-04-08 14:16:11 +03:00
{
2020-05-26 13:28:24 +03:00
std : : stringstream ss ;
2023-06-06 23:05:53 +02:00
ss < < " output # " < < o < < " from tx " < < ptc . tx_hash ( ) < < " with amount " < < print_money_brief ( outs [ i_in_outs ] . amount )
2023-02-21 01:41:33 +01:00
< < " is targeted to this auditable wallet and has INCORRECT mix_attr = " < < ( uint64_t ) out_get_mixin_attr ( out_v ) < < " . Output is IGNORED. " ;
2022-05-19 22:46:57 +02:00
WLT_LOG_RED ( ss . str ( ) , LOG_LEVEL_0 ) ;
2020-05-26 13:28:24 +03:00
if ( m_wcallback )
2022-05-19 22:46:57 +02:00
m_wcallback - > on_message ( i_wallet2_callback : : ms_red , ss . str ( ) ) ;
2023-06-06 23:05:53 +02:00
//if (out.is_native_coin())
//{
//WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(sum_of_native_outs >= out.amount, "sum_of_native_outs: " << sum_of_native_outs << ", out.amount:" << out.amount);
//sum_of_native_outs -= out.amount;
//}
2019-04-08 14:16:11 +03:00
continue ; // skip the output
}
2018-12-27 18:50:45 +03:00
2023-06-15 23:55:22 +02:00
ptc . employed_entries . receive . push_back ( wallet_public : : employed_tx_entry { o , out . amount , out . asset_id } ) ;
2020-05-26 13:28:24 +03:00
2022-05-19 22:46:57 +02:00
m_transfers . push_back ( boost : : value_initialized < transfer_details > ( ) ) ;
transfer_details & td = m_transfers . back ( ) ;
td . m_ptx_wallet_info = pwallet_info ;
td . m_internal_output_index = o ;
td . m_key_image = ki ;
2023-02-08 18:50:26 +01:00
td . m_amount = out . amount ;
2022-05-19 22:46:57 +02:00
if ( m_use_deffered_global_outputs )
2018-12-27 18:50:45 +03:00
{
2022-05-19 22:46:57 +02:00
if ( pglobal_indexes & & pglobal_indexes - > size ( ) > o )
td . m_global_output_index = ( * pglobal_indexes ) [ o ] ;
else
td . m_global_output_index = WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED ;
2018-12-27 18:50:45 +03:00
}
2022-05-19 22:46:57 +02:00
else
2021-01-31 18:05:26 +01:00
{
2022-05-19 22:46:57 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( pglobal_indexes , " pglobal_indexes IS NULL in non mobile wallet " ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( pglobal_indexes - > size ( ) > o , " pglobal_indexes size()( " < < pglobal_indexes - > size ( ) < < " ) <= o " < < o ) ;
td . m_global_output_index = ( * pglobal_indexes ) [ o ] ;
}
2022-06-28 22:27:36 +02:00
if ( ptc . coin_base_tx )
2021-03-26 00:43:50 +03:00
{
2022-05-19 22:46:57 +02:00
//last out in coinbase tx supposed to be change from coinstake
2023-02-21 01:41:33 +01:00
if ( ! ( o = = tx . vout . size ( ) - 1 & & ! ptc . is_derived_from_coinbase ) ) // TODO: @#@# reconsider this condition
2021-03-26 00:43:50 +03:00
{
2022-05-19 22:46:57 +02:00
td . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER ;
2021-03-26 00:43:50 +03:00
}
}
2022-09-12 18:47:38 +02:00
if ( out_type_zc )
2023-02-08 18:50:26 +01:00
{
td . m_zc_info_ptr . reset ( new transfer_details_base : : ZC_out_info ( out . amount_blinding_mask , out . asset_id_blinding_mask , out . asset_id ) ) ;
}
2022-09-12 18:47:38 +02:00
2022-05-19 22:46:57 +02:00
size_t transfer_index = m_transfers . size ( ) - 1 ;
2023-02-21 01:41:33 +01:00
if ( out_type_htlc )
2021-03-26 00:43:50 +03:00
{
2022-07-01 17:19:57 +02:00
const currency : : txout_htlc & hltc = out_get_htlc ( out_v ) ;
2022-05-19 22:46:57 +02:00
//mark this as spent
td . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ;
//create entry for htlc input
htlc_expiration_trigger het = AUTO_VAL_INIT ( het ) ;
het . is_wallet_owns_redeem = ( out_key = = hltc . pkey_redeem ) ? true : false ;
het . transfer_index = transfer_index ;
uint64_t expired_if_more_then = td . m_ptx_wallet_info - > m_block_height + hltc . expiration ;
m_htlcs . insert ( std : : make_pair ( expired_if_more_then , het ) ) ;
if ( het . is_wallet_owns_redeem )
{
td . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM ;
}
//active htlc
2022-06-30 21:47:59 +02:00
auto amount_gindex_pair = std : : make_pair ( td . m_amount , td . m_global_output_index ) ;
2022-05-19 22:46:57 +02:00
m_active_htlcs [ amount_gindex_pair ] = transfer_index ;
2023-06-06 23:05:53 +02:00
m_active_htlcs_txid [ ptc . tx_hash ( ) ] = transfer_index ;
2022-05-19 22:46:57 +02:00
//add payer to extra options
currency : : tx_payer payer = AUTO_VAL_INIT ( payer ) ;
if ( het . is_wallet_owns_redeem )
{
if ( currency : : get_type_in_variant_container ( tx . extra , payer ) )
{
crypto : : chacha_crypt ( payer . acc_addr , derivation ) ;
td . varian_options . push_back ( payer ) ;
}
}
else
{
//since this is refund-mode htlc out, then sender is this wallet itself
payer . acc_addr = m_account . get_public_address ( ) ;
td . varian_options . push_back ( payer ) ;
}
2021-03-26 00:43:50 +03:00
}
2023-06-06 23:05:53 +02:00
else
{
ptc . total_balance_change [ td . get_asset_id ( ) ] + = td . amount ( ) ;
add_transfer_to_transfers_cache ( td . m_amount , transfer_index , td . get_asset_id ( ) ) ;
}
2022-05-19 22:46:57 +02:00
if ( td . m_key_image ! = currency : : null_ki )
m_key_images [ td . m_key_image ] = transfer_index ;
2021-01-24 21:00:43 +01:00
2022-05-19 22:46:57 +02:00
if ( is_watch_only ( ) & & is_auditable ( ) )
{
WLT_CHECK_AND_ASSERT_MES_NO_RET ( td . m_global_output_index ! = WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED , " td.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED validation failed " ) ;
2023-02-21 01:41:33 +01:00
auto amount_gindex_pair = std : : make_pair ( td . amount_for_global_output_index ( ) , td . m_global_output_index ) ;
WLT_CHECK_AND_ASSERT_MES_NO_RET ( m_amount_gindex_to_transfer_id . count ( amount_gindex_pair ) = = 0 , " update m_amount_gindex_to_transfer_id: amount_for_global_output_index: " < < td . amount_for_global_output_index ( ) < < " , gindex: " < < td . m_global_output_index < < " already exists " ) ;
2022-05-19 22:46:57 +02:00
m_amount_gindex_to_transfer_id [ amount_gindex_pair ] = transfer_index ;
}
2019-07-24 19:14:35 +02:00
2022-05-19 22:46:57 +02:00
if ( max_out_unlock_time < get_tx_unlock_time ( tx , o ) )
max_out_unlock_time = get_tx_unlock_time ( tx , o ) ;
2019-07-24 19:14:35 +02:00
2022-09-12 18:47:38 +02:00
if ( out_type_to_key | | out_type_zc )
2022-05-19 22:46:57 +02:00
{
2023-02-21 01:41:33 +01:00
if ( td . is_native_coin ( ) )
{
2023-06-06 23:05:53 +02:00
WLT_LOG_L0 ( " Received native coins, transfer # " < < transfer_index < < " , amount: " < < print_money_brief ( td . amount ( ) ) < < ( out_type_zc ? " (hidden) " : " " ) < < " , with tx: " < < ptc . tx_hash ( ) < < " , at height " < < height ) ;
2023-02-21 01:41:33 +01:00
}
else
{
// TODO @#@# output asset's ticker/name
2023-06-06 23:05:53 +02:00
WLT_LOG_L0 ( " Received asset " < < print16 ( td . get_asset_id ( ) ) < < " , transfer # " < < transfer_index < < " , amount: " < < print_money_brief ( td . amount ( ) ) < < ( out_type_zc ? " (hidden) " : " " ) < < " , with tx: " < < ptc . tx_hash ( ) < < " , at height " < < height ) ;
2023-02-21 01:41:33 +01:00
}
2022-05-19 22:46:57 +02:00
}
2023-02-21 01:41:33 +01:00
else if ( out_type_htlc )
2022-05-19 22:46:57 +02:00
{
2023-06-06 23:05:53 +02:00
WLT_LOG_L0 ( " Detected HTLC[ " < < ( td . m_flags & WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM ? " REDEEM " : " REFUND " ) < < " ], transfer # " < < transfer_index < < " , amount: " < < print_money ( td . amount ( ) ) < < " , with tx: " < < ptc . tx_hash ( ) < < " , at height " < < height ) ;
2022-05-19 22:46:57 +02:00
}
2021-02-06 23:46:53 +01:00
}
2023-02-21 01:41:33 +01:00
else if ( out_type_multisig )
2021-02-06 23:46:53 +01:00
{
2022-05-19 22:46:57 +02:00
crypto : : hash multisig_id = currency : : get_multisig_out_id ( tx , o ) ;
WLT_CHECK_AND_ASSERT_MES_NO_RET ( m_multisig_transfers . count ( multisig_id ) = = 0 , " multisig_id = " < < multisig_id < < " already in multisig container " ) ;
transfer_details_base & tdb = m_multisig_transfers [ multisig_id ] ;
tdb . m_ptx_wallet_info = pwallet_info ;
tdb . m_internal_output_index = o ;
2022-08-08 13:35:38 +02:00
tdb . m_amount = outs [ i_in_outs ] . amount ;
2023-06-06 23:05:53 +02:00
WLT_LOG_L0 ( " Received multisig, multisig out id: " < < multisig_id < < " , amount: " < < tdb . amount ( ) < < " , with tx: " < < ptc . tx_hash ( ) ) ;
2021-02-06 23:46:53 +01:00
}
2023-02-21 01:41:33 +01:00
else
{
2023-06-06 23:05:53 +02:00
WLT_LOG_YELLOW ( " Unexpected output type: " < < out_v . type ( ) . name ( ) < < " , out index: " < < o < < " in tx " < < ptc . tx_hash ( ) , LOG_LEVEL_0 ) ;
2023-02-21 01:41:33 +01:00
}
2018-12-27 18:50:45 +03:00
}
2022-06-30 19:02:22 +02:00
2018-12-27 18:50:45 +03:00
}
}
2023-06-06 23:05:53 +02:00
//do final calculations
bool has_in_transfers = false ;
bool has_out_transfers = false ;
for ( const auto & bce : ptc . total_balance_change )
{
if ( bce . second > 0 )
{
has_in_transfers = true ;
}
else if ( bce . second < 0 )
{
has_out_transfers = true ;
}
}
2018-12-27 18:50:45 +03:00
std : : string payment_id ;
2023-06-06 23:05:53 +02:00
if ( has_in_transfers & & get_payment_id_from_tx ( tx . attachment , payment_id ) & & payment_id . size ( ) )
2018-12-27 18:50:45 +03:00
{
2023-06-06 23:05:53 +02:00
payment_details payment ;
payment . m_tx_hash = ptc . tx_hash ( ) ;
payment . m_amount = 0 ;
payment . m_block_height = height ;
payment . m_unlock_time = max_out_unlock_time ;
2023-02-21 01:41:33 +01:00
2023-06-06 23:05:53 +02:00
for ( const auto & bce : ptc . total_balance_change )
2018-12-27 18:50:45 +03:00
{
2023-06-06 23:05:53 +02:00
if ( bce . second > 0 )
{
if ( bce . first = = currency : : native_coin_asset_id )
{
payment . m_amount = static_cast < uint64_t > ( bce . second ) ;
} else
{
payment . subtransfers . push_back ( payment_details_subtransfer { bce . first , static_cast < uint64_t > ( bce . second ) } ) ;
}
}
2018-12-27 18:50:45 +03:00
}
2023-06-06 23:05:53 +02:00
m_payments . emplace ( payment_id , payment ) ;
WLT_LOG_L2 ( " Payment found, id (hex): " < < epee : : string_tools : : buff_to_hex_nodelimer ( payment_id ) < < " , tx: " < < payment . m_tx_hash < < " , amount: " < < print_money_brief ( payment . m_amount ) < < " subtransfers = " < < payment . subtransfers . size ( ) ) ;
2018-12-27 18:50:45 +03:00
}
2023-06-06 23:05:53 +02:00
if ( ptc . spent_own_native_inputs )
2022-09-27 20:13:49 +02:00
{
//check if there are asset_registration that belong to this wallet
asset_descriptor_operation ado = AUTO_VAL_INIT ( ado ) ;
if ( get_type_in_variant_container ( tx . extra , ado ) )
{
2023-08-14 22:32:52 +02:00
process_ado_in_new_transaction ( ado , height ) ;
2022-09-27 20:13:49 +02:00
}
}
2018-12-27 18:50:45 +03:00
2023-06-20 21:59:02 +02:00
if ( has_in_transfers | | has_out_transfers | | is_derivation_used_to_encrypt ( tx , derivation ) | | ptc . employed_entries . spent . size ( ) )
2023-06-06 23:05:53 +02:00
{
ptc . timestamp = get_block_datetime ( b ) ;
handle_money ( b , ptc ) ;
}
/*
2023-02-21 01:41:33 +01:00
if ( ptc . sum_of_own_native_inputs )
2018-12-27 18:50:45 +03:00
{ //this actually is transfer transaction, notify about spend
2023-02-21 01:41:33 +01:00
if ( ptc . sum_of_own_native_inputs > sum_of_native_outs )
2018-12-27 18:50:45 +03:00
{ //usual transfer
2023-02-21 01:41:33 +01:00
handle_money_spent2 ( b , tx , ptc . sum_of_own_native_inputs - ( sum_of_native_outs + get_tx_fee ( tx ) ) , ptc . mtd , recipients , remote_aliases ) ;
2018-12-27 18:50:45 +03:00
}
else
{ //strange transfer, seems that in one transaction have transfers from different wallets.
if ( ! is_coinbase ( tx ) )
{
2023-06-06 23:05:53 +02:00
WLT_LOG_RED ( " Unusual transaction " < < ptc . tx_hash ( ) < < " , sum_of_native_inputs: " < < ptc . sum_of_own_native_inputs < < " , sum_of_native_outs: " < < sum_of_native_outs , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
2023-02-21 01:41:33 +01:00
handle_money_received2 ( b , tx , ( sum_of_native_outs - ( ptc . sum_of_own_native_inputs - get_tx_fee ( tx ) ) ) , ptc . mtd ) ;
2018-12-27 18:50:45 +03:00
}
}
else
{
2023-02-21 01:41:33 +01:00
if ( sum_of_native_outs ! = 0 )
{
handle_money_received2 ( b , tx , sum_of_native_outs , ptc . mtd ) ;
}
2018-12-27 18:50:45 +03:00
else if ( currency : : is_derivation_used_to_encrypt ( tx , derivation ) )
{
2023-06-06 23:05:53 +02:00
//transaction doesn't transfer actually money, but bring some information
2022-06-28 22:27:36 +02:00
handle_money_received2 ( b , tx , 0 , ptc . mtd ) ;
2018-12-27 18:50:45 +03:00
}
2022-06-28 22:27:36 +02:00
else if ( ptc . mtd . spent_indices . size ( ) )
2018-12-27 18:50:45 +03:00
{
// multisig spend detected
2022-06-28 22:27:36 +02:00
handle_money_spent2 ( b , tx , 0 , ptc . mtd , recipients , remote_aliases ) ;
2018-12-27 18:50:45 +03:00
}
2023-06-06 23:05:53 +02:00
} */
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : prepare_wti_decrypted_attachments ( wallet_public : : wallet_transfer_info & wti , const std : : vector < currency : : payload_items_v > & decrypted_att )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::prepare_wti_decrypted_attachments " ) ;
2020-06-12 16:36:38 +03:00
if ( ! wti . payment_id . empty ( ) )
{
LOG_ERROR ( " wti.payment_id is expected to be empty. Go ahead. " ) ;
}
get_payment_id_from_tx ( decrypted_att , wti . payment_id ) ;
2021-07-21 00:25:12 +02:00
for ( const auto & item : decrypted_att )
{
if ( item . type ( ) = = typeid ( currency : : tx_service_attachment ) )
{
wti . service_entries . push_back ( boost : : get < currency : : tx_service_attachment > ( item ) ) ;
}
}
2023-06-06 23:05:53 +02:00
if ( wti . is_income_mode_encryption ( ) )
2018-12-27 18:50:45 +03:00
{
2020-04-28 15:58:25 +03:00
account_public_address sender_address = AUTO_VAL_INIT ( sender_address ) ;
2023-06-06 23:05:53 +02:00
wti . show_sender = handle_2_alternative_types_in_variant_container < tx_payer , tx_payer_old > ( decrypted_att , [ & ] ( const tx_payer & p ) { sender_address = p . acc_addr ; return false ; /* <- continue? */ } ) ;
2020-04-28 15:58:25 +03:00
if ( wti . show_sender )
2023-06-20 21:59:02 +02:00
if ( ! wti . remote_addresses . size ( ) )
wti . remote_addresses . push_back ( currency : : get_account_address_as_str ( sender_address ) ) ;
2018-12-27 18:50:45 +03:00
}
else
{
2020-04-29 21:06:08 +03:00
if ( wti . remote_addresses . empty ( ) )
{
handle_2_alternative_types_in_variant_container < tx_receiver , tx_receiver_old > ( decrypted_att , [ & ] ( const tx_receiver & p ) {
2020-06-11 22:20:00 +03:00
std : : string addr_str ;
if ( wti . payment_id . empty ( ) )
addr_str = currency : : get_account_address_as_str ( p . acc_addr ) ;
else
addr_str = currency : : get_account_address_and_payment_id_as_str ( p . acc_addr , wti . payment_id ) ; // show integrated address if there's a payment id provided
2020-04-29 21:06:08 +03:00
wti . remote_addresses . push_back ( addr_str ) ;
2023-06-06 23:05:53 +02:00
LOG_PRINT_YELLOW ( " prepare_wti_decrypted_attachments, income=false, rem. addr = " < < addr_str , LOG_LEVEL_0 ) ;
2020-04-29 21:06:08 +03:00
return true ; // continue iterating through the container
} ) ;
}
2018-12-27 18:50:45 +03:00
}
currency : : tx_comment cm ;
if ( currency : : get_type_in_variant_container ( decrypted_att , cm ) )
wti . comment = cm . comment ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : resend_unconfirmed ( )
{
COMMAND_RPC_FORCE_RELAY_RAW_TXS : : request req = AUTO_VAL_INIT ( req ) ;
COMMAND_RPC_FORCE_RELAY_RAW_TXS : : response res = AUTO_VAL_INIT ( res ) ;
for ( auto & ut : m_unconfirmed_txs )
{
req . txs_as_hex . push_back ( epee : : string_tools : : buff_to_hex_nodelimer ( tx_to_blob ( ut . second . tx ) ) ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Relaying tx: " < < ut . second . tx_hash , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
if ( ! req . txs_as_hex . size ( ) )
return ;
bool r = m_core_proxy - > call_COMMAND_RPC_FORCE_RELAY_RAW_TXS ( req , res ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( r , void ( ) , " wrong result at call_COMMAND_RPC_FORCE_RELAY_RAW_TXS " ) ;
2020-05-07 23:26:41 +02:00
WLT_CHECK_AND_ASSERT_MES ( res . status = = API_RETURN_CODE_OK , void ( ) , " wrong result at call_COMMAND_RPC_FORCE_RELAY_RAW_TXS: status != OK, status= " < < res . status ) ;
2018-12-27 18:50:45 +03:00
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Relayed " < < req . txs_as_hex . size ( ) < < " txs " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
//-----------------------------------------------------------------------------------------------------
void wallet2 : : accept_proposal ( const crypto : : hash & contract_id , uint64_t b_acceptance_fee , currency : : transaction * p_acceptance_tx /* = nullptr */ )
{
auto contr_it = m_contracts . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it ! = m_contracts . end ( ) , " Unknow contract id: " < < contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! contr_it - > second . is_a , " contr_it->second.is_a supposed to be false, but it is " < < contr_it - > second . is_a ) ;
2019-08-27 17:36:53 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it - > second . state = = tools : : wallet_public : : escrow_contract_details_basic : : proposal_sent , " contr_it->second.state supposed to be proposal_sent( " < < tools : : wallet_public : : escrow_contract_details_basic : : proposal_sent < < " ) but it is: " < < tools : : wallet_public : : get_escrow_contract_state_name ( contr_it - > second . state ) ) ;
2018-12-27 18:50:45 +03:00
construct_tx_param construct_param = AUTO_VAL_INIT ( construct_param ) ;
construct_param . fee = b_acceptance_fee ;
2023-03-08 21:19:59 +01:00
mode_separate_context msc = AUTO_VAL_INIT ( msc ) ;
2023-04-28 20:07:35 +02:00
msc . escrow = true ;
2023-03-08 21:19:59 +01:00
msc . tx_for_mode_separate = contr_it - > second . proposal . tx_template ;
currency : : transaction & tx = msc . tx_for_mode_separate ;
2019-04-03 12:48:09 +03:00
crypto : : secret_key one_time_key = contr_it - > second . proposal . tx_onetime_secret_key ;
2018-12-27 18:50:45 +03:00
construct_param . crypt_address = m_account . get_public_address ( ) ;
construct_param . flags = TX_FLAG_SIGNATURE_MODE_SEPARATE ;
construct_param . mark_tx_as_complete = true ;
2022-09-20 22:01:52 +02:00
construct_param . split_strategy_id = get_current_split_strategy ( ) ;
2018-12-27 18:50:45 +03:00
//little hack for now, we add multisig_entry before transaction actually get to blockchain
//to let prepare_transaction (which is called from build_escrow_release_templates) work correct
//this code definitely need to be rewritten later (very bad design)
2019-04-03 12:48:09 +03:00
size_t n = get_multisig_out_index ( tx . vout ) ;
THROW_IF_FALSE_WALLET_EX ( n ! = tx . vout . size ( ) , error : : wallet_internal_error , " Multisig out not found in tx template in proposal " ) ;
2018-12-27 18:50:45 +03:00
transfer_details_base & tdb = m_multisig_transfers [ contract_id ] ;
//create once instance of tx for all entries
std : : shared_ptr < transaction_wallet_info > pwallet_info ( new transaction_wallet_info ( ) ) ;
2022-09-20 22:01:52 +02:00
pwallet_info - > m_tx = tx ;
2018-12-27 18:50:45 +03:00
pwallet_info - > m_block_height = 0 ;
pwallet_info - > m_block_timestamp = 0 ;
tdb . m_ptx_wallet_info = pwallet_info ;
tdb . m_internal_output_index = n ;
tdb . m_flags & = ~ ( WALLET_TRANSFER_DETAIL_FLAG_SPENT ) ;
//---------------------------------
2022-06-30 21:47:59 +02:00
//@#@ todo: proper handling with zarcanum_based stuff
2018-12-27 18:50:45 +03:00
//figure out fee that was left for release contract
2022-05-20 21:32:27 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( tx . vout [ n ] . type ( ) = = typeid ( tx_out_bare ) , " Unexpected output type in accept proposal " ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( boost : : get < tx_out_bare > ( tx . vout [ n ] ) . amount > ( contr_it - > second . private_detailes . amount_to_pay +
2018-12-27 18:50:45 +03:00
contr_it - > second . private_detailes . amount_b_pledge +
contr_it - > second . private_detailes . amount_a_pledge ) , " THere is no left money for fee, contract_id: " < < contract_id ) ;
2022-05-20 21:32:27 +02:00
uint64_t left_for_fee_in_multisig = boost : : get < tx_out_bare > ( tx . vout [ n ] ) . amount - ( contr_it - > second . private_detailes . amount_to_pay +
2018-12-27 18:50:45 +03:00
contr_it - > second . private_detailes . amount_b_pledge +
contr_it - > second . private_detailes . amount_a_pledge ) ;
//prepare templates to let buyer release or burn escrow
bc_services : : escrow_relese_templates_body artb = AUTO_VAL_INIT ( artb ) ;
build_escrow_release_templates ( contract_id ,
left_for_fee_in_multisig ,
artb . tx_normal_template ,
artb . tx_burn_template ,
contr_it - > second . private_detailes ) ;
//---second part of bad design ---
auto it = m_multisig_transfers . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_EX ( it ! = m_multisig_transfers . end ( ) , error : : wallet_internal_error , " Internal error " ) ;
m_multisig_transfers . erase ( it ) ;
//---------------------------------
tx_service_attachment tsa = AUTO_VAL_INIT ( tsa ) ;
bc_services : : pack_attachment_as_gzipped_bin ( artb , tsa ) ;
tsa . service_id = BC_ESCROW_SERVICE_ID ;
tsa . instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_TEMPLATES ;
tsa . flags | = TX_SERVICE_ATTACHMENT_ENCRYPT_BODY ;
construct_param . extra . push_back ( tsa ) ;
//build transaction
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2023-03-08 21:19:59 +01:00
prepare_transaction ( construct_param , ftp , msc ) ;
2019-04-03 12:48:09 +03:00
mark_transfers_as_spent ( ftp . selected_transfers , std : : string ( " contract < " ) + epee : : string_tools : : pod_to_hex ( contract_id ) + " > has been accepted with tx < " + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( tx ) ) + " > " ) ;
2019-11-07 14:44:22 +03:00
try
{
finalize_transaction ( ftp , tx , one_time_key , true ) ;
}
catch ( . . . )
{
clear_transfers_from_flag ( ftp . selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_SPENT , std : : string ( " exception in finalize_transaction, tx: " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( tx ) ) ) ;
throw ;
}
2019-04-03 12:48:09 +03:00
print_tx_sent_message ( tx , " (contract < " + epee : : string_tools : : pod_to_hex ( contract_id ) + " >) " , construct_param . fee ) ;
2018-12-27 18:50:45 +03:00
if ( p_acceptance_tx ! = nullptr )
2019-04-03 12:48:09 +03:00
* p_acceptance_tx = tx ;
2018-12-27 18:50:45 +03:00
}
2022-05-10 23:49:20 +02:00
//---------------------------------------------------------------------------------
uint64_t wallet2 : : get_current_tx_version ( )
{
return currency : : get_tx_version ( this - > get_top_block_height ( ) , this - > m_core_runtime_config . hard_forks ) ;
}
//---------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : finish_contract ( const crypto : : hash & contract_id , const std : : string & release_type , currency : : transaction * p_release_tx /* = nullptr */ )
{
auto contr_it = m_contracts . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it ! = m_contracts . end ( ) , " Unknow contract id: " < < contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it - > second . is_a , " contr_it->second.is_a is supposed to be true, but it is " < < contr_it - > second . is_a ) ;
2019-08-27 17:36:53 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it - > second . state = = tools : : wallet_public : : escrow_contract_details_basic : : contract_accepted
| | contr_it - > second . state = = tools : : wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent ,
" incorrect contract state at finish_contract(): ( " < < contr_it - > second . state < < " ), expected states: contract_accepted ( " < < tools : : wallet_public : : escrow_contract_details_basic : : contract_accepted < < " ), " < <
" contract_cancel_proposal_sent ( " < < tools : : wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent < < " ) " ) ;
2018-12-27 18:50:45 +03:00
auto multisig_it = m_multisig_transfers . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( multisig_it ! = m_multisig_transfers . end ( ) , " Unknow multisig id: " < < contract_id ) ;
transaction tx = AUTO_VAL_INIT ( tx ) ;
if ( release_type = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL )
{
tx = contr_it - > second . release_body . tx_normal_template ;
}
else if ( release_type = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_BURN )
{
tx = contr_it - > second . release_body . tx_burn_template ;
}
else
{
THROW_IF_FALSE_WALLET_INT_ERR_EX ( false , " Unknow release_type = " < < release_type ) ;
}
bool is_input_fully_signed = false ;
bool r = sign_multisig_input_in_tx ( tx , 0 , m_account . get_keys ( ) , multisig_it - > second . m_ptx_wallet_info - > m_tx , & is_input_fully_signed ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " sign_multisig_input_in_tx failed " ) ;
// There's a two-party multisig input in tx and B-party had to already sign it upon template creation, so adding A-party sign here should make the tx fully signed. Make sure it does:
THROW_IF_FALSE_WALLET_INT_ERR_EX ( is_input_fully_signed , " sign_multisig_input_in_tx returned is_input_fully_signed == false " ) ;
send_transaction_to_network ( tx ) ;
if ( p_release_tx ! = nullptr )
* p_release_tx = tx ;
}
//-----------------------------------------------------------------------------------------------------
void wallet2 : : accept_cancel_contract ( const crypto : : hash & contract_id , currency : : transaction * p_cancellation_acceptance_tx /* = nullptr */ )
{
TIME_MEASURE_START_MS ( timing1 ) ;
auto contr_it = m_contracts . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it ! = m_contracts . end ( ) , " Unknow contract id " ) ;
TIME_MEASURE_FINISH_MS ( timing1 ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! contr_it - > second . is_a , " contr_it->second.is_a is supposed to be false, but it is " < < contr_it - > second . is_a ) ;
2019-08-27 17:36:53 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it - > second . state = = tools : : wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent ,
" incorrect contract state: ( " < < contr_it - > second . state < < " ), expected state: contract_cancel_proposal_sent ( " < < tools : : wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent < < " ) " ) ;
2018-12-27 18:50:45 +03:00
TIME_MEASURE_START_MS ( timing2 ) ;
auto multisig_it = m_multisig_transfers . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( multisig_it ! = m_multisig_transfers . end ( ) , " Can't find multisig transfer by id: " < < contract_id ) ;
TIME_MEASURE_FINISH_MS ( timing2 ) ;
transaction tx = contr_it - > second . cancel_body . tx_cancel_template ;
TIME_MEASURE_START_MS ( timing3 ) ;
bool is_input_fully_signed = false ;
bool r = sign_multisig_input_in_tx ( tx , 0 , m_account . get_keys ( ) , multisig_it - > second . m_ptx_wallet_info - > m_tx , & is_input_fully_signed ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " sign_multisig_input_in_tx failed " ) ;
// There's a two-party multisig input in tx and A-party had to already sign it upon template creation, so adding B-party sign here should make the tx fully signed. Make sure it does:
THROW_IF_FALSE_WALLET_INT_ERR_EX ( is_input_fully_signed , " sign_multisig_input_in_tx returned is_input_fully_signed == false " ) ;
send_transaction_to_network ( tx ) ;
TIME_MEASURE_FINISH_MS ( timing3 ) ;
if ( timing1 + timing2 + timing1 > 500 )
2019-02-21 21:33:52 +03:00
WLT_LOG_RED ( " [wallet2::accept_cancel_contract] LOW PERFORMANCE: " < < timing1 < < " , " < < timing2 < < " , " < < timing1 , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
if ( p_cancellation_acceptance_tx ! = nullptr )
* p_cancellation_acceptance_tx = tx ;
}
//-----------------------------------------------------------------------------------------------------
void wallet2 : : request_cancel_contract ( const crypto : : hash & contract_id , uint64_t fee , uint64_t expiration_period , currency : : transaction * p_cancellation_proposal_tx /* = nullptr */ )
{
auto contr_it = m_contracts . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it ! = m_contracts . end ( ) , " Unknow contract id: " < < contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it - > second . is_a , " contr_it->second.is_a supposed to be true at request_cancel_contract " ) ;
2019-08-27 17:36:53 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( contr_it - > second . state = = tools : : wallet_public : : escrow_contract_details_basic : : contract_accepted
| | contr_it - > second . state = = tools : : wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent ,
" incorrect contract state at request_cancel_contract(): " < < tools : : wallet_public : : get_escrow_contract_state_name ( contr_it - > second . state ) < < " , expected states: contract_accepted ( " < < tools : : wallet_public : : escrow_contract_details_basic : : contract_accepted < < " ), " < <
" contract_cancel_proposal_sent ( " < < tools : : wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent < < " ) " ) ;
2018-12-27 18:50:45 +03:00
auto multisig_it = m_multisig_transfers . find ( contract_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( multisig_it ! = m_multisig_transfers . end ( ) , " Unknow multisig id: " < < contract_id ) ;
//////////////////////////////////////////////////////////////////////////
construct_tx_param construct_param = AUTO_VAL_INIT ( construct_param ) ;
construct_param . fee = fee ;
//-------
//prepare templates to let seller cancel escrow
bc_services : : escrow_cancel_templates_body ectb = AUTO_VAL_INIT ( ectb ) ;
build_escrow_cancel_template ( contract_id ,
expiration_period ,
ectb . tx_cancel_template ,
contr_it - > second . private_detailes ) ;
tx_service_attachment tsa = AUTO_VAL_INIT ( tsa ) ;
bc_services : : pack_attachment_as_gzipped_bin ( ectb , tsa ) ;
tsa . service_id = BC_ESCROW_SERVICE_ID ;
tsa . instruction = BC_ESCROW_SERVICE_INSTRUCTION_CANCEL_PROPOSAL ;
tsa . flags | = TX_SERVICE_ATTACHMENT_ENCRYPT_BODY ;
construct_param . extra . push_back ( tsa ) ;
construct_param . crypt_address = contr_it - > second . private_detailes . b_addr ;
2022-09-20 22:01:52 +02:00
construct_param . split_strategy_id = get_current_split_strategy ( ) ;
2018-12-27 18:50:45 +03:00
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2019-04-03 12:48:09 +03:00
prepare_transaction ( construct_param , ftp ) ;
currency : : transaction tx = AUTO_VAL_INIT ( tx ) ;
2019-04-12 16:38:36 +03:00
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
2019-04-03 12:48:09 +03:00
mark_transfers_as_spent ( ftp . selected_transfers , std : : string ( " contract < " ) + epee : : string_tools : : pod_to_hex ( contract_id ) + " > has been requested for cancellaton with tx < " + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( tx ) ) + " > " ) ;
2019-11-07 14:44:22 +03:00
try
{
finalize_transaction ( ftp , tx , sk , true ) ;
}
catch ( . . . )
{
clear_transfers_from_flag ( ftp . selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_SPENT , std : : string ( " exception in finalize_transaction, tx: " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( tx ) ) ) ;
throw ;
}
2018-12-27 18:50:45 +03:00
2019-04-03 12:48:09 +03:00
print_tx_sent_message ( tx , " (transport for cancel proposal) " , fee ) ;
2018-12-27 18:50:45 +03:00
if ( p_cancellation_proposal_tx ! = nullptr )
2019-04-03 12:48:09 +03:00
* p_cancellation_proposal_tx = tx ;
2018-12-27 18:50:45 +03:00
}
//-----------------------------------------------------------------------------------------------------
void wallet2 : : scan_tx_to_key_inputs ( std : : vector < uint64_t > & found_transfers , const currency : : transaction & tx )
{
for ( auto & in : tx . vin )
{
if ( in . type ( ) = = typeid ( currency : : txin_to_key ) )
{
auto it = m_key_images . find ( boost : : get < currency : : txin_to_key > ( in ) . k_image ) ;
if ( it ! = m_key_images . end ( ) )
found_transfers . push_back ( it - > second ) ;
}
}
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : change_contract_state ( wallet_public : : escrow_contract_details_basic & contract , uint32_t new_state , const crypto : : hash & contract_id , const wallet_public : : wallet_transfer_info & wti ) const
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_YELLOW ( " escrow contract STATE CHANGE ( " < < ( contract . is_a ? " A, " : " B, " ) < < contract_id < < " via tx " < < get_transaction_hash ( wti . tx ) < < " , height: " < < wti . height < < " ) : "
2019-08-27 17:36:53 +02:00
< < wallet_public : : get_escrow_contract_state_name ( contract . state ) < < " -> " < < wallet_public : : get_escrow_contract_state_name ( new_state ) , LOG_LEVEL_1 ) ;
2018-12-27 18:50:45 +03:00
contract . state = new_state ;
contract . height = wti . height ; // update height of last state change
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : change_contract_state ( wallet_public : : escrow_contract_details_basic & contract , uint32_t new_state , const crypto : : hash & contract_id , const std : : string & reason /*= "internal intention"*/ ) const
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_YELLOW ( " escrow contract STATE CHANGE ( " < < ( contract . is_a ? " A, " : " B, " ) < < contract_id < < " " < < reason < < " ) : "
2019-08-27 17:36:53 +02:00
< < wallet_public : : get_escrow_contract_state_name ( contract . state ) < < " -> " < < wallet_public : : get_escrow_contract_state_name ( new_state ) , LOG_LEVEL_1 ) ;
2018-12-27 18:50:45 +03:00
contract . state = new_state ;
}
//-----------------------------------------------------------------------------------------------------
2023-06-16 20:19:49 +02:00
void from_outs_to_received_items ( const std : : vector < currency : : wallet_out_info > & outs , std : : vector < tools : : wallet2 : : payment_details_subtransfer > & received , const currency : : transaction & tx )
{
for ( const auto & item : outs )
{
if ( ! out_is_multisig ( tx . vout [ item . index ] ) )
received . push_back ( tools : : wallet2 : : payment_details_subtransfer { item . asset_id , item . amount } ) ;
}
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
bool wallet2 : : handle_proposal ( wallet_public : : wallet_transfer_info & wti , const bc_services : : proposal_body & prop )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::handle_proposal " ) ;
crypto : : hash ms_id = AUTO_VAL_INIT ( ms_id ) ;
bc_services : : contract_private_details cpd = AUTO_VAL_INIT ( cpd ) ;
std : : vector < payload_items_v > decrypted_items ;
if ( ! validate_escrow_proposal ( wti , prop , decrypted_items , ms_id , cpd ) )
return false ;
2019-08-27 17:36:53 +02:00
wallet_public : : escrow_contract_details_basic & ed = epee : : misc_utils : : get_or_insert_value_initialized ( m_contracts , ms_id ) ;
2018-12-27 18:50:45 +03:00
ed . expiration_time = currency : : get_tx_expiration_time ( prop . tx_template ) ;
ed . timestamp = wti . timestamp ;
2020-04-22 23:30:03 +03:00
ed . is_a = cpd . a_addr . spend_public_key = = m_account . get_keys ( ) . account_address . spend_public_key ;
2019-08-27 17:36:53 +02:00
change_contract_state ( ed , wallet_public : : escrow_contract_details_basic : : proposal_sent , ms_id , wti ) ;
2018-12-27 18:50:45 +03:00
ed . private_detailes = cpd ;
currency : : get_payment_id_from_tx ( decrypted_items , ed . payment_id ) ;
ed . proposal = prop ;
ed . height = wti . height ;
wti . contract . resize ( 1 ) ;
2019-08-27 17:36:53 +02:00
static_cast < wallet_public : : escrow_contract_details_basic & > ( wti . contract . back ( ) ) = ed ;
2018-12-27 18:50:45 +03:00
wti . contract . back ( ) . contract_id = ms_id ;
//correct fee in case if it "B", cz fee is paid by "A"
if ( ! ed . is_a )
wti . fee = 0 ;
else
{
//if it's A' then mark proposal template's inputs as spent and add to expiration list
std : : vector < uint64_t > found_transfers ;
scan_tx_to_key_inputs ( found_transfers , prop . tx_template ) ;
//scan outputs to figure out amount of change in escrow
crypto : : key_derivation derivation = AUTO_VAL_INIT ( derivation ) ;
2022-06-28 18:29:00 +02:00
std : : vector < wallet_out_info > outs ;
2023-06-06 23:05:53 +02:00
bool r = lookup_acc_outs ( m_account . get_keys ( ) , prop . tx_template , outs , derivation ) ;
2018-12-27 18:50:45 +03:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " Failed to lookup_acc_outs for tx: " < < get_transaction_hash ( prop . tx_template ) ) ;
2023-06-16 20:19:49 +02:00
std : : vector < payment_details_subtransfer > received ;
from_outs_to_received_items ( outs , received , prop . tx_template ) ;
add_transfers_to_expiration_list ( found_transfers , received , ed . expiration_time , wti . tx_hash ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Locked " < < found_transfers . size ( ) < < " transfers due to proposal " < < ms_id , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
return true ;
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
bool wallet2 : : handle_release_contract ( wallet_public : : wallet_transfer_info & wti , const std : : string & release_instruction )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::handle_release_contract " ) ;
size_t n = get_multisig_in_index ( wti . tx . vin ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( n ! = wti . tx . vin . size ( ) , false , " Multisig out not found in tx template in proposal " ) ;
2018-12-27 18:50:45 +03:00
crypto : : hash ms_id = boost : : get < txin_multisig > ( wti . tx . vin [ n ] ) . multisig_out_id ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( ms_id ! = null_hash , false , " Multisig out not found in tx template in proposal " ) ;
2018-12-27 18:50:45 +03:00
auto it = m_contracts . find ( ms_id ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( it ! = m_contracts . end ( ) , false , " Multisig out not found in tx template in proposal " ) ;
2018-12-27 18:50:45 +03:00
if ( release_instruction = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL )
2019-08-27 17:36:53 +02:00
change_contract_state ( it - > second , wallet_public : : escrow_contract_details_basic : : contract_released_normal , ms_id , wti ) ;
2018-12-27 18:50:45 +03:00
else if ( release_instruction = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_CANCEL )
2019-08-27 17:36:53 +02:00
change_contract_state ( it - > second , wallet_public : : escrow_contract_details_basic : : contract_released_cancelled , ms_id , wti ) ;
2018-12-27 18:50:45 +03:00
else if ( release_instruction = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_BURN )
{
2019-08-27 17:36:53 +02:00
change_contract_state ( it - > second , wallet_public : : escrow_contract_details_basic : : contract_released_burned , ms_id , wti ) ;
2023-06-19 00:19:25 +02:00
wallet_public : : wallet_sub_transfer_info * subptr = nullptr ;
for ( auto & s : wti . subtransfers )
{
if ( s . asset_id = = currency : : native_coin_asset_id )
subptr = & s ;
}
if ( subptr = = nullptr )
{
wti . subtransfers . push_back ( wallet_public : : wallet_sub_transfer_info ( ) ) ;
subptr = & wti . subtransfers . back ( ) ;
}
subptr - > amount = it - > second . private_detailes . amount_to_pay + it - > second . private_detailes . amount_a_pledge + it - > second . private_detailes . amount_b_pledge ;
2018-12-27 18:50:45 +03:00
if ( ! it - > second . is_a )
{
wti . fee = currency : : get_tx_fee ( wti . tx ) ;
}
else
{
wti . fee = 0 ;
}
}
else
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " wrong release_instruction: " < < release_instruction ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
wti . contract . resize ( 1 ) ;
2019-08-27 17:36:53 +02:00
static_cast < wallet_public : : escrow_contract_details_basic & > ( wti . contract . back ( ) ) = it - > second ;
2018-12-27 18:50:45 +03:00
wti . contract . back ( ) . contract_id = ms_id ;
//if it's A(buyer) then fee paid by B(seller)
if ( it - > second . is_a )
{
wti . fee = 0 ;
}
return true ;
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
bool wallet2 : : handle_contract ( wallet_public : : wallet_transfer_info & wti , const bc_services : : contract_private_details & cntr , const std : : vector < currency : : payload_items_v > & decrypted_attach )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::handle_contract " ) ;
bool is_a = cntr . a_addr = = m_account . get_public_address ( ) ;
crypto : : hash ms_id = AUTO_VAL_INIT ( ms_id ) ;
bc_services : : escrow_relese_templates_body rel = AUTO_VAL_INIT ( rel ) ;
if ( ! validate_escrow_contract ( wti , cntr , is_a , decrypted_attach , ms_id , rel ) )
return false ;
2019-08-27 17:36:53 +02:00
wallet_public : : escrow_contract_details_basic & ed = epee : : misc_utils : : get_or_insert_value_initialized ( m_contracts , ms_id ) ;
2018-12-27 18:50:45 +03:00
ed . is_a = is_a ;
ed . expiration_time = currency : : get_tx_expiration_time ( wti . tx ) ;
if ( wti . timestamp )
ed . timestamp = wti . timestamp ;
ed . height = wti . height ;
ed . payment_id = wti . payment_id ;
2019-08-27 17:36:53 +02:00
change_contract_state ( ed , wallet_public : : escrow_contract_details_basic : : contract_accepted , ms_id , wti ) ;
2018-12-27 18:50:45 +03:00
ed . private_detailes = cntr ;
ed . release_body = rel ;
wti . contract . resize ( 1 ) ;
2019-08-27 17:36:53 +02:00
static_cast < wallet_public : : escrow_contract_details_basic & > ( wti . contract . back ( ) ) = ed ;
2018-12-27 18:50:45 +03:00
wti . contract . back ( ) . contract_id = ms_id ;
//fee workaround: in consolidating transactions impossible no figure out which part of participants paid fee for tx, so we correct it
//in code which know escrow protocol, and we know that fee paid by B(seller)
if ( ed . is_a )
{
2023-06-06 23:05:53 +02:00
WLT_CHECK_AND_ASSERT_MES ( wti . subtransfers . size ( ) , false , " Unexpected subtransfers size " ) ; //TODO: subject for refactoring
wti . subtransfers . back ( ) . amount + = wti . fee ;
2018-12-27 18:50:45 +03:00
wti . fee = 0 ;
}
return true ;
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
bool wallet2 : : handle_cancel_proposal ( wallet_public : : wallet_transfer_info & wti , const bc_services : : escrow_cancel_templates_body & ectb , const std : : vector < currency : : payload_items_v > & decrypted_attach )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::handle_cancel_proposal " ) ;
//validate cancel proposal
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( ectb . tx_cancel_template . vin . size ( ) & & ectb . tx_cancel_template . vin [ 0 ] . type ( ) = = typeid ( currency : : txin_multisig ) , false , " Wrong cancel ecrow proposal " ) ;
2018-12-27 18:50:45 +03:00
crypto : : hash contract_id = boost : : get < currency : : txin_multisig > ( ectb . tx_cancel_template . vin [ 0 ] ) . multisig_out_id ;
auto it = m_contracts . find ( contract_id ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( it ! = m_contracts . end ( ) , false , " Multisig out not found in tx template in proposal " ) ;
2018-12-27 18:50:45 +03:00
bool r = validate_escrow_cancel_proposal ( wti , ectb , decrypted_attach , contract_id , it - > second . private_detailes , it - > second . proposal . tx_template ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " failed to validate escrow cancel request, contract id: " < < contract_id ) ;
2018-12-27 18:50:45 +03:00
uint32_t contract_state = it - > second . state ;
switch ( contract_state )
{
2019-08-27 17:36:53 +02:00
case wallet_public : : escrow_contract_details : : contract_accepted :
change_contract_state ( it - > second , wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent , contract_id , wti ) ; BOOST_FALLTHROUGH ;
2018-12-27 18:50:45 +03:00
// pass through
2019-08-27 17:36:53 +02:00
case wallet_public : : escrow_contract_details : : contract_cancel_proposal_sent : // update contract info even if already in that state
2018-12-27 18:50:45 +03:00
it - > second . cancel_body . tx_cancel_template = ectb . tx_cancel_template ;
it - > second . cancel_expiration_time = currency : : get_tx_expiration_time ( ectb . tx_cancel_template ) ;
//update wti info to let GUI know
wti . contract . resize ( 1 ) ;
2019-08-27 17:36:53 +02:00
static_cast < wallet_public : : escrow_contract_details_basic & > ( wti . contract . back ( ) ) = it - > second ;
2018-12-27 18:50:45 +03:00
wti . contract . back ( ) . contract_id = contract_id ;
return true ;
default :
2019-02-21 21:33:52 +03:00
WLT_LOG_RED ( " handle_cancel_proposal for contract ( " < < ( it - > second . is_a ? " A, " : " B, " ) < < contract_id < < " via tx " < < get_transaction_hash ( wti . tx ) < < " , height: " < < wti . height < < " ) : " < < ENDL < <
2019-08-27 17:36:53 +02:00
" incorrect state " < < wallet_public : : get_escrow_contract_state_name ( it - > second . state ) < < " , while 'contract_accepted' or 'contract_cancel_proposal_sent' was expected -- decline cancel proposal " , LOG_LEVEL_1 ) ;
2018-12-27 18:50:45 +03:00
}
return false ;
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
bool wallet2 : : process_contract_info ( wallet_public : : wallet_transfer_info & wti , const std : : vector < currency : : payload_items_v > & decrypted_attach )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::process_contract_info " ) ;
for ( const auto & v : decrypted_attach )
{
if ( v . type ( ) = = typeid ( tx_service_attachment ) )
{
const tx_service_attachment & sa = boost : : get < tx_service_attachment > ( v ) ;
if ( sa . service_id = = BC_ESCROW_SERVICE_ID )
{
if ( sa . instruction = = BC_ESCROW_SERVICE_INSTRUCTION_PROPOSAL )
{
bc_services : : proposal_body prop = AUTO_VAL_INIT ( prop ) ;
if ( ! t_unserializable_object_from_blob ( prop , sa . body ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Failed to unpack attachment for tx: " < < wti . tx_hash ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
handle_proposal ( wti , prop ) ;
}
else if ( sa . instruction = = BC_ESCROW_SERVICE_INSTRUCTION_PRIVATE_DETAILS )
{
//means some related contract appeared in blockchain
bc_services : : contract_private_details cntr = AUTO_VAL_INIT ( cntr ) ;
if ( ! epee : : serialization : : load_t_from_json ( cntr , sa . body ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Failed to unpack attachment for tx: " < < wti . tx_hash ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
handle_contract ( wti , cntr , decrypted_attach ) ;
}
else if (
sa . instruction = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL | |
sa . instruction = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_CANCEL | |
sa . instruction = = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_BURN
)
{
handle_release_contract ( wti , sa . instruction ) ;
}
else if ( sa . instruction = = BC_ESCROW_SERVICE_INSTRUCTION_CANCEL_PROPOSAL )
{
//means some related contract appeared in blockchain
bc_services : : escrow_cancel_templates_body cpb = AUTO_VAL_INIT ( cpb ) ;
if ( ! t_unserializable_object_from_blob ( cpb , sa . body ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Failed to unpack attachment for tx: " < < wti . tx_hash ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
handle_cancel_proposal ( wti , cpb , decrypted_attach ) ;
}
}
}
}
return true ;
}
//-----------------------------------------------------------------------------------------------------
2023-06-06 23:05:53 +02:00
void wallet2 : : prepare_wti ( wallet_public : : wallet_transfer_info & wti , const process_transaction_context & tx_process_context )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::prepare_wti " ) ;
2023-06-06 23:05:53 +02:00
wti . tx = tx_process_context . tx ;
2023-06-15 23:55:22 +02:00
load_wti_from_process_transaction_context ( wti , tx_process_context ) ;
2023-06-06 23:05:53 +02:00
wti . height = tx_process_context . height ;
2023-06-15 23:55:22 +02:00
wti . employed_entries = tx_process_context . employed_entries ;
wti . unlock_time = get_max_unlock_time_from_receive_indices ( tx_process_context . tx , tx_process_context . employed_entries ) ;
2023-06-06 23:05:53 +02:00
wti . timestamp = tx_process_context . timestamp ;
2018-12-27 18:50:45 +03:00
wti . tx_blob_size = static_cast < uint32_t > ( currency : : get_object_blobsize ( wti . tx ) ) ;
2023-06-06 23:05:53 +02:00
wti . tx_hash = tx_process_context . tx_hash ( ) ;
2021-02-08 21:31:46 +01:00
load_wallet_transfer_info_flags ( wti ) ;
2023-06-06 23:05:53 +02:00
bc_services : : extract_market_instructions ( wti . marketplace_entries , wti . tx . attachment ) ;
2018-12-27 18:50:45 +03:00
// escrow transactions, which are built with TX_FLAG_SIGNATURE_MODE_SEPARATE flag actually encrypt attachments
2023-06-15 23:55:22 +02:00
// with buyer as a sender, and seller as receiver, despite the fact that for both sides transaction seen as outgoing.
2018-12-27 18:50:45 +03:00
// so here to decrypt tx properly we need to figure out, if this transaction is actually escrow acceptance.
//we check if spent_indices have zero then input do not belong to this account, which means that we are seller for this
//escrow, and decryption should be processed as income flag
2023-06-06 23:05:53 +02:00
//let's assume that the one who pays for tx fee is sender of tx
bool decrypt_attachment_as_income = ! ( tx_process_context . total_balance_change . count ( currency : : native_coin_asset_id ) & & tx_process_context . total_balance_change . at ( currency : : native_coin_asset_id ) < 0 ) ;
2018-12-27 18:50:45 +03:00
std : : vector < currency : : payload_items_v > decrypted_att ;
2023-06-15 23:55:22 +02:00
bool has_zero_input_as_spent = false ;
for ( const auto & item : tx_process_context . employed_entries . spent )
{
if ( item . index = = 0 )
{
has_zero_input_as_spent = true ;
break ;
}
}
if ( wti . tx_type = = GUI_TX_TYPE_ESCROW_TRANSFER & & ! has_zero_input_as_spent )
2018-12-27 18:50:45 +03:00
decrypt_attachment_as_income = true ;
2023-06-06 23:05:53 +02:00
decrypt_payload_items ( decrypt_attachment_as_income , wti . tx , m_account . get_keys ( ) , decrypted_att ) ;
2023-06-07 23:29:41 +02:00
if ( ( is_watch_only ( ) & & ! decrypt_attachment_as_income ) | | ( wti . height > 638000 & & ! have_type_in_variant_container < etc_tx_flags16_t > ( decrypted_att ) ) )
2020-08-29 01:14:26 +02:00
{
remove_field_of_type_from_extra < tx_receiver_old > ( decrypted_att ) ;
remove_field_of_type_from_extra < tx_payer_old > ( decrypted_att ) ;
}
2023-06-06 23:05:53 +02:00
if ( is_watch_only ( ) & & ! decrypt_attachment_as_income )
2020-08-29 01:14:26 +02:00
{
remove_field_of_type_from_extra < tx_comment > ( decrypted_att ) ;
}
2018-12-27 18:50:45 +03:00
prepare_wti_decrypted_attachments ( wti , decrypted_att ) ;
process_contract_info ( wti , decrypted_att ) ;
}
//----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : rise_on_transfer2 ( const wallet_public : : wallet_transfer_info & wti )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::rise_on_transfer2 " ) ;
if ( ! m_do_rise_transfer )
return ;
2022-12-10 00:00:20 +01:00
std : : list < wallet_public : : asset_balance_entry > balances ;
2023-02-09 23:35:33 +01:00
uint64_t mined_balance = 0 ;
this - > balance ( balances , mined_balance ) ;
m_wcallback - > on_transfer2 ( wti , balances , mined_balance ) ;
// second call for legacy callback handlers
2023-06-15 23:55:22 +02:00
//m_wcallback->on_transfer2(wti, balances, mined_balance);
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2023-06-06 23:05:53 +02:00
/*
2018-12-27 18:50:45 +03:00
void wallet2 : : handle_money_spent2 ( const currency : : block & b ,
const currency : : transaction & in_tx ,
uint64_t amount ,
const money_transfer2_details & td ,
const std : : vector < std : : string > & recipients ,
2022-06-16 17:33:37 +02:00
const std : : vector < std : : string > & remote_aliases )
2018-12-27 18:50:45 +03:00
{
2019-08-27 17:36:53 +02:00
m_transfer_history . push_back ( AUTO_VAL_INIT ( wallet_public : : wallet_transfer_info ( ) ) ) ;
wallet_public : : wallet_transfer_info & wti = m_transfer_history . back ( ) ;
2018-12-27 18:50:45 +03:00
wti . is_income = false ;
wti . remote_addresses = recipients ;
2022-06-16 17:33:37 +02:00
wti . remote_aliases = remote_aliases ;
2021-10-08 08:08:58 +03:00
prepare_wti ( wti , get_block_height ( b ) , get_block_datetime ( b ) , in_tx , amount , td ) ;
2020-08-27 15:10:03 +02:00
WLT_LOG_L1 ( " [MONEY SPENT]: " < < epee : : serialization : : store_t_to_json ( wti ) ) ;
2018-12-27 18:50:45 +03:00
rise_on_transfer2 ( wti ) ;
}
//----------------------------------------------------------------------------------------------------
2023-06-06 23:05:53 +02:00
void wallet2 : : handle_money_received2 ( const currency : : block & b , const currency : : transaction & tx , uint64_t amount , const money_transfer2_details & td )
{
//decrypt attachments
m_transfer_history . push_back ( AUTO_VAL_INIT ( wallet_public : : wallet_transfer_info ( ) ) ) ;
wallet_public : : wallet_transfer_info & wti = m_transfer_history . back ( ) ;
wti . is_income = true ;
// TODO @#@# this function is only able to handle native coins atm, consider changing -- sowle
wti . asset_id = native_coin_asset_id ;
prepare_wti ( wti , get_block_height ( b ) , get_block_datetime ( b ) , tx , amount , td ) ;
WLT_LOG_L1 ( " [MONEY RECEIVED]: " < < epee : : serialization : : store_t_to_json ( wti ) ) ;
rise_on_transfer2 ( wti ) ;
}
*/
2023-06-07 23:29:41 +02:00
//----------------------------------------------------------------------------------------------------
2023-06-15 23:55:22 +02:00
void wallet2 : : load_wti_from_process_transaction_context ( wallet_public : : wallet_transfer_info & wti , const process_transaction_context & tx_process_context )
2023-06-06 23:05:53 +02:00
{
wti . remote_addresses = tx_process_context . recipients ;
wti . remote_aliases = tx_process_context . remote_aliases ;
for ( const auto bce : tx_process_context . total_balance_change )
{
2023-06-07 23:29:41 +02:00
wallet_public : : wallet_sub_transfer_info wsti = AUTO_VAL_INIT ( wsti ) ;
2023-06-06 23:05:53 +02:00
wsti . asset_id = bce . first ;
if ( bce . second = = 0 )
{
continue ;
}
else if ( bce . second > 0 )
{
//in transfer
wsti . is_income = true ;
wsti . amount = static_cast < uint64_t > ( bce . second ) ;
}
else
{
//out transfer
wsti . is_income = false ;
wsti . amount = static_cast < uint64_t > ( bce . second * ( - 1 ) ) ;
}
2023-06-07 23:29:41 +02:00
wti . subtransfers . push_back ( wsti ) ;
2023-06-06 23:05:53 +02:00
}
2023-06-07 23:29:41 +02:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : handle_money ( const currency : : block & b , const process_transaction_context & tx_process_context )
{
m_transfer_history . push_back ( AUTO_VAL_INIT ( wallet_public : : wallet_transfer_info ( ) ) ) ;
wallet_public : : wallet_transfer_info & wti = m_transfer_history . back ( ) ;
2023-06-15 23:55:22 +02:00
prepare_wti ( wti , tx_process_context ) ;
2023-06-06 23:05:53 +02:00
WLT_LOG_L1 ( " [MONEY SPENT]: " < < epee : : serialization : : store_t_to_json ( wti ) ) ;
rise_on_transfer2 ( wti ) ;
}
//----------------------------------------------------------------------------------------------------
2022-06-16 17:33:37 +02:00
void wallet2 : : process_unconfirmed ( const currency : : transaction & tx , std : : vector < std : : string > & recipients , std : : vector < std : : string > & remote_aliases )
2018-12-27 18:50:45 +03:00
{
auto unconf_it = m_unconfirmed_txs . find ( get_transaction_hash ( tx ) ) ;
if ( unconf_it ! = m_unconfirmed_txs . end ( ) )
{
2019-08-27 17:36:53 +02:00
wallet_public : : wallet_transfer_info & wti = unconf_it - > second ;
2018-12-27 18:50:45 +03:00
recipients = wti . remote_addresses ;
2022-06-16 17:33:37 +02:00
remote_aliases = wti . remote_aliases ;
2018-12-27 18:50:45 +03:00
m_unconfirmed_txs . erase ( unconf_it ) ;
}
}
//----------------------------------------------------------------------------------------------------
2021-02-28 23:36:38 +01:00
void wallet2 : : unprocess_htlc_triggers_on_block_removed ( uint64_t height )
{
if ( ! m_htlcs . size ( ) )
return ;
if ( height > m_htlcs . rbegin ( ) - > first )
{
//there is no active htlc that at this height
CHECK_AND_ASSERT_MES ( m_active_htlcs . size ( ) = = 0 , void ( ) , " Self check failed: m_active_htlcs.size() = " < < m_active_htlcs . size ( ) ) ;
return ;
}
//we have to check if there is a htlc that has to become deactivated
auto pair_of_it = m_htlcs . equal_range ( height ) ;
for ( auto it = pair_of_it . first ; it ! = pair_of_it . second ; it + + )
{
auto & tr = m_transfers [ it - > second . transfer_index ] ;
//found contract that supposed to be re-activated and set to active
if ( it - > second . is_wallet_owns_redeem )
{
// this means that wallet received atomic as proposal but never activated it, and now we back to phase where out can be activated
//but we keep spend flag anyway
tr . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ; //re assure that it has spent flag
tr . m_spent_height = 0 ;
}
else
{
// this means that wallet created atomic by itself, and second part didn't redeem it,
// so refund money became available, and now we back again to unavailable state
tr . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ; //reset spent flag
m_found_free_amounts . clear ( ) ; //reset free amounts cache
tr . m_spent_height = 0 ;
}
//re-add to active contracts
2022-05-25 22:31:23 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( tr . m_ptx_wallet_info - > m_tx . vout [ tr . m_internal_output_index ] . type ( ) = = typeid ( tx_out_bare ) , std : : string ( " Unexprected type of out in unprocess_htlc_triggers_on_block_removed : " ) + tr . m_ptx_wallet_info - > m_tx . vout [ tr . m_internal_output_index ] . type ( ) . name ( ) ) ;
2022-06-30 21:47:59 +02:00
auto pair_key = std : : make_pair ( tr . m_amount , tr . m_global_output_index ) ;
2021-02-28 23:36:38 +01:00
auto it_active_htlc = m_active_htlcs . find ( pair_key ) ;
if ( it_active_htlc ! = m_active_htlcs . end ( ) )
{
LOG_ERROR ( " Error at putting back htlc: already exist? " ) ;
it_active_htlc - > second = it - > second . transfer_index ;
}
else
{
m_active_htlcs [ pair_key ] = it - > second . transfer_index ;
}
const crypto : : hash tx_id = tr . tx_hash ( ) ;
auto tx_id_it = m_active_htlcs_txid . find ( tx_id ) ;
if ( tx_id_it ! = m_active_htlcs_txid . end ( ) )
{
LOG_ERROR ( " Error at putting back htlc_txid: already exist? " ) ;
tx_id_it - > second = it - > second . transfer_index ;
}
else
{
m_active_htlcs_txid [ tx_id ] = it - > second . transfer_index ;
}
}
}
2021-01-24 21:00:43 +01:00
void wallet2 : : process_htlc_triggers_on_block_added ( uint64_t height )
{
if ( ! m_htlcs . size ( ) )
return ;
if ( height > m_htlcs . rbegin ( ) - > first )
{
//there is no active htlc that at this height
CHECK_AND_ASSERT_MES ( m_active_htlcs . size ( ) = = 0 , void ( ) , " Self check failed: m_active_htlcs.size() = " < < m_active_htlcs . size ( ) ) ;
return ;
}
//we have to check if there is a htlc that has to become deactivated
auto pair_of_it = m_htlcs . equal_range ( height ) ;
for ( auto it = pair_of_it . first ; it ! = pair_of_it . second ; it + + )
{
2021-02-19 20:18:37 +01:00
auto & tr = m_transfers [ it - > second . transfer_index ] ;
2021-01-24 21:00:43 +01:00
//found contract that supposed to be deactivated and set to innactive
if ( it - > second . is_wallet_owns_redeem )
{
// this means that wallet received atomic as proposal but never activated it, money returned to initiator
tr . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ; //re assure that it has spent flag
tr . m_spent_height = height ;
}
else
{
// this means that wallet created atomic by itself, and second part didn't redeem it, so refund money should become available
tr . m_flags & = ~ ( WALLET_TRANSFER_DETAIL_FLAG_SPENT ) ; //reset spent flag
m_found_free_amounts . clear ( ) ; //reset free amounts cache
tr . m_spent_height = 0 ;
2021-03-08 02:09:08 +03:00
}
//reset cache
m_found_free_amounts . clear ( ) ;
2021-01-24 21:00:43 +01:00
//remove it from active contracts
2022-05-20 21:32:27 +02:00
CHECK_AND_ASSERT_MES ( tr . m_ptx_wallet_info - > m_tx . vout [ tr . m_internal_output_index ] . type ( ) = = typeid ( tx_out_bare ) , void ( ) , " Unexpected type out in process_htlc_triggers_on_block_added: " < < tr . m_ptx_wallet_info - > m_tx . vout [ tr . m_internal_output_index ] . type ( ) . name ( ) ) ;
2022-06-30 21:47:59 +02:00
uint64_t amount = tr . m_amount ;
2022-05-20 21:32:27 +02:00
2022-05-25 22:31:23 +02:00
auto it_active_htlc = m_active_htlcs . find ( std : : make_pair ( amount , tr . m_global_output_index ) ) ;
2021-01-24 21:00:43 +01:00
if ( it_active_htlc = = m_active_htlcs . end ( ) )
{
2021-01-27 22:49:15 +01:00
LOG_ERROR ( " Erasing active htlc(m_active_htlcs), but it seems to be already erased " ) ;
2021-01-24 21:00:43 +01:00
}
else
{
2021-01-31 19:42:02 +01:00
m_active_htlcs . erase ( it_active_htlc ) ;
auto it_tx = m_active_htlcs_txid . find ( tr . tx_hash ( ) ) ;
2021-01-27 22:49:15 +01:00
if ( it_tx = = m_active_htlcs_txid . end ( ) )
{
LOG_ERROR ( " Erasing active htlc(;), but it seems to be already erased " ) ;
}
else
{
m_active_htlcs_txid . erase ( it_tx ) ;
}
2021-01-24 21:00:43 +01:00
}
}
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : process_new_blockchain_entry ( const currency : : block & b , const currency : : block_direct_data_entry & bche , const crypto : : hash & bl_id , uint64_t height )
{
//handle transactions from new block
2020-05-17 19:16:48 +02:00
THROW_IF_TRUE_WALLET_EX ( height ! = get_blockchain_current_size ( ) & &
! ( height = = m_minimum_height | | get_blockchain_current_size ( ) < = 1 ) , error : : wallet_internal_error ,
2020-04-25 00:30:55 +02:00
" current_index= " + std : : to_string ( height ) + " , get_blockchain_current_height()= " + std : : to_string ( get_blockchain_current_size ( ) ) ) ;
2018-12-27 18:50:45 +03:00
2021-02-28 23:36:38 +01:00
2021-01-24 21:00:43 +01:00
2018-12-27 18:50:45 +03:00
//optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
2020-06-12 00:32:08 +02:00
const std : : vector < uint64_t > * pglobal_index = nullptr ;
2018-12-27 18:50:45 +03:00
if ( b . timestamp + 60 * 60 * 24 > m_account . get_createtime ( ) )
{
2020-06-12 00:32:08 +02:00
pglobal_index = nullptr ;
if ( bche . coinbase_ptr . get ( ) )
{
pglobal_index = & ( bche . coinbase_ptr - > m_global_output_indexes ) ;
}
2018-12-27 18:50:45 +03:00
TIME_MEASURE_START ( miner_tx_handle_time ) ;
2020-06-12 00:32:08 +02:00
process_new_transaction ( b . miner_tx , height , b , pglobal_index ) ;
2018-12-27 18:50:45 +03:00
TIME_MEASURE_FINISH ( miner_tx_handle_time ) ;
TIME_MEASURE_START ( txs_handle_time ) ;
for ( const auto & tx_entry : bche . txs_ptr )
{
2020-06-12 00:32:08 +02:00
process_new_transaction ( tx_entry - > tx , height , b , & ( tx_entry - > m_global_output_indexes ) ) ;
2018-12-27 18:50:45 +03:00
}
TIME_MEASURE_FINISH ( txs_handle_time ) ;
2020-02-08 04:21:59 +01:00
WLT_LOG_L3 ( " Processed block: " < < bl_id < < " , height " < < height < < " , " < < miner_tx_handle_time + txs_handle_time < < " ( " < < miner_tx_handle_time < < " / " < < txs_handle_time < < " )ms " ) ;
2018-12-27 18:50:45 +03:00
} else
{
2020-02-08 04:21:59 +01:00
WLT_LOG_L3 ( " Skipped block by timestamp, height: " < < height < < " , block time " < < b . timestamp < < " , account time " < < m_account . get_createtime ( ) ) ;
2018-12-27 18:50:45 +03:00
}
2020-04-30 22:29:08 +02:00
m_chain . push_new_block_id ( bl_id , height ) ; //m_blockchain.push_back(bl_id);
2018-12-27 18:50:45 +03:00
m_last_bc_timestamp = b . timestamp ;
2019-07-20 14:36:12 +02:00
if ( ! is_pos_block ( b ) )
m_last_pow_block_h = height ;
2018-12-27 18:50:45 +03:00
2021-02-28 23:36:38 +01:00
process_htlc_triggers_on_block_added ( height ) ;
2018-12-27 18:50:45 +03:00
m_wcallback - > on_new_block ( height , b ) ;
}
//----------------------------------------------------------------------------------------------------
2020-04-24 01:05:36 +02:00
//----------------------------------------------------------------------------------------------------
2020-04-24 19:18:19 +02:00
// void wallet2::get_short_chain_history(std::list<crypto::hash>& ids)
// {
// ids.clear();
// size_t i = 0;
// size_t current_multiplier = 1;
// size_t sz = get_blockchain_current_height();
// if(!sz)
// return;
// size_t current_back_offset = 1;
// bool genesis_included = false;
// while(current_back_offset < sz)
// {
// ids.push_back(m_blockchain[sz-current_back_offset]);
// if(sz-current_back_offset == 0)
// genesis_included = true;
// if(i < 10)
// {
// ++current_back_offset;
// }else
// {
// current_back_offset += current_multiplier *= 2;
// }
// ++i;
// }
// if(!genesis_included)
// ids.push_back(m_blockchain[0]);
// }
2020-04-28 00:40:31 +02:00
//----------------------------------------------------------------------------------------------------
void wallet2 : : set_minimum_height ( uint64_t h )
2018-12-27 18:50:45 +03:00
{
2020-04-28 00:40:31 +02:00
m_minimum_height = h ;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : get_wallet_minimum_height ( )
{
2020-05-04 17:11:25 +02:00
if ( m_minimum_height ! = WALLET_MINIMUM_HEIGHT_UNSET_CONST )
2020-04-28 00:40:31 +02:00
return m_minimum_height ;
currency : : COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE : : response res = AUTO_VAL_INIT ( res ) ;
req . timestamp = m_account . get_createtime ( ) ;
bool r = m_core_proxy - > call_COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE ( req , res ) ;
THROW_IF_FALSE_WALLET_EX ( r , error : : no_connection_to_daemon , " call_COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE " ) ;
2020-05-07 23:26:41 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( res . status = = API_RETURN_CODE_OK , " FAILED TO CALL COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE " ) ;
2020-05-04 17:11:25 +02:00
m_minimum_height = res . h ;
2020-04-28 00:40:31 +02:00
return res . h ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : pull_blocks ( size_t & blocks_added , std : : atomic < bool > & stop )
{
blocks_added = 0 ;
currency : : COMMAND_RPC_GET_BLOCKS_DIRECT : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_BLOCKS_DIRECT : : response res = AUTO_VAL_INIT ( res ) ;
2020-04-28 00:40:31 +02:00
req . minimum_height = get_wallet_minimum_height ( ) ;
2020-06-12 00:32:08 +02:00
if ( is_auditable ( ) )
req . need_global_indexes = true ;
2020-07-15 18:15:11 +02:00
if ( req . minimum_height > m_height_of_start_sync )
m_height_of_start_sync = req . minimum_height ;
2020-06-12 00:32:08 +02:00
2020-05-01 20:39:39 +02:00
m_chain . get_short_chain_history ( req . block_ids ) ;
2018-12-27 18:50:45 +03:00
bool r = m_core_proxy - > call_COMMAND_RPC_GET_BLOCKS_DIRECT ( req , res ) ;
2019-11-12 11:33:39 +03:00
if ( ! r )
2019-11-14 03:12:22 +03:00
throw error : : no_connection_to_daemon ( LOCATION_STR , " getblocks.bin " ) ;
2020-05-07 23:26:41 +02:00
if ( res . status = = API_RETURN_CODE_GENESIS_MISMATCH )
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_MAGENTA ( " Reseting genesis block... " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
COMMAND_RPC_GET_BLOCKS_DETAILS : : request gbd_req = AUTO_VAL_INIT ( gbd_req ) ;
COMMAND_RPC_GET_BLOCKS_DETAILS : : response gbd_res = AUTO_VAL_INIT ( gbd_res ) ;
gbd_req . height_start = 0 ;
gbd_req . count = 1 ;
gbd_req . ignore_transactions = true ;
r = m_core_proxy - > call_COMMAND_RPC_GET_BLOCKS_DETAILS ( gbd_req , gbd_res ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : no_connection_to_daemon , " get_blocks_details " ) ;
2020-05-07 23:26:41 +02:00
THROW_IF_TRUE_WALLET_EX ( gbd_res . status ! = API_RETURN_CODE_OK , error : : get_blocks_error , gbd_res . status ) ;
2018-12-27 18:50:45 +03:00
THROW_IF_TRUE_WALLET_EX ( gbd_res . blocks . size ( ) = = 0 , error : : get_blocks_error , gbd_res . status ) ;
crypto : : hash new_genesis_id = null_hash ;
r = string_tools : : parse_tpod_from_hex_string ( gbd_res . blocks . back ( ) . id , new_genesis_id ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : no_connection_to_daemon , " get_blocks_details " ) ;
reset_all ( ) ;
2020-04-30 22:29:08 +02:00
m_chain . set_genesis ( new_genesis_id ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_MAGENTA ( " New genesis set for wallet: " < < new_genesis_id , LOG_LEVEL_0 ) ;
2020-05-01 20:39:39 +02:00
m_chain . get_short_chain_history ( req . block_ids ) ;
2018-12-27 18:50:45 +03:00
//req.block_ids.push_back(new_genesis_id);
bool r = m_core_proxy - > call_COMMAND_RPC_GET_BLOCKS_DIRECT ( req , res ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : no_connection_to_daemon , " getblocks.bin " ) ;
}
2020-05-07 23:26:41 +02:00
if ( res . status = = API_RETURN_CODE_BUSY )
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L1 ( " Core is busy, pull cancelled " ) ;
2020-07-13 01:45:12 +02:00
m_core_proxy - > get_editable_proxy_diagnostic_info ( ) - > is_busy = true ;
2018-12-27 18:50:45 +03:00
stop = true ;
return ;
}
2020-07-13 01:45:12 +02:00
m_core_proxy - > get_editable_proxy_diagnostic_info ( ) - > is_busy = false ;
2020-05-07 23:26:41 +02:00
THROW_IF_TRUE_WALLET_EX ( res . status ! = API_RETURN_CODE_OK , error : : get_blocks_error , res . status ) ;
2020-05-04 17:11:25 +02:00
THROW_IF_TRUE_WALLET_EX ( get_blockchain_current_size ( ) & & get_blockchain_current_size ( ) < = res . start_height & & res . start_height ! = m_minimum_height , error : : wallet_internal_error ,
2018-12-27 18:50:45 +03:00
" wrong daemon response: m_start_height= " + std : : to_string ( res . start_height ) +
2020-04-25 00:30:55 +02:00
" not less than local blockchain size= " + std : : to_string ( get_blockchain_current_size ( ) ) ) ;
2018-12-27 18:50:45 +03:00
handle_pulled_blocks ( blocks_added , stop , res ) ;
}
2020-04-24 23:54:30 +02:00
2018-12-27 18:50:45 +03:00
//----------------------------------------------------------------------------------------------------
void wallet2 : : handle_pulled_blocks ( size_t & blocks_added , std : : atomic < bool > & stop ,
currency : : COMMAND_RPC_GET_BLOCKS_DIRECT : : response & res )
{
size_t current_index = res . start_height ;
2020-05-01 20:39:39 +02:00
bool been_matched_block = false ;
2020-04-25 00:30:55 +02:00
if ( res . start_height = = 0 & & get_blockchain_current_size ( ) = = 1 & & ! res . blocks . empty ( ) )
2018-12-27 18:50:45 +03:00
{
const currency : : block & genesis = res . blocks . front ( ) . block_ptr - > bl ;
THROW_IF_TRUE_WALLET_EX ( get_block_height ( genesis ) ! = 0 , error : : wallet_internal_error , " first block expected to be genesis " ) ;
process_genesis_if_needed ( genesis ) ;
res . blocks . pop_front ( ) ;
+ + current_index ;
2020-05-01 20:39:39 +02:00
been_matched_block = true ;
2018-12-27 18:50:45 +03:00
}
2020-04-24 23:54:30 +02:00
uint64_t last_matched_index = 0 ;
2018-12-27 18:50:45 +03:00
for ( const auto & bl_entry : res . blocks )
{
if ( stop )
break ;
const currency : : block & bl = bl_entry . block_ptr - > bl ;
2020-04-24 23:54:30 +02:00
uint64_t height = get_block_height ( bl ) ;
2020-04-25 00:30:55 +02:00
uint64_t processed_blocks_count = get_blockchain_current_size ( ) ;
2018-12-27 18:50:45 +03:00
//TODO: get_block_hash is slow
crypto : : hash bl_id = get_block_hash ( bl ) ;
2020-05-04 17:11:25 +02:00
if ( processed_blocks_count ! = 1 & & height > processed_blocks_count )
2020-05-17 18:50:40 +02:00
{
if ( height ! = m_minimum_height )
{
//internal error:
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( false ,
" height{ " < < height < < " } > processed_blocks_count{ " < < processed_blocks_count < < " } " ) ;
}
else
{
//possible case, wallet rewound to m_minimum_height
m_chain . clear ( ) ;
}
2018-12-27 18:50:45 +03:00
}
2022-11-25 22:03:21 +01:00
else if ( height = = processed_blocks_count & & been_matched_block )
2018-12-27 18:50:45 +03:00
{
2020-04-24 23:54:30 +02:00
//regular block handling
//self check
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( been_matched_block ,
" internal error: been_matched_block == false on process_new_blockchain_entry, bl_id " < < bl_id < < " h= " < < height
< < " (start_height= " + std : : to_string ( res . start_height ) + " ) " ) ;
2018-12-27 18:50:45 +03:00
process_new_blockchain_entry ( bl , bl_entry , bl_id , current_index ) ;
+ + blocks_added ;
}
else
2020-04-24 23:54:30 +02:00
{
//checking if we need reorganize (might be just first matched block)
bool block_found = false ;
bool block_matched = false ;
bool full_reset_needed = false ;
2020-05-01 20:39:39 +02:00
m_chain . check_if_block_matched ( height , bl_id , block_found , block_matched , full_reset_needed ) ;
2020-04-24 23:54:30 +02:00
if ( block_found & & block_matched )
{
//block matched in that number
last_matched_index = height ;
been_matched_block = true ;
2021-07-01 21:05:50 +02:00
WLT_LOG_L4 ( " Block " < < bl_id < < " @ " < < height < < " is already in wallet's blockchain " ) ;
2020-04-24 23:54:30 +02:00
}
else
{
//this should happen ONLY after block been matched, if not then is internal error
if ( full_reset_needed )
{
last_matched_index = 0 ;
2020-05-02 22:53:52 +02:00
been_matched_block = true ;
2020-04-24 23:54:30 +02:00
}
else
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( been_matched_block ,
" unmatched block while never been mathced block " ) ;
}
2020-04-25 00:30:55 +02:00
//TODO: take into account date of wallet creation
2020-04-24 23:54:30 +02:00
//reorganize
2020-05-02 22:53:52 +02:00
detach_blockchain ( last_matched_index + 1 ) ;
2020-04-24 23:54:30 +02:00
process_new_blockchain_entry ( bl , bl_entry , bl_id , height ) ;
+ + blocks_added ;
}
2018-12-27 18:50:45 +03:00
}
2020-04-24 23:54:30 +02:00
2018-12-27 18:50:45 +03:00
+ + current_index ;
if ( res . current_height > m_height_of_start_sync )
{
uint64_t next_percent = ( 100 * ( current_index - m_height_of_start_sync ) ) / ( res . current_height - m_height_of_start_sync ) ;
if ( next_percent ! = m_last_sync_percent )
{
m_wcallback - > on_sync_progress ( next_percent ) ;
m_last_sync_percent = next_percent ;
}
}
}
2020-08-09 01:30:34 +03:00
WLT_LOG_L2 ( " [PULL BLOCKS] " < < res . start_height < < " --> " < < get_blockchain_current_size ( ) - 1 ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2020-01-17 23:44:22 +01:00
uint64_t wallet2 : : get_sync_progress ( )
{
return m_last_sync_percent ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : refresh ( )
{
size_t blocks_fetched = 0 ;
refresh ( blocks_fetched ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : refresh ( size_t & blocks_fetched )
{
bool received_money = false ;
2020-07-13 01:45:12 +02:00
m_stop = false ;
2018-12-27 18:50:45 +03:00
refresh ( blocks_fetched , received_money , m_stop ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : refresh ( std : : atomic < bool > & stop )
{
bool f ;
size_t n ;
refresh ( n , f , stop ) ;
}
//----------------------------------------------------------------------------------------------------
2022-09-21 22:35:24 +02:00
detail : : split_strategy_id_t wallet2 : : get_current_split_strategy ( )
2022-09-20 22:01:52 +02:00
{
if ( is_need_to_split_outputs ( ) )
return tools : : detail : : ssi_digit ;
else
return tools : : detail : : ssi_void ;
}
//
2023-02-08 18:50:26 +01:00
void wallet2 : : transfer ( uint64_t amount , const currency : : account_public_address & acc , currency : : transaction & result_tx , const crypto : : public_key & asset_id /* = currency::native_coin_asset_id */ )
2018-12-27 18:50:45 +03:00
{
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
std : : vector < tx_destination_entry > dst ;
dst . resize ( 1 ) ;
dst . back ( ) . addr . push_back ( acc ) ;
dst . back ( ) . amount = amount ;
2022-10-06 22:59:00 +02:00
dst . back ( ) . asset_id = asset_id ;
2022-09-20 22:01:52 +02:00
this - > transfer ( dst , 0 , 0 , TX_DEFAULT_FEE , extra , attachments , get_current_split_strategy ( ) , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , result_tx ) ;
2021-02-20 15:54:58 +01:00
}
//----------------------------------------------------------------------------------------------------
2022-11-23 15:10:06 +01:00
void wallet2 : : transfer ( uint64_t amount , size_t fake_outs_count , const currency : : account_public_address & acc , uint64_t fee /* = TX_DEFAULT_FEE*/ ,
2023-02-08 18:50:26 +01:00
const crypto : : public_key & asset_id /* = currency::native_coin_asset_id */ )
2022-11-23 15:10:06 +01:00
{
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
transaction result_tx = AUTO_VAL_INIT ( result_tx ) ;
std : : vector < tx_destination_entry > dst ;
dst . resize ( 1 ) ;
dst . back ( ) . addr . push_back ( acc ) ;
dst . back ( ) . amount = amount ;
dst . back ( ) . asset_id = asset_id ;
this - > transfer ( dst , fake_outs_count , 0 , fee , extra , attachments , get_current_split_strategy ( ) , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , result_tx ) ;
}
//----------------------------------------------------------------------------------------------------
2023-02-08 18:50:26 +01:00
void wallet2 : : transfer ( uint64_t amount , const currency : : account_public_address & acc , const crypto : : public_key & asset_id /* = currency::native_coin_asset_id */ )
2021-02-20 15:54:58 +01:00
{
transaction result_tx = AUTO_VAL_INIT ( result_tx ) ;
2022-10-06 22:59:00 +02:00
this - > transfer ( amount , acc , result_tx , asset_id ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : reset_creation_time ( uint64_t timestamp )
{
m_account . set_createtime ( timestamp ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : update_current_tx_limit ( )
{
currency : : COMMAND_RPC_GET_INFO : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_INFO : : response res = AUTO_VAL_INIT ( res ) ;
bool r = m_core_proxy - > call_COMMAND_RPC_GET_INFO ( req , res ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : no_connection_to_daemon , " getinfo " ) ;
2020-05-07 23:26:41 +02:00
THROW_IF_TRUE_WALLET_EX ( res . status = = API_RETURN_CODE_BUSY , error : : daemon_busy , " getinfo " ) ;
THROW_IF_TRUE_WALLET_EX ( res . status ! = API_RETURN_CODE_OK , error : : get_blocks_error , res . status ) ;
2018-12-27 18:50:45 +03:00
THROW_IF_TRUE_WALLET_EX ( res . current_blocks_median < CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE , error : : get_blocks_error , " bad median size " ) ;
m_upper_transaction_size_limit = res . current_blocks_median - CURRENCY_COINBASE_BLOB_RESERVED_SIZE ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : has_related_alias_entry_unconfirmed ( const currency : : transaction & tx )
{
std : : string local_adr = m_account . get_public_address_str ( ) ;
tx_extra_info tei = AUTO_VAL_INIT ( tei ) ;
parse_and_validate_tx_extra ( tx , tei ) ;
if ( tei . m_alias . m_alias . size ( ) )
{
//have some check address involved
2020-04-22 23:30:03 +03:00
if ( tei . m_alias . m_address . spend_public_key = = m_account . get_keys ( ) . account_address . spend_public_key & &
tei . m_alias . m_address . view_public_key = = m_account . get_keys ( ) . account_address . view_public_key )
2018-12-27 18:50:45 +03:00
return true ;
//check if it's update and address before was our address
currency : : COMMAND_RPC_GET_ALIAS_DETAILS : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_ALIAS_DETAILS : : response res = AUTO_VAL_INIT ( res ) ;
req . alias = tei . m_alias . m_alias ;
m_core_proxy - > call_COMMAND_RPC_GET_ALIAS_DETAILS ( req , res ) ;
2020-05-07 23:26:41 +02:00
if ( res . status ! = API_RETURN_CODE_OK )
2018-12-27 18:50:45 +03:00
return false ;
if ( local_adr = = res . alias_details . address )
return true ;
}
return false ;
}
//----------------------------------------------------------------------------------------------------
2022-06-28 22:27:36 +02:00
uint64_t wallet2 : : get_directly_spent_transfer_index_by_input_in_tracking_wallet ( const currency : : txin_to_key & intk )
{
return get_directly_spent_transfer_index_by_input_in_tracking_wallet ( intk . amount , intk . key_offsets ) ;
}
//----------------------------------------------------------------------------------------------------
2022-08-03 14:00:39 +02:00
uint64_t wallet2 : : get_directly_spent_transfer_index_by_input_in_tracking_wallet ( const currency : : txin_zc_input & inzc )
2022-06-28 22:27:36 +02:00
{
2022-08-03 14:00:39 +02:00
return get_directly_spent_transfer_index_by_input_in_tracking_wallet ( 0 , inzc . key_offsets ) ;
2022-06-28 22:27:36 +02:00
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : get_directly_spent_transfer_index_by_input_in_tracking_wallet ( uint64_t amount , const std : : vector < currency : : txout_ref_v > & key_offsets )
2020-09-07 17:36:41 +03:00
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( is_auditable ( ) & & is_watch_only ( ) , " this is not an auditable-watch-only (tracking) wallet " ) ;
uint64_t tid = UINT64_MAX ;
// try to find a reference among own UTXOs
2022-06-28 22:27:36 +02:00
std : : vector < txout_ref_v > abs_key_offsets = relative_output_offsets_to_absolute ( key_offsets ) ; // potential speed-up: don't convert to abs offsets as we interested only in direct spends for auditable wallets. Now it's kind a bit paranoid.
2020-09-07 17:36:41 +03:00
for ( auto v : abs_key_offsets )
{
if ( v . type ( ) ! = typeid ( uint64_t ) )
continue ;
uint64_t gindex = boost : : get < uint64_t > ( v ) ;
2022-06-28 22:27:36 +02:00
auto it = m_amount_gindex_to_transfer_id . find ( std : : make_pair ( amount , gindex ) ) ;
2020-09-07 17:36:41 +03:00
if ( it ! = m_amount_gindex_to_transfer_id . end ( ) )
{
tid = it - > second ;
2022-06-28 22:27:36 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( tid < m_transfers . size ( ) , " invalid tid: " < < tid < < " , ref from input with amount: " < < amount < < " , gindex: " < < gindex ) ;
2020-09-07 17:36:41 +03:00
auto & td = m_transfers [ it - > second ] ;
2022-06-28 22:27:36 +02:00
if ( key_offsets . size ( ) ! = 1 )
2020-09-07 17:36:41 +03:00
{
// own output was used in non-direct transaction
// the core should not allow this to happen, the only way it may happen - mixing in own output that was sent without mix_attr == 1
// log strange situation
std : : stringstream ss ;
2022-06-28 22:27:36 +02:00
ss < < " own transfer tid= " < < tid < < " tx= " < < td . tx_hash ( ) < < " mix_attr= " < < td . mix_attr ( ) < < " , is referenced by a transaction with mixins, ref from input with amount: " < < amount < < " , gindex: " < < gindex ;
2020-09-07 17:36:41 +03:00
WLT_LOG_YELLOW ( ss . str ( ) , LOG_LEVEL_0 ) ;
if ( m_wcallback )
m_wcallback - > on_message ( i_wallet2_callback : : ms_yellow , ss . str ( ) ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( td . mix_attr ( ) ! = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX , ss . str ( ) ) ; // if mix_attr == 1 this should never happen (mixing in an output with mix_attr = 1) as the core must reject such txs
// our own output has mix_attr != 1 for some reason (a sender did not set correct mix_attr e.g.)
// but mixin count > 1 so we can't say it is spent for sure
tid = UINT64_MAX ;
continue ;
}
2022-06-28 22:27:36 +02:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( td . m_spent_height = = 0 , " transfer is spent in blockchain, tid: " < < tid < < " , ref from input with amount: " < < amount < < " , gindex: " < < gindex ) ;
2020-09-07 17:36:41 +03:00
// okay, own output is being spent, return it
break ;
}
}
return tid ;
}
//----------------------------------------------------------------------------------------------------
2023-06-15 23:55:22 +02:00
void wallet2 : : handle_unconfirmed_tx ( process_transaction_context & ptc )
{
const transaction & tx = ptc . tx ;
2023-06-20 21:59:02 +02:00
ptc . timestamp = m_core_runtime_config . get_core_time ( ) ;
2023-06-15 23:55:22 +02:00
// read extra
std : : vector < wallet_out_info > outs ;
//uint64_t sum_of_received_native_outs = 0;
crypto : : public_key tx_pub_key = null_pkey ;
bool r = parse_and_validate_tx_extra ( tx , tx_pub_key ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : tx_extra_parse_error , tx ) ;
//check if we have money
crypto : : key_derivation derivation = AUTO_VAL_INIT ( derivation ) ;
r = lookup_acc_outs ( m_account . get_keys ( ) , tx , tx_pub_key , outs , derivation ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : acc_outs_lookup_error , tx , tx_pub_key , m_account . get_keys ( ) ) ;
//collect incomes
for ( auto & o : outs )
{
2023-06-16 20:19:49 +02:00
if ( out_is_multisig ( tx . vout [ o . index ] ) )
continue ;
2023-06-15 23:55:22 +02:00
ptc . total_balance_change [ o . asset_id ] + = o . amount ;
ptc . employed_entries . receive . push_back ( wallet_public : : employed_tx_entry { o . index , o . amount , o . asset_id } ) ;
}
bool new_multisig_spend_detected = false ;
//check if we have spendings
//uint64_t sum_of_spent_native_coin = 0;
std : : list < size_t > spend_transfers ;
// check all outputs for spending (compare key images)
for ( size_t i = 0 ; i ! = tx . vin . size ( ) ; i + + )
{
auto & in = tx . vin [ i ] ;
if ( in . type ( ) = = typeid ( currency : : txin_to_key ) )
{
const currency : : txin_to_key & intk = boost : : get < currency : : txin_to_key > ( in ) ;
uint64_t tid = UINT64_MAX ;
if ( is_auditable ( ) & & is_watch_only ( ) )
{
// tracking wallet, assuming all outputs are spent directly because of mix_attr = 1
tid = get_directly_spent_transfer_index_by_input_in_tracking_wallet ( intk . amount , intk . key_offsets ) ;
}
else
{
// wallet with spend secret key -- we can calculate own key images and then search among them
auto it = m_key_images . find ( intk . k_image ) ;
if ( it ! = m_key_images . end ( ) )
{
tid = it - > second ;
}
}
if ( tid ! = UINT64_MAX )
{
// own output is being spent by this input
//sum_of_spent_native_coin += intk.amount;
ptc . employed_entries . spent . push_back ( wallet_public : : employed_tx_entry { i , m_transfers [ tid ] . amount ( ) , m_transfers [ tid ] . get_asset_id ( ) } ) ;
spend_transfers . push_back ( tid ) ;
ptc . total_balance_change [ currency : : native_coin_asset_id ] - = m_transfers [ tid ] . amount ( ) ;
CHECK_AND_ASSERT_THROW_MES ( m_transfers [ tid ] . get_asset_id ( ) = = currency : : native_coin_asset_id , " Unexpected asset id for native txin_to_key " ) ;
}
}
else if ( in . type ( ) = = typeid ( currency : : txin_zc_input ) )
{
// bad design -- remove redundancy like using wallet2::process_input_t()
const currency : : txin_zc_input & zc = boost : : get < currency : : txin_zc_input > ( in ) ;
uint64_t tid = UINT64_MAX ;
if ( is_auditable ( ) & & is_watch_only ( ) )
{
// tracking wallet, assuming all outputs are spent directly because of mix_attr = 1
tid = get_directly_spent_transfer_index_by_input_in_tracking_wallet ( zc ) ;
}
else
{
// wallet with spend secret key -- we can calculate own key images and then search among them
auto it = m_key_images . find ( zc . k_image ) ;
if ( it ! = m_key_images . end ( ) )
{
tid = it - > second ;
}
}
if ( tid ! = UINT64_MAX )
{
ptc . employed_entries . spent . push_back ( wallet_public : : employed_tx_entry { i , m_transfers [ tid ] . amount ( ) , m_transfers [ tid ] . get_asset_id ( ) } ) ;
spend_transfers . push_back ( tid ) ;
ptc . total_balance_change [ m_transfers [ tid ] . get_asset_id ( ) ] - = m_transfers [ tid ] . amount ( ) ;
}
}
else if ( in . type ( ) = = typeid ( currency : : txin_multisig ) )
{
crypto : : hash multisig_id = boost : : get < currency : : txin_multisig > ( in ) . multisig_out_id ;
auto it = m_multisig_transfers . find ( multisig_id ) ;
if ( it ! = m_multisig_transfers . end ( ) )
{
//ptc.employed_entries.spent_indices.push_back(i);
ptc . employed_entries . spent . push_back ( wallet_public : : employed_tx_entry { i } ) ;
if ( ptc . pmultisig_entries )
{
r = ptc . pmultisig_entries - > insert ( std : : make_pair ( multisig_id , std : : make_pair ( tx , ptc . employed_entries ) ) ) . second ;
if ( ! r )
{
WLT_LOG_RED ( " Warning: Receiving the same multisig out id from tx pool more then once: " < < multisig_id , LOG_LEVEL_0 ) ;
}
}
if ( m_unconfirmed_multisig_transfers . count ( multisig_id ) = = 0 )
{
// new unconfirmed multisig (see also comments on multisig tranafers handling below)
uint32_t flags_before = it - > second . m_flags ;
it - > second . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ; // mark as spent
it - > second . m_spent_height = 0 ; // height 0 means unconfirmed
WLT_LOG_L0 ( " From tx pool got new unconfirmed multisig out with id: " < < multisig_id < < " tx: " < < get_transaction_hash ( tx ) < < " Marked as SPENT, flags: " < < flags_before < < " -> " < < it - > second . m_flags ) ;
new_multisig_spend_detected = true ;
}
}
}
}
//do final calculations
bool has_in_transfers = false ;
bool has_out_transfers = false ;
for ( const auto & bce : ptc . total_balance_change )
{
if ( bce . second > 0 )
{
has_in_transfers = true ;
}
else if ( bce . second < 0 )
{
has_out_transfers = true ;
}
}
2023-06-19 00:19:25 +02:00
if ( ! is_tx_expired ( tx , ptc . tx_expiration_ts_median ) & & ( new_multisig_spend_detected | | has_in_transfers | | has_out_transfers | | ( currency : : is_derivation_used_to_encrypt ( tx , derivation ) ) ) )
2023-06-15 23:55:22 +02:00
{
m_unconfirmed_in_transfers [ ptc . tx_hash ( ) ] = tx ;
if ( m_unconfirmed_txs . count ( ptc . tx_hash ( ) ) )
return ;
//prepare notification about pending transaction
wallet_public : : wallet_transfer_info & unconfirmed_wti = misc_utils : : get_or_insert_value_initialized ( m_unconfirmed_txs , ptc . tx_hash ( ) ) ;
prepare_wti ( unconfirmed_wti , ptc ) ;
for ( auto tr_index : spend_transfers )
{
if ( tr_index > m_transfers . size ( ) )
{
WLT_LOG_ERROR ( " INTERNAL ERROR: tr_index " < < tr_index < < " more then m_transfers.size()= " < < m_transfers . size ( ) ) ;
continue ;
}
uint32_t flags_before = m_transfers [ tr_index ] . m_flags ;
m_transfers [ tr_index ] . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ;
WLT_LOG_L1 ( " wallet transfer # " < < tr_index < < " is marked as spent, flags: " < < flags_before < < " -> " < < m_transfers [ tr_index ] . m_flags < < " , reason: UNCONFIRMED tx: " < < ptc . tx_hash ( ) ) ;
unconfirmed_wti . selected_indicies . push_back ( tr_index ) ;
}
rise_on_transfer2 ( unconfirmed_wti ) ;
}
}
2018-12-27 18:50:45 +03:00
void wallet2 : : scan_tx_pool ( bool & has_related_alias_in_unconfirmed )
{
//get transaction pool content
currency : : COMMAND_RPC_GET_TX_POOL : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_TX_POOL : : response res = AUTO_VAL_INIT ( res ) ;
bool r = m_core_proxy - > call_COMMAND_RPC_GET_TX_POOL ( req , res ) ;
2020-05-07 23:26:41 +02:00
if ( res . status = = API_RETURN_CODE_BUSY )
2019-11-14 03:12:22 +03:00
throw error : : daemon_busy ( LOCATION_STR , " get_tx_pool " ) ;
2019-11-12 11:33:39 +03:00
if ( ! r )
2019-11-14 03:12:22 +03:00
throw error : : no_connection_to_daemon ( LOCATION_STR , " get_tx_pool " ) ;
2020-05-07 23:26:41 +02:00
THROW_IF_TRUE_WALLET_EX ( res . status ! = API_RETURN_CODE_OK , error : : get_blocks_error , res . status ) ;
2018-12-27 18:50:45 +03:00
//- @#@ ----- debug
# ifdef _DEBUG
std : : stringstream ss ;
ss < < " TXS FROM POOL: " < < ENDL ;
for ( const auto & tx_blob : res . txs )
{
currency : : transaction tx ;
bool r = parse_and_validate_tx_from_blob ( tx_blob , tx ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : tx_parse_error , tx_blob ) ;
crypto : : hash tx_hash = currency : : get_transaction_hash ( tx ) ;
ss < < tx_hash < < ENDL ;
}
ss < < " UNCONFIRMED TXS: " < < ENDL ;
for ( const auto & tx_it : m_unconfirmed_in_transfers )
{
ss < < tx_it . first < < ENDL ;
}
std : : string config_tx = ss . str ( ) ;
# endif
//- @#@ ----- debug
std : : unordered_map < crypto : : hash , currency : : transaction > unconfirmed_in_transfers_local ( std : : move ( m_unconfirmed_in_transfers ) ) ;
2023-06-15 23:55:22 +02:00
multisig_entries_map unconfirmed_multisig_transfers_from_tx_pool ;
2018-12-27 18:50:45 +03:00
has_related_alias_in_unconfirmed = false ;
2020-03-08 20:30:16 +01:00
uint64_t tx_expiration_ts_median = res . tx_expiration_ts_median ; //get_tx_expiration_median();
2018-12-27 18:50:45 +03:00
for ( const auto & tx_blob : res . txs )
{
currency : : transaction tx ;
2023-06-07 23:29:41 +02:00
//money_transfer2_details td;
process_transaction_context ptc ( tx ) ;
2023-06-15 23:55:22 +02:00
ptc . tx_expiration_ts_median = tx_expiration_ts_median ;
ptc . pmultisig_entries = & unconfirmed_multisig_transfers_from_tx_pool ;
2018-12-27 18:50:45 +03:00
bool r = parse_and_validate_tx_from_blob ( tx_blob , tx ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : tx_parse_error , tx_blob ) ;
has_related_alias_in_unconfirmed | = has_related_alias_entry_unconfirmed ( tx ) ;
2023-06-07 23:29:41 +02:00
//crypto::hash tx_hash = currency::get_transaction_hash(tx);
auto it = unconfirmed_in_transfers_local . find ( ptc . tx_hash ( ) ) ;
2018-12-27 18:50:45 +03:00
if ( it ! = unconfirmed_in_transfers_local . end ( ) )
{
m_unconfirmed_in_transfers . insert ( * it ) ;
continue ;
}
2023-06-15 23:55:22 +02:00
handle_unconfirmed_tx ( ptc ) ;
2018-12-27 18:50:45 +03:00
}
// Compare unconfirmed multisigs containers
// IF EXISTS IN unconfirmed_multisig_transfers_in_tx_pool AND EXISTS IN m_unconfirmed_multisig_transfers => already processed, do nothing
// IF EXISTS IN unconfirmed_multisig_transfers_in_tx_pool AND NOT EXISTS IN m_unconfirmed_multisig_transfers => new unconfirmed, add to m_, mark as spent and nofity (see code above)
// IF NOT EXISTS IN unconfirmed_multisig_transfers_in_tx_pool AND EXISTS IN m_unconfirmed_multisig_transfers => EITHER became confirmed (added to the blockchain) OR removed from the pool for some other reason (clear spent flag if there's spent height == 0, means wasn't added to the blockchain)
std : : unordered_set < crypto : : hash > unconfirmed_in_multisig_transfers ;
for ( auto & el : m_unconfirmed_in_transfers )
for ( auto & in : el . second . vin )
if ( in . type ( ) = = typeid ( txin_multisig ) )
unconfirmed_in_multisig_transfers . insert ( boost : : get < txin_multisig > ( in ) . multisig_out_id ) ;
for ( auto & multisig_id : m_unconfirmed_multisig_transfers )
{
if ( unconfirmed_multisig_transfers_from_tx_pool . count ( multisig_id ) ! = 0 )
continue ;
if ( unconfirmed_in_multisig_transfers . count ( multisig_id ) ! = 0 )
continue ;
// Process unconfirmed tx dissapeared from the pool
auto it = m_multisig_transfers . find ( multisig_id ) ;
if ( it ! = m_multisig_transfers . end ( ) & & it - > second . m_flags & WALLET_TRANSFER_DETAIL_FLAG_SPENT )
{
if ( it - > second . m_spent_height = = 0 )
{
// Looks like this tx didn't get into the blockchain, just removed from the pool for some reason.
// So, clear spent flag.
uint32_t flags_before = it - > second . m_flags ;
it - > second . m_flags & = ~ ( WALLET_TRANSFER_DETAIL_FLAG_SPENT ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Unconfirmed multisig out with id: " < < multisig_id < < " was presiously marked as spent and now seems to be removed from the pool, while still not added to the blockchain. Marked as NOT SPENT " < < ENDL
2018-12-27 18:50:45 +03:00
< < " ms source tx: " < < ( it - > second . m_ptx_wallet_info ! = nullptr ? get_transaction_hash ( it - > second . m_ptx_wallet_info - > m_tx ) : null_hash ) < < " flags: " < < flags_before < < " -> " < < it - > second . m_flags ) ;
}
}
}
// Populate updated unconfirmed list of multisign transfers
m_unconfirmed_multisig_transfers . clear ( ) ;
for ( auto & p : unconfirmed_multisig_transfers_from_tx_pool )
m_unconfirmed_multisig_transfers . insert ( p . first ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : on_idle ( )
{
scan_unconfirmed_outdate_tx ( ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : scan_unconfirmed_outdate_tx ( )
{
uint64_t tx_expiration_ts_median = get_tx_expiration_median ( ) ;
uint64_t time_limit = m_core_runtime_config . get_core_time ( ) - CURRENCY_MEMPOOL_TX_LIVETIME ;
for ( auto it = m_unconfirmed_txs . begin ( ) ; it ! = m_unconfirmed_txs . end ( ) ; )
{
bool tx_outdated = it - > second . timestamp < time_limit ;
if ( tx_outdated | | is_tx_expired ( it - > second . tx , tx_expiration_ts_median ) )
{
2019-04-26 19:08:38 +03:00
WLT_LOG_BLUE ( " removing unconfirmed tx " < < it - > second . tx_hash < < " , reason: " < < ( tx_outdated ? " outdated " : " expired " ) < < " , tx_expiration_ts_median= " < < tx_expiration_ts_median , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
//lookup all used transfer and update flags
for ( auto i : it - > second . selected_indicies )
{
if ( i > = m_transfers . size ( ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Wrong index ' " < < i < < " ' in 'selected_indicies', while m_transfers.size() = " < < m_transfers . size ( ) ) ;
2018-12-27 18:50:45 +03:00
continue ;
}
if ( ! m_transfers [ i ] . m_spent_height )
{
uint32_t flags_before = m_transfers [ i ] . m_flags ;
m_transfers [ i ] . m_flags & = ~ ( WALLET_TRANSFER_DETAIL_FLAG_SPENT ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_BLUE ( " mark transfer # " < < i < < " as unspent, flags: " < < flags_before < < " -> " < < m_transfers [ i ] . m_flags < < " , reason: removing unconfirmed tx " < < it - > second . tx_hash , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
}
//fire some event
m_wcallback - > on_transfer_canceled ( it - > second ) ;
m_unconfirmed_txs . erase ( it + + ) ;
} else
it + + ;
}
//scan marked as spent but don't have height (unconfirmed, and actually not unconfirmed)
std : : unordered_set < crypto : : key_image > ki_in_unconfirmed ;
for ( auto it = m_unconfirmed_txs . begin ( ) ; it ! = m_unconfirmed_txs . end ( ) ; it + + )
{
2023-06-08 18:19:10 +02:00
if ( ! it - > second . has_outgoing_entries ( ) )
2018-12-27 18:50:45 +03:00
continue ;
for ( auto & in : it - > second . tx . vin )
{
if ( in . type ( ) = = typeid ( txin_to_key ) )
{
ki_in_unconfirmed . insert ( boost : : get < txin_to_key > ( in ) . k_image ) ;
}
2023-06-08 18:19:10 +02:00
else if ( in . type ( ) = = typeid ( txin_zc_input ) )
{
ki_in_unconfirmed . insert ( boost : : get < txin_zc_input > ( in ) . k_image ) ;
}
2018-12-27 18:50:45 +03:00
}
}
size_t sz = m_transfers . size ( ) ;
for ( size_t i = 0 ; i ! = sz ; i + + )
{
auto & t = m_transfers [ i ] ;
2023-06-08 18:19:10 +02:00
if ( t . m_flags & WALLET_TRANSFER_DETAIL_FLAG_SPENT & & ! t . m_spent_height & & ! static_cast < bool > ( t . m_flags & WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION )
& & ! t . is_htlc ( ) )
2018-12-27 18:50:45 +03:00
{
//check if there is unconfirmed for this transfer is no longer exist?
if ( ! ki_in_unconfirmed . count ( ( t . m_key_image ) ) )
{
uint32_t flags_before = t . m_flags ;
t . m_flags & = ~ ( WALLET_TRANSFER_DETAIL_FLAG_SPENT ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_BLUE ( " Transfer [ " < < i < < " ] marked as unspent, flags: " < < flags_before < < " -> " < < t . m_flags < < " , reason: there is no unconfirmed tx relataed to this key image " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
}
}
return true ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : refresh ( size_t & blocks_fetched , bool & received_money , std : : atomic < bool > & stop )
{
2022-11-26 23:00:32 +01:00
load_whitelisted_tokens_if_not_loaded ( ) ;
2018-12-27 18:50:45 +03:00
received_money = false ;
blocks_fetched = 0 ;
size_t added_blocks = 0 ;
size_t try_count = 0 ;
crypto : : hash last_tx_hash_id = m_transfers . size ( ) ? get_transaction_hash ( m_transfers . back ( ) . m_ptx_wallet_info - > m_tx ) : null_hash ;
2020-04-25 00:30:55 +02:00
m_height_of_start_sync = get_blockchain_current_size ( ) ;
2018-12-27 18:50:45 +03:00
m_last_sync_percent = 0 ;
while ( ! stop . load ( std : : memory_order_relaxed ) )
{
try
{
pull_blocks ( added_blocks , stop ) ;
blocks_fetched + = added_blocks ;
if ( ! added_blocks )
break ;
}
2019-11-12 11:33:39 +03:00
catch ( error : : no_connection_to_daemon & )
2018-12-27 18:50:45 +03:00
{
blocks_fetched + = added_blocks ;
2019-11-12 11:33:39 +03:00
if ( + + try_count > 3 )
2018-12-27 18:50:45 +03:00
return ;
2019-11-12 11:33:39 +03:00
WLT_LOG_L2 ( " no connection to the daemon, wait and try pull_blocks again (try_count: " < < try_count < < " , blocks_fetched: " < < blocks_fetched < < " ) " ) ;
2020-07-22 14:09:55 +03:00
if ( m_wcallback )
m_wcallback - > on_message ( tools : : i_wallet2_callback : : ms_red , " no connection to daemon " ) ;
2019-11-12 11:33:39 +03:00
std : : this_thread : : sleep_for ( std : : chrono : : seconds ( 3 ) ) ;
}
catch ( const std : : exception & e )
{
blocks_fetched + = added_blocks ;
WLT_LOG_ERROR ( " refresh->pull_blocks failed, try_count: " < < try_count < < " , blocks_fetched: " < < blocks_fetched < < " , exception: " < < e . what ( ) ) ;
2020-07-22 14:09:55 +03:00
if ( m_wcallback )
m_wcallback - > on_message ( tools : : i_wallet2_callback : : ms_red , std : : string ( " error on pulling blocks: " ) + e . what ( ) ) ;
2019-11-12 11:33:39 +03:00
return ;
2018-12-27 18:50:45 +03:00
}
}
2019-11-12 11:33:39 +03:00
2018-12-27 18:50:45 +03:00
if ( last_tx_hash_id ! = ( m_transfers . size ( ) ? get_transaction_hash ( m_transfers . back ( ) . m_ptx_wallet_info - > m_tx ) : null_hash ) )
received_money = true ;
if ( blocks_fetched )
{
on_idle ( ) ;
uint64_t tx_expiration_ts_median = get_tx_expiration_median ( ) ;
handle_expiration_list ( tx_expiration_ts_median ) ;
handle_contract_expirations ( tx_expiration_ts_median ) ;
m_found_free_amounts . clear ( ) ;
}
2022-12-06 21:31:17 +01:00
WLT_LOG ( " Refresh done, blocks received: " < < blocks_fetched , blocks_fetched > 0 ? LOG_LEVEL_1 : LOG_LEVEL_2 ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : handle_expiration_list ( uint64_t tx_expiration_ts_median )
{
for ( auto it = m_money_expirations . begin ( ) ; it ! = m_money_expirations . end ( ) ; )
{
if ( tx_expiration_ts_median > it - > expiration_time - TX_EXPIRATION_MEDIAN_SHIFT )
{
for ( auto tr_ind : it - > selected_transfers )
{
auto & transfer = m_transfers [ tr_ind ] ;
if ( ! transfer . m_spent_height )
{
// Clear WALLET_TRANSFER_DETAIL_FLAG_BLOCKED and WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION flags only.
// Note: transfer may still be marked as spent
uint32_t flags_before = transfer . m_flags ;
transfer . m_flags & = ~ ( WALLET_TRANSFER_DETAIL_FLAG_BLOCKED ) ;
transfer . m_flags & = ~ ( WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Unlocked money from expiration_list: transfer # " < < tr_ind < < " , flags: " < < flags_before < < " -> " < < transfer . m_flags < < " , amount: " < < print_money ( transfer . amount ( ) ) < < " , tx: " < <
2018-12-27 18:50:45 +03:00
( transfer . m_ptx_wallet_info ! = nullptr ? get_transaction_hash ( transfer . m_ptx_wallet_info - > m_tx ) : null_hash ) , LOG_LEVEL_0 ) ;
}
}
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " expiration_list entry removed by median: " < < tx_expiration_ts_median < < " , expiration time: " < < it - > expiration_time < < " , related tx: " < < it - > related_tx_id , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
it = m_money_expirations . erase ( it ) ;
}
else
{
it + + ;
}
}
return true ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : handle_contract_expirations ( uint64_t tx_expiration_ts_median )
{
for ( auto & contract : m_contracts )
{
switch ( contract . second . state )
{
2019-08-27 17:36:53 +02:00
case tools : : wallet_public : : escrow_contract_details_basic : : contract_cancel_proposal_sent :
2018-12-27 18:50:45 +03:00
if ( is_tx_expired ( contract . second . cancel_body . tx_cancel_template , tx_expiration_ts_median ) )
2019-08-27 17:36:53 +02:00
change_contract_state ( contract . second , tools : : wallet_public : : escrow_contract_details_basic : : contract_accepted , contract . first , " cancel proposal expiration " ) ;
2018-12-27 18:50:45 +03:00
break ;
2019-08-27 17:36:53 +02:00
case tools : : wallet_public : : escrow_contract_details_basic : : contract_released_cancelled :
2018-12-27 18:50:45 +03:00
if ( contract . second . height = = 0 & & is_tx_expired ( contract . second . cancel_body . tx_cancel_template , tx_expiration_ts_median ) )
2019-08-27 17:36:53 +02:00
change_contract_state ( contract . second , tools : : wallet_public : : escrow_contract_details_basic : : contract_accepted , contract . first , " cancel acceptance expiration " ) ;
2018-12-27 18:50:45 +03:00
break ;
}
}
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : refresh ( size_t & blocks_fetched , bool & received_money , bool & ok , std : : atomic < bool > & stop )
{
try
{
refresh ( blocks_fetched , received_money , stop ) ;
ok = true ;
}
catch ( . . . )
{
ok = false ;
}
return ok ;
}
2020-04-29 01:23:48 +02:00
2020-04-25 00:30:55 +02:00
//----------------------------------------------------------------------------------------------------
2020-05-02 22:53:52 +02:00
uint64_t wallet2 : : detach_from_block_ids ( uint64_t including_height )
2020-04-25 00:30:55 +02:00
{
//calculate number of erased blocks
2020-05-02 22:53:52 +02:00
uint64_t blocks_detached = get_blockchain_current_size ( ) - including_height ;
2020-04-25 00:30:55 +02:00
//id at height should be kept, the rest - erased
2020-05-02 22:53:52 +02:00
m_chain . detach ( including_height ) ;
2020-04-25 00:30:55 +02:00
return blocks_detached ;
}
2018-12-27 18:50:45 +03:00
//----------------------------------------------------------------------------------------------------
2020-05-25 15:05:25 +03:00
void wallet2 : : remove_transfer_from_amount_gindex_map ( uint64_t tid )
{
for ( auto it = m_amount_gindex_to_transfer_id . begin ( ) ; it ! = m_amount_gindex_to_transfer_id . end ( ) ; )
{
if ( it - > second = = tid )
it = m_amount_gindex_to_transfer_id . erase ( it ) ;
else
+ + it ;
}
}
//----------------------------------------------------------------------------------------------------
2020-05-02 22:53:52 +02:00
void wallet2 : : detach_blockchain ( uint64_t including_height )
2018-12-27 18:50:45 +03:00
{
2020-05-02 22:53:52 +02:00
WLT_LOG_L0 ( " Detaching blockchain on height " < < including_height ) ;
2018-12-27 18:50:45 +03:00
size_t transfers_detached = 0 ;
2019-04-26 19:08:38 +03:00
// rollback incoming transfers from detaching subchain
2018-12-27 18:50:45 +03:00
{
2020-05-02 22:53:52 +02:00
auto it = std : : find_if ( m_transfers . begin ( ) , m_transfers . end ( ) , [ & ] ( const transfer_details & td ) { return td . m_ptx_wallet_info - > m_block_height > = including_height ; } ) ;
2018-12-27 18:50:45 +03:00
if ( it ! = m_transfers . end ( ) )
{
size_t i_start = it - m_transfers . begin ( ) ;
for ( size_t i = i_start ; i ! = m_transfers . size ( ) ; i + + )
{
2021-03-07 23:18:19 +03:00
//check for htlc
2022-05-20 21:32:27 +02:00
if ( m_transfers [ i ] . m_ptx_wallet_info - > m_tx . vout [ m_transfers [ i ] . m_internal_output_index ] . type ( ) = = typeid ( tx_out_bare ) & &
boost : : get < tx_out_bare > ( m_transfers [ i ] . m_ptx_wallet_info - > m_tx . vout [ m_transfers [ i ] . m_internal_output_index ] ) . target . type ( ) = = typeid ( txout_htlc ) )
2021-03-07 23:18:19 +03:00
{
//need to find an entry in m_htlc and remove it
2022-05-20 21:32:27 +02:00
const txout_htlc & hltc = boost : : get < txout_htlc > ( boost : : get < tx_out_bare > ( m_transfers [ i ] . m_ptx_wallet_info - > m_tx . vout [ m_transfers [ i ] . m_internal_output_index ] ) . target ) ;
2021-03-07 23:18:19 +03:00
uint64_t expiration_height = m_transfers [ i ] . m_ptx_wallet_info - > m_block_height + hltc . expiration ;
auto pair_of_it = m_htlcs . equal_range ( expiration_height ) ;
bool found = false ;
for ( auto it = pair_of_it . first ; it ! = pair_of_it . second ; it + + )
{
if ( it - > second . transfer_index = = i )
{
found = true ;
m_htlcs . erase ( it ) ;
break ;
}
}
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( found , " Internal error: not found record in m_htlcs during rollback " ) ;
}
2020-09-29 23:42:09 +03:00
if ( ! ( m_transfers [ i ] . m_key_image = = null_ki & & is_watch_only ( ) ) )
{
auto it_ki = m_key_images . find ( m_transfers [ i ] . m_key_image ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( it_ki ! = m_key_images . end ( ) , " key image " < < m_transfers [ i ] . m_key_image < < " not found " ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( m_transfers [ i ] . m_ptx_wallet_info - > m_block_height > = including_height , " transfer # " < < i < < " block height is less than " < < including_height ) ;
m_key_images . erase ( it_ki ) ;
}
2020-05-25 15:05:25 +03:00
remove_transfer_from_amount_gindex_map ( i ) ;
2018-12-27 18:50:45 +03:00
+ + transfers_detached ;
}
m_transfers . erase ( it , m_transfers . end ( ) ) ;
}
}
2021-02-28 23:36:38 +01:00
for ( uint64_t i = get_top_block_height ( ) ; i ! = including_height - 1 & & i ! = 0 ; i - - )
{
unprocess_htlc_triggers_on_block_removed ( i ) ;
}
2020-05-02 22:53:52 +02:00
size_t blocks_detached = detach_from_block_ids ( including_height ) ;
2018-12-27 18:50:45 +03:00
//rollback spends
2019-04-26 19:08:38 +03:00
// do not clear spent flag in spent transfers as corresponding txs are most likely in the pool
// they will be moved into m_unconfirmed_txs for clearing in future (if tx will expire of removed from pool)
2018-12-27 18:50:45 +03:00
for ( size_t i = 0 , sz = m_transfers . size ( ) ; i < sz ; + + i )
{
auto & tr = m_transfers [ i ] ;
2020-05-02 22:53:52 +02:00
if ( tr . m_spent_height > = including_height )
2018-12-27 18:50:45 +03:00
{
2019-04-26 19:08:38 +03:00
WLT_LOG_BLUE ( " Transfer [ " < < i < < " ] spent height: " < < tr . m_spent_height < < " -> 0, reason: detaching blockchain " , LOG_LEVEL_1 ) ;
2018-12-27 18:50:45 +03:00
tr . m_spent_height = 0 ;
2021-01-24 21:00:43 +01:00
//check if it's hltc contract
2018-12-27 18:50:45 +03:00
}
}
//rollback tranfers history
auto tr_hist_it = m_transfer_history . rend ( ) ;
for ( auto it = m_transfer_history . rbegin ( ) ; it ! = m_transfer_history . rend ( ) ; it + + )
{
2020-05-02 22:53:52 +02:00
if ( it - > height < including_height )
2018-12-27 18:50:45 +03:00
break ;
tr_hist_it = it ; // note that tr_hist_it->height >= height
}
2019-04-30 15:51:12 +03:00
2018-12-27 18:50:45 +03:00
if ( tr_hist_it ! = m_transfer_history . rend ( ) )
2019-04-26 19:08:38 +03:00
{
auto it_from = - - tr_hist_it . base ( ) ;
2019-04-30 15:51:12 +03:00
// before removing wti from m_transfer_history put it into m_unconfirmed_txs as txs from detached blocks are most likely be moved into the pool
2019-04-26 19:08:38 +03:00
for ( auto it = it_from ; it ! = m_transfer_history . end ( ) ; + + it )
{
2019-04-30 15:51:12 +03:00
// skip coinbase txs as they are not expected to go into the pool
if ( is_coinbase ( it - > tx ) )
{
continue ;
}
2019-04-26 19:08:38 +03:00
if ( ! m_unconfirmed_txs . insert ( std : : make_pair ( it - > tx_hash , * it ) ) . second )
{
WLT_LOG_ERROR ( " can't move wti from transfer history to unronfirmed txs because such it is already here, tx hash: " < < it - > tx_hash ) ;
}
}
m_transfer_history . erase ( it_from , m_transfer_history . end ( ) ) ;
}
2018-12-27 18:50:45 +03:00
//rollback payments
for ( auto it = m_payments . begin ( ) ; it ! = m_payments . end ( ) ; )
{
2020-05-02 22:53:52 +02:00
if ( including_height < = it - > second . m_block_height )
2018-12-27 18:50:45 +03:00
it = m_payments . erase ( it ) ;
else
+ + it ;
}
2022-09-29 19:18:05 +02:00
//asset descriptors
2023-08-14 22:32:52 +02:00
handle_rollback_events ( including_height ) ;
2022-09-29 19:18:05 +02:00
2020-05-02 22:53:52 +02:00
WLT_LOG_L0 ( " Detached blockchain on height " < < including_height < < " , transfers detached " < < transfers_detached < < " , blocks detached " < < blocks_detached ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2023-08-14 22:32:52 +02:00
void wallet2 : : operator ( ) ( const asset_register_event & e )
{
auto it = m_own_asset_descriptors . find ( e . asset_id ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( it ! = m_own_asset_descriptors . end ( ) , " asset_id " < < e . asset_id < < " not found during rolling asset_register_event " ) ;
m_own_asset_descriptors . erase ( it ) ;
}
void wallet2 : : operator ( ) ( const asset_update_event & e )
{
auto it = m_own_asset_descriptors . find ( e . asset_id ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( it ! = m_own_asset_descriptors . end ( ) , " asset_id " < < e . asset_id < < " not found during rolling asset_update_event " ) ;
it - > second = e . own_context ;
}
void wallet2 : : operator ( ) ( const asset_unown_event & e )
{
auto it = m_own_asset_descriptors . find ( e . asset_id ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( it = = m_own_asset_descriptors . end ( ) , " asset_id " < < e . asset_id < < " unexpectedly found during rolling asset_unown_event " ) ;
m_own_asset_descriptors [ e . asset_id ] = e . own_context ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : handle_rollback_events ( uint64_t including_height )
{
while ( m_rollback_events . size ( ) & & m_rollback_events . back ( ) . first > = including_height )
{
boost : : apply_visitor ( * this , m_rollback_events . back ( ) ) ;
m_rollback_events . pop_back ( ) ;
}
}
//----------------------------------------------------------------------------------------------------
m_rollback_events
2018-12-27 18:50:45 +03:00
bool wallet2 : : deinit ( )
{
2019-07-25 19:22:25 +02:00
m_wcallback . reset ( ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : clear ( )
{
reset_all ( ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : reset_all ( )
{
2020-04-26 01:49:57 +02:00
//m_blockchain.clear();
2020-04-29 01:23:48 +02:00
m_chain . clear ( ) ;
2018-12-27 18:50:45 +03:00
m_transfers . clear ( ) ;
2020-05-25 15:05:25 +03:00
m_amount_gindex_to_transfer_id . clear ( ) ;
2018-12-27 18:50:45 +03:00
m_key_images . clear ( ) ;
2019-04-08 14:16:11 +03:00
// m_pending_key_images is not cleared intentionally
2018-12-27 18:50:45 +03:00
m_unconfirmed_in_transfers . clear ( ) ;
m_unconfirmed_txs . clear ( ) ;
m_unconfirmed_multisig_transfers . clear ( ) ;
2019-04-03 12:48:09 +03:00
// m_tx_keys is not cleared intentionally, considered to be safe
2018-12-27 18:50:45 +03:00
m_multisig_transfers . clear ( ) ;
m_payments . clear ( ) ;
m_transfer_history . clear ( ) ;
//m_account = AUTO_VAL_INIT(m_account);
2020-04-29 01:23:48 +02:00
//m_local_bc_size = 1; //including genesis
2018-12-27 18:50:45 +03:00
m_last_bc_timestamp = 0 ;
m_height_of_start_sync = 0 ;
m_last_sync_percent = 0 ;
2019-07-20 14:36:12 +02:00
m_last_pow_block_h = 0 ;
2020-06-03 21:29:43 +02:00
m_current_wallet_file_size = 0 ;
2023-05-29 22:28:13 +02:00
m_custom_assets . clear ( ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2020-07-19 00:31:55 +02:00
bool wallet2 : : store_keys ( std : : string & buff , const std : : string & password , wallet2 : : keys_file_data & keys_file_data , bool store_as_watch_only /* = false */ )
2018-12-27 18:50:45 +03:00
{
2019-04-08 14:16:11 +03:00
currency : : account_base acc = m_account ;
if ( store_as_watch_only )
acc . make_account_watch_only ( ) ;
2018-12-27 18:50:45 +03:00
std : : string account_data ;
2019-04-08 14:16:11 +03:00
bool r = epee : : serialization : : store_t_to_binary ( acc , account_data ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " failed to serialize wallet keys " ) ;
2019-04-08 14:16:11 +03:00
2018-12-27 18:50:45 +03:00
crypto : : chacha8_key key ;
crypto : : generate_chacha8_key ( password , key ) ;
std : : string cipher ;
cipher . resize ( account_data . size ( ) ) ;
keys_file_data . iv = crypto : : rand < crypto : : chacha8_iv > ( ) ;
crypto : : chacha8 ( account_data . data ( ) , account_data . size ( ) , key , keys_file_data . iv , & cipher [ 0 ] ) ;
keys_file_data . account_data = cipher ;
r = : : serialization : : dump_binary ( keys_file_data , buff ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : backup_keys ( const std : : string & path )
{
std : : string buff ;
2020-07-19 00:31:55 +02:00
wallet2 : : keys_file_data keys_file_data = AUTO_VAL_INIT ( keys_file_data ) ;
bool r = store_keys ( buff , m_password , keys_file_data ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " Failed to store keys " ) ;
2018-12-27 18:50:45 +03:00
r = file_io_utils : : save_string_to_file ( path , buff ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " Failed to save_string_to_file at store keys " ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : reset_password ( const std : : string & pass )
{
m_password = pass ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : is_password_valid ( const std : : string & pass )
{
return pass = = m_password ;
}
//----------------------------------------------------------------------------------------------------
namespace
{
bool verify_keys ( const crypto : : secret_key & sec , const crypto : : public_key & expected_pub )
{
crypto : : public_key pub ;
bool r = crypto : : secret_key_to_public_key ( sec , pub ) ;
return r & & expected_pub = = pub ;
}
}
//----------------------------------------------------------------------------------------------------
2019-02-27 00:59:02 +03:00
void wallet2 : : init_log_prefix ( )
{
m_log_prefix = m_account . get_public_address_str ( ) . substr ( 0 , 6 ) ;
}
//----------------------------------------------------------------------------------------------------
2019-04-03 12:48:09 +03:00
void wallet2 : : load_keys2ki ( bool create_if_not_exist , bool & need_to_resync )
{
m_pending_key_images_file_container . close ( ) ; // just in case it was opened
bool pki_corrupted = false ;
std : : string reason ;
bool ok = m_pending_key_images_file_container . open ( m_pending_ki_file , create_if_not_exist , & pki_corrupted , & reason ) ;
THROW_IF_FALSE_WALLET_EX ( ok , error : : file_not_found , m_log_prefix + " : error opening file " + string_encoding : : convert_to_ansii ( m_pending_ki_file ) ) ;
if ( pki_corrupted )
{
WLT_LOG_ERROR ( " file " < < string_encoding : : convert_to_ansii ( m_pending_ki_file ) < < " is corrupted! " < < reason ) ;
}
if ( m_pending_key_images . size ( ) < m_pending_key_images_file_container . size ( ) )
{
WLT_LOG_RED ( " m_pending_key_images size: " < < m_pending_key_images . size ( ) < < " is LESS than m_pending_key_images_file_container size: " < < m_pending_key_images_file_container . size ( ) , LOG_LEVEL_0 ) ;
WLT_LOG_L0 ( " Restoring m_pending_key_images from file container... " ) ;
m_pending_key_images . clear ( ) ;
for ( size_t i = 0 , size = m_pending_key_images_file_container . size ( ) ; i < size ; + + i )
{
2019-09-24 18:30:25 +02:00
out_key_to_ki item = AUTO_VAL_INIT_T ( out_key_to_ki ) ;
2019-04-03 12:48:09 +03:00
ok = m_pending_key_images_file_container . get_item ( i , item ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ok , " m_pending_key_images_file_container.get_item() failed for index " < < i < < " , size: " < < m_pending_key_images_file_container . size ( ) ) ;
ok = m_pending_key_images . insert ( std : : make_pair ( item . out_key , item . key_image ) ) . second ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ok , " m_pending_key_images.insert failed for index " < < i < < " , size: " < < m_pending_key_images_file_container . size ( ) ) ;
WLT_LOG_L2 ( " pending key image restored: ( " < < item . out_key < < " , " < < item . key_image < < " ) " ) ;
}
WLT_LOG_L0 ( m_pending_key_images . size ( ) < < " elements restored, requesting full wallet resync " ) ;
WLT_LOG_L0 ( " m_pending_key_images size: " < < m_pending_key_images . size ( ) < < " , m_pending_key_images_file_container size: " < < m_pending_key_images_file_container . size ( ) ) ;
need_to_resync = true ;
}
else if ( m_pending_key_images . size ( ) > m_pending_key_images_file_container . size ( ) )
{
WLT_LOG_RED ( " m_pending_key_images size: " < < m_pending_key_images . size ( ) < < " is GREATER than m_pending_key_images_file_container size: " < < m_pending_key_images_file_container . size ( ) , LOG_LEVEL_0 ) ;
WLT_LOG_RED ( " UNRECOVERABLE ERROR, wallet stops " , LOG_LEVEL_0 ) ;
2019-12-17 12:22:36 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( false , " UNRECOVERABLE ERROR, wallet stops: m_pending_key_images > m_pending_key_images_file_container " < < ENDL < < " Missing/wrong " < < string_encoding : : convert_to_ansii ( m_pending_ki_file ) < < " file? " ) ;
2019-04-03 12:48:09 +03:00
}
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : prepare_file_names ( const std : : wstring & file_path )
{
m_wallet_file = file_path ;
m_pending_ki_file = string_tools : : cut_off_extension ( m_wallet_file ) + L " .outkey2ki " ;
// make sure file path is accessible and exists
boost : : filesystem : : path pp = boost : : filesystem : : path ( file_path ) . parent_path ( ) ;
if ( ! pp . empty ( ) )
boost : : filesystem : : create_directories ( pp ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2020-07-19 00:31:55 +02:00
void wallet2 : : load_keys ( const std : : string & buff , const std : : string & password , uint64_t file_signature , keys_file_data & kf_data )
2018-12-27 18:50:45 +03:00
{
2020-05-19 20:12:43 +03:00
bool r = false ;
if ( file_signature = = WALLET_FILE_SIGNATURE_OLD )
{
wallet2 : : keys_file_data_old kf_data_old ;
r = : : serialization : : parse_binary ( buff , kf_data_old ) ;
kf_data = wallet2 : : keys_file_data : : from_old ( kf_data_old ) ;
}
else if ( file_signature = = WALLET_FILE_SIGNATURE_V2 )
{
r = : : serialization : : parse_binary ( buff , kf_data ) ;
}
2018-12-27 18:50:45 +03:00
THROW_IF_TRUE_WALLET_EX ( ! r , error : : wallet_internal_error , " internal error: failed to deserialize " ) ;
crypto : : chacha8_key key ;
crypto : : generate_chacha8_key ( password , key ) ;
std : : string account_data ;
2020-05-19 20:12:43 +03:00
account_data . resize ( kf_data . account_data . size ( ) ) ;
crypto : : chacha8 ( kf_data . account_data . data ( ) , kf_data . account_data . size ( ) , key , kf_data . iv , & account_data [ 0 ] ) ;
2018-12-27 18:50:45 +03:00
const currency : : account_keys & keys = m_account . get_keys ( ) ;
r = epee : : serialization : : load_t_from_binary ( m_account , account_data ) ;
2020-04-22 23:30:03 +03:00
r = r & & verify_keys ( keys . view_secret_key , keys . account_address . view_public_key ) ;
if ( keys . spend_secret_key = = currency : : null_skey )
2019-04-03 12:48:09 +03:00
m_watch_only = true ;
else
2020-04-22 23:30:03 +03:00
r = r & & verify_keys ( keys . spend_secret_key , keys . account_address . spend_public_key ) ;
2018-12-27 18:50:45 +03:00
if ( ! r )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Wrong password for wallet " < < string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
2018-12-27 18:50:45 +03:00
tools : : error : : throw_wallet_ex < error : : invalid_password > ( std : : string ( __FILE__ " : " STRINGIZE ( __LINE__ ) ) ) ;
}
2019-02-27 00:59:02 +03:00
init_log_prefix ( ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : assign_account ( const currency : : account_base & acc )
{
clear ( ) ;
m_account = acc ;
2019-02-27 00:59:02 +03:00
init_log_prefix ( ) ;
2020-10-02 16:19:08 +03:00
if ( m_account . is_watch_only ( ) )
m_watch_only = true ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2020-05-02 23:59:37 +03:00
void wallet2 : : generate ( const std : : wstring & path , const std : : string & pass , bool auditable_wallet )
2018-12-27 18:50:45 +03:00
{
2019-10-02 03:41:24 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( validate_password ( pass ) , " new wallet generation failed: password contains forbidden characters " )
2018-12-27 18:50:45 +03:00
clear ( ) ;
2019-04-03 12:48:09 +03:00
prepare_file_names ( path ) ;
2019-10-09 16:01:33 +03:00
2018-12-27 18:50:45 +03:00
m_password = pass ;
2020-05-02 23:59:37 +03:00
m_account . generate ( auditable_wallet ) ;
2019-02-27 00:59:02 +03:00
init_log_prefix ( ) ;
2018-12-27 18:50:45 +03:00
boost : : system : : error_code ignored_ec ;
THROW_IF_TRUE_WALLET_EX ( boost : : filesystem : : exists ( m_wallet_file , ignored_ec ) , error : : file_exists , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
2020-05-27 23:04:23 +03:00
if ( m_watch_only & & ! auditable_wallet )
2019-04-08 14:16:11 +03:00
{
bool stub ;
load_keys2ki ( true , stub ) ;
}
2018-12-27 18:50:45 +03:00
store ( ) ;
}
//----------------------------------------------------------------------------------------------------
2020-11-18 21:20:08 +01:00
void wallet2 : : restore ( const std : : wstring & path , const std : : string & pass , const std : : string & seed_or_tracking_seed , bool tracking_wallet , const std : : string & seed_password )
2018-12-27 18:50:45 +03:00
{
2020-05-27 23:49:34 +03:00
bool r = false ;
2018-12-27 18:50:45 +03:00
clear ( ) ;
2019-04-03 12:48:09 +03:00
prepare_file_names ( path ) ;
2018-12-27 18:50:45 +03:00
m_password = pass ;
2020-05-27 23:49:34 +03:00
2020-07-22 16:14:17 +03:00
if ( tracking_wallet )
2020-05-27 23:49:34 +03:00
{
2020-06-29 22:55:25 +03:00
r = m_account . restore_from_tracking_seed ( seed_or_tracking_seed ) ;
2020-05-27 23:49:34 +03:00
init_log_prefix ( ) ;
2020-07-22 16:14:17 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( r , " Could not load tracking wallet from a given seed: invalid tracking seed " ) ;
2020-06-01 16:09:36 +03:00
m_watch_only = true ;
2020-05-27 23:49:34 +03:00
}
else
{
2020-11-18 21:20:08 +01:00
r = m_account . restore_from_seed_phrase ( seed_or_tracking_seed , seed_password ) ;
2020-05-27 23:49:34 +03:00
init_log_prefix ( ) ;
THROW_IF_FALSE_WALLET_EX ( r , error : : wallet_wrong_seed_error , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
}
2018-12-27 18:50:45 +03:00
boost : : system : : error_code ignored_ec ;
THROW_IF_TRUE_WALLET_EX ( boost : : filesystem : : exists ( m_wallet_file , ignored_ec ) , error : : file_exists , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
store ( ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : check_connection ( )
{
return m_core_proxy - > check_connection ( ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : load ( const std : : wstring & wallet_ , const std : : string & password )
{
clear ( ) ;
2019-04-03 12:48:09 +03:00
prepare_file_names ( wallet_ ) ;
2019-10-09 16:01:33 +03:00
2018-12-27 18:50:45 +03:00
m_password = password ;
std : : string keys_buff ;
std : : string body_buff ;
boost : : system : : error_code e ;
bool exists = boost : : filesystem : : exists ( m_wallet_file , e ) ;
THROW_IF_TRUE_WALLET_EX ( e | | ! exists , error : : file_not_found , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
boost : : filesystem : : ifstream data_file ;
data_file . open ( m_wallet_file , std : : ios_base : : binary | std : : ios_base : : in ) ;
2020-05-19 20:12:43 +03:00
THROW_IF_TRUE_WALLET_EX ( data_file . fail ( ) , error : : file_read_error , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
2018-12-27 18:50:45 +03:00
wallet_file_binary_header wbh = AUTO_VAL_INIT ( wbh ) ;
data_file . read ( ( char * ) & wbh , sizeof ( wbh ) ) ;
2020-05-19 20:12:43 +03:00
THROW_IF_TRUE_WALLET_EX ( data_file . fail ( ) , error : : file_read_error , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
2018-12-27 18:50:45 +03:00
2020-05-19 20:12:43 +03:00
THROW_IF_TRUE_WALLET_EX ( wbh . m_signature ! = WALLET_FILE_SIGNATURE_OLD & & wbh . m_signature ! = WALLET_FILE_SIGNATURE_V2 , error : : file_read_error , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
2020-07-19 00:31:55 +02:00
THROW_IF_TRUE_WALLET_EX (
2020-05-19 20:12:43 +03:00
wbh . m_cb_keys > WALLET_FILE_MAX_KEYS_SIZE , error : : file_read_error , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
2018-12-27 18:50:45 +03:00
keys_buff . resize ( wbh . m_cb_keys ) ;
data_file . read ( ( char * ) keys_buff . data ( ) , wbh . m_cb_keys ) ;
2020-07-19 00:31:55 +02:00
wallet2 : : keys_file_data kf_data = AUTO_VAL_INIT ( kf_data ) ;
load_keys ( keys_buff , password , wbh . m_signature , kf_data ) ;
2018-12-27 18:50:45 +03:00
2020-07-19 00:31:55 +02:00
bool need_to_resync = false ;
2021-06-02 23:52:18 +03:00
if ( wbh . m_ver = = WALLET_FILE_BINARY_HEADER_VERSION_INITAL )
2020-07-19 00:31:55 +02:00
{
2021-06-07 15:00:58 +03:00
// old WALLET_FILE_BINARY_HEADER_VERSION version means no encryption
2020-07-19 00:31:55 +02:00
need_to_resync = ! tools : : portable_unserialize_obj_from_stream ( * this , data_file ) ;
2021-06-03 15:15:05 +03:00
WLT_LOG_L0 ( " Detected format: WALLET_FILE_BINARY_HEADER_VERSION_INITAL(need_to_resync= " < < need_to_resync < < " ) " ) ;
2020-07-19 00:31:55 +02:00
}
2021-06-02 23:52:18 +03:00
else if ( wbh . m_ver = = WALLET_FILE_BINARY_HEADER_VERSION_2 )
{
tools : : encrypt_chacha_in_filter decrypt_filter ( password , kf_data . iv ) ;
boost : : iostreams : : filtering_istream in ;
in . push ( decrypt_filter ) ;
in . push ( data_file ) ;
need_to_resync = ! tools : : portable_unserialize_obj_from_stream ( * this , in ) ;
2021-06-03 15:15:05 +03:00
WLT_LOG_L0 ( " Detected format: WALLET_FILE_BINARY_HEADER_VERSION_2(need_to_resync= " < < need_to_resync < < " ) " ) ;
2021-06-02 23:52:18 +03:00
}
else
{
WLT_LOG_L0 ( " Unknown wallet body version( " < < wbh . m_ver < < " ), resync initiated. " ) ;
need_to_resync = true ;
}
2020-07-19 00:31:55 +02:00
2018-12-27 18:50:45 +03:00
2020-05-27 23:04:23 +03:00
if ( m_watch_only & & ! is_auditable ( ) )
2019-04-03 12:48:09 +03:00
load_keys2ki ( true , need_to_resync ) ;
2021-05-28 20:36:17 +02:00
boost : : system : : error_code ec = AUTO_VAL_INIT ( ec ) ;
m_current_wallet_file_size = boost : : filesystem : : file_size ( wallet_ , ec ) ;
WLT_LOG_L0 ( " Loaded wallet file " < < ( m_watch_only ? " (WATCH ONLY) " : " " ) < < string_encoding : : convert_to_ansii ( m_wallet_file )
< < " with public address: " < < m_account . get_public_address_str ( )
< < " , file_size= " < < m_current_wallet_file_size
< < " , blockchain_size: " < < m_chain . get_blockchain_current_size ( )
) ;
2021-06-03 15:15:05 +03:00
WLT_LOG_L0 ( " [LOADING]Blockchain shortener state: " < < ENDL < < m_chain . get_internal_state_text ( ) ) ;
2021-05-28 20:36:17 +02:00
2020-05-25 15:05:25 +03:00
WLT_LOG_L0 ( " (after loading: pending_key_images: " < < m_pending_key_images . size ( ) < < " , pki file elements: " < < m_pending_key_images_file_container . size ( ) < < " , tx_keys: " < < m_tx_keys . size ( ) < < " ) " ) ;
2019-04-03 12:48:09 +03:00
if ( need_to_resync )
2018-12-27 18:50:45 +03:00
{
reset_history ( ) ;
2020-05-25 15:05:25 +03:00
WLT_LOG_L0 ( " Unable to load history data from wallet file, wallet will be resynced! " ) ;
2020-06-03 21:29:43 +02:00
}
2020-05-25 15:05:25 +03:00
THROW_IF_TRUE_WALLET_EX ( need_to_resync , error : : wallet_load_notice_wallet_restored , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : store ( )
{
2019-04-12 09:48:33 +03:00
store ( m_wallet_file , m_password ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2019-04-13 01:10:29 +03:00
void wallet2 : : store ( const std : : wstring & path )
{
store ( path , m_password ) ;
}
//----------------------------------------------------------------------------------------------------
2019-04-12 09:48:33 +03:00
void wallet2 : : store ( const std : : wstring & path_to_save , const std : : string & password )
2018-12-27 18:50:45 +03:00
{
2019-04-03 12:48:09 +03:00
LOG_PRINT_L0 ( " (before storing: pending_key_images: " < < m_pending_key_images . size ( ) < < " , pki file elements: " < < m_pending_key_images_file_container . size ( ) < < " , tx_keys: " < < m_tx_keys . size ( ) < < " ) " ) ;
2019-10-09 16:01:33 +03:00
std : : string ascii_path_to_save = epee : : string_encoding : : convert_to_ansii ( path_to_save ) ;
2018-12-27 18:50:45 +03:00
//prepare data
std : : string keys_buff ;
2020-07-19 00:31:55 +02:00
wallet2 : : keys_file_data keys_file_data = AUTO_VAL_INIT ( keys_file_data ) ;
bool r = store_keys ( keys_buff , password , keys_file_data , m_watch_only ) ;
2019-10-09 16:01:33 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( r , " failed to store_keys for wallet " < < ascii_path_to_save ) ;
2018-12-27 18:50:45 +03:00
//store data
2019-10-09 16:01:33 +03:00
wallet_file_binary_header wbh = AUTO_VAL_INIT ( wbh ) ;
2020-05-19 20:12:43 +03:00
wbh . m_signature = WALLET_FILE_SIGNATURE_V2 ;
2018-12-27 18:50:45 +03:00
wbh . m_cb_keys = keys_buff . size ( ) ;
//@#@ change it to proper
2021-06-03 18:46:44 +03:00
wbh . m_ver = WALLET_FILE_BINARY_HEADER_VERSION_2 ;
2018-12-27 18:50:45 +03:00
std : : string header_buff ( ( const char * ) & wbh , sizeof ( wbh ) ) ;
2019-10-09 16:01:33 +03:00
uint64_t ts = m_core_runtime_config . get_core_time ( ) ;
// save to tmp file, then rename
boost : : filesystem : : path tmp_file_path = boost : : filesystem : : path ( path_to_save ) ;
tmp_file_path + = L " .newtmp_ " + std : : to_wstring ( ts ) ;
2018-12-27 18:50:45 +03:00
boost : : filesystem : : ofstream data_file ;
2019-10-09 16:01:33 +03:00
data_file . open ( tmp_file_path , std : : ios_base : : binary | std : : ios_base : : out | std : : ios : : trunc ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ! data_file . fail ( ) , " failed to open binary wallet file for saving: " < < tmp_file_path . string ( ) ) ;
2018-12-27 18:50:45 +03:00
data_file < < header_buff < < keys_buff ;
2019-10-09 16:01:33 +03:00
2019-12-02 15:08:45 +03:00
WLT_LOG_L0 ( " Storing to temporary file " < < tmp_file_path . string ( ) < < " ... " ) ;
2020-07-19 00:31:55 +02:00
//creating encryption stream
tools : : encrypt_chacha_out_filter decrypt_filter ( m_password , keys_file_data . iv ) ;
boost : : iostreams : : filtering_ostream out ;
out . push ( decrypt_filter ) ;
out . push ( data_file ) ;
2019-04-08 14:16:11 +03:00
2021-06-03 18:46:44 +03:00
r = tools : : portble_serialize_obj_to_stream ( * this , out ) ;
2019-10-09 16:01:33 +03:00
if ( ! r )
{
2019-10-24 08:13:37 +03:00
data_file . close ( ) ;
2019-10-09 16:01:33 +03:00
boost : : filesystem : : remove ( tmp_file_path ) ; // remove tmp file if smth went wrong
2019-10-24 08:13:37 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( false , " IO error while storing wallet to " < < tmp_file_path . string ( ) < < " (portble_serialize_obj_to_stream failed) " ) ;
2019-10-09 16:01:33 +03:00
}
2018-12-27 18:50:45 +03:00
data_file . flush ( ) ;
data_file . close ( ) ;
2021-05-28 20:36:17 +02:00
boost : : uintmax_t tmp_file_size = boost : : filesystem : : file_size ( tmp_file_path ) ;
WLT_LOG_L0 ( " Stored successfully to temporary file " < < tmp_file_path . string ( ) < < " , file size= " < < tmp_file_size ) ;
2019-12-02 15:08:45 +03:00
2021-06-03 15:15:05 +03:00
WLT_LOG_L0 ( " [LOADING]Blockchain shortener state: " < < ENDL < < m_chain . get_internal_state_text ( ) ) ;
2019-10-09 16:01:33 +03:00
// for the sake of safety perform a double-renaming: wallet file -> old tmp, new tmp -> wallet file, remove old tmp
boost : : filesystem : : path tmp_old_file_path = boost : : filesystem : : path ( path_to_save ) ;
tmp_old_file_path + = L " .oldtmp_ " + std : : to_wstring ( ts ) ;
if ( boost : : filesystem : : is_regular_file ( path_to_save ) )
2019-12-02 15:08:45 +03:00
{
2019-10-09 16:01:33 +03:00
boost : : filesystem : : rename ( path_to_save , tmp_old_file_path ) ;
2019-12-02 15:08:45 +03:00
WLT_LOG_L1 ( " Renamed: " < < ascii_path_to_save < < " -> " < < tmp_old_file_path . string ( ) ) ;
}
2019-10-09 16:01:33 +03:00
boost : : filesystem : : rename ( tmp_file_path , path_to_save ) ;
2019-12-02 15:08:45 +03:00
WLT_LOG_L1 ( " Renamed: " < < tmp_file_path . string ( ) < < " -> " < < ascii_path_to_save ) ;
if ( boost : : filesystem : : remove ( tmp_old_file_path ) )
{
WLT_LOG_L1 ( " Removed temporary file: " < < tmp_old_file_path . string ( ) ) ;
}
bool path_to_save_exists = boost : : filesystem : : is_regular_file ( path_to_save ) ;
bool tmp_file_path_exists = boost : : filesystem : : is_regular_file ( tmp_file_path ) ;
bool tmp_old_file_path_exists = boost : : filesystem : : is_regular_file ( tmp_old_file_path ) ;
2020-06-03 21:29:43 +02:00
boost : : system : : error_code ec = AUTO_VAL_INIT ( ec ) ;
m_current_wallet_file_size = boost : : filesystem : : file_size ( path_to_save , ec ) ;
2019-12-02 15:08:45 +03:00
if ( path_to_save_exists & & ! tmp_file_path_exists & & ! tmp_old_file_path_exists )
{
2021-05-28 20:36:17 +02:00
WLT_LOG_L0 ( " Wallet was successfully stored to " < < ascii_path_to_save < < " , file size= " < < m_current_wallet_file_size
< < " blockchain_size: " < < m_chain . get_blockchain_current_size ( ) ) ;
2019-12-02 15:08:45 +03:00
}
else
{
WLT_LOG_ERROR ( " Wallet stroing to " < < ascii_path_to_save < < " might not be successfull: path_to_save_exists= " < < path_to_save_exists < < " , tmp_file_path_exists= " < < tmp_file_path_exists < < " , tmp_old_file_path_exists= " < < tmp_old_file_path_exists ) ;
throw tools : : error : : wallet_common_error ( LOCATION_STR , " Wallet file storing might not be successfull. Please make sure you have backed up your seed phrase and check log for details. " ) ;
}
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2020-06-03 21:29:43 +02:00
uint64_t wallet2 : : get_wallet_file_size ( ) const
{
return m_current_wallet_file_size ;
}
//----------------------------------------------------------------------------------------------------
2020-06-18 20:37:01 +02:00
void wallet2 : : set_use_deffered_global_outputs ( bool use )
{
LOG_PRINT_L0 ( " [DEFFERED_MODE]: " < < use ) ;
m_use_deffered_global_outputs = use ;
}
//----------------------------------------------------------------------------------------------------
2023-06-02 20:09:13 +02:00
void wallet2 : : set_use_assets_whitelisting ( bool use )
{
LOG_PRINT_L0 ( " [ASSET_WHITELISTING_MODE]: " < < use ) ;
m_use_assets_whitelisting = use ;
}
//----------------------------------------------------------------------------------------------------
2019-04-12 09:48:33 +03:00
void wallet2 : : store_watch_only ( const std : : wstring & path_to_save , const std : : string & password ) const
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( path_to_save ! = m_wallet_file , " trying to save watch-only wallet to the same wallet file! " ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! m_watch_only , " saving watch-only wallet into a watch-only wallet is not allowed " ) ;
// prepare data for watch-only wallet
wallet2 wo ;
// wallet2 wo(*this); copy-constructor is not working, so do a this serialization workaround
std : : stringstream stream_buffer ;
tools : : portble_serialize_obj_to_stream ( * this , stream_buffer ) ;
tools : : portable_unserialize_obj_from_stream ( wo , stream_buffer ) ;
wo . m_watch_only = true ;
wo . m_account = m_account ;
wo . m_account . make_account_watch_only ( ) ;
wo . prepare_file_names ( path_to_save ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! boost : : filesystem : : exists ( wo . m_wallet_file ) , " file " < < epee : : string_encoding : : convert_to_ansii ( wo . m_wallet_file ) < < " already exists " ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! boost : : filesystem : : exists ( wo . m_pending_ki_file ) , " file " < < epee : : string_encoding : : convert_to_ansii ( wo . m_pending_ki_file ) < < " already exists " ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( wo . m_pending_key_images . empty ( ) , " pending key images is expected to be empty " ) ;
2020-05-27 23:04:23 +03:00
if ( ! is_auditable ( ) )
{
bool stub = false ;
wo . load_keys2ki ( true , stub ) ; // to create outkey2ki file
}
2019-04-12 09:48:33 +03:00
// populate pending key images for spent outputs (this will help to resync watch-only wallet)
for ( size_t ti = 0 ; ti < wo . m_transfers . size ( ) ; + + ti )
{
const auto & td = wo . m_transfers [ ti ] ;
if ( ! td . is_spent ( ) )
continue ; // only spent transfers really need to be stored, because watch-only wallet will not be able to figure out they were spent otherwise
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( td . m_internal_output_index < td . m_ptx_wallet_info - > m_tx . vout . size ( ) , " invalid transfer # " < < ti ) ;
2022-05-20 21:32:27 +02:00
if ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . type ( ) ! = typeid ( tx_out_bare ) )
continue ;
const currency : : txout_target_v & out_t = boost : : get < tx_out_bare > ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] ) . target ;
2019-04-12 09:48:33 +03:00
if ( out_t . type ( ) ! = typeid ( currency : : txout_to_key ) )
continue ;
const crypto : : public_key & out_key = boost : : get < txout_to_key > ( out_t ) . key ;
wo . m_pending_key_images . insert ( std : : make_pair ( out_key , td . m_key_image ) ) ;
2020-03-08 20:55:57 +01:00
wo . m_pending_key_images_file_container . push_back ( tools : : out_key_to_ki { out_key , td . m_key_image } ) ;
2019-04-12 09:48:33 +03:00
WLT_LOG_L1 ( " preparing watch-only wallet: added pending ki ( " < < out_key < < " , " < < td . m_key_image < < " ) " ) ;
}
// TODO additional clearing for watch-only wallet's data
wo . store ( path_to_save , password ) ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
uint64_t wallet2 : : unlocked_balance ( ) const
{
uint64_t stub = 0 ;
uint64_t unlocked_balance = 0 ;
balance ( unlocked_balance , stub , stub , stub ) ;
return unlocked_balance ;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : balance ( uint64_t & unloked ) const
{
uint64_t fake = 0 ;
return balance ( unloked , fake , fake , fake ) ;
}
//----------------------------------------------------------------------------------------------------
2023-02-08 18:50:26 +01:00
uint64_t wallet2 : : balance ( uint64_t & unlocked , uint64_t & awaiting_in , uint64_t & awaiting_out , uint64_t & mined , const crypto : : public_key & asset_id /* = currency::native_coin_asset_id */ ) const
2018-12-27 18:50:45 +03:00
{
2022-09-15 22:05:10 +02:00
uint64_t total = 0 ;
2019-05-06 19:38:06 +02:00
unlocked = 0 ;
2018-12-27 18:50:45 +03:00
awaiting_in = 0 ;
awaiting_out = 0 ;
mined = 0 ;
2023-02-08 18:50:26 +01:00
std : : unordered_map < crypto : : public_key , wallet_public : : asset_balance_entry_base > balances ;
2022-09-15 22:05:10 +02:00
balance ( balances , mined ) ;
2023-02-08 18:50:26 +01:00
auto it = balances . find ( asset_id ) ;
2022-09-15 22:05:10 +02:00
if ( it ! = balances . end ( ) )
{
total = it - > second . total ;
unlocked = it - > second . unlocked ;
awaiting_in = it - > second . awaiting_in ;
awaiting_out = it - > second . awaiting_out ;
}
return total ;
}
//----------------------------------------------------------------------------------------------------
2023-02-08 18:50:26 +01:00
bool wallet2 : : balance ( std : : unordered_map < crypto : : public_key , wallet_public : : asset_balance_entry_base > & balances , uint64_t & mined ) const
2022-09-15 22:05:10 +02:00
{
mined = 0 ;
2018-12-27 18:50:45 +03:00
for ( auto & td : m_transfers )
{
2019-10-28 21:07:34 +03:00
if ( td . is_spendable ( ) | | ( td . is_reserved_for_escrow ( ) & & ! td . is_spent ( ) ) )
2018-12-27 18:50:45 +03:00
{
2022-09-16 18:35:36 +02:00
wallet_public : : asset_balance_entry_base & e = balances [ td . get_asset_id ( ) ] ;
2022-09-15 22:05:10 +02:00
e . total + = td . amount ( ) ;
2018-12-27 18:50:45 +03:00
if ( is_transfer_unlocked ( td ) )
2022-09-15 22:05:10 +02:00
e . unlocked + = td . amount ( ) ;
2018-12-27 18:50:45 +03:00
if ( td . m_flags & WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER )
mined + = td . amount ( ) ;
}
}
for ( auto & utx : m_unconfirmed_txs )
{
2023-06-08 18:19:10 +02:00
for ( auto & subtransfer : utx . second . subtransfers )
2018-12-27 18:50:45 +03:00
{
2023-06-08 18:19:10 +02:00
wallet_public : : asset_balance_entry_base & e = balances [ subtransfer . asset_id ] ;
if ( subtransfer . is_income )
{
e . total + = subtransfer . amount ;
e . awaiting_in + = subtransfer . amount ;
}
else
{
e . awaiting_out + = subtransfer . amount ;
2023-06-15 23:55:22 +02:00
if ( subtransfer . asset_id = = currency : : native_coin_asset_id )
{
2023-06-19 00:19:25 +02:00
// this "if" present here only due to sophisticated checks in escrow_custom_test, which
// inaccuracy might be driven by tangled processing of sent transactions and unconfirmed
// transactions in pre-refactoring era (few weeks before this commit)
if ( ! ( utx . second . contract . size ( ) & & utx . second . contract [ 0 ] . state = = wallet_public : : escrow_contract_details_basic : : contract_released_burned ) )
{
e . awaiting_out - = currency : : get_tx_fee ( utx . second . tx ) ;
}
2023-06-15 23:55:22 +02:00
}
2023-06-08 18:19:10 +02:00
}
2018-12-27 18:50:45 +03:00
}
2023-06-15 23:55:22 +02:00
if ( utx . second . has_outgoing_entries ( ) )
{
//collect change to unconfirmed
for ( const auto & emp_entry : utx . second . employed_entries . receive )
{
wallet_public : : asset_balance_entry_base & e = balances [ emp_entry . asset_id ] ;
e . total + = emp_entry . amount ;
}
}
2018-12-27 18:50:45 +03:00
}
2022-09-15 22:05:10 +02:00
return true ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2022-11-18 21:04:05 +01:00
bool wallet2 : : balance ( std : : list < wallet_public : : asset_balance_entry > & balances , uint64_t & mined ) const
2022-11-18 16:11:15 +01:00
{
2022-11-28 22:10:50 +01:00
load_whitelisted_tokens_if_not_loaded ( ) ;
2022-12-01 19:05:17 +01:00
balances . clear ( ) ;
2023-02-08 18:50:26 +01:00
std : : unordered_map < crypto : : public_key , wallet_public : : asset_balance_entry_base > balances_map ;
2022-11-18 16:11:15 +01:00
this - > balance ( balances_map , mined ) ;
2023-02-08 18:50:26 +01:00
std : : unordered_map < crypto : : public_key , currency : : asset_descriptor_base > custom_assets_local = m_custom_assets ;
2022-12-02 15:24:50 +01:00
for ( auto & own_asset : m_own_asset_descriptors )
{
2022-12-02 22:29:23 +01:00
if ( m_whitelisted_assets . find ( own_asset . first ) = = m_whitelisted_assets . end ( ) )
{
custom_assets_local [ own_asset . first ] = own_asset . second . asset_descriptor ;
}
2022-12-02 15:24:50 +01:00
}
asset_descriptor_base native_asset_info = AUTO_VAL_INIT ( native_asset_info ) ;
2023-02-08 18:50:26 +01:00
native_asset_info . full_name = CURRENCY_NAME_SHORT_BASE ;
native_asset_info . ticker = CURRENCY_NAME_ABR ;
native_asset_info . decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT ;
custom_assets_local [ currency : : native_coin_asset_id ] = native_asset_info ;
2022-12-02 15:24:50 +01:00
2022-11-18 16:11:15 +01:00
for ( const auto & item : balances_map )
{
2022-12-02 15:24:50 +01:00
asset_descriptor_base asset_info = AUTO_VAL_INIT ( asset_info ) ;
2022-11-18 16:11:15 +01:00
//check if asset is whitelisted or customly added
2022-12-15 17:11:55 +01:00
//check if it custom asset
auto it_cust = custom_assets_local . find ( item . first ) ;
if ( it_cust = = custom_assets_local . end ( ) )
2022-11-26 23:00:32 +01:00
{
2022-12-15 17:11:55 +01:00
auto it_local = m_whitelisted_assets . find ( item . first ) ;
if ( it_local = = m_whitelisted_assets . end ( ) )
2022-11-18 16:11:15 +01:00
{
2023-05-01 13:34:37 +02:00
WLT_LOG_YELLOW ( " WARNING: unknown asset " < < item . first < < " found and skipped; it's NOT included in balance " , LOG_LEVEL_0 ) ;
2022-12-06 21:31:17 +01:00
continue ;
2022-11-18 16:11:15 +01:00
}
2022-12-15 17:11:55 +01:00
else
2022-11-18 16:11:15 +01:00
{
2022-12-15 17:11:55 +01:00
asset_info = it_local - > second ;
2022-11-18 16:11:15 +01:00
}
2022-12-06 21:31:17 +01:00
}
2022-12-15 17:11:55 +01:00
else
2022-12-06 21:31:17 +01:00
{
2022-12-15 17:11:55 +01:00
asset_info = it_cust - > second ;
custom_assets_local . erase ( it_cust ) ;
}
2022-11-18 16:11:15 +01:00
balances . push_back ( wallet_public : : asset_balance_entry ( ) ) ;
wallet_public : : asset_balance_entry & new_item = balances . back ( ) ;
2022-11-18 21:04:05 +01:00
static_cast < wallet_public : : asset_balance_entry_base & > ( new_item ) = item . second ;
2022-11-18 16:11:15 +01:00
new_item . asset_info . asset_id = item . first ;
2022-12-02 15:24:50 +01:00
static_cast < currency : : asset_descriptor_base & > ( new_item . asset_info ) = asset_info ;
}
//manually added assets should be always present, at least as zero balanced items
for ( auto & asset : custom_assets_local )
{
balances . push_back ( wallet_public : : asset_balance_entry ( ) ) ;
wallet_public : : asset_balance_entry & new_item = balances . back ( ) ;
new_item . asset_info . asset_id = asset . first ;
static_cast < currency : : asset_descriptor_base & > ( new_item . asset_info ) = asset . second ;
2022-11-18 16:11:15 +01:00
}
return true ;
}
//----------------------------------------------------------------------------------------------------
2023-06-21 22:45:31 +02:00
bool wallet2 : : get_asset_id_info ( const crypto : : public_key & asset_id , currency : : asset_descriptor_base & asset_info , bool & whitelist_ ) const
{
if ( asset_id = = currency : : native_coin_asset_id )
{
return CURRENCY_NAME_ABR ;
}
//check if asset is whitelisted or customly added
whitelist_ = false ;
auto it_white = m_whitelisted_assets . find ( asset_id ) ;
if ( it_white = = m_whitelisted_assets . end ( ) )
{
//check if it custom asset
auto it_cust = m_custom_assets . find ( asset_id ) ;
if ( it_cust = = m_custom_assets . end ( ) )
{
return false ;
}
else
{
asset_info = it_cust - > second ;
}
}
else
{
asset_info = it_white - > second ;
whitelist_ = true ;
}
return true ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
uint64_t wallet2 : : balance ( ) const
{
uint64_t stub = 0 ;
return balance ( stub , stub , stub , stub ) ;
}
//----------------------------------------------------------------------------------------------------
2023-02-08 18:50:26 +01:00
bool wallet2 : : add_custom_asset_id ( const crypto : : public_key & asset_id , asset_descriptor_base & asset_descriptor )
2022-11-15 21:54:55 +01:00
{
currency : : COMMAND_RPC_GET_ASSET_INFO : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_ASSET_INFO : : response resp = AUTO_VAL_INIT ( resp ) ;
2022-12-02 22:29:23 +01:00
req . asset_id = asset_id ;
2022-11-15 21:54:55 +01:00
bool r = m_core_proxy - > call_COMMAND_RPC_GET_ASSET_INFO ( req , resp ) ;
if ( resp . status = = API_RETURN_CODE_OK )
{
m_custom_assets [ asset_id ] = resp . asset_descriptor ;
2022-12-02 15:24:50 +01:00
asset_descriptor = resp . asset_descriptor ;
2022-11-15 21:54:55 +01:00
return true ;
}
return false ;
}
//----------------------------------------------------------------------------------------------------
2023-02-08 18:50:26 +01:00
bool wallet2 : : delete_custom_asset_id ( const crypto : : public_key & asset_id )
2022-11-15 21:54:55 +01:00
{
auto it = m_custom_assets . find ( asset_id ) ;
if ( it ! = m_custom_assets . end ( ) )
{
m_custom_assets . erase ( it ) ;
}
return true ;
2022-11-16 22:16:15 +01:00
}
//----------------------------------------------------------------------------------------------------
2022-11-28 22:10:50 +01:00
bool wallet2 : : load_whitelisted_tokens ( ) const
2022-11-16 22:16:15 +01:00
{
2023-06-02 20:09:13 +02:00
if ( ! m_use_assets_whitelisting )
return true ;
2023-04-04 23:19:48 +02:00
2023-06-02 20:09:13 +02:00
2023-04-04 23:19:48 +02:00
2022-11-26 23:00:32 +01:00
m_whitelisted_assets . clear ( ) ;
2022-11-17 21:58:36 +01:00
std : : string body ;
wallet_public : : assets_whitelist aw = AUTO_VAL_INIT ( aw ) ;
if ( epee : : net_utils : : get_http_json_t ( WALLET_ASSETS_WHITELIST_URL , aw ) )
{
for ( auto it = aw . assets . begin ( ) ; it ! = aw . assets . end ( ) ; it + + )
{
m_whitelisted_assets [ it - > asset_id ] = static_cast < currency : : asset_descriptor_base > ( * it ) ;
}
}
2022-11-17 20:46:21 +01:00
return true ;
2022-11-15 21:54:55 +01:00
}
//----------------------------------------------------------------------------------------------------
2022-11-28 22:10:50 +01:00
bool wallet2 : : load_whitelisted_tokens_if_not_loaded ( ) const
2022-11-26 23:00:32 +01:00
{
if ( m_whitelisted_assets . size ( ) )
{
return true ;
}
return load_whitelisted_tokens ( ) ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : get_transfers ( wallet2 : : transfer_container & incoming_transfers ) const
{
incoming_transfers = m_transfers ;
2019-11-27 03:19:10 +01:00
}
//----------------------------------------------------------------------------------------------------
2023-06-09 01:10:16 +02:00
bool wallet2 : : generate_utxo_defragmentation_transaction_if_needed ( currency : : transaction & tx )
2019-11-27 03:19:10 +01:00
{
2019-11-28 05:28:36 +01:00
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
2023-06-09 01:10:16 +02:00
ctp . create_utxo_defragmentation_tx = true ;
2023-06-12 20:37:26 +02:00
finalized_tx ftp { } ;
2019-11-27 03:19:10 +01:00
2023-06-12 20:37:26 +02:00
transfer ( ctp , ftp , false , nullptr ) ;
2019-11-27 03:19:10 +01:00
2023-06-12 20:37:26 +02:00
if ( ftp . was_not_prepared )
return false ; // no such UTXO were found, not an error
tx = ftp . tx ;
2019-11-28 05:28:36 +01:00
return true ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2019-04-12 09:48:33 +03:00
std : : string wallet2 : : get_transfers_str ( bool include_spent /*= true*/ , bool include_unspent /*= true*/ ) const
{
2022-10-20 18:28:47 +02:00
static const char * header = " index amount g_index flags block tx out# key image " ;
2019-04-12 09:48:33 +03:00
std : : stringstream ss ;
ss < < header < < ENDL ;
size_t count = 0 ;
for ( size_t i = 0 ; i ! = m_transfers . size ( ) ; + + i )
{
const transfer_details & td = m_transfers [ i ] ;
if ( ( td . is_spent ( ) & & ! include_spent ) | | ( ! td . is_spent ( ) & & ! include_unspent ) )
continue ;
ss < < std : : right < <
std : : setw ( 5 ) < < i < < " " < <
std : : setw ( 21 ) < < print_money ( td . amount ( ) ) < < " " < <
std : : setw ( 7 ) < < td . m_global_output_index < < " " < <
std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < td . m_flags < < std : : setfill ( ' ' ) < < " : " < <
std : : setw ( 5 ) < < transfer_flags_to_str ( td . m_flags ) < < " " < <
std : : setw ( 7 ) < < td . m_ptx_wallet_info - > m_block_height < < " " < <
get_transaction_hash ( td . m_ptx_wallet_info - > m_tx ) < < " " < <
std : : setw ( 4 ) < < td . m_internal_output_index < < " " < <
td . m_key_image < < ENDL ;
+ + count ;
}
ss < < " printed " < < count < < " outputs of " < < m_transfers . size ( ) < < " total " < < ENDL ;
return ss . str ( ) ;
}
//----------------------------------------------------------------------------------------------------
2023-05-26 22:01:01 +02:00
std : : string wallet2 : : get_balance_str ( ) const
{
// balance unlocked / [balance total] ticker asset id
// 1391306.970000000000 / 1391306.970000000000 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a
// 1391306.97 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a
// 106.971 / 206.4 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a
static const char * header = " balance unlocked / [balance total] ticker asset id " ;
std : : stringstream ss ;
ss < < header < < ENDL ;
std : : list < tools : : wallet_public : : asset_balance_entry > balances ;
uint64_t mined = 0 ;
balance ( balances , mined ) ;
for ( const tools : : wallet_public : : asset_balance_entry & b : balances )
{
ss < < " " < < std : : setw ( 20 ) < < print_fixed_decimal_point_with_trailing_spaces ( b . unlocked , b . asset_info . decimal_point ) ;
if ( b . total = = b . unlocked )
ss < < " " ;
else
ss < < " / " < < std : : setw ( 20 ) < < print_fixed_decimal_point_with_trailing_spaces ( b . total , b . asset_info . decimal_point ) ;
ss < < " " < < std : : setw ( 8 ) < < std : : left < < b . asset_info . ticker < < " " < < b . asset_info . asset_id < < ENDL ;
}
return ss . str ( ) ;
}
//----------------------------------------------------------------------------------------------------
2019-04-03 12:48:09 +03:00
void wallet2 : : get_payments ( const std : : string & payment_id , std : : list < wallet2 : : payment_details > & payments , uint64_t min_height ) const
2018-12-27 18:50:45 +03:00
{
auto range = m_payments . equal_range ( payment_id ) ;
2019-04-03 12:48:09 +03:00
std : : for_each ( range . first , range . second , [ & payments , & min_height ] ( const payment_container : : value_type & x )
{
if ( min_height < = x . second . m_block_height )
{
payments . push_back ( x . second ) ;
}
2018-12-27 18:50:45 +03:00
} ) ;
}
//----------------------------------------------------------------------------------------------------
2019-04-05 00:42:28 +03:00
void wallet2 : : sign_transfer ( const std : : string & tx_sources_blob , std : : string & signed_tx_blob , currency : : transaction & tx )
2019-04-03 12:48:09 +03:00
{
// assumed to be called from normal, non-watch-only wallet
THROW_IF_FALSE_WALLET_EX ( ! m_watch_only , error : : wallet_common_error , " watch-only wallet is unable to sign transfers, you need to use normal wallet for that " ) ;
// decrypt the blob
2020-04-22 23:30:03 +03:00
std : : string decrypted_src_blob = crypto : : chacha_crypt ( tx_sources_blob , m_account . get_keys ( ) . view_secret_key ) ;
2019-04-03 12:48:09 +03:00
2019-04-12 09:48:33 +03:00
// deserialize args
2021-02-04 01:49:38 +01:00
currency : : finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
2019-04-12 09:48:33 +03:00
bool r = t_unserializable_object_from_blob ( ft . ftp , decrypted_src_blob ) ;
2019-04-03 12:48:09 +03:00
THROW_IF_FALSE_WALLET_EX ( r , error : : wallet_common_error , " Failed to decrypt tx sources blob " ) ;
// make sure unsigned tx was created with the same keys
2020-04-22 23:30:03 +03:00
THROW_IF_FALSE_WALLET_EX ( ft . ftp . spend_pub_key = = m_account . get_keys ( ) . account_address . spend_public_key , error : : wallet_common_error , " The was created in a different wallet, keys missmatch " ) ;
2019-04-05 00:42:28 +03:00
finalize_transaction ( ft . ftp , ft . tx , ft . one_time_key , false ) ;
// calculate key images for each change output
crypto : : key_derivation derivation = AUTO_VAL_INIT ( derivation ) ;
2019-05-21 19:53:20 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX (
Coverity (#28)
* stratum_server: resolve CID 210144 (UNINIT_CTOR)
* stratum_server: resolve CID 210042/210085/210104 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger,
remove_blockchain_update_listener, and any paths involving the logger
(including CATCH_ENTRY_*).
* epee: misc_log_ex: create CATCH_ENTRY_NO_RETURN macro
A temporary substition for what I hope will eventually be a full-fledged
exception-dispatcher (class-based, not macro).
* stratum_server: resolve CID 210080/210084/210089 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger,
remove_protocol_handler, and any paths involving the logger
(including CATCH_ENTRY_*).
* epee: levin_protocol_handler_async: resolve CID 210140/210182/210165 (UNCAUGHT_EXCEPT)
The potential to throw exists within guarded_critical_region_t, and any
paths involving the logger (including CATCH_ENTRY_*).
* epee: levin_protocol_handler_async: resolve CID 210110/210119/210155 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger, del_connection, and any
paths involving the logger (including CATCH_ENTRY_*).
* epee: misc_log_ex: move macros to *top* of file
so they can be used *within* this file.
* daemon: resolve CID 210069/210092/210166 (UNCAUGHT_EXCEPT)
The potential to throw exists within log_space, and any paths involving
the logger (including CATCH_ENTRY_*).
* daemon: return cstdlib proper types in main
* simplewallet: resolve 6 different CIDs (UNCAUGHT_EXCEPT)
CID: 210082
CID: 210086
CID: 210096
CID: 210147
CID: 210149
CID: 210150
The potential to throw exists throughout various paths in main.
* simplewallet: return cstdlib proper types in main
* simplewallet: resolve CID 210128/210160 (UNCAUGHT_EXCEPT)
The potential to throw exists within various paths, and any paths
involving the logger (including CATCH_ENTRY_*).
* conn_tool: resolve 5 different CIDs (UNCAUGHT_EXCEPT)
CID: 210038
CID: 210047
CID: 210108
CID: 210122
CID: 210157
The potential to throw exists throughout various paths in main.
* conn_tool: return cstdlib proper types in main
* miniupnp_helper: resolve CID 210050 (UNCAUGHT_EXCEPT)
The potential to throw exists within deinit, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: profile_tools: resolve CID 210055 (UNCAUGHT_EXCEPT)
The potential to throw exists within boost microsec_clock::localtime(),
and any paths involving the logger (including CATCH_ENTRY_*).
* db_backend_lmdb: resolve CID 210056/210133 (UNCAUGHT_EXCEPT)
The potential to throw exists within close(), including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: misc_log_ex: resolve CID 210060/210124 (UNCAUGHT_EXCEPT)
The potential to throw exists within several paths, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: misc_language: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210064
CID: 210093
CID: 210136
CID: 210139
The potential to throw exists within m_func(), including any paths
involving the logger (including CATCH_ENTRY_*).
* db_abstract_accessor: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210072
CID: 210094
CID: 210116
CID: 210141
The potential to throw exists within m_cache.clear(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: net_helper: resolve CID 210100 (UNCAUGHT_EXCEPT)
The potential to throw exists within shutdown(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: syncobj: resolve CID 210123 (UNCAUGHT_EXCEPT)
The potential to throw exists within unlock(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: profile_tools: resolve CID 210145/210154 (UNCAUGHT_EXCEPT)
The potential to throw exists within various paths, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: http_base: resolve CID 210176 (UNINIT_CTOR)
* p2p: net_node: resolve CID 210173 (UNINIT_CTOR)
* epee: net_helper: resolve CID 210138 (UNINIT_CTOR)
* p2p: net_peerlist: resolve CID 210137 (UNINIT_CTOR)
* currency_basic: resolve CID 210117 (UNINIT_CTOR)
* epee: abstract_tcp_server2: resolve 3 CIDs (UNINIT_CTOR)
CID: 210040
CID: 210090
CID: 210105
* simplewallet: resolve CID 210103 (UNINIT_CTOR)
* epee: levin_protocol_handler_async: resolve CID 210091 (UNINIT_CTOR)
* json_archive: resolve CID 210087 (UNINIT_CTOR)
* epee: levin_protocol_handler_async: resolve CID 210073 (UNINIT_CTOR)
* miniupnp_helper: resolve CID 210037 (UNINIT_CTOR)
* crypto: ge_frombytes_vartime: resolve CID 210142 (CHECKED_RETURN)
* wallet2: resolve CID 210041 (CHECKED_RETURN)
* epee: misc_log_ex: resolve CID 210127 (DEADCODE)
* epee: levin_protocol_handler_sync: resolve 3 CIDs (PASS_BY_VALUE)
CID: 210167
CID: 210170
CID: 210180
* p2p: net_node: resolve CID 210065 (PASS_BY_VALUE)
* epee: levin_abstract_invoke2: resolve CID 210049 (PASS_BY_VALUE)
* epee: abstract_tcp_server2: resolve CID 210045 (PASS_BY_VALUE)
* epee: misc_log_ex: add NESTED_*_ENTRY macros
* simplewallet: use NESTED_*_ENTRY in message_writer dtor
* stratum_protocol_handler_config: use NESTED_*_ENTRY in dtor
* stratum_protocol_handler: use NESTED_*_ENTRY in dtor
* lmdb_db_backend: use NESTED_*_ENTRY in dtor
* epee: abstract_tcp_server2: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210088
CID: 210106
CID: 210164
CID: 210179
The potential to throw exists within various paths, including any
paths involving the logger (including CATCH_ENTRY_*).
* db_abstract_accessor: use NESTED_*_ENTRY in dtor
* miniupnp_helper: use NESTED_*_ENTRY in dtor
* epee: misc_log_ex: use NESTED_*_ENTRY in log_frame dtor
* epee: levin_protocol_handler_async: use NESTED_*_ENTRY in dtors
* epee: net_helper: use NESTED_*_ENTRY in dtor
* epee: profile_tools: use NESTED_*_ENTRY in dtors
* epee: misc_language: use NESTED_*_ENTRY in dtor
* epee: syncobj: use NESTED_*_ENTRY in dtor
* zano: license contact w/ zano.org email instead of sekreta.org email
2019-05-20 09:32:36 +00:00
crypto : : generate_key_derivation (
2020-04-22 23:30:03 +03:00
m_account . get_keys ( ) . account_address . view_public_key ,
Coverity (#28)
* stratum_server: resolve CID 210144 (UNINIT_CTOR)
* stratum_server: resolve CID 210042/210085/210104 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger,
remove_blockchain_update_listener, and any paths involving the logger
(including CATCH_ENTRY_*).
* epee: misc_log_ex: create CATCH_ENTRY_NO_RETURN macro
A temporary substition for what I hope will eventually be a full-fledged
exception-dispatcher (class-based, not macro).
* stratum_server: resolve CID 210080/210084/210089 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger,
remove_protocol_handler, and any paths involving the logger
(including CATCH_ENTRY_*).
* epee: levin_protocol_handler_async: resolve CID 210140/210182/210165 (UNCAUGHT_EXCEPT)
The potential to throw exists within guarded_critical_region_t, and any
paths involving the logger (including CATCH_ENTRY_*).
* epee: levin_protocol_handler_async: resolve CID 210110/210119/210155 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger, del_connection, and any
paths involving the logger (including CATCH_ENTRY_*).
* epee: misc_log_ex: move macros to *top* of file
so they can be used *within* this file.
* daemon: resolve CID 210069/210092/210166 (UNCAUGHT_EXCEPT)
The potential to throw exists within log_space, and any paths involving
the logger (including CATCH_ENTRY_*).
* daemon: return cstdlib proper types in main
* simplewallet: resolve 6 different CIDs (UNCAUGHT_EXCEPT)
CID: 210082
CID: 210086
CID: 210096
CID: 210147
CID: 210149
CID: 210150
The potential to throw exists throughout various paths in main.
* simplewallet: return cstdlib proper types in main
* simplewallet: resolve CID 210128/210160 (UNCAUGHT_EXCEPT)
The potential to throw exists within various paths, and any paths
involving the logger (including CATCH_ENTRY_*).
* conn_tool: resolve 5 different CIDs (UNCAUGHT_EXCEPT)
CID: 210038
CID: 210047
CID: 210108
CID: 210122
CID: 210157
The potential to throw exists throughout various paths in main.
* conn_tool: return cstdlib proper types in main
* miniupnp_helper: resolve CID 210050 (UNCAUGHT_EXCEPT)
The potential to throw exists within deinit, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: profile_tools: resolve CID 210055 (UNCAUGHT_EXCEPT)
The potential to throw exists within boost microsec_clock::localtime(),
and any paths involving the logger (including CATCH_ENTRY_*).
* db_backend_lmdb: resolve CID 210056/210133 (UNCAUGHT_EXCEPT)
The potential to throw exists within close(), including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: misc_log_ex: resolve CID 210060/210124 (UNCAUGHT_EXCEPT)
The potential to throw exists within several paths, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: misc_language: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210064
CID: 210093
CID: 210136
CID: 210139
The potential to throw exists within m_func(), including any paths
involving the logger (including CATCH_ENTRY_*).
* db_abstract_accessor: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210072
CID: 210094
CID: 210116
CID: 210141
The potential to throw exists within m_cache.clear(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: net_helper: resolve CID 210100 (UNCAUGHT_EXCEPT)
The potential to throw exists within shutdown(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: syncobj: resolve CID 210123 (UNCAUGHT_EXCEPT)
The potential to throw exists within unlock(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: profile_tools: resolve CID 210145/210154 (UNCAUGHT_EXCEPT)
The potential to throw exists within various paths, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: http_base: resolve CID 210176 (UNINIT_CTOR)
* p2p: net_node: resolve CID 210173 (UNINIT_CTOR)
* epee: net_helper: resolve CID 210138 (UNINIT_CTOR)
* p2p: net_peerlist: resolve CID 210137 (UNINIT_CTOR)
* currency_basic: resolve CID 210117 (UNINIT_CTOR)
* epee: abstract_tcp_server2: resolve 3 CIDs (UNINIT_CTOR)
CID: 210040
CID: 210090
CID: 210105
* simplewallet: resolve CID 210103 (UNINIT_CTOR)
* epee: levin_protocol_handler_async: resolve CID 210091 (UNINIT_CTOR)
* json_archive: resolve CID 210087 (UNINIT_CTOR)
* epee: levin_protocol_handler_async: resolve CID 210073 (UNINIT_CTOR)
* miniupnp_helper: resolve CID 210037 (UNINIT_CTOR)
* crypto: ge_frombytes_vartime: resolve CID 210142 (CHECKED_RETURN)
* wallet2: resolve CID 210041 (CHECKED_RETURN)
* epee: misc_log_ex: resolve CID 210127 (DEADCODE)
* epee: levin_protocol_handler_sync: resolve 3 CIDs (PASS_BY_VALUE)
CID: 210167
CID: 210170
CID: 210180
* p2p: net_node: resolve CID 210065 (PASS_BY_VALUE)
* epee: levin_abstract_invoke2: resolve CID 210049 (PASS_BY_VALUE)
* epee: abstract_tcp_server2: resolve CID 210045 (PASS_BY_VALUE)
* epee: misc_log_ex: add NESTED_*_ENTRY macros
* simplewallet: use NESTED_*_ENTRY in message_writer dtor
* stratum_protocol_handler_config: use NESTED_*_ENTRY in dtor
* stratum_protocol_handler: use NESTED_*_ENTRY in dtor
* lmdb_db_backend: use NESTED_*_ENTRY in dtor
* epee: abstract_tcp_server2: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210088
CID: 210106
CID: 210164
CID: 210179
The potential to throw exists within various paths, including any
paths involving the logger (including CATCH_ENTRY_*).
* db_abstract_accessor: use NESTED_*_ENTRY in dtor
* miniupnp_helper: use NESTED_*_ENTRY in dtor
* epee: misc_log_ex: use NESTED_*_ENTRY in log_frame dtor
* epee: levin_protocol_handler_async: use NESTED_*_ENTRY in dtors
* epee: net_helper: use NESTED_*_ENTRY in dtor
* epee: profile_tools: use NESTED_*_ENTRY in dtors
* epee: misc_language: use NESTED_*_ENTRY in dtor
* epee: syncobj: use NESTED_*_ENTRY in dtor
* zano: license contact w/ zano.org email instead of sekreta.org email
2019-05-20 09:32:36 +00:00
ft . one_time_key ,
derivation ) ,
" internal error: sign_transfer: failed to generate key derivation( "
2020-04-22 23:30:03 +03:00
< < m_account . get_keys ( ) . account_address . view_public_key
Coverity (#28)
* stratum_server: resolve CID 210144 (UNINIT_CTOR)
* stratum_server: resolve CID 210042/210085/210104 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger,
remove_blockchain_update_listener, and any paths involving the logger
(including CATCH_ENTRY_*).
* epee: misc_log_ex: create CATCH_ENTRY_NO_RETURN macro
A temporary substition for what I hope will eventually be a full-fledged
exception-dispatcher (class-based, not macro).
* stratum_server: resolve CID 210080/210084/210089 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger,
remove_protocol_handler, and any paths involving the logger
(including CATCH_ENTRY_*).
* epee: levin_protocol_handler_async: resolve CID 210140/210182/210165 (UNCAUGHT_EXCEPT)
The potential to throw exists within guarded_critical_region_t, and any
paths involving the logger (including CATCH_ENTRY_*).
* epee: levin_protocol_handler_async: resolve CID 210110/210119/210155 (UNCAUGHT_EXCEPT)
The potential to throw exists within the logger, del_connection, and any
paths involving the logger (including CATCH_ENTRY_*).
* epee: misc_log_ex: move macros to *top* of file
so they can be used *within* this file.
* daemon: resolve CID 210069/210092/210166 (UNCAUGHT_EXCEPT)
The potential to throw exists within log_space, and any paths involving
the logger (including CATCH_ENTRY_*).
* daemon: return cstdlib proper types in main
* simplewallet: resolve 6 different CIDs (UNCAUGHT_EXCEPT)
CID: 210082
CID: 210086
CID: 210096
CID: 210147
CID: 210149
CID: 210150
The potential to throw exists throughout various paths in main.
* simplewallet: return cstdlib proper types in main
* simplewallet: resolve CID 210128/210160 (UNCAUGHT_EXCEPT)
The potential to throw exists within various paths, and any paths
involving the logger (including CATCH_ENTRY_*).
* conn_tool: resolve 5 different CIDs (UNCAUGHT_EXCEPT)
CID: 210038
CID: 210047
CID: 210108
CID: 210122
CID: 210157
The potential to throw exists throughout various paths in main.
* conn_tool: return cstdlib proper types in main
* miniupnp_helper: resolve CID 210050 (UNCAUGHT_EXCEPT)
The potential to throw exists within deinit, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: profile_tools: resolve CID 210055 (UNCAUGHT_EXCEPT)
The potential to throw exists within boost microsec_clock::localtime(),
and any paths involving the logger (including CATCH_ENTRY_*).
* db_backend_lmdb: resolve CID 210056/210133 (UNCAUGHT_EXCEPT)
The potential to throw exists within close(), including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: misc_log_ex: resolve CID 210060/210124 (UNCAUGHT_EXCEPT)
The potential to throw exists within several paths, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: misc_language: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210064
CID: 210093
CID: 210136
CID: 210139
The potential to throw exists within m_func(), including any paths
involving the logger (including CATCH_ENTRY_*).
* db_abstract_accessor: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210072
CID: 210094
CID: 210116
CID: 210141
The potential to throw exists within m_cache.clear(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: net_helper: resolve CID 210100 (UNCAUGHT_EXCEPT)
The potential to throw exists within shutdown(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: syncobj: resolve CID 210123 (UNCAUGHT_EXCEPT)
The potential to throw exists within unlock(), including any
paths involving the logger (including CATCH_ENTRY_*).
* epee: profile_tools: resolve CID 210145/210154 (UNCAUGHT_EXCEPT)
The potential to throw exists within various paths, including any paths
involving the logger (including CATCH_ENTRY_*).
* epee: http_base: resolve CID 210176 (UNINIT_CTOR)
* p2p: net_node: resolve CID 210173 (UNINIT_CTOR)
* epee: net_helper: resolve CID 210138 (UNINIT_CTOR)
* p2p: net_peerlist: resolve CID 210137 (UNINIT_CTOR)
* currency_basic: resolve CID 210117 (UNINIT_CTOR)
* epee: abstract_tcp_server2: resolve 3 CIDs (UNINIT_CTOR)
CID: 210040
CID: 210090
CID: 210105
* simplewallet: resolve CID 210103 (UNINIT_CTOR)
* epee: levin_protocol_handler_async: resolve CID 210091 (UNINIT_CTOR)
* json_archive: resolve CID 210087 (UNINIT_CTOR)
* epee: levin_protocol_handler_async: resolve CID 210073 (UNINIT_CTOR)
* miniupnp_helper: resolve CID 210037 (UNINIT_CTOR)
* crypto: ge_frombytes_vartime: resolve CID 210142 (CHECKED_RETURN)
* wallet2: resolve CID 210041 (CHECKED_RETURN)
* epee: misc_log_ex: resolve CID 210127 (DEADCODE)
* epee: levin_protocol_handler_sync: resolve 3 CIDs (PASS_BY_VALUE)
CID: 210167
CID: 210170
CID: 210180
* p2p: net_node: resolve CID 210065 (PASS_BY_VALUE)
* epee: levin_abstract_invoke2: resolve CID 210049 (PASS_BY_VALUE)
* epee: abstract_tcp_server2: resolve CID 210045 (PASS_BY_VALUE)
* epee: misc_log_ex: add NESTED_*_ENTRY macros
* simplewallet: use NESTED_*_ENTRY in message_writer dtor
* stratum_protocol_handler_config: use NESTED_*_ENTRY in dtor
* stratum_protocol_handler: use NESTED_*_ENTRY in dtor
* lmdb_db_backend: use NESTED_*_ENTRY in dtor
* epee: abstract_tcp_server2: resolve 4 CIDs (UNCAUGHT_EXCEPT)
CID: 210088
CID: 210106
CID: 210164
CID: 210179
The potential to throw exists within various paths, including any
paths involving the logger (including CATCH_ENTRY_*).
* db_abstract_accessor: use NESTED_*_ENTRY in dtor
* miniupnp_helper: use NESTED_*_ENTRY in dtor
* epee: misc_log_ex: use NESTED_*_ENTRY in log_frame dtor
* epee: levin_protocol_handler_async: use NESTED_*_ENTRY in dtors
* epee: net_helper: use NESTED_*_ENTRY in dtor
* epee: profile_tools: use NESTED_*_ENTRY in dtors
* epee: misc_language: use NESTED_*_ENTRY in dtor
* epee: syncobj: use NESTED_*_ENTRY in dtor
* zano: license contact w/ zano.org email instead of sekreta.org email
2019-05-20 09:32:36 +00:00
< < " , view secret key: " < < ft . one_time_key < < " ) " ) ;
2019-04-05 00:42:28 +03:00
for ( size_t i = 0 ; i < ft . tx . vout . size ( ) ; + + i )
{
2022-05-20 21:32:27 +02:00
VARIANT_SWITCH_BEGIN ( ft . tx . vout [ i ] ) ;
2022-05-25 22:31:23 +02:00
VARIANT_CASE_CONST ( tx_out_bare , out )
2019-04-05 00:42:28 +03:00
{
2022-05-20 21:32:27 +02:00
if ( out . target . type ( ) ! = typeid ( txout_to_key ) )
continue ;
const txout_to_key & otk = boost : : get < txout_to_key > ( out . target ) ;
2019-04-03 12:48:09 +03:00
2022-05-20 21:32:27 +02:00
crypto : : public_key ephemeral_pub = AUTO_VAL_INIT ( ephemeral_pub ) ;
if ( ! crypto : : derive_public_key ( derivation , i , m_account . get_keys ( ) . account_address . spend_public_key , ephemeral_pub ) )
{
WLT_LOG_ERROR ( " derive_public_key failed for tx " < < get_transaction_hash ( ft . tx ) < < " , out # " < < i ) ;
}
if ( otk . key = = ephemeral_pub )
{
// this is the output to the given keys
// derive secret key and calculate key image
crypto : : secret_key ephemeral_sec = AUTO_VAL_INIT ( ephemeral_sec ) ;
crypto : : derive_secret_key ( derivation , i , m_account . get_keys ( ) . spend_secret_key , ephemeral_sec ) ;
crypto : : key_image ki = AUTO_VAL_INIT ( ki ) ;
crypto : : generate_key_image ( ephemeral_pub , ephemeral_sec , ki ) ;
ft . outs_key_images . push_back ( make_serializable_pair ( static_cast < uint64_t > ( i ) , ki ) ) ;
}
2019-04-05 00:42:28 +03:00
}
2022-05-25 22:31:23 +02:00
VARIANT_CASE_CONST ( tx_out_zarcanum , o ) ;
2022-05-20 21:32:27 +02:00
//@#@
VARIANT_SWITCH_END ( ) ;
2019-04-05 00:42:28 +03:00
}
2019-04-03 12:48:09 +03:00
// serialize and encrypt the result
2019-04-05 00:42:28 +03:00
signed_tx_blob = t_serializable_object_to_blob ( ft ) ;
2020-04-22 23:30:03 +03:00
crypto : : chacha_crypt ( signed_tx_blob , m_account . get_keys ( ) . view_secret_key ) ;
2019-04-12 09:48:33 +03:00
tx = ft . tx ;
2019-04-03 12:48:09 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : sign_transfer_files ( const std : : string & tx_sources_file , const std : : string & signed_tx_file , currency : : transaction & tx )
{
std : : string sources_blob ;
bool r = epee : : file_io_utils : : load_file_to_string ( tx_sources_file , sources_blob ) ;
2019-04-12 09:48:33 +03:00
THROW_IF_FALSE_WALLET_CMN_ERR_EX ( r , " failed to open file " < < tx_sources_file ) ;
2019-04-03 12:48:09 +03:00
std : : string signed_tx_blob ;
sign_transfer ( sources_blob , signed_tx_blob , tx ) ;
r = epee : : file_io_utils : : save_string_to_file ( signed_tx_file , signed_tx_blob ) ;
2019-04-12 09:48:33 +03:00
THROW_IF_FALSE_WALLET_CMN_ERR_EX ( r , " failed to store signed tx to file " < < signed_tx_file ) ;
2019-04-03 12:48:09 +03:00
}
2019-04-05 00:42:28 +03:00
//----------------------------------------------------------------------------------------------------
2019-12-03 02:18:45 +01:00
bool wallet2 : : get_utxo_distribution ( std : : map < uint64_t , uint64_t > & distribution )
{
2022-09-20 22:01:52 +02:00
//TODO@#@
/*
2019-12-03 02:18:45 +01:00
prepare_free_transfers_cache ( 0 ) ;
for ( auto ent : m_found_free_amounts )
{
distribution [ ent . first ] = ent . second . size ( ) ;
}
2022-09-20 22:01:52 +02:00
*/
return false ;
2019-12-03 02:18:45 +01:00
}
//----------------------------------------------------------------------------------------------------
2019-04-05 00:42:28 +03:00
void wallet2 : : submit_transfer ( const std : : string & signed_tx_blob , currency : : transaction & tx )
{
// decrypt sources
2020-04-22 23:30:03 +03:00
std : : string decrypted_src_blob = crypto : : chacha_crypt ( signed_tx_blob , m_account . get_keys ( ) . view_secret_key ) ;
2019-04-05 00:42:28 +03:00
// deserialize tx data
2021-02-04 01:49:38 +01:00
currency : : finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
2019-04-05 00:42:28 +03:00
bool r = t_unserializable_object_from_blob ( ft , decrypted_src_blob ) ;
2019-04-12 09:48:33 +03:00
THROW_IF_FALSE_WALLET_EX ( r , error : : wallet_common_error , " Failed to decrypt signed tx data " ) ;
tx = ft . tx ;
crypto : : hash tx_hash = get_transaction_hash ( tx ) ;
2019-04-05 00:42:28 +03:00
2019-04-12 09:48:33 +03:00
// foolproof
2020-04-22 23:30:03 +03:00
THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ft . ftp . spend_pub_key = = m_account . get_keys ( ) . account_address . spend_public_key , " The given tx was created in a different wallet, keys missmatch, tx hash: " < < tx_hash ) ;
2019-04-05 00:42:28 +03:00
try
{
2019-04-12 09:48:33 +03:00
send_transaction_to_network ( tx ) ;
2019-04-05 00:42:28 +03:00
}
catch ( . . . )
{
2019-04-12 09:48:33 +03:00
// clear transfers flags if smth went wrong
uint32_t flag = WALLET_TRANSFER_DETAIL_FLAG_SPENT | WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION ;
clear_transfers_from_flag ( ft . ftp . selected_transfers , flag , " broadcasting tx " + epee : : string_tools : : pod_to_hex ( tx_hash ) + " was unsuccessful " ) ;
2019-04-05 00:42:28 +03:00
throw ;
}
2019-04-12 09:48:33 +03:00
add_sent_tx_detailed_info ( tx , ft . ftp . prepared_destinations , ft . ftp . selected_transfers ) ;
2019-04-08 14:16:11 +03:00
m_tx_keys . insert ( std : : make_pair ( tx_hash , ft . one_time_key ) ) ;
2019-04-05 00:42:28 +03:00
if ( m_watch_only )
{
std : : vector < std : : pair < crypto : : public_key , crypto : : key_image > > pk_ki_to_be_added ;
std : : vector < std : : pair < uint64_t , crypto : : key_image > > tri_ki_to_be_added ;
for ( auto & p : ft . outs_key_images )
{
THROW_IF_FALSE_WALLET_INT_ERR_EX ( p . first < tx . vout . size ( ) , " outs_key_images has invalid out index: " < < p . first < < " , tx.vout.size() = " < < tx . vout . size ( ) ) ;
2022-05-20 21:32:27 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( tx . vout [ p . first ] . type ( ) = = typeid ( tx_out_bare ) , " Unexpected type in submit_transfer: " < < tx . vout [ p . first ] . type ( ) . name ( ) ) ;
auto & out = boost : : get < tx_out_bare > ( tx . vout [ p . first ] ) ;
2019-04-05 00:42:28 +03:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( out . target . type ( ) = = typeid ( txout_to_key ) , " outs_key_images has invalid out type, index: " < < p . first ) ;
const txout_to_key & otk = boost : : get < txout_to_key > ( out . target ) ;
pk_ki_to_be_added . push_back ( std : : make_pair ( otk . key , p . second ) ) ;
}
THROW_IF_FALSE_WALLET_INT_ERR_EX ( tx . vin . size ( ) = = ft . ftp . sources . size ( ) , " tx.vin and ft.ftp.sources sizes missmatch " ) ;
for ( size_t i = 0 ; i < tx . vin . size ( ) ; + + i )
{
const txin_v & in = tx . vin [ i ] ;
THROW_IF_FALSE_WALLET_CMN_ERR_EX ( in . type ( ) = = typeid ( txin_to_key ) , " tx " < < tx_hash < < " has a non txin_to_key input " ) ;
const crypto : : key_image & ki = boost : : get < txin_to_key > ( in ) . k_image ;
const auto & src = ft . ftp . sources [ i ] ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( src . real_output < src . outputs . size ( ) , " src.real_output is out of bounds: " < < src . real_output ) ;
2022-07-13 17:17:04 +02:00
const crypto : : public_key & out_key = src . outputs [ src . real_output ] . stealth_address ;
2019-04-05 00:42:28 +03:00
tri_ki_to_be_added . push_back ( std : : make_pair ( src . transfer_index , ki ) ) ;
pk_ki_to_be_added . push_back ( std : : make_pair ( out_key , ki ) ) ;
}
for ( auto & p : pk_ki_to_be_added )
{
auto it = m_pending_key_images . find ( p . first ) ;
if ( it ! = m_pending_key_images . end ( ) )
{
LOG_PRINT_YELLOW ( " warning: for tx " < < tx_hash < < " out pub key " < < p . first < < " already exist in m_pending_key_images, ki: " < < it - > second < < " , proposed new ki: " < < p . second , LOG_LEVEL_0 ) ;
}
else
{
m_pending_key_images [ p . first ] = p . second ;
2020-03-08 20:55:57 +01:00
m_pending_key_images_file_container . push_back ( tools : : out_key_to_ki { p . first , p . second } ) ;
2019-04-05 00:42:28 +03:00
LOG_PRINT_L2 ( " for tx " < < tx_hash < < " pending key image added ( " < < p . first < < " , " < < p . second < < " ) " ) ;
}
}
for ( auto & p : tri_ki_to_be_added )
{
THROW_IF_FALSE_WALLET_INT_ERR_EX ( p . first < m_transfers . size ( ) , " incorrect transfer index: " < < p . first ) ;
auto & tr = m_transfers [ p . first ] ;
2019-04-08 14:16:11 +03:00
if ( tr . m_key_image ! = currency : : null_ki & & tr . m_key_image ! = p . second )
2019-04-05 00:42:28 +03:00
{
2019-04-08 14:16:11 +03:00
LOG_PRINT_YELLOW ( " transfer # " < < p . first < < " already has not null key image " < < tr . m_key_image < < " and it will be replaced with ki " < < p . second , LOG_LEVEL_0 ) ;
2019-04-05 00:42:28 +03:00
}
tr . m_key_image = p . second ;
m_key_images [ p . second ] = p . first ;
LOG_PRINT_L2 ( " for tx " < < tx_hash < < " key image " < < p . second < < " was associated with transfer # " < < p . first ) ;
}
}
2019-04-08 14:16:11 +03:00
// TODO: print inputs' key images
2019-04-12 09:48:33 +03:00
print_tx_sent_message ( tx , " (from submit_transfer) " ) ;
2019-04-05 00:42:28 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : submit_transfer_files ( const std : : string & signed_tx_file , currency : : transaction & tx )
{
std : : string signed_tx_blob ;
bool r = epee : : file_io_utils : : load_file_to_string ( signed_tx_file , signed_tx_blob ) ;
THROW_IF_FALSE_WALLET_EX ( r , error : : wallet_common_error , std : : string ( " failed to open file " ) + signed_tx_file ) ;
submit_transfer ( signed_tx_blob , tx ) ;
}
2019-04-03 12:48:09 +03:00
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
uint64_t wallet2 : : get_recent_transfers_total_count ( )
{
return m_transfer_history . size ( ) ;
}
//----------------------------------------------------------------------------------------------------
2020-01-23 03:26:25 +01:00
uint64_t wallet2 : : get_transfer_entries_count ( )
{
return m_transfers . size ( ) ;
}
//----------------------------------------------------------------------------------------------------
2021-07-20 15:36:47 +02:00
template < typename callback_t , typename iterator_t >
bool enum_container ( iterator_t it_begin , iterator_t it_end , callback_t cb )
{
for ( iterator_t it = it_begin ; it ! = it_end ; it + + )
{
if ( ! cb ( * it , it - it_begin ) )
return true ;
}
return true ;
}
//----------------------------------------------------------------------------------------------------
2023-06-09 01:10:16 +02:00
bool wallet2 : : is_defragmentation_transaction ( const wallet_public : : wallet_transfer_info & wti )
2022-06-08 17:33:02 +02:00
{
2023-06-22 23:32:17 +02:00
if ( wti . employed_entries . receive . size ( ) & & wti . employed_entries . spent . size ( ) & & wti . subtransfers . size ( ) = = 1 )
2022-06-08 17:33:02 +02:00
{
2023-06-22 23:32:17 +02:00
if ( wti . subtransfers [ 0 ] . asset_id = = currency : : native_coin_asset_id & & ! wti . subtransfers [ 0 ] . is_income & & wti . subtransfers [ 0 ] . amount = = get_tx_fee ( wti . tx ) )
return true ;
2022-06-08 17:33:02 +02:00
}
return false ;
}
//----------------------------------------------------------------------------------------------------
2021-07-20 15:36:47 +02:00
void wallet2 : : get_recent_transfers_history ( std : : vector < wallet_public : : wallet_transfer_info > & trs , size_t offset , size_t count , uint64_t & total , uint64_t & last_item_index , bool exclude_mining_txs , bool start_from_end )
2018-12-27 18:50:45 +03:00
{
2020-09-01 00:26:22 +02:00
if ( ! count | | offset > = m_transfer_history . size ( ) )
2018-12-27 18:50:45 +03:00
return ;
2021-07-20 15:36:47 +02:00
auto cb = [ & ] ( wallet_public : : wallet_transfer_info & wti , size_t local_offset ) {
2020-09-01 00:26:22 +02:00
if ( exclude_mining_txs )
{
2023-06-09 01:10:16 +02:00
if ( currency : : is_coinbase ( wti . tx ) | | is_defragmentation_transaction ( wti ) )
2021-07-20 15:36:47 +02:00
return true ;
2020-09-01 00:26:22 +02:00
}
2021-07-20 15:36:47 +02:00
trs . push_back ( wti ) ;
2021-02-08 21:31:46 +01:00
load_wallet_transfer_info_flags ( trs . back ( ) ) ;
2021-07-20 15:36:47 +02:00
last_item_index = offset + local_offset ;
2021-07-01 21:05:50 +02:00
trs . back ( ) . transfer_internal_index = last_item_index ;
2021-07-20 15:36:47 +02:00
2022-06-16 17:02:00 +02:00
if ( wti . remote_addresses . size ( ) = = 1 )
{
2022-06-16 17:33:37 +02:00
wti . remote_aliases = get_aliases_for_address ( wti . remote_addresses [ 0 ] ) ;
2022-06-16 17:02:00 +02:00
}
2020-09-01 00:26:22 +02:00
if ( trs . size ( ) > = count )
{
2021-07-20 15:36:47 +02:00
return false ;
2020-09-01 00:26:22 +02:00
}
2021-07-20 15:36:47 +02:00
return true ;
} ;
if ( start_from_end )
enum_container ( m_transfer_history . rbegin ( ) + offset , m_transfer_history . rend ( ) , cb ) ;
else
enum_container ( m_transfer_history . begin ( ) + offset , m_transfer_history . end ( ) , cb ) ;
2019-12-01 23:31:05 +01:00
total = m_transfer_history . size ( ) ;
2021-07-20 15:36:47 +02:00
2021-10-20 21:18:21 +02:00
}
2023-06-11 00:20:11 +02:00
void wallet2 : : wti_to_csv_entry ( std : : ostream & ss , const wallet_public : : wallet_transfer_info & wti , size_t index )
{
for ( auto & subtr : wti . subtransfers )
{
ss < < index < < " , " ;
ss < < epee : : misc_utils : : get_time_str ( wti . timestamp ) < < " , " ;
ss < < print_money ( subtr . amount ) < < " , " ;
ss < < subtr . asset_id < < " , " ;
ss < < " \" " < < wti . comment < < " \" , " ;
ss < < " [ " ;
std : : copy ( wti . remote_addresses . begin ( ) , wti . remote_addresses . end ( ) , std : : ostream_iterator < std : : string > ( ss , " " ) ) ;
ss < < " ] " < < " , " ;
ss < < wti . tx_hash < < " , " ;
ss < < wti . height < < " , " ;
ss < < wti . unlock_time < < " , " ;
ss < < wti . tx_blob_size < < " , " ;
ss < < epee : : string_tools : : buff_to_hex_nodelimer ( wti . payment_id ) < < " , " ;
ss < < " [ " ;
std : : copy ( wti . remote_aliases . begin ( ) , wti . remote_aliases . end ( ) , std : : ostream_iterator < std : : string > ( ss , " " ) ) ;
ss < < " ] " < < " , " ;
ss < < ( subtr . is_income ? " in " : " out " ) < < " , " ;
ss < < ( wti . is_service ? " [SERVICE] " : " " ) < < ( wti . is_mixing ? " [MIXINS] " : " " ) < < ( wti . is_mining ? " [MINING] " : " " ) < < " , " ;
ss < < wti . tx_type < < " , " ;
ss < < print_money ( wti . fee ) < < ENDL ;
}
2021-10-20 21:18:21 +02:00
} ;
void wallet2 : : wti_to_txt_line ( std : : ostream & ss , const wallet_public : : wallet_transfer_info & wti , size_t index )
{
2023-06-11 00:20:11 +02:00
for ( auto & subtr : wti . subtransfers )
{
ss < < ( subtr . is_income ? " [INC] " : " [OUT] " ) < < " \t "
< < epee : : misc_utils : : get_time_str ( wti . timestamp ) < < " \t "
< < print_money ( subtr . amount ) < < " \t "
< < subtr . asset_id < < " \t "
< < print_money ( wti . fee ) < < " \t "
< < wti . remote_addresses < < " \t "
< < wti . comment < < ENDL ;
}
2021-10-20 21:18:21 +02:00
} ;
void wallet2 : : wti_to_json_line ( std : : ostream & ss , const wallet_public : : wallet_transfer_info & wti , size_t index )
{
ss < < epee : : serialization : : store_t_to_json ( wti , 4 ) < < " , " ;
} ;
//----------------------------------------------------------------------------------------------------
void wallet2 : : export_transaction_history ( std : : ostream & ss , const std : : string & format , bool include_pos_transactions )
{
//typedef int(*t_somefunc)(int, int);
typedef void ( * playout_cb_type ) ( std : : ostream & , const wallet_public : : wallet_transfer_info & , size_t ) ;
playout_cb_type cb_csv = & wallet2 : : wti_to_csv_entry ;
playout_cb_type cb_json = & wallet2 : : wti_to_json_line ;
playout_cb_type cb_plain_text = & wallet2 : : wti_to_txt_line ;
playout_cb_type cb = cb_csv ;
if ( format = = " json " )
{
ss < < " { \" history \" : [ " ;
cb = cb_json ;
}
else if ( format = = " text " )
{
cb = cb_plain_text ;
}
else
{
//csv by default
2023-06-11 00:20:11 +02:00
ss < < " N, Date, Amount, AssetID, Comment, Address, ID, Height, Unlock timestamp, Tx size, Alias, PaymentID, In/Out, Flags, Type, Fee " < < ENDL ;
2021-10-20 21:18:21 +02:00
}
enum_container ( m_transfer_history . begin ( ) , m_transfer_history . end ( ) , [ & ] ( wallet_public : : wallet_transfer_info & wti , size_t index ) {
if ( ! include_pos_transactions )
{
if ( currency : : is_coinbase ( wti . tx ) )
return true ;
}
2022-03-15 21:12:51 +02:00
wti . fee = currency : : get_tx_fee ( wti . tx ) ;
2021-10-20 21:18:21 +02:00
cb ( ss , wti , index ) ;
return true ;
} ) ;
if ( format = = " json " )
{
ss < < " {}]} " ;
}
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_transfer_address ( const std : : string & adr_str , currency : : account_public_address & addr , std : : string & payment_id )
{
return m_core_proxy - > get_transfer_address ( adr_str , addr , payment_id ) ;
}
//----------------------------------------------------------------------------------------------------
2022-10-23 23:48:43 +02:00
bool wallet2 : : is_transfer_okay_for_pos ( const transfer_details & tr , bool is_zarcanum_hf , uint64_t & stake_unlock_time ) const
2018-12-27 18:50:45 +03:00
{
2023-03-10 13:46:33 +01:00
if ( is_zarcanum_hf )
{
if ( ! tr . is_zc ( ) )
return false ;
if ( ! tr . is_native_coin ( ) )
return false ;
}
2022-10-23 23:48:43 +02:00
2019-04-12 09:48:33 +03:00
if ( ! tr . is_spendable ( ) )
2018-12-27 18:50:45 +03:00
return false ;
//blockchain conditions
2019-07-24 19:14:35 +02:00
if ( ! is_transfer_unlocked ( tr , true , stake_unlock_time ) )
2018-12-27 18:50:45 +03:00
return false ;
2019-07-24 19:14:35 +02:00
//prevent staking of after-last-pow-coins
2020-04-25 00:30:55 +02:00
if ( get_blockchain_current_size ( ) - tr . m_ptx_wallet_info - > m_block_height < = m_core_runtime_config . min_coinstake_age )
2018-12-27 18:50:45 +03:00
return false ;
2019-07-20 14:36:12 +02:00
2019-08-03 19:15:56 +02:00
if ( tr . m_ptx_wallet_info - > m_block_height > m_last_pow_block_h )
2019-07-20 14:36:12 +02:00
return false ;
2018-12-27 18:50:45 +03:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2022-05-14 00:00:34 +02:00
void wallet2 : : get_mining_history ( wallet_public : : mining_history & hist , uint64_t timestamp_from )
2018-12-27 18:50:45 +03:00
{
for ( auto & tr : m_transfer_history )
{
2022-05-14 00:00:34 +02:00
if ( currency : : is_coinbase ( tr . tx ) & & tr . tx . vin . size ( ) = = 2 & & tr . timestamp > timestamp_from )
2018-12-27 18:50:45 +03:00
{
2019-08-27 17:36:53 +02:00
tools : : wallet_public : : mining_history_entry mhe = AUTO_VAL_INIT ( mhe ) ;
2023-06-11 00:20:11 +02:00
mhe . a = tr . get_native_income_amount ( ) ;
2018-12-27 18:50:45 +03:00
mhe . t = tr . timestamp ;
mhe . h = tr . height ;
hist . mined_entries . push_back ( mhe ) ;
}
}
}
//----------------------------------------------------------------------------------------------------
2022-09-05 12:30:08 +02:00
size_t wallet2 : : get_pos_entries_count ( )
2018-12-27 18:50:45 +03:00
{
2022-10-23 23:48:43 +02:00
bool is_zarcanum_hf = is_in_hardfork_zone ( ZANO_HARDFORK_04_ZARCANUM ) ;
2022-09-05 12:30:08 +02:00
size_t counter = 0 ;
for ( size_t i = 0 , size = m_transfers . size ( ) ; i < size ; i + + )
2018-12-27 18:50:45 +03:00
{
auto & tr = m_transfers [ i ] ;
2020-06-14 19:31:10 +02:00
2019-07-24 19:14:35 +02:00
uint64_t stake_unlock_time = 0 ;
2022-10-23 23:48:43 +02:00
if ( ! is_transfer_okay_for_pos ( tr , is_zarcanum_hf , stake_unlock_time ) )
2018-12-27 18:50:45 +03:00
continue ;
2020-06-14 19:31:10 +02:00
2022-09-05 12:30:08 +02:00
+ + counter ;
}
return counter ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_pos_entries ( std : : vector < currency : : pos_entry > & entries )
{
2022-10-23 23:48:43 +02:00
bool is_zarcanum_hf = is_in_hardfork_zone ( ZANO_HARDFORK_04_ZARCANUM ) ;
2022-09-05 12:30:08 +02:00
for ( size_t i = 0 ; i ! = m_transfers . size ( ) ; i + + )
{
auto & tr = m_transfers [ i ] ;
uint64_t stake_unlock_time = 0 ;
2022-10-23 23:48:43 +02:00
if ( ! is_transfer_okay_for_pos ( tr , is_zarcanum_hf , stake_unlock_time ) )
2022-09-05 12:30:08 +02:00
continue ;
2020-06-14 19:31:10 +02:00
2022-09-05 12:30:08 +02:00
pos_entry pe = AUTO_VAL_INIT ( pe ) ;
2018-12-27 18:50:45 +03:00
pe . amount = tr . amount ( ) ;
2022-09-08 22:47:32 +02:00
pe . g_index = tr . m_global_output_index ;
2018-12-27 18:50:45 +03:00
pe . keyimage = tr . m_key_image ;
pe . wallet_index = i ;
2019-07-24 19:14:35 +02:00
pe . stake_unlock_time = stake_unlock_time ;
2018-12-27 18:50:45 +03:00
pe . block_timestamp = tr . m_ptx_wallet_info - > m_block_timestamp ;
2022-09-05 12:30:08 +02:00
entries . push_back ( pe ) ;
2018-12-27 18:50:45 +03:00
}
return true ;
}
//----------------------------------------------------------------------------------------------------
2022-10-12 17:54:42 +02:00
bool wallet2 : : is_in_hardfork_zone ( uint64_t hardfork_index ) const
2022-05-23 21:03:13 +02:00
{
2022-10-12 17:54:42 +02:00
return m_core_runtime_config . is_hardfork_active_for_height ( hardfork_index , get_blockchain_current_size ( ) ) ;
2022-05-23 21:03:13 +02:00
}
//----------------------------------------------------------------------------------------------------
2023-06-09 01:19:37 +02:00
bool wallet2 : : prepare_and_sign_pos_block ( const mining_context & cxt , uint64_t full_block_reward , const currency : : pos_entry & pe , currency : : tx_generation_context & miner_tx_tgc , currency : : block & b ) const
2018-12-27 18:50:45 +03:00
{
2022-10-14 19:18:24 +02:00
bool r = false ;
2022-10-12 18:02:22 +02:00
WLT_CHECK_AND_ASSERT_MES ( pe . wallet_index < m_transfers . size ( ) , false , " invalid pe.wallet_index: " < < pe . wallet_index ) ;
2022-10-14 19:18:24 +02:00
const transfer_details & td = m_transfers [ pe . wallet_index ] ;
const transaction & source_tx = td . m_ptx_wallet_info - > m_tx ;
2022-10-16 03:17:18 +02:00
const crypto : : public_key source_tx_pub_key = get_tx_pub_key_from_extra ( source_tx ) ;
2022-10-14 19:18:24 +02:00
WLT_CHECK_AND_ASSERT_MES ( pe . tx_out_index < source_tx . vout . size ( ) , false , " invalid pe.tx_out_index: " < < pe . tx_out_index ) ;
const currency : : tx_out_v & stake_out_v = source_tx . vout [ pe . tx_out_index ] ;
2022-11-08 00:09:34 +01:00
// calculate stake_out_derivation and secret_x (derived ephemeral secret key)
crypto : : key_derivation stake_out_derivation = AUTO_VAL_INIT ( stake_out_derivation ) ;
r = crypto : : generate_key_derivation ( source_tx_pub_key , m_account . get_keys ( ) . view_secret_key , stake_out_derivation ) ; // d = 8 * v * R
WLT_CHECK_AND_ASSERT_MES ( r , false , " generate_key_derivation failed, tid: " < < pe . wallet_index < < " , pe.tx_id: " < < pe . tx_id ) ;
crypto : : secret_key secret_x = AUTO_VAL_INIT ( secret_x ) ;
crypto : : derive_secret_key ( stake_out_derivation , pe . tx_out_index , m_account . get_keys ( ) . spend_secret_key , secret_x ) ; // x = Hs(8 * v * R, i) + s
2022-10-14 19:18:24 +02:00
if ( ! cxt . zarcanum )
2022-05-23 21:03:13 +02:00
{
2022-09-05 12:30:08 +02:00
// old PoS with non-hidden amounts
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 0 ] . type ( ) = = typeid ( currency : : txin_gen ) , false , " Wrong input 0 type in transaction: " < < b . miner_tx . vin [ 0 ] . type ( ) . name ( ) ) ;
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 1 ] . type ( ) = = typeid ( currency : : txin_to_key ) , false , " Wrong input 1 type in transaction: " < < b . miner_tx . vin [ 1 ] . type ( ) . name ( ) ) ;
2022-11-03 21:01:49 +01:00
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . signatures . size ( ) = = 1 & & b . miner_tx . signatures [ 0 ] . type ( ) = = typeid ( NLSAG_sig ) , false , " wrong sig prepared in a PoS block " ) ;
WLT_CHECK_AND_ASSERT_MES ( stake_out_v . type ( ) = = typeid ( tx_out_bare ) , false , " unexpected stake output type: " < < stake_out_v . type ( ) . name ( ) < < " , expected: tx_out_bare " ) ;
const tx_out_bare & stake_out = boost : : get < tx_out_bare > ( stake_out_v ) ;
WLT_CHECK_AND_ASSERT_MES ( stake_out . target . type ( ) = = typeid ( txout_to_key ) , false , " unexpected stake output target type: " < < stake_out . target . type ( ) . name ( ) < < " , expected: txout_to_key " ) ;
NLSAG_sig & sig = boost : : get < NLSAG_sig > ( b . miner_tx . signatures [ 0 ] ) ;
txin_to_key & stake_input = boost : : get < txin_to_key > ( b . miner_tx . vin [ 1 ] ) ;
const txout_to_key & stake_out_target = boost : : get < txout_to_key > ( stake_out . target ) ;
2022-11-08 00:09:34 +01:00
// fill stake input
2022-11-03 21:01:49 +01:00
stake_input . k_image = pe . keyimage ;
stake_input . amount = pe . amount ;
stake_input . key_offsets . push_back ( pe . g_index ) ;
2018-12-27 18:50:45 +03:00
2022-05-23 21:03:13 +02:00
// sign block actually in coinbase transaction
crypto : : hash block_hash = currency : : get_block_hash ( b ) ;
2018-12-27 18:50:45 +03:00
2022-10-12 18:02:22 +02:00
// get stake output pub key (stealth address) for ring signature generation
std : : vector < const crypto : : public_key * > keys_ptrs ;
2022-11-03 21:01:49 +01:00
keys_ptrs . push_back ( & stake_out_target . key ) ;
2022-10-12 18:02:22 +02:00
2022-11-08 00:09:34 +01:00
// generate sring signature
sig . s . resize ( 1 ) ;
2022-11-16 23:04:29 +01:00
crypto : : generate_ring_signature ( block_hash , stake_input . k_image , keys_ptrs , secret_x , 0 , sig . s . data ( ) ) ;
2018-12-27 18:50:45 +03:00
2022-11-03 21:01:49 +01:00
WLT_LOG_L4 ( " GENERATED RING SIGNATURE for PoS block coinbase: block_id " < < block_hash
< < " txin.k_image " < < stake_input . k_image
2022-05-23 21:03:13 +02:00
< < " key_ptr: " < < * keys_ptrs [ 0 ]
2022-11-03 21:01:49 +01:00
< < " signature: " < < sig . s ) ;
2018-12-27 18:50:45 +03:00
2022-05-23 21:03:13 +02:00
return true ;
}
2022-09-05 12:30:08 +02:00
// Zarcanum
2022-10-16 03:17:18 +02:00
WLT_CHECK_AND_ASSERT_MES ( td . is_zc ( ) , false , " the transfer [ " < < pe . wallet_index < < " ] is not zc type, which is required for zarcanum " ) ;
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 0 ] . type ( ) = = typeid ( currency : : txin_gen ) , false , " Wrong input 0 type in transaction: " < < b . miner_tx . vin [ 0 ] . type ( ) . name ( ) ) ;
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 1 ] . type ( ) = = typeid ( currency : : txin_zc_input ) , false , " Wrong input 1 type in transaction: " < < b . miner_tx . vin [ 1 ] . type ( ) . name ( ) ) ;
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . signatures . size ( ) = = 1 & & b . miner_tx . signatures [ 0 ] . type ( ) = = typeid ( zarcanum_sig ) , false , " wrong sig prepared in a PoS block " ) ;
WLT_CHECK_AND_ASSERT_MES ( stake_out_v . type ( ) = = typeid ( tx_out_zarcanum ) , false , " unexpected stake output type: " < < stake_out_v . type ( ) . name ( ) < < " , expected: tx_out_zarcanum " ) ;
zarcanum_sig & sig = boost : : get < zarcanum_sig > ( b . miner_tx . signatures [ 0 ] ) ;
txin_zc_input & stake_input = boost : : get < txin_zc_input > ( b . miner_tx . vin [ 1 ] ) ;
const tx_out_zarcanum & stake_out = boost : : get < tx_out_zarcanum > ( stake_out_v ) ;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : response decoys_resp = AUTO_VAL_INIT ( decoys_resp ) ;
2023-03-20 21:25:08 +01:00
std : : vector < crypto : : CLSAG_GGXXG_input_ref_t > ring ;
2022-10-16 03:17:18 +02:00
uint64_t secret_index = 0 ; // index of the real stake output
// get decoys outputs and construct miner tx
2022-10-18 04:34:32 +02:00
static size_t required_decoys_count = 4 ; // TODO @#@# set them somewhere else
2022-10-16 03:17:18 +02:00
static bool use_only_forced_to_mix = false ; // TODO @#@# set them somewhere else
if ( required_decoys_count > 0 )
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : request decoys_req = AUTO_VAL_INIT ( decoys_req ) ;
decoys_req . height_upper_limit = m_last_pow_block_h ; // request decoys to be either older than, or the same age as stake output's height
decoys_req . use_forced_mix_outs = use_only_forced_to_mix ;
decoys_req . decoys_count = required_decoys_count + 1 ; // one more to be able to skip a decoy in case it hits the real output
decoys_req . amounts . push_back ( 0 ) ; // request one batch of decoys for hidden amounts
r = m_core_proxy - > call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS ( decoys_req , decoys_resp ) ;
// TODO @#@# do we need these exceptions?
THROW_IF_FALSE_WALLET_EX ( r , error : : no_connection_to_daemon , " getrandom_outs.bin " ) ;
THROW_IF_FALSE_WALLET_EX ( decoys_resp . status ! = API_RETURN_CODE_BUSY , error : : daemon_busy , " getrandom_outs.bin " ) ;
THROW_IF_FALSE_WALLET_EX ( decoys_resp . status = = API_RETURN_CODE_OK , error : : get_random_outs_error , decoys_resp . status ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( decoys_resp . outs . size ( ) = = 1 , " got wrong number of decoys batches: " < < decoys_resp . outs . size ( ) ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( decoys_resp . outs [ 0 ] . outs . size ( ) = = required_decoys_count + 1 , " for PoS stake tx got less decoys to mix than requested: " < < decoys_resp . outs [ 0 ] . outs . size ( ) < < " < " < < required_decoys_count + 1 ) ;
auto & decoys = decoys_resp . outs [ 0 ] . outs ;
2023-02-08 18:50:26 +01:00
decoys . emplace_front ( td . m_global_output_index , stake_out . stealth_address , stake_out . amount_commitment , stake_out . concealing_point , stake_out . blinded_asset_id ) ;
2022-10-18 04:34:32 +02:00
std : : unordered_set < uint64_t > used_gindices ;
size_t good_outs_count = 0 ;
2022-10-16 03:17:18 +02:00
for ( auto it = decoys . begin ( ) ; it ! = decoys . end ( ) ; )
{
if ( used_gindices . count ( it - > global_amount_index ) ! = 0 )
{
it = decoys . erase ( it ) ;
continue ;
}
used_gindices . insert ( it - > global_amount_index ) ;
2022-10-18 04:34:32 +02:00
if ( + + good_outs_count = = required_decoys_count + 1 )
2022-10-16 03:17:18 +02:00
{
decoys . erase ( + + it , decoys . end ( ) ) ;
break ;
}
+ + it ;
}
2022-10-18 04:34:32 +02:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( decoys . size ( ) = = required_decoys_count + 1 , " for PoS stake got less good decoys than required: " < < decoys . size ( ) < < " < " < < required_decoys_count ) ;
2022-11-08 00:17:34 +01:00
decoys . sort ( [ ] ( auto & l , auto & r ) { return l . global_amount_index < r . global_amount_index ; } ) ; // sort them now (note absolute_sorted_output_offsets_to_relative_in_place() below)
2022-10-16 03:17:18 +02:00
uint64_t i = 0 ;
for ( auto & el : decoys )
{
2022-10-18 04:34:32 +02:00
uint64_t gindex = el . global_amount_index ;
if ( gindex = = td . m_global_output_index )
secret_index = i ;
+ + i ;
2023-03-20 21:25:08 +01:00
ring . emplace_back ( el . stealth_address , el . amount_commitment , el . blinded_asset_id , el . concealing_point ) ;
2022-10-16 03:17:18 +02:00
stake_input . key_offsets . push_back ( el . global_amount_index ) ;
}
2022-11-08 00:17:34 +01:00
r = absolute_sorted_output_offsets_to_relative_in_place ( stake_input . key_offsets ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( r , " absolute_sorted_output_offsets_to_relative_in_place failed " ) ;
2022-10-16 03:17:18 +02:00
}
else
{
// no decoys, the ring consist of one element -- the real stake output
2023-03-20 21:25:08 +01:00
ring . emplace_back ( stake_out . stealth_address , stake_out . amount_commitment , stake_out . blinded_asset_id , stake_out . concealing_point ) ;
2022-10-16 03:17:18 +02:00
stake_input . key_offsets . push_back ( td . m_global_output_index ) ;
}
stake_input . k_image = pe . keyimage ;
# ifndef NDEBUG
{
2023-02-08 18:50:26 +01:00
crypto : : point_t source_amount_commitment = crypto : : c_scalar_1div8 * td . m_amount * crypto : : c_point_H + crypto : : c_scalar_1div8 * td . m_zc_info_ptr - > amount_blinding_mask * crypto : : c_point_G ;
2023-03-20 21:25:08 +01:00
WLT_CHECK_AND_ASSERT_MES ( stake_out . amount_commitment = = source_amount_commitment . to_public_key ( ) , false , " real output amount commitment check failed " ) ;
WLT_CHECK_AND_ASSERT_MES ( ring [ secret_index ] . amount_commitment = = stake_out . amount_commitment , false , " ring secret member doesn't match with the stake output " ) ;
WLT_CHECK_AND_ASSERT_MES ( cxt . stake_amount = = td . m_amount , false , " stake_amount missmatch " ) ;
//WLT_CHECK_AND_ASSERT_MES(source_amount_commitment == cxt.stake_amount * cxt.stake_out_blinded_asset_id + cxt.stake_out_amount_blinding_mask * crypto::c_point_G, false, "source_amount_commitment missmatch");
2022-10-16 03:17:18 +02:00
}
# endif
2023-03-17 23:59:21 +01:00
crypto : : hash hash_for_zarcanum_sig = get_block_hash ( b ) ;
2022-10-24 21:01:45 +02:00
2023-04-12 20:13:54 +02:00
WLT_CHECK_AND_ASSERT_MES ( miner_tx_tgc . pseudo_out_amount_blinding_masks_sum . is_zero ( ) , false , " pseudo_out_amount_blinding_masks_sum is nonzero " ) ; // it should be zero because there's only one input (stake), and thus one pseudo out
crypto : : scalar_t pseudo_out_amount_blinding_mask = miner_tx_tgc . amount_blinding_masks_sum ; // sum of outputs' amount blinding masks
2023-03-20 21:25:08 +01:00
2023-04-12 20:13:54 +02:00
miner_tx_tgc . pseudo_outs_blinded_asset_ids . emplace_back ( currency : : native_coin_asset_id_pt ) ;
miner_tx_tgc . pseudo_outs_plus_real_out_blinding_masks . emplace_back ( 0 ) ;
miner_tx_tgc . real_zc_ins_asset_ids . emplace_back ( td . m_zc_info_ptr - > asset_id ) ;
2022-10-24 21:01:45 +02:00
2022-10-16 03:17:18 +02:00
uint8_t err = 0 ;
2023-03-17 23:59:21 +01:00
r = crypto : : zarcanum_generate_proof ( hash_for_zarcanum_sig , cxt . kernel_hash , ring , cxt . last_pow_block_id_hashed , cxt . sk . kimage ,
2023-03-20 21:25:08 +01:00
secret_x , cxt . secret_q , secret_index , td . m_zc_info_ptr - > asset_id_blinding_mask , pseudo_out_amount_blinding_mask , cxt . stake_amount , cxt . stake_out_amount_blinding_mask ,
2022-10-16 03:17:18 +02:00
static_cast < crypto : : zarcanum_proof & > ( sig ) , & err ) ;
WLT_CHECK_AND_ASSERT_MES ( r , false , " zarcanum_generate_proof failed, err: " < < ( int ) err ) ;
2023-03-21 02:34:29 +01:00
// TODO @#@# [architecture] the same value is calculated in zarcanum_generate_proof(), consider an impovement
2023-04-12 20:13:54 +02:00
miner_tx_tgc . pseudo_out_amount_commitments_sum + = cxt . stake_amount * currency : : native_coin_asset_id_pt + pseudo_out_amount_blinding_mask * crypto : : c_point_G ;
2023-03-21 02:34:29 +01:00
2023-03-17 23:59:21 +01:00
//
// The miner tx prefix should be sealed by now, and the tx hash should be defined.
// Any changes made below should only affect the signatures/proofs and should not impact the prefix hash calculation.
//
crypto : : hash miner_tx_id = get_transaction_hash ( b . miner_tx ) ;
// proofs for miner_tx
2023-03-22 23:28:01 +01:00
// asset surjection proof
currency : : zc_asset_surjection_proof asp { } ;
2023-04-12 20:13:54 +02:00
r = generate_asset_surjection_proof ( miner_tx_id , false , miner_tx_tgc , asp ) ; // has_non_zc_inputs == false because after the HF4 PoS mining is only allowed for ZC stakes inputs
2023-04-01 06:49:40 +02:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " generete_asset_surjection_proof failed " ) ;
2023-03-22 23:28:01 +01:00
b . miner_tx . proofs . emplace_back ( std : : move ( asp ) ) ;
// range proofs
2023-04-01 06:49:40 +02:00
currency : : zc_outs_range_proof range_proofs { } ;
2023-04-12 20:13:54 +02:00
r = generate_zc_outs_range_proof ( miner_tx_id , 0 , miner_tx_tgc , b . miner_tx . vout , range_proofs ) ;
2023-03-20 21:25:08 +01:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " Failed to generate zc_outs_range_proof() " ) ;
2023-03-17 23:59:21 +01:00
b . miner_tx . proofs . emplace_back ( std : : move ( range_proofs ) ) ;
2023-03-22 23:28:01 +01:00
// balance proof
2023-03-17 23:59:21 +01:00
currency : : zc_balance_proof balance_proof { } ;
2023-06-09 01:19:37 +02:00
r = generate_tx_balance_proof ( b . miner_tx , miner_tx_id , miner_tx_tgc , full_block_reward , balance_proof ) ;
2023-03-20 21:25:08 +01:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " generate_tx_balance_proof failed " ) ;
2023-03-17 23:59:21 +01:00
b . miner_tx . proofs . emplace_back ( std : : move ( balance_proof ) ) ;
2023-05-22 04:27:40 +02:00
// the following line are for debugging when necessary -- sowle
//err = 0;
//r = crypto::zarcanum_verify_proof(hash_for_zarcanum_sig, cxt.kernel_hash, ring, cxt.last_pow_block_id_hashed, cxt.sk.kimage, cxt.basic_diff, sig, &err);
//WLT_CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << (int)err);
2022-10-16 03:17:18 +02:00
return true ;
2018-12-27 18:50:45 +03:00
}
//------------------------------------------------------------------
bool wallet2 : : fill_mining_context ( mining_context & ctx )
{
currency : : COMMAND_RPC_GET_POS_MINING_DETAILS : : request pos_details_req = AUTO_VAL_INIT ( pos_details_req ) ;
currency : : COMMAND_RPC_GET_POS_MINING_DETAILS : : response pos_details_resp = AUTO_VAL_INIT ( pos_details_resp ) ;
m_core_proxy - > call_COMMAND_RPC_GET_POS_MINING_DETAILS ( pos_details_req , pos_details_resp ) ;
2020-05-07 23:26:41 +02:00
if ( pos_details_resp . status ! = API_RETURN_CODE_OK )
2018-12-27 18:50:45 +03:00
return false ;
2022-08-29 23:00:34 +02:00
2022-10-23 23:48:43 +02:00
ctx = mining_context { } ;
ctx . init ( wide_difficulty_type ( pos_details_resp . pos_basic_difficulty ) , pos_details_resp . sm , is_in_hardfork_zone ( ZANO_HARDFORK_04_ZARCANUM ) ) ;
2022-08-29 23:00:34 +02:00
2022-09-09 20:16:14 +02:00
ctx . last_block_hash = pos_details_resp . last_block_hash ;
ctx . is_pos_allowed = pos_details_resp . pos_mining_allowed ;
ctx . starter_timestamp = pos_details_resp . starter_timestamp ;
2022-10-23 23:48:43 +02:00
ctx . status = API_RETURN_CODE_NOT_FOUND ;
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------
bool wallet2 : : try_mint_pos ( )
2019-12-10 00:50:30 +03:00
{
return try_mint_pos ( m_account . get_public_address ( ) ) ;
}
//------------------------------------------------------------------
bool wallet2 : : try_mint_pos ( const currency : : account_public_address & miner_address )
2018-12-27 18:50:45 +03:00
{
2023-05-26 22:11:23 +02:00
TIME_MEASURE_START_MS ( mining_duration_ms ) ;
2018-12-27 18:50:45 +03:00
mining_context ctx = AUTO_VAL_INIT ( ctx ) ;
2019-06-27 02:30:43 +03:00
WLT_LOG_L1 ( " Starting PoS mining iteration " ) ;
2018-12-27 18:50:45 +03:00
fill_mining_context ( ctx ) ;
2022-09-09 20:16:14 +02:00
if ( ! ctx . is_pos_allowed )
2018-12-27 18:50:45 +03:00
{
2019-06-27 02:30:43 +03:00
WLT_LOG_YELLOW ( " POS MINING NOT ALLOWED YET " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
2019-06-27 02:30:43 +03:00
2018-12-27 18:50:45 +03:00
std : : atomic < bool > stop ( false ) ;
scan_pos ( ctx , stop , [ this ] ( ) {
size_t blocks_fetched ;
refresh ( blocks_fetched ) ;
if ( blocks_fetched )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Detected new block, minting interrupted " ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
return true ;
} , m_core_runtime_config ) ;
2022-09-09 20:16:14 +02:00
if ( ctx . status = = API_RETURN_CODE_OK )
2018-12-27 18:50:45 +03:00
{
2022-09-05 12:30:08 +02:00
build_minted_block ( ctx , miner_address ) ;
2018-12-27 18:50:45 +03:00
}
2023-05-26 22:11:23 +02:00
TIME_MEASURE_FINISH_MS ( mining_duration_ms ) ;
2019-06-27 02:30:43 +03:00
2023-05-26 22:11:23 +02:00
WLT_LOG_L0 ( " PoS mining: " < < ctx . iterations_processed < < " iterations finished ( " < < std : : fixed < < std : : setprecision ( 2 ) < < ( mining_duration_ms / 1000.0f ) < < " s), status: " < < ctx . status < < " , " < < ctx . total_items_checked < < " entries with total amount: " < < print_money_brief ( ctx . total_amount_checked ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
2022-08-29 23:00:34 +02:00
//------------------------------------------------------------------
2022-09-12 18:47:38 +02:00
void wallet2 : : do_pos_mining_prepare_entry ( mining_context & context , size_t transfer_index )
2022-08-29 23:00:34 +02:00
{
2022-09-12 18:47:38 +02:00
CHECK_AND_ASSERT_MES_NO_RET ( transfer_index < m_transfers . size ( ) , " transfer_index is out of bounds: " < < transfer_index ) ;
const transfer_details & td = m_transfers [ transfer_index ] ;
2022-08-29 23:00:34 +02:00
2023-02-08 18:50:26 +01:00
crypto : : scalar_t amount_blinding_mask { } ;
if ( td . is_zc ( ) )
amount_blinding_mask = td . m_zc_info_ptr - > amount_blinding_mask ;
2022-10-18 04:32:40 +02:00
2022-10-23 23:48:43 +02:00
context . prepare_entry ( td . amount ( ) , td . m_key_image , get_tx_pub_key_from_extra ( td . m_ptx_wallet_info - > m_tx ) , td . m_internal_output_index ,
2023-02-08 18:50:26 +01:00
amount_blinding_mask , m_account . get_keys ( ) . view_secret_key ) ;
2022-08-29 23:00:34 +02:00
}
//------------------------------------------------------------------
2022-09-12 18:47:38 +02:00
bool wallet2 : : do_pos_mining_iteration ( mining_context & context , size_t transfer_index , uint64_t ts )
2022-08-29 23:00:34 +02:00
{
2022-10-23 23:48:43 +02:00
return context . do_iteration ( ts ) ;
2022-08-29 23:00:34 +02:00
}
2018-12-27 18:50:45 +03:00
//-------------------------------
bool wallet2 : : reset_history ( )
{
std : : string pass = m_password ;
std : : wstring file_path = m_wallet_file ;
account_base acc_tmp = m_account ;
clear ( ) ;
m_account = acc_tmp ;
m_password = pass ;
2019-04-03 12:48:09 +03:00
prepare_file_names ( file_path ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//-------------------------------
2022-10-12 18:02:22 +02:00
bool wallet2 : : build_minted_block ( const mining_context & cxt )
2018-12-27 18:50:45 +03:00
{
2022-10-12 18:02:22 +02:00
return build_minted_block ( cxt , m_account . get_public_address ( ) ) ;
2018-12-27 18:50:45 +03:00
}
2022-10-12 18:02:22 +02:00
bool wallet2 : : build_minted_block ( const mining_context & cxt , const currency : : account_public_address & miner_address )
2018-12-27 18:50:45 +03:00
{
2022-09-14 22:50:43 +02:00
//found a block, construct it, sign and push to daemon
WLT_LOG_GREEN ( " Found kernel, constructing block " , LOG_LEVEL_0 ) ;
WLT_CHECK_AND_ASSERT_MES ( cxt . index < m_transfers . size ( ) , false , " cxt.index = " < < cxt . index < < " is out of bounds " ) ;
const transfer_details & td = m_transfers [ cxt . index ] ;
currency : : COMMAND_RPC_GETBLOCKTEMPLATE : : request tmpl_req = AUTO_VAL_INIT ( tmpl_req ) ;
currency : : COMMAND_RPC_GETBLOCKTEMPLATE : : response tmpl_rsp = AUTO_VAL_INIT ( tmpl_rsp ) ;
tmpl_req . wallet_address = get_account_address_as_str ( miner_address ) ;
tmpl_req . stakeholder_address = get_account_address_as_str ( m_account . get_public_address ( ) ) ;
tmpl_req . pos_block = true ;
2022-10-14 19:18:24 +02:00
tmpl_req . extra_text = m_miner_text_info ;
2022-10-12 18:02:22 +02:00
tmpl_req . pe = AUTO_VAL_INIT ( tmpl_req . pe ) ;
tmpl_req . pe . amount = td . amount ( ) ;
tmpl_req . pe . block_timestamp = td . m_ptx_wallet_info - > m_block_timestamp ;
tmpl_req . pe . g_index = td . m_global_output_index ;
tmpl_req . pe . keyimage = td . m_key_image ;
tmpl_req . pe . stake_unlock_time = cxt . stake_unlock_time ;
tmpl_req . pe . tx_id = td . tx_hash ( ) ;
tmpl_req . pe . tx_out_index = td . m_internal_output_index ;
tmpl_req . pe . wallet_index = cxt . index ;
2023-06-15 17:09:33 +02:00
// mark stake source as spent and make sure it will be restored in case of error
const std : : vector < uint64_t > stake_transfer_idx_vec { cxt . index } ;
mark_transfers_as_spent ( stake_transfer_idx_vec , " stake source " ) ;
bool gracefull_leaving = false ;
auto stake_transfer_spent_flag_restorer = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) {
if ( ! gracefull_leaving )
clear_transfers_from_flag ( stake_transfer_idx_vec , WALLET_TRANSFER_DETAIL_FLAG_SPENT , " stake source " ) ;
} ) ;
2023-06-09 01:10:16 +02:00
// generate UTXO Defragmentation Transaction - to reduce the UTXO set size
transaction udtx { } ;
if ( generate_utxo_defragmentation_transaction_if_needed ( udtx ) )
2022-09-14 22:50:43 +02:00
{
2023-06-09 01:10:16 +02:00
tx_to_blob ( udtx , tmpl_req . explicit_transaction ) ;
WLT_LOG_GREEN ( " Note: " < < udtx . vin . size ( ) < < " inputs were aggregated into UTXO defragmentation tx " < < get_transaction_hash ( udtx ) , LOG_LEVEL_0 ) ;
2022-09-14 22:50:43 +02:00
}
m_core_proxy - > call_COMMAND_RPC_GETBLOCKTEMPLATE ( tmpl_req , tmpl_rsp ) ;
WLT_CHECK_AND_ASSERT_MES ( tmpl_rsp . status = = API_RETURN_CODE_OK , false , " Failed to create block template after kernel hash found! " ) ;
currency : : block b = AUTO_VAL_INIT ( b ) ;
currency : : blobdata block_blob ;
bool res = epee : : string_tools : : parse_hexstr_to_binbuff ( tmpl_rsp . blocktemplate_blob , block_blob ) ;
2022-10-12 18:02:22 +02:00
WLT_CHECK_AND_ASSERT_MES ( res , false , " parse_hexstr_to_binbuff() failed after kernel hash found! " ) ;
2022-09-14 22:50:43 +02:00
res = parse_and_validate_block_from_blob ( block_blob , b ) ;
2022-10-12 18:02:22 +02:00
WLT_CHECK_AND_ASSERT_MES ( res , false , " parse_and_validate_block_from_blob() failed after kernel hash found! " ) ;
2022-09-14 22:50:43 +02:00
if ( cxt . last_block_hash ! = b . prev_id )
{
WLT_LOG_YELLOW ( " Kernel was found but block is behindhand, b.prev_id= " < < b . prev_id < < " , last_block_hash= " < < cxt . last_block_hash , LOG_LEVEL_0 ) ;
return false ;
}
2018-12-27 18:50:45 +03:00
2022-09-14 22:50:43 +02:00
// set the timestamp from stake kernel
b . timestamp = cxt . sk . block_timestamp ;
uint64_t current_timestamp = m_core_runtime_config . get_core_time ( ) ;
set_block_datetime ( current_timestamp , b ) ;
WLT_LOG_MAGENTA ( " Applying actual timestamp: " < < current_timestamp , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
2023-06-09 01:19:37 +02:00
uint64_t full_block_reward = tmpl_rsp . block_reward_without_fee + tmpl_rsp . txs_fee ;
res = prepare_and_sign_pos_block ( cxt , full_block_reward , tmpl_req . pe , tmpl_rsp . miner_tx_tgc , b ) ;
2023-04-05 06:45:27 +02:00
WLT_CHECK_AND_ASSERT_MES ( res , false , " Failed to prepare_and_sign_pos_block " ) ;
2022-09-14 22:50:43 +02:00
crypto : : hash block_hash = get_block_hash ( b ) ;
2023-06-15 17:09:33 +02:00
WLT_LOG_GREEN ( " Block " < < print16 ( block_hash ) < < " @ " < < get_block_height ( b ) < < " has been constructed, sending to core... " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
2022-09-14 22:50:43 +02:00
currency : : COMMAND_RPC_SUBMITBLOCK2 : : request subm_req = AUTO_VAL_INIT ( subm_req ) ;
currency : : COMMAND_RPC_SUBMITBLOCK2 : : response subm_rsp = AUTO_VAL_INIT ( subm_rsp ) ;
subm_req . b = t_serializable_object_to_blob ( b ) ;
if ( tmpl_req . explicit_transaction . size ( ) )
subm_req . explicit_txs . push_back ( hexemizer { tmpl_req . explicit_transaction } ) ;
2019-11-29 21:43:17 +01:00
2022-09-14 22:50:43 +02:00
m_core_proxy - > call_COMMAND_RPC_SUBMITBLOCK2 ( subm_req , subm_rsp ) ;
if ( subm_rsp . status ! = API_RETURN_CODE_OK )
{
WLT_LOG_ERROR ( " Constructed block " < < print16 ( block_hash ) < < " was rejected by the core, status: " < < subm_rsp . status ) ;
return false ;
}
WLT_LOG_GREEN ( " PoS block " < < print16 ( block_hash ) < < " generated and accepted, congrats! " , LOG_LEVEL_0 ) ;
m_wcallback - > on_pos_block_found ( b ) ;
2021-10-12 17:00:01 +03:00
2023-06-15 17:09:33 +02:00
gracefull_leaving = true ; // to prevent source transfer flags be cleared in scope leave handler
2022-09-14 22:50:43 +02:00
return true ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2020-09-01 00:26:22 +02:00
void wallet2 : : get_unconfirmed_transfers ( std : : vector < wallet_public : : wallet_transfer_info > & trs , bool exclude_mining_txs )
2018-12-27 18:50:45 +03:00
{
for ( auto & u : m_unconfirmed_txs )
2020-09-01 00:26:22 +02:00
{
2021-02-13 00:57:24 +01:00
if ( exclude_mining_txs & & currency : : is_coinbase ( u . second . tx ) )
2020-09-01 00:26:22 +02:00
{
continue ;
}
2018-12-27 18:50:45 +03:00
trs . push_back ( u . second ) ;
2020-09-01 00:26:22 +02:00
}
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : set_core_runtime_config ( const currency : : core_runtime_config & pc )
{
m_core_runtime_config = pc ;
}
//----------------------------------------------------------------------------------------------------
currency : : core_runtime_config & wallet2 : : get_core_runtime_config ( )
{
return m_core_runtime_config ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : is_transfer_unlocked ( const transfer_details & td ) const
2019-07-24 19:14:35 +02:00
{
uint64_t stub = 0 ;
return is_transfer_unlocked ( td , false , stub ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : is_transfer_unlocked ( const transfer_details & td , bool for_pos_mining , uint64_t & stake_lock_time ) const
2018-12-27 18:50:45 +03:00
{
if ( td . m_flags & WALLET_TRANSFER_DETAIL_FLAG_BLOCKED )
return false ;
2020-04-25 00:30:55 +02:00
if ( td . m_ptx_wallet_info - > m_block_height + WALLET_DEFAULT_TX_SPENDABLE_AGE > get_blockchain_current_size ( ) )
2018-12-27 18:50:45 +03:00
return false ;
2019-07-24 19:14:35 +02:00
2018-12-27 18:50:45 +03:00
2019-07-24 19:14:35 +02:00
uint64_t unlock_time = get_tx_unlock_time ( td . m_ptx_wallet_info - > m_tx , td . m_internal_output_index ) ;
2022-05-26 16:53:40 +02:00
if ( for_pos_mining & & m_core_runtime_config . is_hardfork_active_for_height ( 1 , get_blockchain_current_size ( ) ) )
2019-07-24 19:14:35 +02:00
{
//allowed of staking locked coins with
stake_lock_time = unlock_time ;
}
else
{
2020-04-25 00:30:55 +02:00
if ( ! currency : : is_tx_spendtime_unlocked ( unlock_time , get_blockchain_current_size ( ) , m_core_runtime_config . get_core_time ( ) ) )
2019-07-24 19:14:35 +02:00
return false ;
}
2018-12-27 18:50:45 +03:00
return true ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : push_offer ( const bc_services : : offer_details_ex & od , currency : : transaction & res_tx )
{
currency : : tx_destination_entry tx_dest ;
2020-04-22 23:30:03 +03:00
tx_dest . addr . push_back ( m_account . get_keys ( ) . account_address ) ;
2018-12-27 18:50:45 +03:00
tx_dest . amount = m_core_runtime_config . tx_default_fee ;
std : : vector < currency : : tx_destination_entry > destinations ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
bc_services : : put_offer_into_attachment ( static_cast < bc_services : : offer_details > ( od ) , attachments ) ;
destinations . push_back ( tx_dest ) ;
2022-09-20 22:01:52 +02:00
transfer ( destinations , 0 , 0 , od . fee , extra , attachments , get_current_split_strategy ( ) , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , res_tx ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
const transaction & wallet2 : : get_transaction_by_id ( const crypto : : hash & tx_hash )
{
for ( auto it = m_transfer_history . rbegin ( ) ; it ! = m_transfer_history . rend ( ) ; it + + )
{
if ( it - > tx_hash = = tx_hash )
return it - > tx ;
}
ASSERT_MES_AND_THROW ( " Tx " < < tx_hash < < " not found in wallet " ) ;
}
//----------------------------------------------------------------------------------------------------
2019-07-19 18:59:31 +03:00
void wallet2 : : cancel_offer_by_id ( const crypto : : hash & tx_id , uint64_t of_ind , uint64_t fee , currency : : transaction & res_tx )
2018-12-27 18:50:45 +03:00
{
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
bc_services : : cancel_offer co = AUTO_VAL_INIT ( co ) ;
co . offer_index = of_ind ;
co . tx_id = tx_id ;
const transaction & related_tx = get_transaction_by_id ( tx_id ) ;
crypto : : public_key related_tx_pub_key = get_tx_pub_key_from_extra ( related_tx ) ;
currency : : keypair ephemeral = AUTO_VAL_INIT ( ephemeral ) ;
bool r = currency : : derive_ephemeral_key_helper ( m_account . get_keys ( ) , related_tx_pub_key , of_ind , ephemeral ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " derive_ephemeral_key_helper failed, tx_id: " < < tx_id < < " , of_ind: " < < of_ind ) ;
blobdata sig_blob = bc_services : : make_offer_sig_blob ( co ) ;
crypto : : generate_signature ( crypto : : cn_fast_hash ( sig_blob . data ( ) , sig_blob . size ( ) ) , ephemeral . pub , ephemeral . sec , co . sig ) ;
bc_services : : put_offer_into_attachment ( co , attachments ) ;
2022-09-20 22:01:52 +02:00
transfer ( std : : vector < currency : : tx_destination_entry > ( ) , 0 , 0 , fee , extra , attachments , get_current_split_strategy ( ) , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , res_tx ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : update_offer_by_id ( const crypto : : hash & tx_id , uint64_t of_ind , const bc_services : : offer_details_ex & od , currency : : transaction & res_tx )
{
currency : : tx_destination_entry tx_dest ;
2020-04-22 23:30:03 +03:00
tx_dest . addr . push_back ( m_account . get_keys ( ) . account_address ) ;
2018-12-27 18:50:45 +03:00
tx_dest . amount = m_core_runtime_config . tx_default_fee ;
std : : vector < currency : : tx_destination_entry > destinations ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
bc_services : : update_offer uo = AUTO_VAL_INIT ( uo ) ;
uo . offer_index = of_ind ;
uo . tx_id = tx_id ;
uo . of = od ;
const transaction & related_tx = get_transaction_by_id ( tx_id ) ;
crypto : : public_key related_tx_pub_key = get_tx_pub_key_from_extra ( related_tx ) ;
currency : : keypair ephemeral ;
bool r = currency : : derive_ephemeral_key_helper ( m_account . get_keys ( ) , related_tx_pub_key , of_ind , ephemeral ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Failed to derive_ephemeral_key_helper " < < tx_id ) ;
blobdata sig_blob = bc_services : : make_offer_sig_blob ( uo ) ;
crypto : : generate_signature ( crypto : : cn_fast_hash ( sig_blob . data ( ) , sig_blob . size ( ) ) , ephemeral . pub , ephemeral . sec , uo . sig ) ;
bc_services : : put_offer_into_attachment ( uo , attachments ) ;
destinations . push_back ( tx_dest ) ;
2022-09-20 22:01:52 +02:00
transfer ( destinations , 0 , 0 , od . fee , extra , attachments , get_current_split_strategy ( ) , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , res_tx ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2020-05-20 17:39:42 +03:00
void wallet2 : : push_alias_info_to_extra_according_to_hf_status ( const currency : : extra_alias_entry & ai , std : : vector < currency : : extra_v > & extra )
{
2022-05-26 16:53:40 +02:00
if ( m_core_runtime_config . is_hardfork_active_for_height ( 2 , get_top_block_height ( ) ) )
2020-05-20 17:39:42 +03:00
{
// after HF2
extra . push_back ( ai ) ;
}
else
{
// before HF2
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ! ai . m_address . is_auditable ( ) , " auditable addresses are not supported in aliases prior to HF2 " ) ;
extra . push_back ( ai . to_old ( ) ) ;
}
}
//----------------------------------------------------------------------------------------------------
2022-06-16 17:02:00 +02:00
uint64_t wallet2 : : get_alias_cost ( const std : : string & alias )
{
currency : : COMMAND_RPC_GET_ALIAS_REWARD : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_ALIAS_REWARD : : response rsp = AUTO_VAL_INIT ( rsp ) ;
req . alias = alias ;
if ( ! m_core_proxy - > call_COMMAND_RPC_GET_ALIAS_REWARD ( req , rsp ) )
{
throw std : : runtime_error ( std : : string ( " Failed to get alias cost " ) ) ;
}
2023-08-06 21:43:33 +02:00
return rsp . reward ;
2022-06-16 17:02:00 +02:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : request_alias_registration ( currency : : extra_alias_entry & ai , currency : : transaction & res_tx , uint64_t fee , uint64_t reward , const crypto : : secret_key & authority_key )
2018-12-27 18:50:45 +03:00
{
if ( ! validate_alias_name ( ai . m_alias ) )
{
throw std : : runtime_error ( std : : string ( " wrong alias characters: " ) + ai . m_alias ) ;
}
2022-06-16 17:02:00 +02:00
if ( ai . m_alias . size ( ) < ALIAS_MINIMUM_PUBLIC_SHORT_NAME_ALLOWED )
{
if ( authority_key = = currency : : null_skey )
{
throw std : : runtime_error ( std : : string ( " Short aliases is not allowed without authority key: " ) + ALIAS_SHORT_NAMES_VALIDATION_PUB_KEY ) ;
}
crypto : : public_key authority_pub = AUTO_VAL_INIT ( authority_pub ) ;
bool r = crypto : : secret_key_to_public_key ( authority_key , authority_pub ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Failed to generate pub key from secrete authority key " ) ;
if ( string_tools : : pod_to_hex ( authority_pub ) ! = ALIAS_SHORT_NAMES_VALIDATION_PUB_KEY )
{
throw std : : runtime_error ( std : : string ( " Short aliases is not allowed to register by this authority key " ) ) ;
}
r = currency : : sign_extra_alias_entry ( ai , authority_pub , authority_key ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Failed to sign alias update " ) ;
WLT_LOG_L2 ( " Generated update alias info: " < < ENDL
< < " alias: " < < ai . m_alias < < ENDL
< < " signature: " < < currency : : print_t_array ( ai . m_sign ) < < ENDL
< < " signed(owner) pub key: " < < m_account . get_keys ( ) . account_address . spend_public_key < < ENDL
< < " to address: " < < get_account_address_as_str ( ai . m_address ) < < ENDL
< < " sign_buff_hash: " < < currency : : get_sign_buff_hash_for_alias_update ( ai )
) ;
}
if ( ! reward )
{
reward = get_alias_cost ( ai . m_alias ) ;
}
2018-12-27 18:50:45 +03:00
std : : vector < currency : : tx_destination_entry > destinations ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
2020-05-04 14:57:29 +03:00
2020-05-20 17:39:42 +03:00
push_alias_info_to_extra_according_to_hf_status ( ai , extra ) ;
2018-12-27 18:50:45 +03:00
currency : : tx_destination_entry tx_dest_alias_reward ;
tx_dest_alias_reward . addr . resize ( 1 ) ;
get_aliases_reward_account ( tx_dest_alias_reward . addr . back ( ) ) ;
tx_dest_alias_reward . amount = reward ;
2023-08-06 03:39:20 +02:00
tx_dest_alias_reward . flags | = tx_destination_entry_flags : : tdef_explicit_native_asset_id | tx_destination_entry_flags : : tdef_zero_amount_blinding_mask ;
2018-12-27 18:50:45 +03:00
destinations . push_back ( tx_dest_alias_reward ) ;
2022-09-20 22:01:52 +02:00
transfer ( destinations , 0 , 0 , fee , extra , attachments , get_current_split_strategy ( ) , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , res_tx , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
2022-09-26 21:57:24 +02:00
}
//----------------------------------------------------------------------------------------------------
2023-05-29 22:28:13 +02:00
void wallet2 : : deploy_new_asset ( const currency : : asset_descriptor_base & asset_info , const std : : vector < currency : : tx_destination_entry > & destinations , currency : : transaction & result_tx , crypto : : public_key & new_asset_id )
2022-09-26 21:57:24 +02:00
{
asset_descriptor_operation asset_reg_info = AUTO_VAL_INIT ( asset_reg_info ) ;
asset_reg_info . descriptor = asset_info ;
asset_reg_info . operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER ;
2022-09-27 20:13:49 +02:00
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
2022-09-30 21:23:06 +02:00
ctp . dsts = destinations ;
2022-09-27 20:13:49 +02:00
ctp . extra . push_back ( asset_reg_info ) ;
2023-06-15 20:06:05 +02:00
ctp . need_at_least_1_zc = true ;
2022-09-26 21:57:24 +02:00
2023-08-05 20:27:46 +02:00
finalized_tx ft = AUTO_VAL_INIT ( ft ) ; 6 + +
2022-09-27 20:13:49 +02:00
this - > transfer ( ctp , ft , true , nullptr ) ;
result_tx = ft . tx ;
2022-10-03 22:03:54 +02:00
//get generated asset id
currency : : asset_descriptor_operation ado = AUTO_VAL_INIT ( ado ) ;
bool r = get_type_in_variant_container ( result_tx . extra , ado ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Failed find asset info in tx " ) ;
2023-02-08 18:50:26 +01:00
calculate_asset_id ( ado . descriptor . owner , nullptr , & new_asset_id ) ;
2023-05-29 22:28:13 +02:00
m_custom_assets [ new_asset_id ] = ado . descriptor ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2023-08-05 20:27:46 +02:00
void wallet2 : : emmit_asset ( const crypto : : public_key asset_id , std : : vector < currency : : tx_destination_entry > & destinations , currency : : transaction & result_tx )
2023-08-03 20:01:41 +02:00
{
auto own_asset_entry_it = m_own_asset_descriptors . find ( asset_id ) ;
CHECK_AND_ASSERT_THROW_MES ( own_asset_entry_it ! = m_own_asset_descriptors . end ( ) , " Failed find asset_id " < < asset_id < < " in own assets list " ) ;
2023-08-05 20:27:46 +02:00
asset_descriptor_operation asset_emmit_info = AUTO_VAL_INIT ( asset_emmit_info ) ;
asset_emmit_info . descriptor = own_asset_entry_it - > second . asset_descriptor ;
asset_emmit_info . operation_type = ASSET_DESCRIPTOR_OPERATION_EMMIT ;
asset_emmit_info . asset_id = asset_id ;
2023-08-03 20:01:41 +02:00
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
ctp . dsts = destinations ;
ctp . extra . push_back ( asset_reg_info ) ;
ctp . need_at_least_1_zc = true ;
2023-08-14 22:32:52 +02:00
ctp . asset_deploy_control_key = own_asset_entry_it - > second . control_key ;
finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
this - > transfer ( ctp , ft , true , nullptr ) ;
result_tx = ft . tx ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : update_asset ( const crypto : : public_key asset_id , const asset_descriptor_base new_descriptor , currency : : transaction & result_tx )
{
auto own_asset_entry_it = m_own_asset_descriptors . find ( asset_id ) ;
CHECK_AND_ASSERT_THROW_MES ( own_asset_entry_it ! = m_own_asset_descriptors . end ( ) , " Failed find asset_id " < < asset_id < < " in own assets list " ) ;
asset_descriptor_operation asset_emmit_info = AUTO_VAL_INIT ( asset_emmit_info ) ;
asset_emmit_info . descriptor = new_descriptor ;
asset_emmit_info . operation_type = ASSET_DESCRIPTOR_OPERATION_UPDATE ;
asset_emmit_info . asset_id = asset_id ;
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
ctp . extra . push_back ( asset_reg_info ) ;
ctp . need_at_least_1_zc = true ;
ctp . asset_deploy_control_key = own_asset_entry_it - > second . control_key ;
finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
this - > transfer ( ctp , ft , true , nullptr ) ;
result_tx = ft . tx ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : burn_asset ( const crypto : : public_key asset_id , uint64_t amount_to_burn , currency : : transaction & result_tx )
{
auto own_asset_entry_it = m_own_asset_descriptors . find ( asset_id ) ;
CHECK_AND_ASSERT_THROW_MES ( own_asset_entry_it ! = m_own_asset_descriptors . end ( ) , " Failed find asset_id " < < asset_id < < " in own assets list " ) ;
asset_descriptor_operation asset_emmit_info = AUTO_VAL_INIT ( asset_emmit_info ) ;
asset_emmit_info . descriptor = own_asset_entry_it - > second . asset_descriptor ;
CHECK_AND_ASSERT_THROW_MES ( asset_emmit_info . descriptor . current_supply > amount_to_burn , " Wrong amount to burn (current_supply " < < asset_emmit_info . descriptor . current_supply < < " is less then " < < amount_to_burn < < " ) " ) ;
currency : : tx_destination_entry dst_to_burn = AUTO_VAL_INIT ( dst_to_burn ) ;
dst_to_burn . amount = amount_to_burn ;
dst_to_burn . asset_id = asset_id ;
asset_emmit_info . operation_type = ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN ;
asset_emmit_info . asset_id = asset_id ;
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
ctp . extra . push_back ( asset_reg_info ) ;
ctp . need_at_least_1_zc = true ;
ctp . asset_deploy_control_key = own_asset_entry_it - > second . control_key ;
ctp . dsts . push_back ( dst_to_burn ) ;
2023-08-03 20:01:41 +02:00
finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
this - > transfer ( ctp , ft , true , nullptr ) ;
result_tx = ft . tx ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : request_alias_update ( currency : : extra_alias_entry & ai , currency : : transaction & res_tx , uint64_t fee , uint64_t reward )
{
if ( ! validate_alias_name ( ai . m_alias ) )
{
throw std : : runtime_error ( std : : string ( " wrong alias characters: " ) + ai . m_alias ) ;
}
2020-04-22 23:30:03 +03:00
bool r = currency : : sign_extra_alias_entry ( ai , m_account . get_keys ( ) . account_address . spend_public_key , m_account . get_keys ( ) . spend_secret_key ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_THROW_MES ( r , " Failed to sign alias update " ) ;
2020-06-23 21:36:30 +03:00
WLT_LOG_L2 ( " Generated update alias info: " < < ENDL
2018-12-27 18:50:45 +03:00
< < " alias: " < < ai . m_alias < < ENDL
< < " signature: " < < currency : : print_t_array ( ai . m_sign ) < < ENDL
2020-04-22 23:30:03 +03:00
< < " signed(owner) pub key: " < < m_account . get_keys ( ) . account_address . spend_public_key < < ENDL
2018-12-27 18:50:45 +03:00
< < " transfered to address: " < < get_account_address_as_str ( ai . m_address ) < < ENDL
2020-06-23 21:36:30 +03:00
< < " sign_buff_hash: " < < currency : : get_sign_buff_hash_for_alias_update ( ai )
2018-12-27 18:50:45 +03:00
) ;
std : : vector < currency : : tx_destination_entry > destinations ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
2020-05-04 14:57:29 +03:00
2020-05-20 17:39:42 +03:00
push_alias_info_to_extra_according_to_hf_status ( ai , extra ) ;
2020-05-04 14:57:29 +03:00
2022-09-20 22:01:52 +02:00
transfer ( destinations , 0 , 0 , fee , extra , attachments , get_current_split_strategy ( ) , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , res_tx , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : check_available_sources ( std : : list < uint64_t > & amounts )
{
2022-09-20 22:01:52 +02:00
/*
2018-12-27 18:50:45 +03:00
std : : list < std : : vector < uint64_t > > holds ;
amounts . sort ( ) ;
bool res = true ;
for ( uint64_t am : amounts )
{
holds . push_back ( std : : vector < uint64_t > ( ) ) ;
std : : vector < uint64_t > & current_holds = holds . back ( ) ;
uint64_t found = select_transfers ( am , 0 , DEFAULT_DUST_THRESHOLD , current_holds ) ;
if ( found < am )
{
res = false ;
break ;
}
mark_transfers_with_flag ( current_holds , WALLET_TRANSFER_DETAIL_FLAG_BLOCKED , " check_available_sources " ) ;
}
for ( auto & h : holds )
{
clear_transfers_from_flag ( h , WALLET_TRANSFER_DETAIL_FLAG_BLOCKED , " check_available_sources " ) ;
add_transfers_to_transfers_cache ( h ) ;
}
2019-02-21 21:33:52 +03:00
WLT_LOG_MAGENTA ( " [CHECK_AVAILABLE_SOURCES]: " < < amounts < < " res: " < < res < < ENDL < < " holds: " < < holds , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
return res ;
2022-09-20 22:01:52 +02:00
*/
return false ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
std : : string get_random_rext ( size_t len )
{
std : : string buff ( len / 2 , 0 ) ;
crypto : : generate_random_bytes ( len / 2 , ( void * ) buff . data ( ) ) ;
return string_tools : : buff_to_hex_nodelimer ( buff ) ;
}
//----------------------------------------------------------------------------------------------------
// local_transfers_struct - structure to avoid copying the whole m_transfers
struct local_transfers_struct
{
local_transfers_struct ( wallet2 : : transfer_container & tf ) : l_transfers_ref ( tf )
{ }
wallet2 : : transfer_container & l_transfers_ref ;
BEGIN_KV_SERIALIZE_MAP ( )
KV_SERIALIZE ( l_transfers_ref )
END_KV_SERIALIZE_MAP ( )
} ;
void wallet2 : : dump_trunsfers ( std : : stringstream & ss , bool verbose ) const
{
if ( verbose )
{
std : : string res_buff ;
local_transfers_struct lt ( const_cast < transfer_container & > ( m_transfers ) ) ;
epee : : serialization : : store_t_to_json ( lt , res_buff ) ;
ss < < res_buff ;
}
else
{
2019-11-20 10:24:47 +03:00
boost : : io : : ios_flags_saver ifs ( ss ) ;
2022-10-20 18:28:47 +02:00
ss < < " index amount spent_h g_index block block_ts flg tx out# key image " < < ENDL ;
2018-12-27 18:50:45 +03:00
for ( size_t i = 0 ; i ! = m_transfers . size ( ) ; i + + )
{
const transfer_details & td = m_transfers [ i ] ;
ss < < std : : right < <
std : : setw ( 5 ) < < i < < " " < <
std : : setw ( 21 ) < < print_money ( td . amount ( ) ) < < " " < <
std : : setw ( 7 ) < < td . m_spent_height < < " " < <
std : : setw ( 7 ) < < td . m_global_output_index < < " " < <
std : : setw ( 6 ) < < td . m_ptx_wallet_info - > m_block_height < < " " < <
std : : setw ( 10 ) < < td . m_ptx_wallet_info - > m_block_timestamp < < " " < <
std : : setw ( 4 ) < < td . m_flags < < " " < <
get_transaction_hash ( td . m_ptx_wallet_info - > m_tx ) < < " " < <
std : : setw ( 4 ) < < td . m_internal_output_index < < " " < <
td . m_key_image < < ENDL ;
}
}
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : dump_trunsfers ( bool verbose ) const
{
std : : stringstream ss ;
dump_trunsfers ( ss , verbose ) ;
return ss . str ( ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : dump_key_images ( std : : stringstream & ss )
{
for ( auto & ki : m_key_images )
{
ss < < " [ " < < ki . first < < " ]: " < < ki . second < < ENDL ;
}
}
void wallet2 : : get_multisig_transfers ( multisig_transfer_container & ms_transfers )
{
ms_transfers = m_multisig_transfers ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_contracts ( escrow_contracts_container & contracts )
{
contracts = m_contracts ;
return true ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : build_escrow_release_templates ( crypto : : hash multisig_id ,
uint64_t fee ,
currency : : transaction & tx_release_template ,
currency : : transaction & tx_burn_template ,
const bc_services : : contract_private_details & ecrow_details )
{
construct_tx_param construct_params = AUTO_VAL_INIT ( construct_params ) ;
construct_params . fee = fee ;
construct_params . multisig_id = multisig_id ;
2022-09-20 22:01:52 +02:00
construct_params . split_strategy_id = get_current_split_strategy ( ) ;
2018-12-27 18:50:45 +03:00
construct_params . dsts . resize ( 2 ) ;
//0 - addr_a
//1 - addr_b
construct_params . dsts [ 0 ] . addr . push_back ( ecrow_details . a_addr ) ;
construct_params . dsts [ 1 ] . addr . push_back ( ecrow_details . b_addr ) ;
//generate normal escrow release
construct_params . dsts [ 0 ] . amount = ecrow_details . amount_a_pledge ;
construct_params . dsts [ 1 ] . amount = ecrow_details . amount_b_pledge + ecrow_details . amount_to_pay ;
//in case of ecrow_details.amount_a_pledge == 0 then exclude a
if ( construct_params . dsts [ 0 ] . amount = = 0 )
construct_params . dsts . erase ( construct_params . dsts . begin ( ) ) ;
tx_service_attachment tsa = AUTO_VAL_INIT ( tsa ) ;
tsa . service_id = BC_ESCROW_SERVICE_ID ;
tsa . instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL ;
construct_params . extra . push_back ( tsa ) ;
2019-04-03 19:18:34 +03:00
{
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2019-04-03 19:18:34 +03:00
prepare_transaction ( construct_params , ftp ) ;
2019-04-12 16:38:36 +03:00
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
finalize_transaction ( ftp , tx_release_template , sk , false ) ;
2019-04-03 19:18:34 +03:00
}
2018-12-27 18:50:45 +03:00
//generate burn escrow
construct_params . dsts . resize ( 1 ) ;
construct_params . dsts [ 0 ] . addr . clear ( ) ;
construct_params . dsts [ 0 ] . addr . push_back ( null_pub_addr ) ;
construct_params . dsts [ 0 ] . amount = ecrow_details . amount_a_pledge + ecrow_details . amount_b_pledge + ecrow_details . amount_to_pay ;
construct_params . extra . clear ( ) ;
tsa . instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_BURN ;
construct_params . extra . push_back ( tsa ) ;
2019-04-03 19:18:34 +03:00
{
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2019-04-03 19:18:34 +03:00
prepare_transaction ( construct_params , ftp ) ;
2019-04-12 16:38:36 +03:00
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
finalize_transaction ( ftp , tx_burn_template , sk , false ) ;
2019-04-03 19:18:34 +03:00
}
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : build_escrow_cancel_template ( crypto : : hash multisig_id ,
uint64_t expiration_period ,
currency : : transaction & tx_cancel_template ,
const bc_services : : contract_private_details & ecrow_details )
{
auto it = m_multisig_transfers . find ( multisig_id ) ;
THROW_IF_FALSE_WALLET_EX ( it ! = m_multisig_transfers . end ( ) , error : : wallet_internal_error ,
" unable to find multisig id " ) ;
THROW_IF_FALSE_WALLET_EX ( it - > second . amount ( ) > ( ecrow_details . amount_a_pledge + ecrow_details . amount_to_pay + ecrow_details . amount_b_pledge ) , error : : wallet_internal_error ,
" multisig id out amount no more than escrow total amount " ) ;
construct_tx_param construct_params = AUTO_VAL_INIT ( construct_params ) ;
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2018-12-27 18:50:45 +03:00
construct_params . fee = it - > second . amount ( ) - ( ecrow_details . amount_a_pledge + ecrow_details . amount_to_pay + ecrow_details . amount_b_pledge ) ;
construct_params . multisig_id = multisig_id ;
2022-09-20 22:01:52 +02:00
construct_params . split_strategy_id = get_current_split_strategy ( ) ;
2018-12-27 18:50:45 +03:00
construct_params . dsts . resize ( 2 ) ;
//0 - addr_a
//1 - addr_b
construct_params . dsts [ 0 ] . addr . push_back ( ecrow_details . a_addr ) ;
construct_params . dsts [ 1 ] . addr . push_back ( ecrow_details . b_addr ) ;
//generate cancel escrow proposal
construct_params . dsts [ 0 ] . amount = ecrow_details . amount_a_pledge + ecrow_details . amount_to_pay ;
construct_params . dsts [ 1 ] . amount = ecrow_details . amount_b_pledge ;
2019-07-17 15:20:10 +03:00
if ( construct_params . dsts [ 0 ] . amount = = 0 )
construct_params . dsts . erase ( construct_params . dsts . begin ( ) ) ;
else if ( construct_params . dsts [ 1 ] . amount = = 0 )
construct_params . dsts . erase ( construct_params . dsts . begin ( ) + 1 ) ;
2018-12-27 18:50:45 +03:00
tx_service_attachment tsa = AUTO_VAL_INIT ( tsa ) ;
tsa . service_id = BC_ESCROW_SERVICE_ID ;
tsa . instruction = BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_CANCEL ;
construct_params . extra . push_back ( tsa ) ;
currency : : etc_tx_details_expiration_time expir = AUTO_VAL_INIT ( expir ) ;
expir . v = m_core_runtime_config . get_core_time ( ) + expiration_period ;
construct_params . extra . push_back ( expir ) ;
2019-04-03 12:48:09 +03:00
prepare_transaction ( construct_params , ftp ) ;
2019-04-12 16:38:36 +03:00
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
finalize_transaction ( ftp , tx_cancel_template , sk , false ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : build_escrow_template ( const bc_services : : contract_private_details & ecrow_details ,
size_t fake_outputs_count ,
uint64_t unlock_time ,
uint64_t expiration_time ,
uint64_t b_release_fee ,
const std : : string & payment_id ,
currency : : transaction & tx ,
std : : vector < uint64_t > & selected_transfers ,
2019-04-03 12:48:09 +03:00
crypto : : secret_key & one_time_key )
{
construct_tx_param ctp = AUTO_VAL_INIT ( ctp ) ;
ctp . crypt_address = ecrow_details . b_addr ;
ctp . dust_policy = tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) ;
ctp . fake_outputs_count = fake_outputs_count ;
ctp . fee = 0 ;
ctp . flags = TX_FLAG_SIGNATURE_MODE_SEPARATE ;
ctp . mark_tx_as_complete = false ;
ctp . multisig_id = currency : : null_hash ;
ctp . shuffle = true ;
2022-09-20 22:01:52 +02:00
ctp . split_strategy_id = get_current_split_strategy ( ) ;
2019-04-03 12:48:09 +03:00
ctp . tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED ;
ctp . unlock_time = unlock_time ;
2018-12-27 18:50:45 +03:00
etc_tx_details_expiration_time t = AUTO_VAL_INIT ( t ) ;
2019-04-03 12:48:09 +03:00
t . v = expiration_time ; // TODO: move it to construct_tx_param
ctp . extra . push_back ( t ) ;
2018-12-27 18:50:45 +03:00
currency : : tx_service_attachment att = AUTO_VAL_INIT ( att ) ;
bc_services : : pack_attachment_as_gzipped_json ( ecrow_details , att ) ;
att . flags | = TX_SERVICE_ATTACHMENT_ENCRYPT_BODY ;
att . service_id = BC_ESCROW_SERVICE_ID ;
att . instruction = BC_ESCROW_SERVICE_INSTRUCTION_PRIVATE_DETAILS ;
2019-04-03 12:48:09 +03:00
ctp . extra . push_back ( att ) ;
ctp . dsts . resize ( 1 ) ;
ctp . dsts . back ( ) . amount = ecrow_details . amount_a_pledge + ecrow_details . amount_b_pledge + ecrow_details . amount_to_pay + b_release_fee ;
ctp . dsts . back ( ) . amount_to_provide = ecrow_details . amount_a_pledge + ecrow_details . amount_to_pay ;
ctp . dsts . back ( ) . addr . push_back ( ecrow_details . a_addr ) ;
ctp . dsts . back ( ) . addr . push_back ( ecrow_details . b_addr ) ;
ctp . dsts . back ( ) . minimum_sigs = 2 ;
2018-12-27 18:50:45 +03:00
if ( payment_id . size ( ) )
{
currency : : tx_service_attachment att = AUTO_VAL_INIT ( att ) ;
att . body = payment_id ;
att . service_id = BC_PAYMENT_ID_SERVICE_ID ;
att . flags = TX_SERVICE_ATTACHMENT_DEFLATE_BODY ;
2019-04-03 12:48:09 +03:00
ctp . attachments . push_back ( att ) ;
2018-12-27 18:50:45 +03:00
}
2019-04-03 12:48:09 +03:00
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2023-03-08 21:19:59 +01:00
prepare_transaction ( ctp , ftp ) ;
2019-04-03 12:48:09 +03:00
selected_transfers = ftp . selected_transfers ;
finalize_transaction ( ftp , tx , one_time_key , false ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2023-06-16 20:19:49 +02:00
void wallet2 : : add_transfers_to_expiration_list ( const std : : vector < uint64_t > & selected_transfers , const std : : vector < payment_details_subtransfer > & received , uint64_t expiration , const crypto : : hash & related_tx_id )
2018-12-27 18:50:45 +03:00
{
// check all elements in selected_transfers for being already mentioned in m_money_expirations
std : : vector < uint64_t > selected_transfers_local ;
for ( auto transfer_index : selected_transfers )
{
bool found = false ;
for ( auto it = m_money_expirations . begin ( ) ; ! found & & it ! = m_money_expirations . end ( ) ; + + it )
{
auto & st = it - > selected_transfers ;
found = std : : find ( st . begin ( ) , st . end ( ) , transfer_index ) ! = st . end ( ) ;
}
if ( ! found )
selected_transfers_local . push_back ( transfer_index ) ; // populate selected_transfers_local only with indices, not containing in m_money_expirations
}
if ( selected_transfers_local . empty ( ) )
return ;
m_money_expirations . push_back ( AUTO_VAL_INIT ( expiration_entry_info ( ) ) ) ;
m_money_expirations . back ( ) . expiration_time = expiration ;
m_money_expirations . back ( ) . selected_transfers = selected_transfers_local ;
m_money_expirations . back ( ) . related_tx_id = related_tx_id ;
2023-06-16 20:19:49 +02:00
m_money_expirations . back ( ) . receved = received ;
2018-12-27 18:50:45 +03:00
std : : stringstream ss ;
for ( auto tr_ind : m_money_expirations . back ( ) . selected_transfers )
{
THROW_IF_FALSE_WALLET_INT_ERR_EX ( tr_ind < m_transfers . size ( ) , " invalid transfer index " ) ;
uint32_t flags_before = m_transfers [ tr_ind ] . m_flags ;
m_transfers [ tr_ind ] . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_BLOCKED ;
m_transfers [ tr_ind ] . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION ;
ss < < " " < < std : : right < < std : : setw ( 4 ) < < tr_ind < < " " < < std : : setw ( 21 ) < < print_money ( m_transfers [ tr_ind ] . amount ( ) ) < < " "
< < std : : setw ( 2 ) < < std : : left < < flags_before < < " -> " < < std : : setw ( 2 ) < < std : : left < < m_transfers [ tr_ind ] . m_flags < < " "
< < get_transaction_hash ( m_transfers [ tr_ind ] . m_ptx_wallet_info - > m_tx ) < < std : : endl ;
}
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( m_money_expirations . back ( ) . selected_transfers . size ( ) < < " transfer(s) added to expiration list: " < < ENDL < <
2018-12-27 18:50:45 +03:00
" index amount flags tx hash " < < ENDL < <
2023-06-11 00:20:11 +02:00
ss . str ( ) < < " , expire(s) at: " < < expiration , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : remove_transfer_from_expiration_list ( uint64_t transfer_index )
{
THROW_IF_FALSE_WALLET_INT_ERR_EX ( transfer_index < m_transfers . size ( ) , " invalid transfer index " ) ;
for ( auto it = m_money_expirations . begin ( ) ; it ! = m_money_expirations . end ( ) ; /* nothing */ )
{
auto & st = it - > selected_transfers ;
auto jt = std : : find ( st . begin ( ) , st . end ( ) , transfer_index ) ;
if ( jt ! = st . end ( ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Transfer [ " < < transfer_index < < " ], amount: " < < print_money ( m_transfers [ transfer_index ] . amount ( ) ) < < " , tx: " < < get_transaction_hash ( m_transfers [ transfer_index ] . m_ptx_wallet_info - > m_tx ) < <
2018-12-27 18:50:45 +03:00
" was removed from the expiration list " , LOG_LEVEL_0 ) ;
st . erase ( jt ) ;
if ( st . empty ( ) )
{
it = m_money_expirations . erase ( it ) ;
continue ;
}
}
+ + it ;
}
// clear proposal reservation flag and blocked flag
uint32_t flags_before = m_transfers [ transfer_index ] . m_flags ;
m_transfers [ transfer_index ] . m_flags & = ~ WALLET_TRANSFER_DETAIL_FLAG_BLOCKED ;
m_transfers [ transfer_index ] . m_flags & = ~ WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION ;
2019-02-21 21:33:52 +03:00
WLT_LOG_BLUE ( " Transfer [ " < < transfer_index < < " ] was cleared from escrow proposal reservation, flags: " < < flags_before < < " -> " < < m_transfers [ transfer_index ] . m_flags < < " , reason: intentional removing from expiration list " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
// (don't change m_spent flag, because transfer status is unclear - the caller should take care of it)
}
//----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : send_escrow_proposal ( const wallet_public : : create_proposal_param & wp ,
currency : : transaction & proposal_tx ,
currency : : transaction & escrow_template_tx )
{
2019-08-27 22:11:45 +02:00
return send_escrow_proposal ( wp . details , wp . fake_outputs_count , wp . unlock_time , wp . expiration_period , wp . fee , wp . b_fee , wp . payment_id , proposal_tx , escrow_template_tx ) ;
2019-08-27 17:36:53 +02:00
}
//----------------------------------------------------------------------------------------------------
2019-04-03 12:48:09 +03:00
void wallet2 : : send_escrow_proposal ( const bc_services : : contract_private_details & ecrow_details ,
2018-12-27 18:50:45 +03:00
size_t fake_outputs_count ,
uint64_t unlock_time ,
uint64_t expiration_period ,
uint64_t fee ,
uint64_t b_release_fee ,
const std : : string & payment_id ,
currency : : transaction & tx ,
2019-04-03 12:48:09 +03:00
currency : : transaction & template_tx )
2018-12-27 18:50:45 +03:00
{
if ( ! is_connected_to_net ( ) )
{
THROW_IF_TRUE_WALLET_EX ( true , error : : wallet_internal_error ,
" Transfer attempt while daemon offline " ) ;
}
2019-04-03 12:48:09 +03:00
crypto : : secret_key one_time_key = AUTO_VAL_INIT ( one_time_key ) ;
2018-12-27 18:50:45 +03:00
uint64_t expiration_time = m_core_runtime_config . get_core_time ( ) + expiration_period ;
2019-04-03 12:48:09 +03:00
std : : vector < uint64_t > selected_transfers_for_template ;
build_escrow_template ( ecrow_details , fake_outputs_count , unlock_time , expiration_time , b_release_fee , payment_id , template_tx , selected_transfers_for_template , one_time_key ) ;
2019-10-31 10:54:37 +03:00
crypto : : hash ms_id = get_multisig_out_id ( template_tx , get_multisig_out_index ( template_tx . vout ) ) ;
2018-12-27 18:50:45 +03:00
const uint32_t mask_to_mark_escrow_template_locked_transfers = WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION ;
2019-04-03 12:48:09 +03:00
mark_transfers_with_flag ( selected_transfers_for_template , mask_to_mark_escrow_template_locked_transfers , " preparing escrow template tx, contract: " + epee : : string_tools : : pod_to_hex ( ms_id ) ) ;
2018-12-27 18:50:45 +03:00
2019-04-03 12:48:09 +03:00
construct_tx_param ctp = AUTO_VAL_INIT ( ctp ) ;
2018-12-27 18:50:45 +03:00
bc_services : : proposal_body pb = AUTO_VAL_INIT ( pb ) ;
2019-04-03 12:48:09 +03:00
pb . tx_onetime_secret_key = one_time_key ;
2018-12-27 18:50:45 +03:00
pb . tx_template = template_tx ;
2019-04-03 12:48:09 +03:00
currency : : tx_service_attachment att = AUTO_VAL_INIT ( att ) ;
2018-12-27 18:50:45 +03:00
att . body = t_serializable_object_to_blob ( pb ) ;
att . service_id = BC_ESCROW_SERVICE_ID ;
att . instruction = BC_ESCROW_SERVICE_INSTRUCTION_PROPOSAL ;
att . flags = TX_SERVICE_ATTACHMENT_ENCRYPT_BODY | TX_SERVICE_ATTACHMENT_DEFLATE_BODY ;
2019-04-03 12:48:09 +03:00
ctp . attachments . push_back ( att ) ;
ctp . crypt_address = ecrow_details . b_addr ;
ctp . dust_policy = tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) ;
ctp . fake_outputs_count = fake_outputs_count ;
ctp . fee = fee ;
ctp . shuffle = true ;
2022-09-20 22:01:52 +02:00
ctp . split_strategy_id = get_current_split_strategy ( ) ;
2019-04-03 12:48:09 +03:00
ctp . tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED ;
ctp . unlock_time = unlock_time ;
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2018-12-27 18:50:45 +03:00
try
{
2019-04-03 12:48:09 +03:00
prepare_transaction ( ctp , ftp ) ;
2019-04-12 16:38:36 +03:00
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
finalize_transaction ( ftp , tx , sk , false ) ;
2018-12-27 18:50:45 +03:00
}
catch ( . . . )
{
clear_transfers_from_flag ( selected_transfers_for_template , mask_to_mark_escrow_template_locked_transfers , " undo prepared escrow template tx " ) ; // don't forget to unlock template transfers if smth went wrong
add_transfers_to_transfers_cache ( selected_transfers_for_template ) ;
throw ;
}
send_transaction_to_network ( tx ) ;
2019-04-03 12:48:09 +03:00
mark_transfers_as_spent ( ftp . selected_transfers , std : : string ( " escrow proposal sent, tx < " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( tx ) ) + " >, contract: " + epee : : string_tools : : pod_to_hex ( ms_id ) ) ;
add_sent_tx_detailed_info ( tx , ftp . prepared_destinations , ftp . selected_transfers ) ;
2018-12-27 18:50:45 +03:00
print_tx_sent_message ( tx , " (from multisig) " , fee ) ;
2021-01-27 22:49:15 +01:00
}
//----------------------------------------------------------------------------------------------------
2021-02-13 00:57:24 +01:00
void wallet2 : : create_htlc_proposal ( uint64_t amount , const currency : : account_public_address & addr , uint64_t lock_blocks_count , currency : : transaction & tx , const crypto : : hash & htlc_hash , std : : string & origin )
2021-01-27 22:49:15 +01:00
{
2021-02-04 01:49:38 +01:00
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
ctp . fee = TX_DEFAULT_FEE ;
ctp . dsts . resize ( 1 ) ;
ctp . dsts . back ( ) . addr . push_back ( addr ) ;
ctp . dsts . back ( ) . amount = amount ;
destination_option_htlc_out & htlc_option = ctp . dsts . back ( ) . htlc_options ;
2021-02-13 00:57:24 +01:00
htlc_option . expiration = lock_blocks_count ; //about 12 hours
htlc_option . htlc_hash = htlc_hash ;
2021-01-27 22:49:15 +01:00
2021-03-17 21:01:09 +03:00
currency : : create_and_add_tx_payer_to_container_from_address ( ctp . extra ,
get_account ( ) . get_keys ( ) . account_address , get_top_block_height ( ) , get_core_runtime_config ( ) ) ;
2021-02-04 01:49:38 +01:00
finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
this - > transfer ( ctp , ft , true , nullptr ) ;
origin = ft . htlc_origin ;
2021-02-28 23:36:38 +01:00
tx = ft . tx ;
2021-01-27 22:49:15 +01:00
}
//----------------------------------------------------------------------------------------------------
2021-02-15 02:17:59 +01:00
void wallet2 : : get_list_of_active_htlc ( std : : list < wallet_public : : htlc_entry_info > & htlcs , bool only_redeem_txs )
2021-01-27 22:49:15 +01:00
{
for ( auto htlc_entry : m_active_htlcs_txid )
{
2021-01-31 19:42:02 +01:00
const transfer_details & td = m_transfers [ htlc_entry . second ] ;
2021-01-31 18:05:26 +01:00
if ( only_redeem_txs & & ! ( td . m_flags & WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM ) )
{
continue ;
}
2021-01-31 19:42:02 +01:00
wallet_public : : htlc_entry_info entry = AUTO_VAL_INIT ( entry ) ;
2021-01-27 22:49:15 +01:00
entry . tx_id = htlc_entry . first ;
2022-05-20 21:32:27 +02:00
if ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . type ( ) ! = typeid ( tx_out_bare ) )
{
//@#@
LOG_ERROR ( " Unexpected output type in get_list_of_active_htlc: " < < td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . type ( ) . name ( ) ) ;
continue ;
}
const tx_out_bare out_b = boost : : get < tx_out_bare > ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] ) ;
entry . amount = out_b . amount ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( out_b . target . type ( ) = = typeid ( txout_htlc ) ,
2021-01-27 22:49:15 +01:00
" [get_list_of_active_htlc]Internal error: unexpected type of out " ) ;
2022-05-20 21:32:27 +02:00
const txout_htlc & htlc = boost : : get < txout_htlc > ( out_b . target ) ;
2021-01-27 22:49:15 +01:00
entry . sha256_hash = htlc . htlc_hash ;
2021-03-18 22:35:11 +03:00
currency : : tx_payer payer = AUTO_VAL_INIT ( payer ) ;
2021-03-26 00:43:50 +03:00
if ( currency : : get_type_in_variant_container ( td . varian_options , payer ) )
2021-03-18 22:35:11 +03:00
entry . counterparty_address = payer . acc_addr ;
2021-01-31 18:05:26 +01:00
entry . is_redeem = td . m_flags & WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM ? true : false ;
2021-01-27 22:49:15 +01:00
htlcs . push_back ( entry ) ;
}
}
//----------------------------------------------------------------------------------------------------
2021-02-20 15:54:58 +01:00
void wallet2 : : redeem_htlc ( const crypto : : hash & htlc_tx_id , const std : : string & origin )
{
currency : : transaction result_tx = AUTO_VAL_INIT ( result_tx ) ;
return redeem_htlc ( htlc_tx_id , origin , result_tx ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : redeem_htlc ( const crypto : : hash & htlc_tx_id , const std : : string & origin , currency : : transaction & result_tx )
2021-01-27 22:49:15 +01:00
{
2021-02-02 19:02:28 +01:00
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
ctp . fee = TX_DEFAULT_FEE ;
ctp . htlc_tx_id = htlc_tx_id ;
ctp . htlc_origin = origin ;
2021-02-03 00:13:44 +01:00
ctp . dsts . resize ( 1 ) ;
ctp . dsts . back ( ) . addr . push_back ( m_account . get_keys ( ) . account_address ) ;
2021-02-15 02:17:59 +01:00
auto it = m_active_htlcs_txid . find ( htlc_tx_id ) ;
WLT_THROW_IF_FALSE_WITH_CODE ( it ! = m_active_htlcs_txid . end ( ) ,
" htlc not found with tx_id = " < < htlc_tx_id , API_RETURN_CODE_NOT_FOUND ) ;
ctp . dsts . back ( ) . amount = m_transfers [ it - > second ] . amount ( ) - ctp . fee ;
2021-02-02 19:02:28 +01:00
this - > transfer ( ctp , result_tx , true , nullptr ) ;
2021-02-14 00:28:39 +01:00
}
//----------------------------------------------------------------------------------------------------
2021-03-18 22:35:11 +03:00
bool wallet2 : : check_htlc_redeemed ( const crypto : : hash & htlc_tx_id , std : : string & origin , crypto : : hash & redeem_tx_id )
2021-02-14 00:28:39 +01:00
{
auto it = m_active_htlcs_txid . find ( htlc_tx_id ) ;
2021-02-02 19:02:28 +01:00
2021-02-14 00:28:39 +01:00
WLT_THROW_IF_FALSE_WITH_CODE ( it ! = m_active_htlcs_txid . end ( ) ,
" htlc not found with tx_id = " < < htlc_tx_id , API_RETURN_CODE_NOT_FOUND ) ;
2021-02-02 19:02:28 +01:00
2021-02-14 00:28:39 +01:00
transfer_details_extra_option_htlc_info htlc_options = AUTO_VAL_INIT ( htlc_options ) ;
if ( ! currency : : get_type_in_variant_container ( m_transfers [ it - > second ] . varian_options , htlc_options ) )
{
return false ;
}
if ( htlc_options . origin . size ( ) )
{
origin = htlc_options . origin ;
2021-03-18 22:35:11 +03:00
redeem_tx_id = htlc_options . redeem_tx_id ;
2021-02-14 00:28:39 +01:00
return true ;
}
return false ;
2018-12-27 18:50:45 +03:00
}
2023-03-13 22:14:39 +01:00
//----------------------------------------------------------------------------------------------------
2023-04-17 19:35:55 +02:00
bool wallet2 : : create_ionic_swap_proposal ( const wallet_public : : ionic_swap_proposal_info & proposal_details , const currency : : account_public_address & destination_addr , wallet_public : : ionic_swap_proposal & proposal )
2023-02-23 19:10:22 +01:00
{
std : : vector < uint64_t > selected_transfers_for_template ;
2023-03-16 19:15:11 +01:00
2023-04-13 20:07:43 +02:00
build_ionic_swap_template ( proposal_details , destination_addr , proposal , selected_transfers_for_template ) ;
2023-02-23 19:10:22 +01:00
const uint32_t mask_to_mark_escrow_template_locked_transfers = WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION ;
2023-02-28 21:35:28 +01:00
mark_transfers_with_flag ( selected_transfers_for_template , mask_to_mark_escrow_template_locked_transfers , " preparing ionic_swap " ) ;
return true ;
2023-02-23 19:10:22 +01:00
}
//----------------------------------------------------------------------------------------------------
2023-04-07 22:53:50 +02:00
bool wallet2 : : build_ionic_swap_template ( const wallet_public : : ionic_swap_proposal_info & proposal_detais , const currency : : account_public_address & destination_addr ,
2023-04-17 19:35:55 +02:00
wallet_public : : ionic_swap_proposal & proposal ,
2023-04-13 20:07:43 +02:00
std : : vector < uint64_t > & selected_transfers )
2023-02-23 19:10:22 +01:00
{
construct_tx_param ctp = get_default_construct_tx_param ( ) ;
2023-03-08 21:19:59 +01:00
ctp . fake_outputs_count = proposal_detais . mixins ;
2023-04-20 22:08:20 +02:00
ctp . fee = proposal_detais . fee_paid_by_a ;
2023-03-08 21:19:59 +01:00
ctp . flags = TX_FLAG_SIGNATURE_MODE_SEPARATE ;
ctp . mark_tx_as_complete = false ;
2023-04-20 22:08:20 +02:00
ctp . crypt_address = destination_addr ;
2023-03-08 21:19:59 +01:00
etc_tx_details_expiration_time t = AUTO_VAL_INIT ( t ) ;
2023-02-23 19:10:22 +01:00
t . v = proposal_detais . expiration_time ;
ctp . extra . push_back ( t ) ;
2023-04-20 22:08:20 +02:00
ctp . dsts . resize ( proposal_detais . to_bob . size ( ) + proposal_detais . to_alice . size ( ) ) ;
2023-02-28 21:35:28 +01:00
size_t i = 0 ;
// Here is an proposed for exchange funds
2023-04-20 22:08:20 +02:00
for ( ; i ! = proposal_detais . to_bob . size ( ) ; i + + )
2023-02-28 21:35:28 +01:00
{
2023-04-20 22:08:20 +02:00
ctp . dsts [ i ] . amount = proposal_detais . to_bob [ i ] . amount ;
ctp . dsts [ i ] . amount_to_provide = proposal_detais . to_bob [ i ] . amount ;
ctp . dsts [ i ] . flags | = tx_destination_entry_flags : : tdef_explicit_amount_to_provide ;
2023-02-28 21:35:28 +01:00
ctp . dsts [ i ] . addr . push_back ( destination_addr ) ;
2023-04-20 22:08:20 +02:00
ctp . dsts [ i ] . asset_id = proposal_detais . to_bob [ i ] . asset_id ;
2023-02-28 21:35:28 +01:00
}
// Here is an expected in return funds
2023-06-16 20:19:49 +02:00
std : : vector < payment_details_subtransfer > for_expiration_list ;
2023-04-20 22:08:20 +02:00
for ( size_t j = 0 ; j ! = proposal_detais . to_alice . size ( ) ; j + + , i + + )
2023-02-28 21:35:28 +01:00
{
2023-04-20 22:08:20 +02:00
ctp . dsts [ i ] . amount = proposal_detais . to_alice [ j ] . amount ;
2023-02-28 21:35:28 +01:00
ctp . dsts [ i ] . amount_to_provide = 0 ;
2023-04-20 22:08:20 +02:00
ctp . dsts [ i ] . flags | = tx_destination_entry_flags : : tdef_explicit_amount_to_provide ;
2023-02-28 21:35:28 +01:00
ctp . dsts [ i ] . addr . push_back ( m_account . get_public_address ( ) ) ;
2023-04-20 22:08:20 +02:00
ctp . dsts [ i ] . asset_id = proposal_detais . to_alice [ j ] . asset_id ;
2023-06-16 20:19:49 +02:00
for_expiration_list . push_back ( payment_details_subtransfer { ctp . dsts [ i ] . asset_id , ctp . dsts [ i ] . amount } ) ;
2023-02-28 21:35:28 +01:00
}
2023-02-23 19:10:22 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2023-04-20 22:08:20 +02:00
ftp . mode_separate_fee = ctp . fee ;
2023-02-23 19:10:22 +01:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2023-03-08 21:19:59 +01:00
prepare_transaction ( ctp , ftp ) ;
2023-02-23 19:10:22 +01:00
selected_transfers = ftp . selected_transfers ;
2023-04-13 20:07:43 +02:00
currency : : finalized_tx finalize_result = AUTO_VAL_INIT ( finalize_result ) ;
finalize_transaction ( ftp , finalize_result , false ) ;
2023-06-16 20:19:49 +02:00
add_transfers_to_expiration_list ( selected_transfers , for_expiration_list , proposal_detais . expiration_time , currency : : null_hash ) ;
2023-04-13 20:07:43 +02:00
//wrap it all
proposal . tx_template = finalize_result . tx ;
2023-04-17 19:35:55 +02:00
wallet_public : : ionic_swap_proposal_context ispc = AUTO_VAL_INIT ( ispc ) ;
2023-04-13 20:07:43 +02:00
ispc . gen_context = finalize_result . ftp . gen_context ;
ispc . one_time_skey = finalize_result . one_time_key ;
std : : string proposal_context_blob = t_serializable_object_to_blob ( ispc ) ;
2023-04-20 22:08:20 +02:00
proposal . encrypted_context = crypto : : chacha_crypt ( static_cast < const std : : string & > ( proposal_context_blob ) , finalize_result . derivation ) ;
2023-02-28 21:35:28 +01:00
return true ;
2023-02-23 19:10:22 +01:00
}
//----------------------------------------------------------------------------------------------------
2023-04-13 20:07:43 +02:00
bool wallet2 : : get_ionic_swap_proposal_info ( const std : : string & raw_proposal , wallet_public : : ionic_swap_proposal_info & proposal_info )
2023-03-03 18:17:16 +01:00
{
2023-04-13 20:07:43 +02:00
wallet_public : : ionic_swap_proposal proposal = AUTO_VAL_INIT ( proposal ) ;
2023-04-17 19:35:55 +02:00
bool r = t_unserializable_object_from_blob ( proposal , raw_proposal ) ;
2023-04-13 20:07:43 +02:00
THROW_IF_TRUE_WALLET_EX ( ! r , error : : wallet_internal_error , " Failed to parse proposal " ) ;
return get_ionic_swap_proposal_info ( proposal , proposal_info ) ;
2023-03-08 21:19:59 +01:00
}
//----------------------------------------------------------------------------------------------------
2023-04-13 20:07:43 +02:00
bool wallet2 : : get_ionic_swap_proposal_info ( const wallet_public : : ionic_swap_proposal & proposal , wallet_public : : ionic_swap_proposal_info & proposal_info )
2023-03-08 21:19:59 +01:00
{
2023-04-13 20:07:43 +02:00
wallet_public : : ionic_swap_proposal_context ionic_context = AUTO_VAL_INIT ( ionic_context ) ;
return get_ionic_swap_proposal_info ( proposal , proposal_info , ionic_context ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_ionic_swap_proposal_info ( const wallet_public : : ionic_swap_proposal & proposal , wallet_public : : ionic_swap_proposal_info & proposal_info , wallet_public : : ionic_swap_proposal_context & ionic_context )
{
const transaction & tx = proposal . tx_template ;
2023-03-03 18:17:16 +01:00
crypto : : key_derivation derivation = AUTO_VAL_INIT ( derivation ) ;
std : : vector < wallet_out_info > outs ;
2023-06-11 00:20:11 +02:00
bool r = lookup_acc_outs ( m_account . get_keys ( ) , tx , outs , derivation ) ;
2023-03-03 18:17:16 +01:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " Failed to lookup_acc_outs for tx: " < < get_transaction_hash ( tx ) ) ;
2023-03-06 22:47:06 +01:00
2023-04-13 20:07:43 +02:00
if ( ! outs . size ( ) )
{
return false ;
}
//decrypt context
std : : string decrypted_raw_context = crypto : : chacha_crypt ( proposal . encrypted_context , derivation ) ;
r = t_unserializable_object_from_blob ( ionic_context , decrypted_raw_context ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " Failed to unserialize decrypted ionic_context " ) ;
2023-04-17 19:35:55 +02:00
r = validate_tx_output_details_againt_tx_generation_context ( tx , ionic_context . gen_context , ionic_context . one_time_skey ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " Failed to validate decrypted ionic_context " ) ;
2023-04-13 20:07:43 +02:00
2023-04-20 22:08:20 +02:00
std : : unordered_map < crypto : : public_key , uint64_t > amounts_provided_by_a ;
std : : unordered_map < crypto : : public_key , uint64_t > ammounts_to_a ; //amounts to Alice (the one who created proposal), should be NOT funded
std : : unordered_map < crypto : : public_key , uint64_t > ammounts_to_b ; //amounts to Bob (the one who received proposal), should BE funded
std : : vector < bool > bob_outs ;
bob_outs . resize ( proposal . tx_template . vout . size ( ) ) ;
2023-03-03 18:17:16 +01:00
for ( const auto & o : outs )
{
2023-04-20 22:08:20 +02:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( ionic_context . gen_context . asset_ids . size ( ) > o . index , " Tx gen context has mismatch with tx(asset_ids) " ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( ionic_context . gen_context . asset_ids [ o . index ] . to_public_key ( ) = = o . asset_id , " Tx gen context has mismatch with tx(asset_id != asset_id) " ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( ionic_context . gen_context . amounts [ o . index ] . m_u64 [ 0 ] = = o . amount , " Tx gen context has mismatch with tx(amount != amount) " ) ;
2023-04-17 19:35:55 +02:00
2023-04-20 22:08:20 +02:00
ammounts_to_b [ o . asset_id ] + = o . amount ;
bob_outs [ o . index ] = true ;
2023-03-06 22:47:06 +01:00
}
2023-04-20 22:08:20 +02:00
size_t i = 0 ;
2023-04-17 19:35:55 +02:00
//validate outputs against decrypted tx generation context
2023-03-06 22:47:06 +01:00
for ( i = 0 ; i ! = tx . vout . size ( ) ; i + + )
{
2023-04-17 19:35:55 +02:00
2023-04-20 22:08:20 +02:00
if ( bob_outs [ i ] )
2023-04-17 19:35:55 +02:00
{
2023-03-06 22:47:06 +01:00
continue ;
2023-04-17 19:35:55 +02:00
}
2023-03-06 22:47:06 +01:00
2023-04-17 19:35:55 +02:00
crypto : : public_key asset_id = ionic_context . gen_context . asset_ids [ i ] . to_public_key ( ) ;
uint64_t amount = ionic_context . gen_context . amounts [ i ] . m_u64 [ 0 ] ;
2023-04-20 22:08:20 +02:00
ammounts_to_a [ asset_id ] + = amount ;
2023-03-03 18:17:16 +01:00
}
2023-04-20 22:08:20 +02:00
//read amounts already provided by third party
size_t zc_current_index = 0 ; //some inputs might be old ones, so it's asset id assumed as native and there is no entry for it in real_zc_ins_asset_ids
//THROW_IF_FALSE_WALLET_INT_ERR_EX(ionic_context.gen_context.input_amounts.size() == tx.vin.size(), "Tx gen context has mismatch with tx(amount != amount)");
for ( i = 0 ; i ! = tx . vin . size ( ) ; i + + )
2023-03-06 22:47:06 +01:00
{
2023-04-20 22:08:20 +02:00
size_t mx = 0 ;
uint64_t amount = 0 ;
crypto : : public_key in_asset_id = currency : : native_coin_asset_id ;
if ( tx . vin [ i ] . type ( ) = = typeid ( txin_zc_input ) )
{
in_asset_id = ionic_context . gen_context . real_zc_ins_asset_ids [ zc_current_index ] . to_public_key ( ) ;
2023-04-25 00:16:13 +02:00
amount = ionic_context . gen_context . zc_input_amounts [ zc_current_index ] ;
2023-04-20 22:08:20 +02:00
zc_current_index + + ;
mx = boost : : get < currency : : txin_zc_input > ( tx . vin [ i ] ) . key_offsets . size ( ) - 1 ;
}
else if ( tx . vin [ i ] . type ( ) = = typeid ( txin_to_key ) )
{
amount = boost : : get < txin_to_key > ( tx . vin [ i ] ) . amount ;
mx = boost : : get < currency : : txin_to_key > ( tx . vin [ i ] ) . key_offsets . size ( ) - 1 ;
}
else
{
WLT_LOG_RED ( " Unexpected type of input in ionic_swap tx: " < < tx . vin [ i ] . type ( ) . name ( ) , LOG_LEVEL_0 ) ;
2023-03-06 22:47:06 +01:00
return false ;
2023-04-20 22:08:20 +02:00
}
amounts_provided_by_a [ in_asset_id ] + = amount ;
2023-04-13 20:07:43 +02:00
if ( proposal_info . mixins = = 0 | | proposal_info . mixins > mx )
2023-03-06 22:47:06 +01:00
{
2023-04-13 20:07:43 +02:00
proposal_info . mixins = mx ;
2023-03-06 22:47:06 +01:00
}
2023-04-20 22:08:20 +02:00
}
//this might be 0, if Alice don't want to pay fee herself
proposal_info . fee_paid_by_a = currency : : get_tx_fee ( tx ) ;
if ( proposal_info . fee_paid_by_a )
{
THROW_IF_FALSE_WALLET_INT_ERR_EX ( amounts_provided_by_a [ currency : : native_coin_asset_id ] > = proposal_info . fee_paid_by_a , " Fee mentioned as specified but not provided by A " ) ;
amounts_provided_by_a [ currency : : native_coin_asset_id ] - = proposal_info . fee_paid_by_a ;
}
//proposal_info.fee = currency::get_tx_fee(tx);
//need to make sure that funds for Bob properly funded
for ( const auto & a : ammounts_to_b )
{
uint64_t amount_sent_back_to_alice = ammounts_to_a [ a . first ] ;
if ( amounts_provided_by_a [ a . first ] < ( a . second + amount_sent_back_to_alice ) )
{
WLT_LOG_RED ( " Amount[ " < < a . first < < " ] provided by Alice( " < < amounts_provided_by_a [ a . first ] < < " ) is less then transfered to Bob( " < < a . second < < " ) " , LOG_LEVEL_0 ) ;
return false ;
}
amounts_provided_by_a [ a . first ] - = ( amount_sent_back_to_alice + a . second ) ;
proposal_info . to_bob . push_back ( view : : asset_funds { a . first , a . second } ) ;
//clean accounted assets
ammounts_to_a . erase ( ammounts_to_a . find ( a . first ) ) ;
if ( amounts_provided_by_a [ a . first ] > 0 )
{
WLT_LOG_RED ( " Amount[ " < < a . first < < " ] provided by Alice has unused leftovers: " < < amounts_provided_by_a [ a . first ] , LOG_LEVEL_0 ) ;
return false ;
}
2023-03-06 22:47:06 +01:00
}
2023-04-20 22:08:20 +02:00
//need to see what Alice actually expect in return
for ( const auto & a : ammounts_to_a )
{
//now amount provided by A should be less or equal to what we have in a.second
if ( amounts_provided_by_a [ a . first ] > a . second )
{
//could be fee
WLT_LOG_RED ( " Amount[ " < < a . first < < " ] provided by Alice has unused leftovers: " < < amounts_provided_by_a [ a . first ] , LOG_LEVEL_0 ) ;
return false ;
}
proposal_info . to_alice . push_back ( view : : asset_funds { a . first , a . second - amounts_provided_by_a [ a . first ] } ) ;
}
2023-03-06 22:47:06 +01:00
etc_tx_details_expiration_time t = AUTO_VAL_INIT ( t ) ;
if ( ! get_type_in_variant_container ( tx . extra , t ) )
{
return false ;
}
2023-04-13 20:07:43 +02:00
proposal_info . expiration_time = t . v ;
2023-03-06 22:47:06 +01:00
return true ;
2023-03-03 18:17:16 +01:00
}
2023-03-16 19:15:11 +01:00
2018-12-27 18:50:45 +03:00
//----------------------------------------------------------------------------------------------------
2023-04-13 20:07:43 +02:00
bool wallet2 : : accept_ionic_swap_proposal ( const std : : string & raw_proposal , currency : : transaction & result_tx )
2023-03-08 21:19:59 +01:00
{
2023-04-17 19:35:55 +02:00
wallet_public : : ionic_swap_proposal proposal = AUTO_VAL_INIT ( proposal ) ;
bool r = t_unserializable_object_from_blob ( proposal , raw_proposal ) ;
2023-04-13 20:07:43 +02:00
THROW_IF_TRUE_WALLET_EX ( ! r , error : : wallet_internal_error , " Failed to parse proposal info " ) ;
2023-03-08 21:19:59 +01:00
2023-04-13 20:07:43 +02:00
return accept_ionic_swap_proposal ( proposal , result_tx ) ;
2023-03-16 19:15:11 +01:00
}
//----------------------------------------------------------------------------------------------------
2023-04-13 20:07:43 +02:00
bool wallet2 : : accept_ionic_swap_proposal ( const wallet_public : : ionic_swap_proposal & proposal , currency : : transaction & result_tx )
2023-03-16 19:15:11 +01:00
{
mode_separate_context msc = AUTO_VAL_INIT ( msc ) ;
2023-04-17 19:35:55 +02:00
msc . tx_for_mode_separate = proposal . tx_template ;
2023-04-21 17:33:56 +02:00
result_tx = msc . tx_for_mode_separate ;
2023-03-16 19:15:11 +01:00
2023-04-13 20:07:43 +02:00
wallet_public : : ionic_swap_proposal_context ionic_context = AUTO_VAL_INIT ( ionic_context ) ;
bool r = get_ionic_swap_proposal_info ( proposal , msc . proposal_info , ionic_context ) ;
2023-03-08 21:19:59 +01:00
THROW_IF_TRUE_WALLET_EX ( ! r , error : : wallet_internal_error , " Failed to get info from proposal " ) ;
2023-04-11 17:23:06 +02:00
std : : unordered_map < crypto : : public_key , wallet_public : : asset_balance_entry_base > balances ;
2023-03-08 21:19:59 +01:00
uint64_t mined = 0 ;
this - > balance ( balances , mined ) ;
//validate balances needed
uint64_t native_amount_required = 0 ;
2023-04-20 22:08:20 +02:00
for ( const auto & item : msc . proposal_info . to_alice )
2023-03-08 21:19:59 +01:00
{
if ( balances [ item . asset_id ] . unlocked < item . amount )
{
return false ;
}
2023-04-11 17:23:06 +02:00
if ( item . asset_id = = currency : : native_coin_asset_id )
2023-03-08 21:19:59 +01:00
{
native_amount_required = item . amount ;
}
}
// balances is ok, check if fee is added to tx
uint64_t additional_fee = 0 ;
2023-04-20 22:08:20 +02:00
if ( msc . proposal_info . fee_paid_by_a < m_core_runtime_config . tx_default_fee )
2023-03-08 21:19:59 +01:00
{
2023-04-20 22:08:20 +02:00
additional_fee = m_core_runtime_config . tx_default_fee - msc . proposal_info . fee_paid_by_a ;
2023-04-11 17:23:06 +02:00
if ( balances [ currency : : native_coin_asset_id ] . unlocked < additional_fee + native_amount_required )
2023-03-08 21:19:59 +01:00
{
return false ;
}
}
//everything is seemed to be ok
construct_tx_param construct_param = get_default_construct_tx_param ( ) ;
construct_param . fee = additional_fee ;
2023-04-07 22:53:50 +02:00
2023-04-21 17:33:56 +02:00
crypto : : secret_key one_time_key = ionic_context . one_time_skey ;
2023-03-08 21:19:59 +01:00
construct_param . crypt_address = m_account . get_public_address ( ) ;
construct_param . flags = TX_FLAG_SIGNATURE_MODE_SEPARATE ;
construct_param . mark_tx_as_complete = true ;
2023-04-28 20:07:35 +02:00
construct_param . need_at_least_1_zc = true ;
2023-03-08 21:19:59 +01:00
//build transaction
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
ftp . tx_version = this - > get_current_tx_version ( ) ;
2023-04-13 20:07:43 +02:00
ftp . gen_context = ionic_context . gen_context ;
2023-03-08 21:19:59 +01:00
prepare_transaction ( construct_param , ftp , msc ) ;
2023-04-25 00:16:13 +02:00
2023-03-08 21:19:59 +01:00
try
{
2023-04-07 22:53:50 +02:00
finalize_transaction ( ftp , result_tx , one_time_key , true ) ;
2023-03-08 21:19:59 +01:00
}
catch ( . . . )
{
2023-04-07 22:53:50 +02:00
clear_transfers_from_flag ( ftp . selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_SPENT , std : : string ( " exception in finalize_transaction, tx: " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( result_tx ) ) ) ;
2023-03-08 21:19:59 +01:00
throw ;
}
2023-04-07 22:53:50 +02:00
mark_transfers_as_spent ( ftp . selected_transfers , std : : string ( " Proposal has been accepted with tx < " + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( result_tx ) ) ) + " > " ) ;
2023-03-08 21:19:59 +01:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2023-04-03 20:32:18 +02:00
// Signing and auth
bool wallet2 : : sign_buffer ( const std : : string & buff , crypto : : signature & sig )
{
crypto : : hash h = crypto : : cn_fast_hash ( buff . data ( ) , buff . size ( ) ) ;
crypto : : generate_signature ( h , m_account . get_public_address ( ) . spend_public_key , m_account . get_keys ( ) . spend_secret_key , sig ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : validate_sign ( const std : : string & buff , const crypto : : signature & sig , const crypto : : public_key & pkey )
{
crypto : : hash h = crypto : : cn_fast_hash ( buff . data ( ) , buff . size ( ) ) ;
2023-04-07 22:53:50 +02:00
return crypto : : check_signature ( h , pkey , sig ) ;
2023-04-03 20:32:18 +02:00
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : encrypt_buffer ( const std : : string & buff , std : : string & res_buff )
{
res_buff = buff ;
crypto : : chacha_crypt ( res_buff , m_account . get_keys ( ) . view_secret_key ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : decrypt_buffer ( const std : : string & buff , std : : string & res_buff )
{
res_buff = buff ;
crypto : : chacha_crypt ( res_buff , m_account . get_keys ( ) . view_secret_key ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2023-06-09 01:10:16 +02:00
bool wallet2 : : prepare_tx_sources_for_defragmentation_tx ( std : : vector < currency : : tx_source_entry > & sources , std : : vector < uint64_t > & selected_indicies , uint64_t & found_money )
2019-11-27 03:19:10 +01:00
{
2023-06-09 01:10:16 +02:00
//prepare_free_transfers_cache(fake_outputs_count);
//free_amounts_cache_type& free_amounts_for_native_coin = m_found_free_amounts[currency::native_coin_asset_id];
2023-02-08 18:50:26 +01:00
2023-06-09 19:44:27 +02:00
std : : stringstream ss ;
if ( epee : : log_space : : log_singletone : : get_log_detalisation_level ( ) > = LOG_LEVEL_2 )
ss < < " preparing sources for utxo defragmentation tx: " ;
for ( size_t i = 0 , size = m_transfers . size ( ) ; i < size & & selected_indicies . size ( ) < m_max_utxo_count_for_defragmentation_tx ; + + i )
2019-11-27 03:19:10 +01:00
{
2023-06-09 01:10:16 +02:00
const auto & td = m_transfers [ i ] ;
if ( ! td . is_native_coin ( ) | | td . m_amount > CURRENCY_BLOCK_REWARD )
continue ;
if ( is_transfer_ready_to_go ( td , m_decoys_count_for_defragmentation_tx ) )
2019-11-27 03:19:10 +01:00
{
2023-06-09 01:10:16 +02:00
found_money + = td . m_amount ;
selected_indicies . push_back ( i ) ;
2023-06-09 19:44:27 +02:00
if ( epee : : log_space : : log_singletone : : get_log_detalisation_level ( ) > = LOG_LEVEL_2 )
ss < < " selected transfer # " < < i < < " , amount: " < < print_money_brief ( td . m_amount ) < < " , height: " < < td . m_ptx_wallet_info - > m_block_height < < " , " < < ( td . is_zc ( ) ? " ZC " : " " ) ;
2019-11-27 03:19:10 +01:00
}
}
2023-06-09 19:44:27 +02:00
if ( selected_indicies . size ( ) < m_min_utxo_count_for_defragmentation_tx )
{
// too few outputs were found, hence don't create a defragmentation tx
selected_indicies . clear ( ) ;
found_money = 0 ;
return false ;
}
WLT_LOG ( ss . str ( ) , LOG_LEVEL_2 ) ;
2023-06-09 01:10:16 +02:00
return prepare_tx_sources ( m_decoys_count_for_defragmentation_tx , sources , selected_indicies ) ;
2019-11-27 03:19:10 +01:00
}
//----------------------------------------------------------------------------------------------------
2022-09-20 22:01:52 +02:00
bool wallet2 : : prepare_tx_sources ( assets_selection_context & needed_money_map , size_t fake_outputs_count , uint64_t dust_threshold , std : : vector < currency : : tx_source_entry > & sources , std : : vector < uint64_t > & selected_indicies )
2018-12-27 18:50:45 +03:00
{
2022-09-20 22:01:52 +02:00
bool r = select_transfers ( needed_money_map , fake_outputs_count , dust_threshold , selected_indicies ) ;
if ( ! r )
return r ;
return prepare_tx_sources ( fake_outputs_count , sources , selected_indicies ) ;
2019-11-27 03:19:10 +01:00
}
//----------------------------------------------------------------------------------------------------
2022-09-21 22:35:24 +02:00
void wallet2 : : prefetch_global_indicies_if_needed ( const std : : vector < uint64_t > & selected_indicies )
2020-06-12 23:32:06 +02:00
{
std : : list < std : : reference_wrapper < const currency : : transaction > > txs ;
std : : list < uint64_t > indices_that_requested_global_indicies ;
for ( uint64_t i : selected_indicies )
{
if ( m_transfers [ i ] . m_global_output_index = = WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED )
{
indices_that_requested_global_indicies . push_back ( i ) ;
txs . push_back ( m_transfers [ i ] . m_ptx_wallet_info - > m_tx ) ;
}
}
std : : vector < std : : vector < uint64_t > > outputs_for_all_txs ;
fetch_tx_global_indixes ( txs , outputs_for_all_txs ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( txs . size ( ) = = outputs_for_all_txs . size ( ) , " missmatch sizes txs.size() == outputs_for_all_txs.size() " ) ;
auto it_indices = indices_that_requested_global_indicies . begin ( ) ;
auto it_ooutputs = outputs_for_all_txs . begin ( ) ;
for ( ; it_ooutputs ! = outputs_for_all_txs . end ( ) ; )
{
transfer_details & td = m_transfers [ * it_indices ] ;
td . m_global_output_index = ( * it_ooutputs ) [ td . m_internal_output_index ] ;
2020-06-13 21:04:21 +02:00
it_ooutputs + + ; it_indices + + ;
2020-06-12 23:32:06 +02:00
}
}
//----------------------------------------------------------------------------------------------------
2022-09-20 22:01:52 +02:00
bool wallet2 : : prepare_tx_sources ( size_t fake_outputs_count , std : : vector < currency : : tx_source_entry > & sources , const std : : vector < uint64_t > & selected_indicies )
2019-11-27 03:19:10 +01:00
{
2018-12-27 18:50:45 +03:00
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : out_entry out_entry ;
typedef currency : : tx_source_entry : : output_entry tx_output_entry ;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : response daemon_resp = AUTO_VAL_INIT ( daemon_resp ) ;
if ( fake_outputs_count )
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : request req = AUTO_VAL_INIT ( req ) ;
2022-11-23 18:09:49 +01:00
req . height_upper_limit = m_last_pow_block_h ; // request decoys to be either older than, or the same age as stake output's height
req . use_forced_mix_outs = false ; // TODO: add this feature to UI later
req . decoys_count = fake_outputs_count + 1 ; // one more to be able to skip a decoy in case it hits the real output
2018-12-27 18:50:45 +03:00
for ( uint64_t i : selected_indicies )
{
auto it = m_transfers . begin ( ) + i ;
2019-04-03 12:48:09 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( it - > m_ptx_wallet_info - > m_tx . vout . size ( ) > it - > m_internal_output_index ,
" m_internal_output_index = " < < it - > m_internal_output_index < <
" is greater or equal to outputs count = " < < it - > m_ptx_wallet_info - > m_tx . vout . size ( ) ) ;
2022-11-23 18:09:49 +01:00
req . amounts . push_back ( it - > is_zc ( ) ? 0 : it - > m_amount ) ;
2018-12-27 18:50:45 +03:00
}
bool r = m_core_proxy - > call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS ( req , daemon_resp ) ;
2019-04-03 12:48:09 +03:00
THROW_IF_FALSE_WALLET_EX ( r , error : : no_connection_to_daemon , " getrandom_outs.bin " ) ;
2020-05-07 23:26:41 +02:00
THROW_IF_FALSE_WALLET_EX ( daemon_resp . status ! = API_RETURN_CODE_BUSY , error : : daemon_busy , " getrandom_outs.bin " ) ;
THROW_IF_FALSE_WALLET_EX ( daemon_resp . status = = API_RETURN_CODE_OK , error : : get_random_outs_error , daemon_resp . status ) ;
2019-04-03 12:48:09 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( daemon_resp . outs . size ( ) = = selected_indicies . size ( ) ,
" daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " < < daemon_resp . outs . size ( ) < < " , expected: " < < selected_indicies . size ( ) ) ;
2018-12-27 18:50:45 +03:00
std : : vector < COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount > scanty_outs ;
2019-04-03 12:48:09 +03:00
for ( COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount & amount_outs : daemon_resp . outs )
2018-12-27 18:50:45 +03:00
{
2022-10-12 18:07:04 +02:00
if ( amount_outs . outs . size ( ) < req . decoys_count )
2018-12-27 18:50:45 +03:00
{
scanty_outs . push_back ( amount_outs ) ;
}
}
2019-04-03 12:48:09 +03:00
THROW_IF_FALSE_WALLET_EX ( scanty_outs . empty ( ) , error : : not_enough_outs_to_mix , scanty_outs , fake_outputs_count ) ;
2018-12-27 18:50:45 +03:00
}
2020-06-12 23:32:06 +02:00
//lets prefetch m_global_output_index for selected_indicies
prefetch_global_indicies_if_needed ( selected_indicies ) ;
2018-12-27 18:50:45 +03:00
//prepare inputs
size_t i = 0 ;
for ( uint64_t J : selected_indicies )
{
auto it = m_transfers . begin ( ) + J ;
sources . push_back ( AUTO_VAL_INIT ( currency : : tx_source_entry ( ) ) ) ;
currency : : tx_source_entry & src = sources . back ( ) ;
transfer_details & td = * it ;
2019-04-05 00:42:28 +03:00
src . transfer_index = it - m_transfers . begin ( ) ;
2018-12-27 18:50:45 +03:00
src . amount = td . amount ( ) ;
2022-09-20 22:01:52 +02:00
src . asset_id = td . get_asset_id ( ) ;
2018-12-27 18:50:45 +03:00
//paste mixin transaction
if ( daemon_resp . outs . size ( ) )
{
daemon_resp . outs [ i ] . outs . sort ( [ ] ( const out_entry & a , const out_entry & b ) { return a . global_amount_index < b . global_amount_index ; } ) ;
2019-04-03 12:48:09 +03:00
for ( out_entry & daemon_oe : daemon_resp . outs [ i ] . outs )
2018-12-27 18:50:45 +03:00
{
if ( td . m_global_output_index = = daemon_oe . global_amount_index )
continue ;
2022-07-13 17:17:04 +02:00
tx_output_entry oe = AUTO_VAL_INIT ( oe ) ;
2022-10-12 18:02:22 +02:00
oe . amount_commitment = daemon_oe . amount_commitment ;
oe . concealing_point = daemon_oe . concealing_point ;
oe . out_reference = daemon_oe . global_amount_index ;
oe . stealth_address = daemon_oe . stealth_address ;
2023-03-08 21:26:09 +01:00
oe . blinded_asset_id = daemon_oe . blinded_asset_id ; // TODO @#@# BAD DESING, consider refactoring -- sowle
2018-12-27 18:50:45 +03:00
src . outputs . push_back ( oe ) ;
if ( src . outputs . size ( ) > = fake_outputs_count )
break ;
}
}
//paste real transaction to the random index
auto it_to_insert = std : : find_if ( src . outputs . begin ( ) , src . outputs . end ( ) , [ & ] ( const tx_output_entry & a )
{
2022-07-13 17:17:04 +02:00
if ( a . out_reference . type ( ) . hash_code ( ) = = typeid ( uint64_t ) . hash_code ( ) )
return static_cast < bool > ( boost : : get < uint64_t > ( a . out_reference ) > = td . m_global_output_index ) ;
2018-12-27 18:50:45 +03:00
return false ; // TODO: implement deterministics real output placement in case there're ref_by_id outs
} ) ;
//size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
2022-07-13 17:17:04 +02:00
tx_output_entry real_oe = AUTO_VAL_INIT ( real_oe ) ;
real_oe . out_reference = td . m_global_output_index ; // TODO: use ref_by_id when neccessary
2022-05-20 21:32:27 +02:00
VARIANT_SWITCH_BEGIN ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] ) ;
2022-05-25 22:31:23 +02:00
VARIANT_CASE_CONST ( tx_out_bare , o )
2021-02-19 20:18:37 +01:00
{
2022-05-25 22:31:23 +02:00
VARIANT_SWITCH_BEGIN ( o . target ) ;
VARIANT_CASE_CONST ( txout_to_key , o )
2022-07-13 17:17:04 +02:00
real_oe . stealth_address = o . key ;
2022-05-25 22:31:23 +02:00
VARIANT_CASE_CONST ( txout_htlc , htlc )
2022-07-13 17:17:04 +02:00
real_oe . stealth_address = htlc . pkey_refund ;
2022-05-20 21:32:27 +02:00
VARIANT_CASE_OTHER ( )
{
WLT_THROW_IF_FALSE_WITH_CODE ( false ,
" Internal error: unexpected type of target: " < < o . target . type ( ) . name ( ) ,
API_RETURN_CODE_INTERNAL_ERROR ) ;
}
VARIANT_SWITCH_END ( ) ;
}
2022-05-25 22:31:23 +02:00
VARIANT_CASE_CONST ( tx_out_zarcanum , o ) ;
2023-02-08 18:50:26 +01:00
real_oe . amount_commitment = o . amount_commitment ; // TODO @#@# consider using shorter code like in sweep_below() (or better reuse it)
real_oe . concealing_point = o . concealing_point ;
real_oe . stealth_address = o . stealth_address ;
real_oe . blinded_asset_id = o . blinded_asset_id ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( td . is_zc ( ) , " transfer # " < < J < < " , amount: " < < print_money_brief ( td . amount ( ) ) < < " is not a ZC " ) ;
src . real_out_amount_blinding_mask = td . m_zc_info_ptr - > amount_blinding_mask ;
src . real_out_asset_id_blinding_mask = td . m_zc_info_ptr - > asset_id_blinding_mask ;
src . asset_id = td . m_zc_info_ptr - > asset_id ;
2023-03-07 12:38:30 +01:00
# ifndef NDEBUG
WLT_CHECK_AND_ASSERT_MES ( crypto : : point_t ( src . asset_id ) + src . real_out_asset_id_blinding_mask * crypto : : c_point_X = = crypto : : point_t ( real_oe . blinded_asset_id ) . modify_mul8 ( ) , false , " real_out_asset_id_blinding_mask doesn't match real_oe.blinded_asset_id " ) ;
WLT_CHECK_AND_ASSERT_MES ( td . m_amount * crypto : : point_t ( real_oe . blinded_asset_id ) . modify_mul8 ( ) + src . real_out_amount_blinding_mask * crypto : : c_point_G = = crypto : : point_t ( real_oe . amount_commitment ) . modify_mul8 ( ) , false , " real_out_amount_blinding_mask doesn't match real_oe.amount_commitment " ) ;
# endif
2022-05-20 21:32:27 +02:00
VARIANT_SWITCH_END ( ) ;
2022-10-01 21:14:32 +02:00
auto interted_it = src . outputs . insert ( it_to_insert , real_oe ) ;
src . real_out_tx_key = get_tx_pub_key_from_extra ( td . m_ptx_wallet_info - > m_tx ) ;
src . real_output = interted_it - src . outputs . begin ( ) ;
src . real_output_in_tx_index = td . m_internal_output_index ;
std : : stringstream ss ;
ss < < " source entry [ " < < i < < " ], td_idx: " < < J < < " , " ;
print_source_entry ( ss , src ) ;
WLT_LOG_L1 ( ss . str ( ) ) ;
2022-05-20 21:32:27 +02:00
2018-12-27 18:50:45 +03:00
+ + i ;
}
return true ;
}
//----------------------------------------------------------------------------------------------------------------
bool wallet2 : : prepare_tx_sources ( crypto : : hash multisig_id , std : : vector < currency : : tx_source_entry > & sources , uint64_t & found_money )
{
auto it = m_multisig_transfers . find ( multisig_id ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( it ! = m_multisig_transfers . end ( ) , " can't find multisig_id: " + epee : : string_tools : : pod_to_hex ( multisig_id ) ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! it - > second . is_spent ( ) , " output with multisig_id: " + epee : : string_tools : : pod_to_hex ( multisig_id ) + " has already been spent by other party at height " + epee : : string_tools : : num_to_string_fast ( it - > second . m_spent_height ) ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( it - > second . m_internal_output_index < it - > second . m_ptx_wallet_info - > m_tx . vout . size ( ) , " it->second.m_internal_output_index < it->second.m_tx.vout.size() " ) ;
2022-05-20 21:32:27 +02:00
//@#@
THROW_IF_FALSE_WALLET_INT_ERR_EX ( it - > second . m_ptx_wallet_info - > m_tx . vout [ it - > second . m_internal_output_index ] . type ( ) = = typeid ( tx_out_bare ) , " Unknown type id in prepare_tx_sources: " < < it - > second . m_ptx_wallet_info - > m_tx . vout [ it - > second . m_internal_output_index ] . type ( ) . name ( ) ) ;
const tx_out_bare & out = boost : : get < tx_out_bare > ( it - > second . m_ptx_wallet_info - > m_tx . vout [ it - > second . m_internal_output_index ] ) ;
2018-12-27 18:50:45 +03:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( out . target . type ( ) = = typeid ( txout_multisig ) , " ms out target type is " < < out . target . type ( ) . name ( ) < < " , expected: txout_multisig " ) ;
2019-04-03 12:48:09 +03:00
const txout_multisig & ms_out = boost : : get < txout_multisig > ( out . target ) ;
2018-12-27 18:50:45 +03:00
2019-04-03 12:48:09 +03:00
sources . push_back ( AUTO_VAL_INIT ( currency : : tx_source_entry ( ) ) ) ;
currency : : tx_source_entry & src = sources . back ( ) ;
2018-12-27 18:50:45 +03:00
src . amount = found_money = out . amount ;
src . real_output_in_tx_index = it - > second . m_internal_output_index ;
src . real_out_tx_key = get_tx_pub_key_from_extra ( it - > second . m_ptx_wallet_info - > m_tx ) ;
src . multisig_id = multisig_id ;
src . ms_sigs_count = ms_out . minimum_sigs ;
src . ms_keys_count = ms_out . keys . size ( ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------------------
2021-02-02 19:02:28 +01:00
bool wallet2 : : prepare_tx_sources_htlc ( crypto : : hash htlc_tx_id , const std : : string & origin , std : : vector < currency : : tx_source_entry > & sources , uint64_t & found_money )
{
2021-02-03 00:13:44 +01:00
typedef currency : : tx_source_entry : : output_entry tx_output_entry ;
2021-02-02 19:02:28 +01:00
//lets figure out, if we have active htlc for this htlc
auto it = m_active_htlcs_txid . find ( htlc_tx_id ) ;
if ( it = = m_active_htlcs_txid . end ( ) )
{
WLT_THROW_IF_FALSE_WITH_CODE ( false ,
" htlc not found with tx_id = " < < htlc_tx_id , API_RETURN_CODE_NOT_FOUND ) ;
}
WLT_THROW_IF_FALSE_WITH_CODE ( m_transfers . size ( ) > it - > second ,
" Internal error: index in m_active_htlcs_txid < " < < it - > second < < " > is bigger then size of m_transfers < " < < m_transfers . size ( ) < < " > " , API_RETURN_CODE_INTERNAL_ERROR ) ;
const transfer_details & td = m_transfers [ it - > second ] ;
2022-05-20 21:32:27 +02:00
//@#@
WLT_THROW_IF_FALSE_WITH_CODE ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . type ( ) = = typeid ( tx_out_bare ) ,
2022-05-25 22:31:23 +02:00
" Unexpected out type in prepare_tx_sources_htlc: " < < td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . type ( ) . name ( ) , API_RETURN_CODE_INTERNAL_ERROR ) ;
2022-05-20 21:32:27 +02:00
const tx_out_bare & out_bare = boost : : get < tx_out_bare > ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] ) ;
WLT_THROW_IF_FALSE_WITH_CODE ( out_bare . target . type ( ) = = typeid ( txout_htlc ) ,
2021-02-02 19:02:28 +01:00
" Unexpected type in active htlc " , API_RETURN_CODE_INTERNAL_ERROR ) ;
2022-05-20 21:32:27 +02:00
const txout_htlc & htlc_out = boost : : get < txout_htlc > ( out_bare . target ) ;
2021-02-02 19:02:28 +01:00
bool use_sha256 = ! ( htlc_out . flags & CURRENCY_TXOUT_HTLC_FLAGS_HASH_TYPE_MASK ) ;
//check origin
WLT_THROW_IF_FALSE_WITH_CODE ( origin . size ( ) ! = 0 ,
" Origin for htlc is empty " , API_RETURN_CODE_BAD_ARG ) ;
crypto : : hash htlc_calculated_hash = currency : : null_hash ;
if ( use_sha256 )
{
htlc_calculated_hash = crypto : : sha256_hash ( origin . data ( ) , origin . size ( ) ) ;
}
else
{
htlc_calculated_hash = crypto : : RIPEMD160_hash_256 ( origin . data ( ) , origin . size ( ) ) ;
}
WLT_THROW_IF_FALSE_WITH_CODE ( htlc_calculated_hash = = htlc_out . htlc_hash ,
" Origin hash is missmatched with txout_htlc " , API_RETURN_CODE_HTLC_ORIGIN_HASH_MISSMATCHED ) ;
sources . push_back ( AUTO_VAL_INIT ( currency : : tx_source_entry ( ) ) ) ;
currency : : tx_source_entry & src = sources . back ( ) ;
2021-02-03 00:13:44 +01:00
tx_output_entry real_oe = AUTO_VAL_INIT ( real_oe ) ;
2022-07-13 17:17:04 +02:00
real_oe . out_reference = td . m_global_output_index ; // TODO: use ref_by_id when necessary
real_oe . stealth_address = htlc_out . pkey_redeem ;
2021-02-02 22:14:34 +01:00
src . outputs . push_back ( real_oe ) ; //m_global_output_index should be prefetched
2021-02-02 19:02:28 +01:00
src . amount = found_money = td . amount ( ) ;
src . real_output_in_tx_index = td . m_internal_output_index ;
2021-02-02 22:14:34 +01:00
src . real_output = 0 ; //no mixins supposed to be in htlc
2021-02-02 19:02:28 +01:00
src . real_out_tx_key = get_tx_pub_key_from_extra ( td . m_ptx_wallet_info - > m_tx ) ;
src . htlc_origin = origin ;
return true ;
}
//----------------------------------------------------------------------------------------------------------------
2022-09-21 22:35:24 +02:00
assets_selection_context wallet2 : : get_needed_money ( uint64_t fee , const std : : vector < currency : : tx_destination_entry > & dsts )
2018-12-27 18:50:45 +03:00
{
2022-09-20 22:01:52 +02:00
assets_selection_context amounts_map ;
2023-02-08 18:50:26 +01:00
amounts_map [ currency : : native_coin_asset_id ] . needed_amount = fee ;
2022-10-01 21:14:32 +02:00
for ( auto & dt : dsts )
2018-12-27 18:50:45 +03:00
{
2023-04-18 16:55:00 +02:00
if ( dt . asset_id = = currency : : null_pkey )
2022-09-30 21:23:06 +02:00
continue ; //this destination for emmition only
2018-12-27 18:50:45 +03:00
THROW_IF_TRUE_WALLET_EX ( 0 = = dt . amount , error : : zero_destination ) ;
uint64_t money_to_add = dt . amount ;
2023-04-20 22:08:20 +02:00
if ( dt . amount_to_provide | | dt . flags & tx_destination_entry_flags : : tdef_explicit_amount_to_provide )
2018-12-27 18:50:45 +03:00
money_to_add = dt . amount_to_provide ;
2022-09-20 22:01:52 +02:00
amounts_map [ dt . asset_id ] . needed_amount + = money_to_add ;
THROW_IF_TRUE_WALLET_EX ( amounts_map [ dt . asset_id ] . needed_amount < money_to_add , error : : tx_sum_overflow , dsts , fee ) ;
2023-04-20 22:08:20 +02:00
//clean up empty entries
if ( amounts_map [ dt . asset_id ] . needed_amount = = 0 )
{
amounts_map . erase ( amounts_map . find ( dt . asset_id ) ) ;
}
2018-12-27 18:50:45 +03:00
}
2022-09-21 22:35:24 +02:00
return std : : move ( amounts_map ) ;
2018-12-27 18:50:45 +03:00
}
2022-03-19 04:12:13 +02:00
//----------------------------------------------------------------------------------------------------------------
void wallet2 : : set_disable_tor_relay ( bool disable )
{
m_disable_tor_relay = disable ;
}
//----------------------------------------------------------------------------------------------------------------
void wallet2 : : notify_state_change ( const std : : string & state_code , const std : : string & details )
{
m_wcallback - > on_tor_status_change ( state_code ) ;
}
2018-12-27 18:50:45 +03:00
//----------------------------------------------------------------------------------------------------------------
void wallet2 : : send_transaction_to_network ( const transaction & tx )
{
2022-04-20 17:17:11 +02:00
# ifndef DISABLE_TOR
2022-03-19 04:12:13 +02:00
if ( ! m_disable_tor_relay )
{
//TODO check that core synchronized
//epee::net_utils::levin_client2 p2p_client;
//make few attempts
tools : : levin_over_tor_client p2p_client ;
p2p_client . get_transport ( ) . set_notifier ( this ) ;
2022-04-10 19:46:43 +02:00
bool succeseful_sent = false ;
2022-03-19 04:12:13 +02:00
for ( size_t i = 0 ; i ! = 3 ; i + + )
{
if ( ! p2p_client . connect ( " 144.76.183.143 " , 2121 , 10000 ) )
{
continue ; //THROW_IF_FALSE_WALLET_EX(false, error::no_connection_to_daemon, "Failed to connect to TOR node");
}
2022-04-02 19:48:36 +03:00
2022-03-21 16:47:11 +02:00
currency : : NOTIFY_OR_INVOKE_NEW_TRANSACTIONS : : request p2p_req = AUTO_VAL_INIT ( p2p_req ) ;
currency : : NOTIFY_OR_INVOKE_NEW_TRANSACTIONS : : response p2p_rsp = AUTO_VAL_INIT ( p2p_rsp ) ;
2022-03-19 04:12:13 +02:00
p2p_req . txs . push_back ( t_serializable_object_to_blob ( tx ) ) ;
2022-04-02 00:59:16 +03:00
this - > notify_state_change ( WALLET_LIB_STATE_SENDING ) ;
2022-03-22 23:38:43 +02:00
epee : : net_utils : : invoke_remote_command2 ( NOTIFY_OR_INVOKE_NEW_TRANSACTIONS : : ID , p2p_req , p2p_rsp , p2p_client ) ;
2022-03-19 04:12:13 +02:00
p2p_client . disconnect ( ) ;
2022-03-24 05:37:58 +02:00
if ( p2p_rsp . code = = API_RETURN_CODE_OK )
{
2022-04-02 00:59:16 +03:00
this - > notify_state_change ( WALLET_LIB_SENT_SUCCESS ) ;
2022-04-10 19:46:43 +02:00
succeseful_sent = true ;
2022-03-24 05:37:58 +02:00
break ;
}
2022-04-02 00:59:16 +03:00
this - > notify_state_change ( WALLET_LIB_SEND_FAILED ) ;
2022-03-21 16:47:11 +02:00
//checking if transaction got relayed to other nodes and
2022-03-19 04:12:13 +02:00
//return;
}
2022-04-10 19:46:43 +02:00
if ( ! succeseful_sent )
{
this - > notify_state_change ( WALLET_LIB_SEND_FAILED ) ;
THROW_IF_FALSE_WALLET_EX ( succeseful_sent , error : : no_connection_to_daemon , " Faile to build TOR stream " ) ;
}
2022-03-19 04:12:13 +02:00
}
else
2022-04-20 17:17:11 +02:00
# endif //
2022-03-19 04:12:13 +02:00
{
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 = m_core_proxy - > call_COMMAND_RPC_SEND_RAW_TX ( req , daemon_send_resp ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : no_connection_to_daemon , " sendrawtransaction " ) ;
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status = = API_RETURN_CODE_BUSY , error : : daemon_busy , " sendrawtransaction " ) ;
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status = = API_RETURN_CODE_DISCONNECTED , error : : no_connection_to_daemon , " Transfer attempt while daemon offline " ) ;
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status ! = API_RETURN_CODE_OK , error : : tx_rejected , tx , daemon_send_resp . status ) ;
WLT_LOG_L2 ( " transaction " < < get_transaction_hash ( tx ) < < " generated ok and sent to daemon: " < < ENDL < < currency : : obj_to_json_str ( tx ) ) ;
}
2022-04-20 17:17:11 +02:00
2018-12-27 18:50:45 +03:00
}
2022-01-08 16:36:33 +01:00
//----------------------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : add_sent_tx_detailed_info ( const transaction & tx ,
const std : : vector < currency : : tx_destination_entry > & destinations ,
const std : : vector < uint64_t > & selected_transfers )
{
2020-06-11 22:20:00 +03:00
payment_id_t payment_id ;
get_payment_id_from_tx ( tx . attachment , payment_id ) ;
2018-12-27 18:50:45 +03:00
std : : vector < std : : string > recipients ;
std : : unordered_set < account_public_address > used_addresses ;
for ( const auto & d : destinations )
{
for ( const auto & addr : d . addr )
{
if ( used_addresses . insert ( addr ) . second & & addr ! = m_account . get_public_address ( ) )
2020-06-11 22:20:00 +03:00
recipients . push_back ( payment_id . empty ( ) ? get_account_address_as_str ( addr ) : get_account_address_and_payment_id_as_str ( addr , payment_id ) ) ;
2018-12-27 18:50:45 +03:00
}
}
if ( ! recipients . size ( ) )
{
//transaction send to ourself
2020-06-11 22:20:00 +03:00
recipients . push_back ( payment_id . empty ( ) ? get_account_address_as_str ( m_account . get_public_address ( ) ) : get_account_address_and_payment_id_as_str ( m_account . get_public_address ( ) , payment_id ) ) ;
2018-12-27 18:50:45 +03:00
}
add_sent_unconfirmed_tx ( tx , recipients , selected_transfers , destinations ) ;
}
//----------------------------------------------------------------------------------------------------
2019-04-12 09:48:33 +03:00
void wallet2 : : mark_transfers_with_flag ( const std : : vector < uint64_t > & selected_transfers , uint32_t flag , const std : : string & reason /* = empty_string */ , bool throw_if_flag_already_set /* = false */ )
2018-12-27 18:50:45 +03:00
{
2019-12-14 18:29:05 +03:00
// check all selected transfers prior to flag change
for ( uint64_t i : selected_transfers )
2019-04-12 09:48:33 +03:00
{
2019-12-14 18:29:05 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( i < m_transfers . size ( ) , " invalid transfer index given: " < < i < < " , m_transfers.size() == " < < m_transfers . size ( ) ) ;
if ( throw_if_flag_already_set )
2019-04-12 09:48:33 +03:00
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ( m_transfers [ i ] . m_flags & flag ) = = 0 , " transfer # " < < i < < " already has flag " < < flag < < " : " < < m_transfers [ i ] . m_flags < < " , transfer info: " < < ENDL < < epee : : serialization : : store_t_to_json ( m_transfers [ i ] ) ) ;
}
}
2019-12-14 18:29:05 +03:00
2018-12-27 18:50:45 +03:00
for ( uint64_t i : selected_transfers )
{
uint32_t flags_before = m_transfers [ i ] . m_flags ;
m_transfers [ i ] . m_flags | = flag ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L1 ( " marking transfer # " < < std : : setfill ( ' 0 ' ) < < std : : right < < std : : setw ( 3 ) < < i < < " with flag " < < flag < < " : " < < flags_before < < " -> " < < m_transfers [ i ] . m_flags < <
2018-12-27 18:50:45 +03:00
( reason . empty ( ) ? " " : " , reason: " ) < < reason ) ;
}
}
//----------------------------------------------------------------------------------------------------
2019-12-13 18:42:15 +03:00
void wallet2 : : clear_transfers_from_flag ( const std : : vector < uint64_t > & selected_transfers , uint32_t flag , const std : : string & reason /* = empty_string */ ) noexcept
2018-12-27 18:50:45 +03:00
{
2019-12-13 18:42:15 +03:00
TRY_ENTRY ( ) ;
2018-12-27 18:50:45 +03:00
for ( uint64_t i : selected_transfers )
{
2019-12-13 18:42:15 +03:00
if ( i > = m_transfers . size ( ) )
{
WLT_LOG_ERROR ( " INTERNAL ERROR: i: " < < i < < " >= m_transfers.size() : " < < m_transfers . size ( ) ) ;
continue ;
}
2018-12-27 18:50:45 +03:00
uint32_t flags_before = m_transfers [ i ] . m_flags ;
m_transfers [ i ] . m_flags & = ~ flag ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L1 ( " clearing transfer # " < < std : : setfill ( ' 0 ' ) < < std : : right < < std : : setw ( 3 ) < < i < < " from flag " < < flag < < " : " < < flags_before < < " -> " < < m_transfers [ i ] . m_flags < <
2018-12-27 18:50:45 +03:00
( reason . empty ( ) ? " " : " , reason: " ) < < reason ) ;
}
2019-12-13 18:42:15 +03:00
CATCH_ENTRY_NO_RETURN ( ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : exception_handler ( )
{
m_found_free_amounts . clear ( ) ;
}
//----------------------------------------------------------------------------------------------------
2019-04-12 09:48:33 +03:00
void wallet2 : : exception_handler ( ) const
{
// do nothing
// TODO: is it correct?
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : mark_transfers_as_spent ( const std : : vector < uint64_t > & selected_transfers , const std : : string & reason /* = empty_string */ )
{
2019-04-08 14:16:11 +03:00
// TODO: design a safe undo for this operation
2018-12-27 18:50:45 +03:00
mark_transfers_with_flag ( selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_SPENT , reason ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : extract_offers_from_transfer_entry ( size_t i , std : : unordered_map < crypto : : hash , bc_services : : offer_details_ex > & offers_local )
{
//TODO: this code supports only one market(offer) instruction per transaction
2021-02-08 21:31:46 +01:00
load_wallet_transfer_info_flags ( m_transfer_history [ i ] ) ;
2018-12-27 18:50:45 +03:00
switch ( m_transfer_history [ i ] . tx_type )
{
case GUI_TX_TYPE_PUSH_OFFER :
{
bc_services : : offer_details od ;
2021-06-24 22:10:09 +02:00
if ( ! get_type_in_variant_container ( m_transfer_history [ i ] . marketplace_entries , od ) )
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Transaction history entry " < < i < < " market as type " < < m_transfer_history [ i ] . tx_type < < " but get_type_in_variant_container returned false for bc_services::offer_details " ) ;
2018-12-27 18:50:45 +03:00
break ;
}
crypto : : hash h = null_hash ;
h = m_transfer_history [ i ] . tx_hash ;
bc_services : : offer_details_ex & ode = offers_local [ h ] ;
ode = AUTO_VAL_INIT ( bc_services : : offer_details_ex ( ) ) ;
static_cast < bc_services : : offer_details & > ( ode ) = od ;
//fill extra fields
ode . tx_hash = m_transfer_history [ i ] . tx_hash ;
ode . index_in_tx = 0 ; // TODO: handle multiple offers in tx, now only one per tx is supported
ode . timestamp = m_transfer_history [ i ] . timestamp ;
ode . fee = m_transfer_history [ i ] . fee ;
ode . stopped = false ;
break ;
}
case GUI_TX_TYPE_UPDATE_OFFER :
{
bc_services : : update_offer uo ;
2021-06-24 22:10:09 +02:00
if ( ! get_type_in_variant_container ( m_transfer_history [ i ] . marketplace_entries , uo ) )
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Transaction history entry " < < i < < " market as type " < < m_transfer_history [ i ] . tx_type < < " but get_type_in_variant_container returned false for update_offer " ) ;
2018-12-27 18:50:45 +03:00
break ;
}
crypto : : hash h = null_hash ;
h = m_transfer_history [ i ] . tx_hash ;
bc_services : : offer_details_ex & ode = offers_local [ h ] ;
ode = AUTO_VAL_INIT ( bc_services : : offer_details_ex ( ) ) ;
static_cast < bc_services : : offer_details & > ( ode ) = uo . of ;
//fill extra fields
ode . tx_hash = m_transfer_history [ i ] . tx_hash ;
ode . index_in_tx = 0 ;
ode . fee = m_transfer_history [ i ] . fee ;
ode . stopped = false ;
ode . tx_original_hash = uo . tx_id ;
//remove old transaction
crypto : : hash h_old = uo . tx_id ;
auto it = offers_local . find ( h_old ) ;
if ( it = = offers_local . end ( ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L3 ( " Unable to find original tx record " < < h_old < < " in update offer " < < h ) ;
2018-12-27 18:50:45 +03:00
break ;
}
//keep original timestamp
ode . timestamp = it - > second . timestamp ;
offers_local . erase ( it ) ;
break ;
}
case GUI_TX_TYPE_CANCEL_OFFER :
{
bc_services : : cancel_offer co ;
2021-06-24 22:10:09 +02:00
if ( ! get_type_in_variant_container ( m_transfer_history [ i ] . marketplace_entries , co ) )
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Transaction history entry " < < i < < " market as type " < < m_transfer_history [ i ] . tx_type < < " but get_type_in_variant_container returned false for cancel_offer " ) ;
2018-12-27 18:50:45 +03:00
break ;
}
crypto : : hash h = co . tx_id ;
auto it = offers_local . find ( h ) ;
if ( it = = offers_local . end ( ) )
{
2020-08-31 18:23:41 +02:00
WLT_LOG_L3 ( " Unable to find original tx record " < < h < < " in cancel offer " < < h ) ;
break ;
2018-12-27 18:50:45 +03:00
}
offers_local . erase ( it ) ;
}
default :
;
}
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : select_my_offers ( std : : list < bc_services : : offer_details_ex > & offers )
{
std : : unordered_map < crypto : : hash , bc_services : : offer_details_ex > offers_local ;
if ( ! m_transfer_history . size ( ) )
return true ;
uint64_t stop_timestamp = m_core_runtime_config . get_core_time ( ) - OFFER_MAXIMUM_LIFE_TIME ;
size_t i = m_transfer_history . size ( ) - 1 ;
for ( ; i ! = 0 ; i - - )
{
if ( m_transfer_history [ i ] . timestamp < stop_timestamp )
{
i + + ;
break ;
}
}
if ( i = = 0 & & m_transfer_history [ 0 ] . timestamp < stop_timestamp )
i + + ;
if ( i > = m_transfer_history . size ( ) )
return true ;
for ( ; i ! = m_transfer_history . size ( ) ; i + + )
{
extract_offers_from_transfer_entry ( i , offers_local ) ;
}
for ( const auto & o : offers_local )
offers . push_back ( o . second ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2019-08-28 22:16:15 +02:00
bool wallet2 : : get_actual_offers ( std : : list < bc_services : : offer_details_ex > & offers )
2018-12-27 18:50:45 +03:00
{
select_my_offers ( offers ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2023-04-25 00:16:13 +02:00
bool wallet2 : : expand_selection_with_zc_input ( assets_selection_context & needed_money_map , uint64_t fake_outputs_count , std : : vector < uint64_t > & selected_indexes )
{
free_amounts_cache_type & found_free_amounts = m_found_free_amounts [ currency : : native_coin_asset_id ] ;
auto & asset_needed_money_item = needed_money_map [ currency : : native_coin_asset_id ] ;
//need to add ZC input
for ( auto it = found_free_amounts . begin ( ) ; it ! = found_free_amounts . end ( ) ; it + + )
{
for ( auto it_in_amount = it - > second . begin ( ) ; it_in_amount ! = it - > second . end ( ) ; it_in_amount + + )
{
if ( ! m_transfers [ * it_in_amount ] . is_zc ( ) )
{
continue ;
}
if ( is_transfer_ready_to_go ( m_transfers [ * it - > second . begin ( ) ] , fake_outputs_count ) )
{
asset_needed_money_item . found_amount + = it - > first ;
selected_indexes . push_back ( * it_in_amount ) ;
it - > second . erase ( it_in_amount ) ;
if ( ! it - > second . size ( ) )
{
found_free_amounts . erase ( it ) ;
}
return true ;
}
}
}
WLT_THROW_IF_FALSE_WALLET_EX_MES ( false , error : : no_zc_inputs , " Missing ZC inputs for TX_FLAG_SIGNATURE_MODE_SEPARATE operation " ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2022-09-20 22:01:52 +02:00
bool wallet2 : : select_indices_for_transfer ( assets_selection_context & needed_money_map , uint64_t fake_outputs_count , std : : vector < uint64_t > & selected_indexes )
{
bool res = true ;
//
for ( auto & item : needed_money_map )
{
auto asset_cashe_it = m_found_free_amounts . find ( item . first ) ;
2022-09-21 22:35:24 +02:00
WLT_THROW_IF_FALSE_WALLET_EX_MES ( asset_cashe_it ! = m_found_free_amounts . end ( ) , error : : not_enough_money , " " , item . second . found_amount , item . second . needed_amount , 0 , item . first ) ;
item . second . found_amount = select_indices_for_transfer ( selected_indexes , asset_cashe_it - > second , item . second . needed_amount , fake_outputs_count ) ;
WLT_THROW_IF_FALSE_WALLET_EX_MES ( item . second . found_amount > = item . second . needed_amount , error : : not_enough_money , " " , item . second . found_amount , item . second . needed_amount , 0 , item . first ) ;
2022-09-20 22:01:52 +02:00
}
2023-04-25 00:16:13 +02:00
if ( m_current_context . pconstruct_tx_param & & m_current_context . pconstruct_tx_param - > need_at_least_1_zc )
{
bool found_zc_input = false ;
for ( auto i : selected_indexes )
{
if ( m_transfers [ i ] . is_zc ( ) )
{
found_zc_input = true ;
break ;
}
}
if ( ! found_zc_input )
{
expand_selection_with_zc_input ( needed_money_map , fake_outputs_count , selected_indexes ) ;
}
}
2022-09-20 22:01:52 +02:00
return res ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
uint64_t wallet2 : : select_indices_for_transfer ( std : : vector < uint64_t > & selected_indexes , free_amounts_cache_type & found_free_amounts , uint64_t needed_money , uint64_t fake_outputs_count )
{
2020-06-23 21:36:30 +03:00
WLT_LOG_GREEN ( " Selecting indices for transfer of " < < print_money_brief ( needed_money ) < < " with " < < fake_outputs_count < < " fake outs, found_free_amounts.size()= " < < found_free_amounts . size ( ) < < " ... " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
uint64_t found_money = 0 ;
2023-04-25 00:16:13 +02:00
//uint64_t found_zc_input = false;
2020-06-23 21:36:30 +03:00
std : : string selected_amounts_str ;
2020-08-31 18:23:41 +02:00
while ( found_money < needed_money & & found_free_amounts . size ( ) )
2018-12-27 18:50:45 +03:00
{
auto it = found_free_amounts . lower_bound ( needed_money - found_money ) ;
if ( ! ( it ! = found_free_amounts . end ( ) & & it - > second . size ( ) ) )
{
it = - - found_free_amounts . end ( ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( it - > second . size ( ) , 0 , " internal error: empty found_free_amounts map " ) ;
2018-12-27 18:50:45 +03:00
}
if ( is_transfer_ready_to_go ( m_transfers [ * it - > second . begin ( ) ] , fake_outputs_count ) )
{
found_money + = it - > first ;
selected_indexes . push_back ( * it - > second . begin ( ) ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Selected index: " < < * it - > second . begin ( ) < < " , transfer_details: " < < ENDL < < epee : : serialization : : store_t_to_json ( m_transfers [ * it - > second . begin ( ) ] ) ) ;
2020-06-23 21:36:30 +03:00
selected_amounts_str + = ( selected_amounts_str . empty ( ) ? " " : " + " ) + print_money_brief ( it - > first ) ;
2018-12-27 18:50:45 +03:00
}
it - > second . erase ( it - > second . begin ( ) ) ;
if ( ! it - > second . size ( ) )
found_free_amounts . erase ( it ) ;
}
2023-04-25 00:16:13 +02:00
2020-06-23 21:36:30 +03:00
WLT_LOG_GREEN ( " Found " < < print_money_brief ( found_money ) < < " as " < < selected_indexes . size ( ) < < " out(s): " < < selected_amounts_str < < " , found_free_amounts.size()= " < < found_free_amounts . size ( ) , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
return found_money ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : is_transfer_ready_to_go ( const transfer_details & td , uint64_t fake_outputs_count )
{
if ( is_transfer_able_to_go ( td , fake_outputs_count ) & & is_transfer_unlocked ( td ) )
{
return true ;
}
return false ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : is_transfer_able_to_go ( const transfer_details & td , uint64_t fake_outputs_count )
{
2019-04-12 09:48:33 +03:00
if ( ! td . is_spendable ( ) )
return false ;
2022-11-28 21:15:49 +01:00
const tx_out_v & out_v = td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] ;
uint8_t mix_attr = CURRENCY_TO_KEY_OUT_RELAXED ;
if ( get_mix_attr_from_tx_out_v ( out_v , mix_attr ) )
2021-02-19 20:18:37 +01:00
{
2022-11-28 21:15:49 +01:00
if ( ! currency : : is_mixattr_applicable_for_fake_outs_counter ( mix_attr , fake_outputs_count ) )
return false ;
}
VARIANT_SWITCH_BEGIN ( out_v ) ;
VARIANT_CASE_CONST ( tx_out_bare , o ) ;
2022-05-20 21:32:27 +02:00
if ( o . target . type ( ) = = typeid ( txout_htlc ) )
{
if ( fake_outputs_count ! = 0 )
return false ;
}
VARIANT_SWITCH_END ( ) ;
2022-11-28 21:15:49 +01:00
2019-04-12 09:48:33 +03:00
return true ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : prepare_free_transfers_cache ( uint64_t fake_outputs_count )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Preparing transfers_cache... " ) ;
2018-12-27 18:50:45 +03:00
uint64_t count = 0 ;
if ( ! m_found_free_amounts . size ( ) | | fake_outputs_count ! = m_fake_outputs_count )
{
m_found_free_amounts . clear ( ) ;
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
const transfer_details & td = m_transfers [ i ] ;
if ( is_transfer_able_to_go ( td , fake_outputs_count ) )
{
2022-05-20 21:32:27 +02:00
//@#@
2022-09-20 22:01:52 +02:00
m_found_free_amounts [ td . get_asset_id ( ) ] [ td . amount ( ) ] . insert ( i ) ;
2018-12-27 18:50:45 +03:00
count + + ;
}
}
m_fake_outputs_count = fake_outputs_count ;
}
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Transfers_cache prepared. " < < count < < " items cached for " < < m_found_free_amounts . size ( ) < < " amounts " ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : add_transfers_to_transfers_cache ( const std : : vector < uint64_t > & indexs )
{
2022-05-20 21:32:27 +02:00
//@#@
2018-12-27 18:50:45 +03:00
for ( auto i : indexs )
2022-09-20 22:01:52 +02:00
add_transfer_to_transfers_cache ( m_transfers [ i ] . amount ( ) , i , m_transfers [ i ] . get_asset_id ( ) ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2023-02-08 18:50:26 +01:00
void wallet2 : : add_transfer_to_transfers_cache ( uint64_t amount , uint64_t index , const crypto : : public_key & asset_id /* = currency::native_coin_asset_id */ )
2018-12-27 18:50:45 +03:00
{
2022-09-20 22:01:52 +02:00
m_found_free_amounts [ asset_id ] [ amount ] . insert ( index ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2022-09-20 22:01:52 +02:00
bool wallet2 : : select_transfers ( assets_selection_context & needed_money_map , size_t fake_outputs_count , uint64_t dust , std : : vector < uint64_t > & selected_indicies )
2018-12-27 18:50:45 +03:00
{
prepare_free_transfers_cache ( fake_outputs_count ) ;
2022-09-20 22:01:52 +02:00
return select_indices_for_transfer ( needed_money_map , fake_outputs_count , selected_indicies ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2023-05-01 13:34:37 +02:00
void wallet2 : : add_sent_unconfirmed_tx ( const currency : : transaction & tx ,
const std : : vector < std : : string > & recipients ,
const std : : vector < uint64_t > & selected_indicies ,
const std : : vector < currency : : tx_destination_entry > & splitted_dsts )
2018-12-27 18:50:45 +03:00
{
2023-05-01 13:34:37 +02:00
PROFILE_FUNC ( " wallet2::add_sent_unconfirmed_tx " ) ;
2023-06-15 23:55:22 +02:00
process_transaction_context ptc ( tx ) ;
ptc . recipients = recipients ;
2023-05-01 13:34:37 +02:00
for ( auto addr : recipients )
2023-06-15 23:55:22 +02:00
ptc . remote_aliases . push_back ( get_alias_for_address ( addr ) ) ;
2018-12-27 18:50:45 +03:00
2023-06-15 23:55:22 +02:00
handle_unconfirmed_tx ( ptc ) ;
wallet_public : : wallet_transfer_info & unconfirmed_wti = misc_utils : : get_or_insert_value_initialized ( m_unconfirmed_txs , currency : : get_transaction_hash ( tx ) ) ;
//override some info that might be missing
2023-06-11 00:20:11 +02:00
unconfirmed_wti . selected_indicies = selected_indicies ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : get_alias_for_address ( const std : : string & addr )
2022-06-16 17:02:00 +02:00
{
std : : vector < std : : string > aliases = get_aliases_for_address ( addr ) ;
if ( aliases . size ( ) )
return aliases . front ( ) ;
return " " ;
}
//----------------------------------------------------------------------------------------------------
std : : vector < std : : string > wallet2 : : get_aliases_for_address ( const std : : string & addr )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::get_alias_for_address " ) ;
currency : : COMMAND_RPC_GET_ALIASES_BY_ADDRESS : : request req = addr ;
currency : : COMMAND_RPC_GET_ALIASES_BY_ADDRESS : : response res = AUTO_VAL_INIT ( res ) ;
2022-06-16 17:02:00 +02:00
std : : vector < std : : string > aliases ;
2018-12-27 18:50:45 +03:00
if ( ! m_core_proxy - > call_COMMAND_RPC_GET_ALIASES_BY_ADDRESS ( req , res ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Failed to COMMAND_RPC_GET_ALIASES_BY_ADDRESS " ) ;
2022-06-16 17:02:00 +02:00
return aliases ;
}
for ( auto & e : res . alias_info_list )
{
aliases . push_back ( e . alias ) ;
2018-12-27 18:50:45 +03:00
}
2022-06-16 17:02:00 +02:00
return aliases ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : transfer ( const std : : vector < currency : : tx_destination_entry > & dsts , size_t fake_outputs_count ,
uint64_t unlock_time , uint64_t fee , const std : : vector < currency : : extra_v > & extra ,
2019-04-03 12:48:09 +03:00
const std : : vector < currency : : attachment_v > & attachments ,
2018-12-27 18:50:45 +03:00
currency : : transaction & tx )
{
2022-09-20 22:01:52 +02:00
transfer ( dsts , fake_outputs_count , unlock_time , fee , extra , attachments , get_current_split_strategy ( ) , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , tx ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : transfer ( const std : : vector < currency : : tx_destination_entry > & dsts , size_t fake_outputs_count ,
uint64_t unlock_time , uint64_t fee , const std : : vector < currency : : extra_v > & extra ,
2019-04-03 12:48:09 +03:00
const std : : vector < currency : : attachment_v > & attachments )
2018-12-27 18:50:45 +03:00
{
currency : : transaction tx ;
transfer ( dsts , fake_outputs_count , unlock_time , fee , extra , attachments , tx ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : is_connected_to_net ( )
{
currency : : COMMAND_RPC_GET_INFO : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_INFO : : response res = AUTO_VAL_INIT ( res ) ;
if ( ! m_core_proxy - > call_COMMAND_RPC_GET_INFO ( req , res ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Failed to COMMAND_RPC_GET_INFO " ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
return ( res . synchronized_connections_count ) ? true : false ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : process_genesis_if_needed ( const currency : : block & genesis )
{
if ( ! m_transfers . empty ( ) | | ! m_key_images . empty ( ) )
return ;
2020-04-25 00:30:55 +02:00
THROW_IF_TRUE_WALLET_EX ( get_blockchain_current_size ( ) > 1 , error : : wallet_internal_error , " Can't change wallet genesis block once the blockchain has been populated " ) ;
2018-12-27 18:50:45 +03:00
crypto : : hash genesis_hash = get_block_hash ( genesis ) ;
2020-04-30 22:29:08 +02:00
if ( get_blockchain_current_size ( ) = = 1 & & m_chain . get_genesis ( ) ! = genesis_hash )
WLT_LOG_L0 ( " Changing genesis block for wallet " < < m_account . get_public_address_str ( ) < < " : " < < ENDL < < " " < < m_chain . get_genesis ( ) < < " -> " < < genesis_hash ) ;
2018-12-27 18:50:45 +03:00
2020-04-26 01:49:57 +02:00
//m_blockchain.clear();
2018-12-27 18:50:45 +03:00
2020-04-26 01:49:57 +02:00
//m_blockchain.push_back(genesis_hash);
2020-04-30 22:29:08 +02:00
m_chain . set_genesis ( genesis_hash ) ;
2018-12-27 18:50:45 +03:00
m_last_bc_timestamp = genesis . timestamp ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Processing genesis block: " < < genesis_hash ) ;
2020-06-12 23:32:06 +02:00
process_new_transaction ( genesis . miner_tx , 0 , genesis , nullptr ) ;
2018-12-27 18:50:45 +03:00
}
2020-04-30 22:29:08 +02:00
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : set_genesis ( const crypto : : hash & genesis_hash )
{
2020-04-25 00:30:55 +02:00
THROW_IF_TRUE_WALLET_EX ( get_blockchain_current_size ( ) ! = 1 , error : : wallet_internal_error , " Can't change wallet genesis hash once the blockchain has been populated " ) ;
2020-04-30 22:29:08 +02:00
WLT_LOG_L0 ( " Changing genesis hash for wallet " < < m_account . get_public_address_str ( ) < < " : " < < ENDL < < " " < < m_chain . get_genesis ( ) < < " -> " < < genesis_hash ) ;
m_chain . set_genesis ( genesis_hash ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
2019-04-08 14:16:11 +03:00
void wallet2 : : print_tx_sent_message ( const currency : : transaction & tx , const std : : string & description , uint64_t fee /* = UINT64_MAX */ )
2018-12-27 18:50:45 +03:00
{
//uint64_t balance_unlocked = 0;
//uint64_t balance_total = balance(balance_unlocked);
2019-04-08 14:16:11 +03:00
std : : stringstream ss ;
if ( fee ! = UINT64_MAX )
ss < < " Commission: " < < std : : setw ( 21 ) < < std : : right < < print_money ( fee ) < < ENDL ;
2019-02-21 21:33:52 +03:00
WLT_LOG_CYAN ( " Transaction " < < get_transaction_hash ( tx ) < < " was successfully sent " < < description < < ENDL
2019-04-08 14:16:11 +03:00
< < ss . str ( )
2018-12-27 18:50:45 +03:00
// << "Balance: " << std::setw(21) << print_money(balance_total) << ENDL
// << "Unlocked: " << std::setw(21) << print_money(balance_unlocked) << ENDL
< < " Please, wait for confirmation for your balance to be unlocked. " ,
LOG_LEVEL_0 ) ;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : get_tx_expiration_median ( ) const
{
currency : : COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN : : request req = AUTO_VAL_INIT ( req ) ;
currency : : COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN : : response res = AUTO_VAL_INIT ( res ) ;
m_core_proxy - > call_COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN ( req , res ) ;
2020-05-07 23:26:41 +02:00
if ( res . status ! = API_RETURN_CODE_OK )
2018-12-27 18:50:45 +03:00
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN failed, status: " < < res . status ) ;
2018-12-27 18:50:45 +03:00
return 0 ;
}
return res . expiration_median ;
}
//----------------------------------------------------------------------------------------------------
2022-10-01 21:14:32 +02:00
void wallet2 : : print_source_entry ( std : : stringstream & output , const currency : : tx_source_entry & src ) const
2019-02-21 21:33:52 +03:00
{
2022-10-01 21:14:32 +02:00
std : : stringstream ss ;
for ( auto & el : src . outputs )
ss < < el . out_reference < < " " ;
2023-02-21 01:41:33 +01:00
output < < " amount: " < < print_money_brief ( src . amount ) < < ( src . is_zc ( ) ? " (hidden) " : " " )
2022-10-01 21:14:32 +02:00
< < " , real_output: " < < src . real_output
< < " , real_output_in_tx_index: " < < src . real_output_in_tx_index
< < " , indexes: " < < ss . str ( ) ;
2022-12-03 21:12:31 +01:00
2023-02-08 18:50:26 +01:00
if ( src . asset_id ! = currency : : native_coin_asset_id )
2022-12-03 21:12:31 +01:00
output < < " , asset_id: " < < print16 ( src . asset_id ) ;
2019-02-21 21:33:52 +03:00
}
//----------------------------------------------------------------------------------------------------
2019-04-03 12:48:09 +03:00
bool wallet2 : : get_tx_key ( const crypto : : hash & txid , crypto : : secret_key & tx_key ) const
{
const std : : unordered_map < crypto : : hash , crypto : : secret_key > : : const_iterator i = m_tx_keys . find ( txid ) ;
if ( i = = m_tx_keys . end ( ) )
return false ;
tx_key = i - > second ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2022-09-20 22:01:52 +02:00
bool wallet2 : : is_need_to_split_outputs ( )
{
2022-09-28 03:45:10 +02:00
return ! is_in_hardfork_zone ( ZANO_HARDFORK_04_ZARCANUM ) ;
2022-09-20 22:01:52 +02:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : prepare_tx_destinations ( const assets_selection_context & needed_money_map ,
detail : : split_strategy_id_t destination_split_strategy_id ,
const tx_dust_policy & dust_policy ,
const std : : vector < currency : : tx_destination_entry > & dsts ,
2022-10-01 21:17:17 +02:00
std : : vector < currency : : tx_destination_entry > & final_destinations )
2022-09-20 22:01:52 +02:00
{
for ( auto & el : needed_money_map )
{
2022-10-01 21:17:17 +02:00
prepare_tx_destinations ( el . second . needed_amount , el . second . found_amount , destination_split_strategy_id , dust_policy , dsts , final_destinations , el . first ) ;
}
2022-12-03 21:12:31 +01:00
if ( is_in_hardfork_zone ( ZANO_HARDFORK_04_ZARCANUM ) )
2022-10-01 21:17:17 +02:00
{
2022-12-03 21:12:31 +01:00
// special case for asset minting destinations
for ( auto & dst : dsts )
2023-04-18 16:55:00 +02:00
if ( dst . asset_id = = currency : : null_pkey )
2022-12-03 21:12:31 +01:00
final_destinations . emplace_back ( dst . amount , dst . addr , dst . asset_id ) ;
2023-08-06 03:36:40 +02:00
if ( final_destinations . empty ( ) )
2022-12-03 21:12:31 +01:00
{
2023-08-06 03:36:40 +02:00
// if there's no destinations -- make CURRENCY_TX_MIN_ALLOWED_OUTS empty destinations
for ( size_t i = 0 ; i < CURRENCY_TX_MIN_ALLOWED_OUTS ; + + i )
final_destinations . emplace_back ( 0 , m_account . get_public_address ( ) ) ;
}
else if ( final_destinations . size ( ) < CURRENCY_TX_MIN_ALLOWED_OUTS )
{
// if there's not ehough destinations items (i.e. outputs), split the last one
2022-12-03 21:12:31 +01:00
tx_destination_entry de = final_destinations . back ( ) ;
final_destinations . pop_back ( ) ;
size_t items_to_be_added = CURRENCY_TX_MIN_ALLOWED_OUTS - final_destinations . size ( ) ;
// TODO: consider allowing to set them somewhere
size_t num_digits_to_keep = CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP ;
decompose_amount_randomly ( de . amount , [ & ] ( uint64_t amount ) { de . amount = amount ; final_destinations . push_back ( de ) ; } , items_to_be_added , num_digits_to_keep ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( final_destinations . size ( ) = = CURRENCY_TX_MIN_ALLOWED_OUTS ,
" can't get necessary number of outputs using decompose_amount_randomly(), got " < < final_destinations . size ( ) < < " while mininum is " < < CURRENCY_TX_MIN_ALLOWED_OUTS ) ;
}
2023-08-14 22:32:52 +02:00
//exclude destinations that supposed to be burned (for ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN)
for ( size_t i = 0 ; i < final_destinations . size ( ) ; )
{
if ( final_destinations [ i ] . addr . size ( ) = = 0 )
{
final_destinations . erase ( final_destinations . begin ( ) + i ) ;
}
else
{
i + + ;
}
}
2022-09-20 22:01:52 +02:00
}
}
//----------------------------------------------------------------------------------------------------
2019-04-03 12:48:09 +03:00
void wallet2 : : prepare_tx_destinations ( uint64_t needed_money ,
uint64_t found_money ,
detail : : split_strategy_id_t destination_split_strategy_id ,
const tx_dust_policy & dust_policy ,
const std : : vector < currency : : tx_destination_entry > & dsts ,
2023-02-08 18:50:26 +01:00
std : : vector < currency : : tx_destination_entry > & final_destinations , const crypto : : public_key & asset_id )
2019-04-03 12:48:09 +03:00
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( found_money > = needed_money , " needed_money== " < < needed_money < < " < found_money== " < < found_money ) ;
2022-11-25 23:01:45 +01:00
if ( is_in_hardfork_zone ( ZANO_HARDFORK_04_ZARCANUM ) )
2022-10-06 22:59:00 +02:00
{
2022-11-25 23:01:45 +01:00
for ( auto & dst : dsts )
2022-10-06 22:59:00 +02:00
{
2022-12-03 21:12:31 +01:00
if ( dst . asset_id = = asset_id )
2023-08-06 03:39:20 +02:00
final_destinations . emplace_back ( dst ) ;
2022-10-06 22:59:00 +02:00
}
2022-12-03 21:12:31 +01:00
if ( found_money > needed_money )
final_destinations . emplace_back ( found_money - needed_money , m_account . get_public_address ( ) , asset_id ) ; // returning back the change
2022-10-06 22:59:00 +02:00
}
2022-11-25 23:01:45 +01:00
else
2019-04-03 12:48:09 +03:00
{
2022-11-25 23:01:45 +01:00
// pre-HF4
2023-02-08 18:50:26 +01:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( asset_id = = currency : : native_coin_asset_id , " assets are not allowed prior to HF4 " ) ;
2022-12-03 21:12:31 +01:00
currency : : tx_destination_entry change_dts = AUTO_VAL_INIT ( change_dts ) ;
if ( needed_money < found_money )
2022-11-25 23:01:45 +01:00
{
2022-12-03 21:12:31 +01:00
change_dts . addr . push_back ( m_account . get_keys ( ) . account_address ) ;
change_dts . amount = found_money - needed_money ;
2022-11-25 23:01:45 +01:00
}
uint64_t dust = 0 ;
bool r = detail : : apply_split_strategy_by_id ( destination_split_strategy_id , dsts , change_dts , dust_policy . dust_threshold , final_destinations , dust , WALLET_MAX_ALLOWED_OUTPUT_AMOUNT ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " invalid split strategy id: " < < destination_split_strategy_id ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( dust_policy . dust_threshold > = dust , " invalid dust value: dust = " < < dust < < " , dust_threshold = " < < dust_policy . dust_threshold ) ;
if ( 0 ! = dust & & ! dust_policy . add_to_fee )
{
2022-12-03 21:12:31 +01:00
final_destinations . emplace_back ( dust , dust_policy . addr_for_dust ) ;
2022-11-25 23:01:45 +01:00
}
2019-04-03 12:48:09 +03:00
}
}
//----------------------------------------------------------------------------------------------------
2023-06-12 20:37:26 +02:00
bool wallet2 : : prepare_transaction ( construct_tx_param & ctp , currency : : finalize_tx_param & ftp , const mode_separate_context & msc )
2019-04-03 12:48:09 +03:00
{
2023-04-25 00:16:13 +02:00
SET_CONTEXT_OBJ_FOR_SCOPE ( pconstruct_tx_param , ctp ) ;
SET_CONTEXT_OBJ_FOR_SCOPE ( pfinalize_tx_param , ftp ) ;
2023-04-28 20:07:35 +02:00
SET_CONTEXT_OBJ_FOR_SCOPE ( pmode_separate_context , msc ) ;
2023-04-25 00:16:13 +02:00
2019-04-03 12:48:09 +03:00
TIME_MEASURE_START_MS ( get_needed_money_time ) ;
2023-04-25 00:16:13 +02:00
2023-04-28 20:07:35 +02:00
const currency : : transaction & tx_for_mode_separate = msc . tx_for_mode_separate ;
2022-09-20 22:01:52 +02:00
assets_selection_context needed_money_map = get_needed_money ( ctp . fee , ctp . dsts ) ;
2023-08-14 22:32:52 +02:00
ftp . asset_control_key = ctp . asset_deploy_control_key ;
2023-02-08 18:50:26 +01:00
//
// TODO @#@# need to do refactoring over this part to support hidden amounts and asset_id
//
2022-09-20 22:01:52 +02:00
if ( ctp . flags & TX_FLAG_SIGNATURE_MODE_SEPARATE & & tx_for_mode_separate . vout . size ( ) )
2019-04-03 12:48:09 +03:00
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( get_tx_flags ( tx_for_mode_separate ) & TX_FLAG_SIGNATURE_MODE_SEPARATE , " tx_param.flags differs from tx.flags " ) ;
2023-04-28 20:07:35 +02:00
if ( ftp . tx_version > TRANSACTION_VERSION_PRE_HF4 )
2023-03-09 19:49:40 +01:00
{
2023-04-28 20:07:35 +02:00
for ( const auto & el : msc . proposal_info . to_alice )
2023-05-01 13:34:37 +02:00
needed_money_map [ el . asset_id ] . needed_amount + = el . amount ;
2023-03-09 19:49:40 +01:00
}
2023-04-28 20:07:35 +02:00
if ( msc . escrow )
needed_money_map [ currency : : native_coin_asset_id ] . needed_amount + = ( currency : : get_outs_money_amount ( tx_for_mode_separate ) - get_inputs_money_amount ( tx_for_mode_separate ) ) ;
2023-08-14 22:32:52 +02:00
} else if ( ctp . )
2019-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH_MS ( get_needed_money_time ) ;
2022-09-20 22:01:52 +02:00
//uint64_t found_money = 0;
2019-04-03 12:48:09 +03:00
TIME_MEASURE_START_MS ( prepare_tx_sources_time ) ;
2023-06-09 01:10:16 +02:00
if ( ctp . create_utxo_defragmentation_tx )
2021-02-02 19:02:28 +01:00
{
2023-06-15 16:43:28 +02:00
try
{
if ( ! prepare_tx_sources_for_defragmentation_tx ( ftp . sources , ftp . selected_transfers , needed_money_map [ currency : : native_coin_asset_id ] . found_amount ) )
return false ;
}
catch ( const error : : not_enough_outs_to_mix & ) { return false ; } // if there's not enough decoys, return false to indicate minor non-fatal error
2021-02-02 19:02:28 +01:00
}
else if ( ctp . htlc_tx_id ! = currency : : null_hash )
{
//htlc
2022-09-20 22:01:52 +02:00
//@#@ need to do refactoring over this part to support hidden amounts and asset_id
2023-04-26 00:24:29 +02:00
prepare_tx_sources_htlc ( ctp . htlc_tx_id , ctp . htlc_origin , ftp . sources , needed_money_map [ currency : : native_coin_asset_id ] . found_amount ) ;
2021-02-03 00:13:44 +01:00
WLT_THROW_IF_FALSE_WITH_CODE ( ctp . dsts . size ( ) = = 1 ,
" htlc: unexpected ctp.dsts.size() = " < < ctp . dsts . size ( ) , API_RETURN_CODE_INTERNAL_ERROR ) ;
2023-04-26 00:24:29 +02:00
WLT_THROW_IF_FALSE_WITH_CODE ( needed_money_map [ currency : : native_coin_asset_id ] . found_amount > ctp . fee ,
2021-02-03 00:13:44 +01:00
" htlc: found money less then fee " , API_RETURN_CODE_INTERNAL_ERROR ) ;
//fill amount
2023-04-26 00:24:29 +02:00
ctp . dsts . begin ( ) - > amount = needed_money_map [ currency : : native_coin_asset_id ] . found_amount - ctp . fee ;
2021-02-03 00:13:44 +01:00
2021-02-02 19:02:28 +01:00
}
else if ( ctp . multisig_id ! = currency : : null_hash )
{
//multisig
2022-09-20 22:01:52 +02:00
//@#@ need to do refactoring over this part to support hidden amounts and asset_id
2023-04-28 20:07:35 +02:00
prepare_tx_sources ( ctp . multisig_id , ftp . sources , needed_money_map [ currency : : native_coin_asset_id ] . found_amount ) ;
2021-02-02 19:02:28 +01:00
}
else
{
//regular tx
2022-09-20 22:01:52 +02:00
prepare_tx_sources ( needed_money_map , ctp . fake_outputs_count , ctp . dust_policy . dust_threshold , ftp . sources , ftp . selected_transfers ) ;
2021-02-02 19:02:28 +01:00
}
2019-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH_MS ( prepare_tx_sources_time ) ;
TIME_MEASURE_START_MS ( prepare_tx_destinations_time ) ;
2022-09-20 22:01:52 +02:00
prepare_tx_destinations ( needed_money_map , static_cast < detail : : split_strategy_id_t > ( ctp . split_strategy_id ) , ctp . dust_policy , ctp . dsts , ftp . prepared_destinations ) ;
2019-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH_MS ( prepare_tx_destinations_time ) ;
2022-10-12 20:03:42 +02:00
2019-04-03 12:48:09 +03:00
if ( ctp . mark_tx_as_complete & & ! ftp . sources . empty ( ) )
ftp . sources . back ( ) . separately_signed_tx_complete = true ;
ftp . unlock_time = ctp . unlock_time ;
ftp . extra = ctp . extra ; // TODO consider move semantic
ftp . attachments = ctp . attachments ; // TODO consider move semantic
ftp . crypt_address = ctp . crypt_address ;
ftp . tx_outs_attr = ctp . tx_outs_attr ;
ftp . shuffle = ctp . shuffle ;
ftp . flags = ctp . flags ;
ftp . multisig_id = ctp . multisig_id ;
2020-04-22 22:02:37 +03:00
ftp . spend_pub_key = m_account . get_public_address ( ) . spend_public_key ;
2019-04-03 12:48:09 +03:00
/* TODO
WLT_LOG_GREEN ( " [prepare_transaction]: get_needed_money_time: " < < get_needed_money_time < < " ms "
< < " , prepare_tx_sources_time: " < < prepare_tx_sources_time < < " ms "
< < " , prepare_tx_destinations_time: " < < prepare_tx_destinations_time < < " ms "
< < " , construct_tx_time: " < < construct_tx_time < < " ms "
< < " , sign_ms_input_time: " < < sign_ms_input_time < < " ms " ,
LOG_LEVEL_0 ) ; */
2023-06-12 20:37:26 +02:00
return true ;
2019-04-03 12:48:09 +03:00
}
//----------------------------------------------------------------------------------------------------
2021-02-04 01:49:38 +01:00
void wallet2 : : finalize_transaction ( const currency : : finalize_tx_param & ftp , currency : : transaction & tx , crypto : : secret_key & tx_key , bool broadcast_tx , bool store_tx_secret_key /* = true */ )
{
currency : : finalized_tx result = AUTO_VAL_INIT ( result ) ;
2021-03-10 03:23:54 +03:00
result . tx = tx ;
result . one_time_key = tx_key ;
2021-02-04 01:49:38 +01:00
finalize_transaction ( ftp , result , broadcast_tx , store_tx_secret_key ) ;
tx = result . tx ;
tx_key = result . one_time_key ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : finalize_transaction ( const currency : : finalize_tx_param & ftp , currency : : finalized_tx & result , bool broadcast_tx , bool store_tx_secret_key /* = true */ )
2019-04-03 12:48:09 +03:00
{
2019-10-31 10:52:59 +03:00
// NOTE: if broadcast_tx == true callback rise_on_transfer2() may be called at the end of this function.
// That callback may call balance(), so it's important to have all used/spending transfers
// to be correctly marked with corresponding flags PRIOR to calling finalize_transaction()
2019-12-13 18:44:00 +03:00
// broadcasting tx without secret key storing is forbidden to avoid lost key issues
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( ! broadcast_tx | | store_tx_secret_key , " finalize_tx is requested to broadcast a tx without storing the key " ) ;
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_START_MS(construct_tx_time);
2019-04-03 12:48:09 +03:00
bool r = currency : : construct_tx ( m_account . get_keys ( ) ,
2021-02-04 01:49:38 +01:00
ftp , result ) ;
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_FINISH_MS(construct_tx_time);
2019-04-03 12:48:09 +03:00
THROW_IF_FALSE_WALLET_EX ( r , error : : tx_not_constructed , ftp . sources , ftp . prepared_destinations , ftp . unlock_time ) ;
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_START_MS(sign_ms_input_time);
2019-04-03 12:48:09 +03:00
if ( ftp . multisig_id ! = currency : : null_hash )
{
// In case there's multisig input is used -- sign it partially with this wallet's keys (we don't have any others here).
// NOTE: this tx will not be ready to send until all other necessary signs for ms input would made.
auto it = m_multisig_transfers . find ( ftp . multisig_id ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( it ! = m_multisig_transfers . end ( ) , " can't find multisig_id: " < < ftp . multisig_id ) ;
const currency : : transaction & ms_source_tx = it - > second . m_ptx_wallet_info - > m_tx ;
bool is_tx_input_fully_signed = false ;
2021-02-04 01:49:38 +01:00
r = sign_multisig_input_in_tx ( result . tx , 0 , m_account . get_keys ( ) , ms_source_tx , & is_tx_input_fully_signed ) ; // it's assumed that ms input is the first one (index 0)
2019-04-03 12:48:09 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( r & & ! is_tx_input_fully_signed , " sign_multisig_input_in_tx failed: r = " < < r < < " , is_tx_input_fully_signed = " < < is_tx_input_fully_signed ) ;
}
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_FINISH_MS(sign_ms_input_time);
2019-04-03 12:48:09 +03:00
2021-02-04 01:49:38 +01:00
THROW_IF_FALSE_WALLET_EX ( get_object_blobsize ( result . tx ) < CURRENCY_MAX_TRANSACTION_BLOB_SIZE , error : : tx_too_big , result . tx , m_upper_transaction_size_limit ) ;
2019-04-03 12:48:09 +03:00
2019-12-13 18:44:00 +03:00
if ( store_tx_secret_key )
2021-02-04 01:49:38 +01:00
m_tx_keys . insert ( std : : make_pair ( get_transaction_hash ( result . tx ) , result . one_time_key ) ) ;
2019-12-13 18:44:00 +03:00
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_START(send_transaction_to_network_time);
2019-04-03 12:48:09 +03:00
if ( broadcast_tx )
2021-02-04 01:49:38 +01:00
send_transaction_to_network ( result . tx ) ;
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_FINISH(send_transaction_to_network_time);
2019-04-03 12:48:09 +03:00
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_START(add_sent_tx_detailed_info_time);
2019-04-03 12:48:09 +03:00
if ( broadcast_tx )
2021-02-04 01:49:38 +01:00
add_sent_tx_detailed_info ( result . tx , ftp . prepared_destinations , ftp . selected_transfers ) ;
2019-09-24 18:30:25 +02:00
//TIME_MEASURE_FINISH(add_sent_tx_detailed_info_time);
2019-04-03 12:48:09 +03:00
/* TODO
WLT_LOG_GREEN ( " [prepare_transaction]: get_needed_money_time: " < < get_needed_money_time < < " ms "
< < " , prepare_tx_sources_time: " < < prepare_tx_sources_time < < " ms "
< < " , prepare_tx_destinations_time: " < < prepare_tx_destinations_time < < " ms "
< < " , construct_tx_time: " < < construct_tx_time < < " ms "
< < " , sign_ms_input_time: " < < sign_ms_input_time < < " ms " ,
LOG_LEVEL_0 ) ; */
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : transfer ( const std : : vector < currency : : tx_destination_entry > & dsts , size_t fake_outputs_count ,
uint64_t unlock_time , uint64_t fee , const std : : vector < currency : : extra_v > & extra , const std : : vector < currency : : attachment_v > & attachments , detail : : split_strategy_id_t destination_split_strategy_id , const tx_dust_policy & dust_policy )
{
currency : : transaction tx ;
transfer ( dsts , fake_outputs_count , unlock_time , fee , extra , attachments , destination_split_strategy_id , dust_policy , tx ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : transfer ( const std : : vector < currency : : tx_destination_entry > & dsts ,
size_t fake_outputs_count ,
uint64_t unlock_time ,
uint64_t fee ,
const std : : vector < currency : : extra_v > & extra ,
const std : : vector < currency : : attachment_v > & attachments ,
detail : : split_strategy_id_t destination_split_strategy_id ,
const tx_dust_policy & dust_policy ,
currency : : transaction & tx ,
uint8_t tx_outs_attr ,
bool shuffle ,
uint8_t flags ,
2019-04-08 14:16:11 +03:00
bool send_to_network ,
2020-07-20 13:05:25 +03:00
std : : string * p_unsigned_filename_or_tx_blob_str )
2019-04-03 12:48:09 +03:00
{
2019-12-03 02:25:02 +01:00
//TIME_MEASURE_START(precalculation_time);
2019-04-12 09:48:33 +03:00
construct_tx_param ctp = AUTO_VAL_INIT ( ctp ) ;
ctp . attachments = attachments ;
ctp . crypt_address = currency : : get_crypt_address_from_destinations ( m_account . get_keys ( ) , dsts ) ;
ctp . dsts = dsts ;
ctp . dust_policy = dust_policy ;
ctp . extra = extra ;
ctp . fake_outputs_count = fake_outputs_count ;
ctp . fee = fee ;
ctp . flags = flags ;
// ctp.mark_tx_as_complete
// ctp.multisig_id
ctp . shuffle = shuffle ;
ctp . split_strategy_id = destination_split_strategy_id ;
ctp . tx_outs_attr = tx_outs_attr ;
ctp . unlock_time = unlock_time ;
2019-12-03 02:25:02 +01:00
//TIME_MEASURE_FINISH(precalculation_time);
2020-07-20 13:05:25 +03:00
transfer ( ctp , tx , send_to_network , p_unsigned_filename_or_tx_blob_str ) ;
2019-11-28 05:28:36 +01:00
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
construct_tx_param wallet2 : : get_default_construct_tx_param_inital ( )
{
construct_tx_param ctp = AUTO_VAL_INIT ( ctp ) ;
2019-04-03 12:48:09 +03:00
2019-11-28 05:28:36 +01:00
ctp . fee = m_core_runtime_config . tx_default_fee ;
ctp . dust_policy = tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) ;
2022-09-20 22:01:52 +02:00
ctp . split_strategy_id = get_current_split_strategy ( ) ;
2019-11-28 05:28:36 +01:00
ctp . tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED ;
ctp . shuffle = 0 ;
return ctp ;
}
2022-10-13 00:07:40 +02:00
construct_tx_param wallet2 : : get_default_construct_tx_param ( )
2019-11-28 05:28:36 +01:00
{
2022-10-13 00:07:40 +02:00
return get_default_construct_tx_param_inital ( ) ;
2019-11-28 05:28:36 +01:00
}
//----------------------------------------------------------------------------------------------------
2021-02-04 01:49:38 +01:00
bool wallet2 : : store_unsigned_tx_to_file_and_reserve_transfers ( const currency : : finalize_tx_param & ftp , const std : : string & filename , std : : string * p_unsigned_tx_blob_str /* = nullptr */ )
2019-12-14 18:29:05 +03:00
{
TIME_MEASURE_START ( store_unsigned_tx_time ) ;
blobdata bl = t_serializable_object_to_blob ( ftp ) ;
2020-04-22 23:30:03 +03:00
crypto : : chacha_crypt ( bl , m_account . get_keys ( ) . view_secret_key ) ;
2019-12-14 18:29:05 +03:00
2019-12-17 12:22:36 +03:00
if ( ! filename . empty ( ) )
{
bool r = epee : : file_io_utils : : save_string_to_file ( filename , bl ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to store unsigned tx to " < < filename ) ;
LOG_PRINT_L0 ( " Transaction stored to " < < filename < < " . You need to sign this tx using a full-access wallet. " ) ;
}
else
{
CHECK_AND_ASSERT_MES ( p_unsigned_tx_blob_str ! = nullptr , false , " empty filename and p_unsigned_tx_blob_str == null " ) ;
* p_unsigned_tx_blob_str = bl ;
}
2019-12-14 18:29:05 +03:00
TIME_MEASURE_FINISH ( store_unsigned_tx_time ) ;
// reserve transfers at the very end
TIME_MEASURE_START ( mark_transfers_as_spent_time ) ;
mark_transfers_with_flag ( ftp . selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION , std : : string ( " cold sig reservation for money transfer " ) , true ) ;
TIME_MEASURE_FINISH ( mark_transfers_as_spent_time ) ;
WLT_LOG_GREEN ( " [wallet::store_unsigned_tx_to_file_and_reserve_transfers] "
< < " store_unsigned_tx_time: " < < print_fixed_decimal_point ( store_unsigned_tx_time , 3 )
< < " , mark_transfers_as_spent_time: " < < print_fixed_decimal_point ( mark_transfers_as_spent_time , 3 )
, LOG_LEVEL_1 ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
2020-01-23 20:24:22 +03:00
void wallet2 : : check_and_throw_if_self_directed_tx_with_payment_id_requested ( const construct_tx_param & ctp )
{
// If someone sends coins to his own address, all tx outputs will be detected as own outputs.
// It's totally okay unless payment id is used, because it would be impossible to distinguish
// between change outs and transfer outs. Thus, such tx with a payment id can't be correctly
// obtained via RPC by the given payment id. It could be a problem for an exchange or other
// service when a user, identifyied by payment id sends coins to another user on the same
// exchange/service. Coins will be received but RPCs like get_payments won't give the transfer.
// To avoid such issues we prohibit such txs with a soft rule on sender side.
for ( auto & d : ctp . dsts )
{
for ( auto & addr : d . addr )
{
if ( addr ! = m_account . get_public_address ( ) )
return ; // at least one destination address is not our address -- it's not self-directed tx
}
}
// it's self-directed tx
payment_id_t pid ;
bool has_payment_id = get_payment_id_from_tx ( ctp . attachments , pid ) & & ! pid . empty ( ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ! has_payment_id , " sending funds to yourself with payment id is not allowed " ) ;
}
//----------------------------------------------------------------------------------------------------
2021-02-03 00:13:44 +01:00
void wallet2 : : transfer ( construct_tx_param & ctp ,
2019-11-28 05:28:36 +01:00
currency : : transaction & tx ,
bool send_to_network ,
2020-07-20 13:05:25 +03:00
std : : string * p_unsigned_filename_or_tx_blob_str )
2021-02-04 01:49:38 +01:00
{
currency : : finalized_tx result = AUTO_VAL_INIT ( result ) ;
transfer ( ctp , result , send_to_network , p_unsigned_filename_or_tx_blob_str ) ;
tx = result . tx ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : transfer ( construct_tx_param & ctp ,
currency : : finalized_tx & result ,
bool send_to_network ,
std : : string * p_unsigned_filename_or_tx_blob_str )
2019-11-28 05:28:36 +01:00
{
2020-05-27 13:14:14 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ! is_auditable ( ) | | ! is_watch_only ( ) , " You can't initiate coins transfer using an auditable watch-only wallet. " ) ; // btw, watch-only wallets can call transfer() within cold-signing process
2020-01-23 20:24:22 +03:00
check_and_throw_if_self_directed_tx_with_payment_id_requested ( ctp ) ;
2023-06-13 17:06:56 +02:00
bool asset_operation_requested = count_type_in_variant_container < asset_descriptor_operation > ( ctp . extra ) ! = 0 ;
bool dont_have_zero_asset_ids_in_destinations = std : : count_if ( ctp . dsts . begin ( ) , ctp . dsts . end ( ) , [ ] ( const tx_destination_entry & de ) { return de . asset_id = = null_pkey ; } ) = = 0 ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( asset_operation_requested | | dont_have_zero_asset_ids_in_destinations , " zero asset id is used errounesly (no asset operation was requested) " ) ;
2021-03-26 00:43:50 +03:00
if ( ctp . crypt_address . spend_public_key = = currency : : null_pkey )
{
ctp . crypt_address = currency : : get_crypt_address_from_destinations ( m_account . get_keys ( ) , ctp . dsts ) ;
}
2019-04-03 12:48:09 +03:00
TIME_MEASURE_START ( prepare_transaction_time ) ;
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2023-08-14 22:32:52 +02:00
ftp . p_construct_tx_param = & ctp ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2023-06-12 21:41:08 +02:00
if ( ! prepare_transaction ( ctp , ftp ) )
2023-06-12 20:37:26 +02:00
{
result . was_not_prepared = true ;
return ;
}
2019-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH ( prepare_transaction_time ) ;
if ( m_watch_only )
{
2020-07-21 14:34:54 +03:00
bool r = store_unsigned_tx_to_file_and_reserve_transfers ( ftp , ( p_unsigned_filename_or_tx_blob_str ! = nullptr ? * p_unsigned_filename_or_tx_blob_str : " zano_tx_unsigned " ) , p_unsigned_filename_or_tx_blob_str ) ;
2019-12-14 18:29:05 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( r , " failed to store unsigned tx " ) ;
WLT_LOG_GREEN ( " [wallet::transfer] " < < " prepare_transaction_time: " < < print_fixed_decimal_point ( prepare_transaction_time , 3 ) , LOG_LEVEL_0 ) ;
2019-04-03 12:48:09 +03:00
return ;
}
TIME_MEASURE_START ( mark_transfers_as_spent_time ) ;
2021-02-04 01:49:38 +01:00
mark_transfers_as_spent ( ftp . selected_transfers , std : : string ( " money transfer, tx: " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( result . tx ) ) ) ;
2019-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH ( mark_transfers_as_spent_time ) ;
2019-05-06 20:45:27 +02:00
TIME_MEASURE_START ( finalize_transaction_time ) ;
try
{
2021-02-04 01:49:38 +01:00
finalize_transaction ( ftp , result , send_to_network ) ;
2019-05-06 20:45:27 +02:00
}
catch ( . . . )
{
2021-02-04 01:49:38 +01:00
clear_transfers_from_flag ( ftp . selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_SPENT , std : : string ( " exception on money transfer, tx: " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( result . tx ) ) ) ;
2019-05-06 20:45:27 +02:00
throw ;
}
TIME_MEASURE_FINISH ( finalize_transaction_time ) ;
2019-04-30 19:34:44 +02:00
WLT_LOG_GREEN ( " [wallet::transfer] "
2019-11-28 23:34:24 +01:00
//<< " precalculation_time: " << print_fixed_decimal_point(precalculation_time, 3)
2019-04-30 19:34:44 +02:00
< < " , prepare_transaction_time: " < < print_fixed_decimal_point ( prepare_transaction_time , 3 )
< < " , finalize_transaction_time: " < < print_fixed_decimal_point ( finalize_transaction_time , 3 )
2019-04-03 12:48:09 +03:00
< < " , mark_transfers_as_spent_time: " < < print_fixed_decimal_point ( mark_transfers_as_spent_time , 3 )
2019-04-30 19:34:44 +02:00
, LOG_LEVEL_0 ) ;
2019-04-03 12:48:09 +03:00
2021-02-04 01:49:38 +01:00
print_tx_sent_message ( result . tx , std : : string ( ) + " (transfer) " , ctp . fee ) ;
2019-04-03 12:48:09 +03:00
}
2019-12-14 18:31:19 +03:00
//----------------------------------------------------------------------------------------------------
void wallet2 : : sweep_below ( size_t fake_outs_count , const currency : : account_public_address & destination_addr , uint64_t threshold_amount , const currency : : payment_id_t & payment_id ,
2022-11-25 23:01:45 +01:00
uint64_t fee , size_t & outs_total , uint64_t & amount_total , size_t & outs_swept , uint64_t & amount_swept , currency : : transaction * p_result_tx /* = nullptr */ , std : : string * p_filename_or_unsigned_tx_blob_str /* = nullptr */ )
2019-12-14 18:31:19 +03:00
{
2019-12-16 14:51:36 +03:00
static const size_t estimated_bytes_per_input = 78 ;
const size_t estimated_max_inputs = static_cast < size_t > ( CURRENCY_MAX_TRANSACTION_BLOB_SIZE / ( estimated_bytes_per_input * ( fake_outs_count + 1.5 ) ) ) ; // estimated number of maximum tx inputs under the tx size limit
const size_t tx_sources_for_querying_random_outs_max = estimated_max_inputs * 2 ;
2019-12-14 18:31:19 +03:00
bool r = false ;
outs_total = 0 ;
amount_total = 0 ;
outs_swept = 0 ;
2022-11-25 23:01:45 +01:00
amount_swept = 0 ;
2019-12-14 18:31:19 +03:00
2020-06-14 02:17:10 +02:00
std : : vector < uint64_t > selected_transfers ;
2019-12-14 18:31:19 +03:00
selected_transfers . reserve ( m_transfers . size ( ) ) ;
2020-06-14 02:17:10 +02:00
for ( uint64_t i = 0 ; i < m_transfers . size ( ) ; + + i )
2019-12-14 18:31:19 +03:00
{
const transfer_details & td = m_transfers [ i ] ;
uint64_t amount = td . amount ( ) ;
if ( amount < threshold_amount & &
is_transfer_ready_to_go ( td , fake_outs_count ) )
{
selected_transfers . push_back ( i ) ;
outs_total + = 1 ;
amount_total + = amount ;
}
}
2019-12-16 14:51:36 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ! selected_transfers . empty ( ) , " No spendable outputs meet the criterion " ) ;
2019-12-14 18:31:19 +03:00
// sort by amount descending in order to spend bigger outputs first
2020-06-14 02:17:10 +02:00
std : : sort ( selected_transfers . begin ( ) , selected_transfers . end ( ) , [ this ] ( uint64_t a , uint64_t b ) { return m_transfers [ b ] . amount ( ) < m_transfers [ a ] . amount ( ) ; } ) ;
2019-12-14 18:31:19 +03:00
2019-12-16 14:51:36 +03:00
// limit RPC request with reasonable number of sources
if ( selected_transfers . size ( ) > tx_sources_for_querying_random_outs_max )
selected_transfers . erase ( selected_transfers . begin ( ) + tx_sources_for_querying_random_outs_max , selected_transfers . end ( ) ) ;
2019-12-14 18:31:19 +03:00
2020-06-14 02:17:10 +02:00
prefetch_global_indicies_if_needed ( selected_transfers ) ;
2020-06-13 12:13:41 +03:00
2019-12-14 18:31:19 +03:00
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : out_entry out_entry ;
typedef currency : : tx_source_entry : : output_entry tx_output_entry ;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : response rpc_get_random_outs_resp = AUTO_VAL_INIT ( rpc_get_random_outs_resp ) ;
if ( fake_outs_count > 0 )
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : request req = AUTO_VAL_INIT ( req ) ;
2022-11-25 23:01:45 +01:00
req . height_upper_limit = m_last_pow_block_h ;
2019-12-14 18:31:19 +03:00
req . use_forced_mix_outs = false ;
2022-10-12 18:07:04 +02:00
req . decoys_count = fake_outs_count + 1 ;
2020-06-14 02:17:10 +02:00
for ( uint64_t i : selected_transfers )
2022-11-25 23:01:45 +01:00
req . amounts . push_back ( m_transfers [ i ] . is_zc ( ) ? 0 : m_transfers [ i ] . m_amount ) ;
2019-12-14 18:31:19 +03:00
r = m_core_proxy - > call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS ( req , rpc_get_random_outs_resp ) ;
THROW_IF_FALSE_WALLET_EX ( r , error : : no_connection_to_daemon , " getrandom_outs.bin " ) ;
2020-05-07 23:26:41 +02:00
THROW_IF_FALSE_WALLET_EX ( rpc_get_random_outs_resp . status ! = API_RETURN_CODE_BUSY , error : : daemon_busy , " getrandom_outs.bin " ) ;
THROW_IF_FALSE_WALLET_EX ( rpc_get_random_outs_resp . status = = API_RETURN_CODE_OK , error : : get_random_outs_error , rpc_get_random_outs_resp . status ) ;
2019-12-14 18:31:19 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( rpc_get_random_outs_resp . outs . size ( ) = = selected_transfers . size ( ) ,
" daemon returned wrong number of amounts for getrandom_outs.bin: " < < rpc_get_random_outs_resp . outs . size ( ) < < " , requested: " < < selected_transfers . size ( ) ) ;
std : : vector < COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount > scanty_outs ;
for ( COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount & amount_outs : rpc_get_random_outs_resp . outs )
{
if ( amount_outs . outs . size ( ) < fake_outs_count )
scanty_outs . push_back ( amount_outs ) ;
}
THROW_IF_FALSE_WALLET_EX ( scanty_outs . empty ( ) , error : : not_enough_outs_to_mix , scanty_outs , fake_outs_count ) ;
}
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2022-05-10 23:49:20 +02:00
ftp . tx_version = this - > get_current_tx_version ( ) ;
2019-12-14 18:31:19 +03:00
if ( ! payment_id . empty ( ) )
set_payment_id_to_tx ( ftp . attachments , payment_id ) ;
// put encrypted payer info into the extra
ftp . crypt_address = destination_addr ;
2020-04-27 16:20:31 +03:00
currency : : create_and_add_tx_payer_to_container_from_address ( ftp . extra , m_account . get_public_address ( ) , get_top_block_height ( ) , m_core_runtime_config ) ;
2019-12-20 03:14:57 +03:00
ftp . flags = 0 ;
// ftp.multisig_id -- not required
// ftp.prepared_destinations -- will be filled by prepare_tx_destinations
2019-12-14 18:31:19 +03:00
// ftp.selected_transfers -- needed only at stage of broadcasting or storing unsigned tx
ftp . shuffle = false ;
// ftp.sources -- will be filled in try_construct_tx
2020-04-22 22:02:37 +03:00
ftp . spend_pub_key = m_account . get_public_address ( ) . spend_public_key ; // needed for offline signing
2019-12-14 18:31:19 +03:00
ftp . tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED ;
ftp . unlock_time = 0 ;
enum try_construct_result_t { rc_ok = 0 , rc_too_few_outputs = 1 , rc_too_many_outputs = 2 , rc_create_tx_failed = 3 } ;
2019-12-16 14:51:36 +03:00
auto get_result_t_str = [ ] ( try_construct_result_t t ) - > const char *
{ return t = = rc_ok ? " rc_ok " : t = = rc_too_few_outputs ? " rc_too_few_outputs " : t = = rc_too_many_outputs ? " rc_too_many_outputs " : t = = rc_create_tx_failed ? " rc_create_tx_failed " : " unknown " ; } ;
2019-12-14 18:31:19 +03:00
auto try_construct_tx = [ this , & selected_transfers , & rpc_get_random_outs_resp , & fake_outs_count , & fee , & destination_addr ]
2021-02-04 01:49:38 +01:00
( size_t st_index_upper_boundary , currency : : finalize_tx_param & ftp , uint64_t & amount_swept ) - > try_construct_result_t
2019-12-14 18:31:19 +03:00
{
// prepare inputs
amount_swept = 0 ;
ftp . sources . clear ( ) ;
ftp . sources . resize ( st_index_upper_boundary ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( st_index_upper_boundary < = selected_transfers . size ( ) , " index_upper_boundary = " < < st_index_upper_boundary < < " , selected_transfers.size() = " < < selected_transfers . size ( ) ) ;
for ( size_t st_index = 0 ; st_index < st_index_upper_boundary ; + + st_index )
{
currency : : tx_source_entry & src = ftp . sources [ st_index ] ;
2020-06-14 02:17:10 +02:00
uint64_t tr_index = selected_transfers [ st_index ] ;
2019-12-14 18:31:19 +03:00
transfer_details & td = m_transfers [ tr_index ] ;
src . transfer_index = tr_index ;
src . amount = td . amount ( ) ;
amount_swept + = src . amount ;
// populate src.outputs with mix-ins
if ( rpc_get_random_outs_resp . outs . size ( ) )
{
rpc_get_random_outs_resp . outs [ st_index ] . outs . sort ( [ ] ( const out_entry & a , const out_entry & b ) { return a . global_amount_index < b . global_amount_index ; } ) ;
for ( out_entry & daemon_oe : rpc_get_random_outs_resp . outs [ st_index ] . outs )
{
if ( td . m_global_output_index = = daemon_oe . global_amount_index )
continue ;
2023-02-08 18:50:26 +01:00
src . outputs . emplace_back ( daemon_oe . global_amount_index , daemon_oe . stealth_address , daemon_oe . concealing_point , daemon_oe . amount_commitment , daemon_oe . blinded_asset_id ) ;
2019-12-14 18:31:19 +03:00
if ( src . outputs . size ( ) > = fake_outs_count )
break ;
}
}
2019-04-03 12:48:09 +03:00
2019-12-14 18:31:19 +03:00
// insert real output into src.outputs
2022-11-25 23:01:45 +01:00
// TODO: bad design, we need to get rid of code duplicates below -- sowle
2019-12-14 18:31:19 +03:00
auto it_to_insert = std : : find_if ( src . outputs . begin ( ) , src . outputs . end ( ) , [ & ] ( const tx_output_entry & a )
{
2022-07-13 17:17:04 +02:00
if ( a . out_reference . type ( ) . hash_code ( ) = = typeid ( uint64_t ) . hash_code ( ) )
return static_cast < bool > ( boost : : get < uint64_t > ( a . out_reference ) > = td . m_global_output_index ) ;
2019-12-14 18:31:19 +03:00
return false ; // TODO: implement deterministics real output placement in case there're ref_by_id outs
} ) ;
2022-07-13 17:17:04 +02:00
tx_output_entry real_oe = AUTO_VAL_INIT ( real_oe ) ;
2022-11-25 23:01:45 +01:00
txout_ref_v out_reference = td . m_global_output_index ; // TODO: use ref_by_id when neccessary
std : : vector < tx_output_entry > : : iterator interted_it = src . outputs . end ( ) ;
VARIANT_SWITCH_BEGIN ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] ) ;
VARIANT_CASE_CONST ( tx_out_bare , o )
{
VARIANT_SWITCH_BEGIN ( o . target ) ;
VARIANT_CASE_CONST ( txout_to_key , o )
interted_it = src . outputs . emplace ( it_to_insert , out_reference , o . key ) ;
VARIANT_CASE_CONST ( txout_htlc , htlc )
interted_it = src . outputs . emplace ( it_to_insert , out_reference , htlc . pkey_refund ) ;
VARIANT_CASE_OTHER ( )
{
WLT_THROW_IF_FALSE_WITH_CODE ( false ,
" Internal error: unexpected type of target: " < < o . target . type ( ) . name ( ) ,
API_RETURN_CODE_INTERNAL_ERROR ) ;
}
VARIANT_SWITCH_END ( ) ;
}
VARIANT_CASE_CONST ( tx_out_zarcanum , o ) ;
2023-02-08 18:50:26 +01:00
interted_it = src . outputs . emplace ( it_to_insert , out_reference , o . stealth_address , o . concealing_point , o . amount_commitment , o . blinded_asset_id ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( td . is_zc ( ) , " transfer # " < < tr_index < < " , amount: " < < print_money_brief ( td . amount ( ) ) < < " is not a ZC " ) ;
src . real_out_amount_blinding_mask = td . m_zc_info_ptr - > amount_blinding_mask ;
src . real_out_asset_id_blinding_mask = td . m_zc_info_ptr - > asset_id_blinding_mask ;
src . asset_id = td . m_zc_info_ptr - > asset_id ;
2022-11-25 23:01:45 +01:00
VARIANT_SWITCH_END ( ) ;
2019-12-14 18:31:19 +03:00
src . real_out_tx_key = get_tx_pub_key_from_extra ( td . m_ptx_wallet_info - > m_tx ) ;
2022-11-25 23:01:45 +01:00
src . real_output = interted_it - src . outputs . begin ( ) ;
2019-12-14 18:31:19 +03:00
src . real_output_in_tx_index = td . m_internal_output_index ;
}
if ( amount_swept < = fee )
return rc_too_few_outputs ;
// try to construct a transaction
2022-12-19 23:09:17 +01:00
assets_selection_context needed_money_map ;
2023-02-08 18:50:26 +01:00
needed_money_map [ currency : : native_coin_asset_id ] = { } ;
2019-12-14 18:31:19 +03:00
std : : vector < currency : : tx_destination_entry > dsts ( { tx_destination_entry ( amount_swept - fee , destination_addr ) } ) ;
2022-12-19 23:09:17 +01:00
prepare_tx_destinations ( needed_money_map , get_current_split_strategy ( ) , tools : : tx_dust_policy ( ) , dsts , ftp . prepared_destinations ) ;
2019-12-14 18:31:19 +03:00
currency : : transaction tx = AUTO_VAL_INIT ( tx ) ;
crypto : : secret_key tx_key = AUTO_VAL_INIT ( tx_key ) ;
try
{
finalize_transaction ( ftp , tx , tx_key , false , false ) ;
}
2020-01-24 11:42:11 +03:00
catch ( error : : tx_too_big & )
2019-12-14 18:31:19 +03:00
{
return rc_too_many_outputs ;
}
catch ( . . . )
{
return rc_create_tx_failed ;
}
return rc_ok ;
} ;
2019-12-16 14:51:36 +03:00
size_t st_index_upper_boundary = std : : min ( selected_transfers . size ( ) , estimated_max_inputs ) ;
2019-12-14 18:31:19 +03:00
try_construct_result_t res = try_construct_tx ( st_index_upper_boundary , ftp , amount_swept ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( res ! = rc_too_few_outputs , st_index_upper_boundary < < " biggest unspent outputs have total amount of " < < print_money_brief ( amount_swept )
< < " which is less than required fee: " < < print_money_brief ( fee ) < < " , transaction cannot be constructed " ) ;
if ( res = = rc_too_many_outputs )
{
2019-12-20 03:14:57 +03:00
WLT_LOG_L1 ( " sweep_below: first try of try_construct_tx( " < < st_index_upper_boundary < < " ) returned " < < get_result_t_str ( res ) ) ;
2019-12-14 18:31:19 +03:00
size_t low_bound = 0 ;
size_t high_bound = st_index_upper_boundary ;
2021-02-04 01:49:38 +01:00
currency : : finalize_tx_param ftp_ok = ftp ;
2019-12-14 18:31:19 +03:00
for ( ; ; )
{
if ( low_bound + 1 > = high_bound )
{
st_index_upper_boundary = low_bound ;
res = rc_ok ;
ftp = ftp_ok ;
break ;
}
st_index_upper_boundary = ( low_bound + high_bound ) / 2 ;
try_construct_result_t res = try_construct_tx ( st_index_upper_boundary , ftp , amount_swept ) ;
2019-12-16 14:51:36 +03:00
WLT_LOG_L1 ( " sweep_below: try_construct_tx( " < < st_index_upper_boundary < < " ) returned " < < get_result_t_str ( res ) ) ;
2019-12-14 18:31:19 +03:00
if ( res = = rc_ok )
{
low_bound = st_index_upper_boundary ;
ftp_ok = ftp ;
}
else if ( res = = rc_too_many_outputs )
{
high_bound = st_index_upper_boundary ;
}
else
break ;
}
}
if ( res ! = rc_ok )
{
uint64_t amount_min = UINT64_MAX , amount_max = 0 , amount_sum = 0 ;
for ( auto & i : selected_transfers )
{
uint64_t amount = m_transfers [ i ] . amount ( ) ;
amount_min = std : : min ( amount_min , amount ) ;
amount_max = std : : max ( amount_max , amount ) ;
amount_sum + = amount ;
}
2019-12-20 03:14:57 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( false , " try_construct_tx failed with result: " < < get_result_t_str ( res ) < < " ( " < < res < < " ) " < <
2019-12-14 18:31:19 +03:00
" , selected_transfers stats: \n " < <
" outs: " < < selected_transfers . size ( ) < < ENDL < <
" amount min: " < < print_money ( amount_min ) < < ENDL < <
" amount max: " < < print_money ( amount_max ) < < ENDL < <
" amount avg: " < < ( selected_transfers . empty ( ) ? std : : string ( " n/a " ) : print_money ( amount_sum / selected_transfers . size ( ) ) ) ) ;
}
// populate ftp.selected_transfers from ftp.sources
ftp . selected_transfers . clear ( ) ;
for ( size_t i = 0 ; i < ftp . sources . size ( ) ; + + i )
ftp . selected_transfers . push_back ( ftp . sources [ i ] . transfer_index ) ;
outs_swept = ftp . sources . size ( ) ;
if ( m_watch_only )
{
2019-12-17 12:23:38 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( p_filename_or_unsigned_tx_blob_str ! = nullptr , " p_filename_or_unsigned_tx_blob_str is null " ) ;
bool r = store_unsigned_tx_to_file_and_reserve_transfers ( ftp , * p_filename_or_unsigned_tx_blob_str , p_filename_or_unsigned_tx_blob_str ) ;
2019-12-14 18:31:19 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( r , " failed to store unsigned tx " ) ;
return ;
}
mark_transfers_as_spent ( ftp . selected_transfers , " sweep_below " ) ;
transaction local_tx ;
transaction * p_tx = p_result_tx ! = nullptr ? p_result_tx : & local_tx ;
* p_tx = AUTO_VAL_INIT_T ( transaction ) ;
try
{
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
finalize_transaction ( ftp , * p_tx , sk , true ) ;
}
catch ( . . . )
{
clear_transfers_from_flag ( ftp . selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_SPENT , std : : string ( " exception on sweep_below, tx id (might be wrong): " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( * p_tx ) ) ) ;
throw ;
}
}
2018-12-27 18:50:45 +03:00
} // namespace tools