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
// Copyright (c) 2014-2019 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>
# include <boost/filesystem/fstream.hpp>
# include <iostream>
# include <boost/utility/value_init.hpp>
# include "include_base_utils.h"
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"
using namespace currency ;
2019-10-09 16:01:33 +03:00
# define MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES (100*1024*1024) // 100 MB
2018-12-27 18:50:45 +03:00
# undef LOG_DEFAULT_CHANNEL
# define LOG_DEFAULT_CHANNEL "wallet"
ENABLE_CHANNEL_BY_DEFAULT ( " wallet " )
namespace tools
{
2019-07-24 19:14:35 +02:00
//---------------------------------------------------------------
2019-08-03 00:22:04 +02:00
uint64_t wallet2 : : get_max_unlock_time_from_receive_indices ( const currency : : transaction & tx , const money_transfer2_details & 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 ( ) ) ;
2019-08-03 00:22:04 +02:00
for ( auto ri : td . receive_indices )
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 ( ) ) ;
if ( tx . vout [ ri ] . 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 ] ;
}
}
return max_unlock_time ;
}
2018-12-27 18:50:45 +03:00
//----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : fill_transfer_details ( const currency : : transaction & tx , const tools : : money_transfer2_details & td , tools : : wallet_public : : wallet_transfer_info_details & res_td ) const
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::fill_transfer_details " ) ;
for ( auto si : td . spent_indices )
{
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( si < tx . vin . size ( ) , void ( ) , " Internal error: wrong tx transfer details: spend index= " < < si < < " is greater than transaction inputs vector " < < tx . vin . size ( ) ) ;
2018-12-27 18:50:45 +03:00
if ( tx . vin [ si ] . type ( ) = = typeid ( currency : : txin_to_key ) )
res_td . spn . push_back ( boost : : get < currency : : txin_to_key > ( tx . vin [ si ] ) . amount ) ;
}
for ( auto ri : td . receive_indices )
{
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( ri < tx . vout . size ( ) , void ( ) , " Internal error: wrong tx transfer details: reciev index= " < < ri < < " is greater than transaction outputs vector " < < tx . vout . size ( ) ) ;
2018-12-27 18:50:45 +03:00
if ( tx . vout [ ri ] . target . type ( ) = = typeid ( currency : : txout_to_key ) )
2019-07-24 19:14:35 +02:00
{
2018-12-27 18:50:45 +03:00
res_td . rcv . push_back ( tx . vout [ ri ] . amount ) ;
2019-07-24 19:14:35 +02:00
}
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 ) ;
}
//----------------------------------------------------------------------------------------------------
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 ;
}
//----------------------------------------------------------------------------------------------------
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 ( ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : process_new_transaction ( const currency : : transaction & tx , uint64_t height , const currency : : block & b )
{
std : : vector < std : : string > recipients , recipients_aliases ;
process_unconfirmed ( tx , recipients , recipients_aliases ) ;
std : : vector < size_t > outs ;
uint64_t tx_money_got_in_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 for transaction spends
uint64_t tx_money_spent_in_ins = 0 ;
// check all outputs for spending (compare key images)
money_transfer2_details mtd ;
size_t i = 0 ;
bool is_pos_coinbase = false ;
bool coin_base_tx = is_coinbase ( tx , is_pos_coinbase ) ;
//PoW block don't have change, so all outs supposed to be marked as "mined"
bool is_derived_from_coinbase = ! is_pos_coinbase ;
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 ( ) )
{
tx_money_spent_in_ins + = boost : : get < currency : : txin_to_key > ( in ) . amount ;
transfer_details & td = m_transfers [ it - > second ] ;
uint32_t flags_before = td . m_flags ;
td . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ;
td . m_spent_height = height ;
if ( coin_base_tx & & td . m_flags & WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER )
is_derived_from_coinbase = true ;
else
is_derived_from_coinbase = false ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Spent key out, transfer # " < < it - > second < < " , amount: " < < print_money ( m_transfers [ it - > second ] . amount ( ) ) < < " , with tx: " < < get_transaction_hash ( tx ) < < " , at height " < < height < <
2018-12-27 18:50:45 +03:00
" ; flags: " < < flags_before < < " -> " < < td . m_flags ) ;
mtd . spent_indices . push_back ( i ) ;
remove_transfer_from_expiration_list ( it - > second ) ;
}
}
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 ( ) )
{
it - > second . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ;
it - > second . m_spent_height = height ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Spent multisig out: " < < multisig_id < < " , amount: " < < print_money ( currency : : get_amount_from_variant ( in ) ) < < " , with tx: " < < get_transaction_hash ( tx ) < < " , at height " < < height ) ;
2018-12-27 18:50:45 +03:00
mtd . spent_indices . push_back ( i ) ;
}
}
i + + ;
}
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
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 ) ;
r = lookup_acc_outs ( m_account . get_keys ( ) , tx , tx_pub_key , outs , tx_money_got_in_outs , derivation ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : acc_outs_lookup_error , tx , tx_pub_key , m_account . get_keys ( ) ) ;
if ( ! outs . empty ( ) /*&& tx_money_got_in_outs*/ )
{
//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 ;
//good news - got money! take care about it
//usually we have only one transfer for user in transaction
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 ) ;
req . txid = get_transaction_hash ( tx ) ;
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 = = CORE_RPC_STATUS_BUSY , error : : daemon_busy , " get_o_indexes.bin " ) ;
THROW_IF_TRUE_WALLET_EX ( res . status ! = CORE_RPC_STATUS_OK , error : : get_out_indices_error , res . status ) ;
THROW_IF_TRUE_WALLET_EX ( res . o_indexes . size ( ) ! = tx . vout . size ( ) , error : : wallet_internal_error ,
" transactions outputs size= " + std : : to_string ( tx . vout . size ( ) ) +
" not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size= " + std : : to_string ( res . o_indexes . size ( ) ) ) ;
for ( size_t i_in_outs = 0 ; i_in_outs ! = outs . size ( ) ; i_in_outs + + )
{
size_t o = outs [ i_in_outs ] ;
2019-04-08 14:16:11 +03:00
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( o < tx . vout . size ( ) , " wrong out in transaction: internal index= " < < o < < " , total_outs= " < < tx . vout . size ( ) ) ;
2018-12-27 18:50:45 +03:00
if ( tx . vout [ o ] . target . type ( ) = = typeid ( txout_to_key ) )
{
2019-04-08 14:16:11 +03:00
const currency : : txout_to_key & otk = boost : : get < currency : : txout_to_key > ( tx . vout [ o ] . target ) ;
2018-12-27 18:50:45 +03:00
2019-04-08 14:16:11 +03:00
// obtain key image for this output
crypto : : key_image ki = currency : : null_ki ;
if ( m_watch_only )
{
// 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 ( otk . key ) ;
if ( it ! = m_pending_key_images . end ( ) )
{
ki = it - > second ;
WLT_LOG_L1 ( " pending key image " < < ki < < " was found by out pub key " < < otk . key ) ;
}
else
{
ki = currency : : null_ki ;
WLT_LOG_L1 ( " can't find pending key image by out pub key: " < < otk . key < < " , key image temporarily set to null " ) ;
}
}
else
2018-12-27 18:50:45 +03:00
{
2019-04-08 14:16:11 +03:00
// normal wallet, calculate and store key images for own outs
currency : : keypair 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 = = otk . key , " key_image generated ephemeral public key that does not match with output_key " ) ;
}
if ( ki ! = currency : : null_ki )
{
auto it = m_key_images . find ( ki ) ;
if ( it ! = m_key_images . end ( ) )
{
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 ] ;
WLT_LOG_YELLOW ( " tx " < < get_transaction_hash ( tx ) < < " @ block " < < height < < " has output # " < < o < < " with key image " < < ki < < " that has already been seen in output # " < <
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. " , LOG_LEVEL_0 ) ;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( tx_money_got_in_outs > = tx . vout [ o ] . amount , " tx_money_got_in_outs: " < < tx_money_got_in_outs < < " , tx.vout[o].amount: " < < tx . vout [ o ] . amount ) ;
tx_money_got_in_outs - = tx . vout [ o ] . amount ;
continue ; // skip the output
}
2018-12-27 18:50:45 +03:00
}
mtd . receive_indices . push_back ( o ) ;
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 ;
td . m_global_output_index = res . o_indexes [ o ] ;
if ( coin_base_tx )
{
//last out in coinbase tx supposed to be change from coinstake
if ( ! ( i_in_outs = = outs . size ( ) - 1 & & ! is_derived_from_coinbase ) )
{
td . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER ;
}
}
size_t transfer_index = m_transfers . size ( ) - 1 ;
2019-04-08 14:16:11 +03:00
if ( td . m_key_image ! = currency : : null_ki )
m_key_images [ td . m_key_image ] = transfer_index ;
2018-12-27 18:50:45 +03:00
add_transfer_to_transfers_cache ( tx . vout [ o ] . amount , transfer_index ) ;
2019-07-24 19:14:35 +02:00
if ( max_out_unlock_time < get_tx_unlock_time ( tx , o ) )
max_out_unlock_time = get_tx_unlock_time ( tx , o ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Received money, transfer # " < < transfer_index < < " , amount: " < < print_money ( td . amount ( ) ) < < " , with tx: " < < get_transaction_hash ( tx ) < < " , at height " < < height ) ;
2018-12-27 18:50:45 +03:00
}
else if ( tx . vout [ o ] . target . type ( ) = = typeid ( txout_multisig ) )
{
crypto : : hash multisig_id = currency : : get_multisig_out_id ( tx , o ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES_NO_RET ( m_multisig_transfers . count ( multisig_id ) = = 0 , " multisig_id = " < < multisig_id < < " already in multisig container " ) ;
2018-12-27 18:50:45 +03:00
transfer_details_base & tdb = m_multisig_transfers [ multisig_id ] ;
tdb . m_ptx_wallet_info = pwallet_info ;
tdb . m_internal_output_index = o ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Received multisig, multisig out id: " < < multisig_id < < " , amount: " < < tdb . amount ( ) < < " , with tx: " < < get_transaction_hash ( tx ) ) ;
2018-12-27 18:50:45 +03:00
}
}
}
std : : string payment_id ;
if ( tx_money_got_in_outs & & get_payment_id_from_tx ( tx . attachment , payment_id ) )
{
uint64_t received = ( tx_money_spent_in_ins < tx_money_got_in_outs ) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0 ;
if ( 0 < received & & payment_id . size ( ) )
{
payment_details payment ;
payment . m_tx_hash = currency : : get_transaction_hash ( tx ) ;
payment . m_amount = received ;
payment . m_block_height = height ;
2019-07-24 19:14:35 +02:00
payment . m_unlock_time = max_out_unlock_time ;
2018-12-27 18:50:45 +03:00
m_payments . emplace ( payment_id , payment ) ;
2019-02-21 21:33:52 +03:00
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 ) ) ;
2018-12-27 18:50:45 +03:00
}
}
if ( tx_money_spent_in_ins )
{ //this actually is transfer transaction, notify about spend
if ( tx_money_spent_in_ins > tx_money_got_in_outs )
{ //usual transfer
handle_money_spent2 ( b , tx , tx_money_spent_in_ins - ( tx_money_got_in_outs + get_tx_fee ( tx ) ) , mtd , recipients , recipients_aliases ) ;
}
else
{ //strange transfer, seems that in one transaction have transfers from different wallets.
if ( ! is_coinbase ( tx ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_RED ( " Unusual transaction " < < currency : : get_transaction_hash ( tx ) < < " , tx_money_spent_in_ins: " < < tx_money_spent_in_ins < < " , tx_money_got_in_outs: " < < tx_money_got_in_outs , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
handle_money_received2 ( b , tx , ( tx_money_got_in_outs - ( tx_money_spent_in_ins - get_tx_fee ( tx ) ) ) , mtd ) ;
}
}
else
{
if ( tx_money_got_in_outs )
handle_money_received2 ( b , tx , tx_money_got_in_outs , mtd ) ;
else if ( currency : : is_derivation_used_to_encrypt ( tx , derivation ) )
{
//transaction doesn't transfer actually money, bud bring some information
handle_money_received2 ( b , tx , 0 , mtd ) ;
}
else if ( mtd . spent_indices . size ( ) )
{
// multisig spend detected
handle_money_spent2 ( b , tx , 0 , mtd , recipients , recipients_aliases ) ;
}
}
}
//----------------------------------------------------------------------------------------------------
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 " ) ;
tx_payer tp = AUTO_VAL_INIT ( tp ) ;
wti . show_sender = get_type_in_variant_container ( decrypted_att , tp ) ;
if ( wti . is_income )
{
if ( wti . show_sender )
wti . remote_addresses . push_back ( currency : : get_account_address_as_str ( tp . acc_addr ) ) ;
}
else
{
//TODO: actually recipients could be more then one, handle it in future
tx_receiver tr = AUTO_VAL_INIT ( tr ) ;
if ( ! wti . remote_addresses . size ( ) & & get_type_in_variant_container ( decrypted_att , tr ) )
wti . remote_addresses . push_back ( currency : : get_account_address_as_str ( tr . acc_addr ) ) ;
}
currency : : tx_comment cm ;
if ( currency : : get_type_in_variant_container ( decrypted_att , cm ) )
wti . comment = cm . comment ;
currency : : get_payment_id_from_tx ( decrypted_att , wti . payment_id ) ;
}
//----------------------------------------------------------------------------------------------------
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 " ) ;
WLT_CHECK_AND_ASSERT_MES ( res . status = = CORE_RPC_STATUS_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 ;
2019-04-03 12:48:09 +03:00
currency : : transaction tx = contr_it - > second . proposal . tx_template ;
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 ;
2019-04-03 12:48:09 +03:00
construct_param . split_strategy_id = detail : : ssi_digit ;
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 ( ) ) ;
2019-04-03 12:48:09 +03: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 ) ;
//---------------------------------
//figure out fee that was left for release contract
2019-04-03 12:48:09 +03:00
THROW_IF_FALSE_WALLET_INT_ERR_EX ( 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 ) ;
2019-04-03 12:48:09 +03:00
uint64_t left_for_fee_in_multisig = 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
2019-04-03 12:48:09 +03:00
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
prepare_transaction ( construct_param , ftp , tx ) ;
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
}
//---------------------------
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 ;
2019-04-03 12:48:09 +03:00
construct_param . split_strategy_id = detail : : ssi_digit ;
2018-12-27 18:50:45 +03:00
2019-04-03 12:48:09 +03:00
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
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 ;
}
//-----------------------------------------------------------------------------------------------------
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 ;
ed . is_a = cpd . a_addr . m_spend_public_key = = m_account . get_keys ( ) . m_account_address . m_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 ) ;
std : : vector < size_t > outs ;
uint64_t tx_money_got_in_outs = 0 ;
bool r = lookup_acc_outs ( m_account . get_keys ( ) , prop . tx_template , outs , tx_money_got_in_outs , derivation ) ;
THROW_IF_FALSE_WALLET_INT_ERR_EX ( r , " Failed to lookup_acc_outs for tx: " < < get_transaction_hash ( prop . tx_template ) ) ;
add_transfers_to_expiration_list ( found_transfers , ed . expiration_time , tx_money_got_in_outs , 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 ) ;
2018-12-27 18:50:45 +03:00
wti . amount = it - > second . private_detailes . amount_to_pay + it - > second . private_detailes . amount_a_pledge + it - > second . private_detailes . amount_b_pledge ;
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 )
{
wti . amount + = wti . fee ;
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 ;
}
//-----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : prepare_wti ( wallet_public : : wallet_transfer_info & wti , uint64_t height , uint64_t timestamp , const currency : : transaction & tx , uint64_t amount , const money_transfer2_details & td )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::prepare_wti " ) ;
wti . tx = tx ;
wti . amount = amount ;
wti . height = height ;
fill_transfer_details ( tx , td , wti . td ) ;
2019-08-03 00:22:04 +02:00
wti . unlock_time = get_max_unlock_time_from_receive_indices ( tx , td ) ;
2018-12-27 18:50:45 +03:00
wti . timestamp = timestamp ;
wti . fee = currency : : is_coinbase ( tx ) ? 0 : currency : : get_tx_fee ( tx ) ;
wti . tx_blob_size = static_cast < uint32_t > ( currency : : get_object_blobsize ( wti . tx ) ) ;
wti . tx_hash = currency : : get_transaction_hash ( tx ) ;
wti . is_service = currency : : is_service_tx ( tx ) ;
wti . is_mixing = currency : : is_mixin_tx ( tx ) ;
wti . is_mining = currency : : is_coinbase ( tx ) ;
wti . tx_type = get_tx_type ( tx ) ;
bc_services : : extract_market_instructions ( wti . srv_attachments , tx . attachment ) ;
// escrow transactions, which are built with TX_FLAG_SIGNATURE_MODE_SEPARATE flag actually encrypt attachments
// with buyer as a sender, and seller as receiver, despite the fact that for both sides transaction seen as outgoing
// 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
bool decrypt_attachment_as_income = wti . is_income ;
std : : vector < currency : : payload_items_v > decrypted_att ;
if ( wti . tx_type = = GUI_TX_TYPE_ESCROW_TRANSFER & & std : : find ( td . spent_indices . begin ( ) , td . spent_indices . end ( ) , 0 ) = = td . spent_indices . end ( ) )
decrypt_attachment_as_income = true ;
decrypt_payload_items ( decrypt_attachment_as_income , tx , m_account . get_keys ( ) , decrypted_att ) ;
prepare_wti_decrypted_attachments ( wti , decrypted_att ) ;
process_contract_info ( wti , decrypted_att ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : handle_money_received2 ( const currency : : block & b , const currency : : transaction & tx , uint64_t amount , const money_transfer2_details & td )
{
//decrypt attachments
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 = true ;
prepare_wti ( wti , get_block_height ( b ) , get_actual_timestamp ( b ) , tx , amount , td ) ;
rise_on_transfer2 ( wti ) ;
}
//----------------------------------------------------------------------------------------------------
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 ;
uint64_t fake = 0 ;
uint64_t unlocked_balance = 0 ;
uint64_t mined = 0 ;
uint64_t balance = this - > balance ( unlocked_balance , fake , fake , mined ) ;
m_wcallback - > on_transfer2 ( wti , balance , unlocked_balance , mined ) ;
}
//----------------------------------------------------------------------------------------------------
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 ,
const std : : vector < std : : string > & recipients_aliases )
{
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 ;
wti . recipients_aliases = recipients_aliases ;
prepare_wti ( wti , get_block_height ( b ) , get_actual_timestamp ( b ) , in_tx , amount , td ) ;
rise_on_transfer2 ( wti ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : process_unconfirmed ( const currency : : transaction & tx , std : : vector < std : : string > & recipients , std : : vector < std : : string > & recipients_aliases )
{
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 ;
recipients_aliases = wti . recipients_aliases ;
m_unconfirmed_txs . erase ( unconf_it ) ;
}
}
//----------------------------------------------------------------------------------------------------
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
THROW_IF_TRUE_WALLET_EX ( height ! = m_blockchain . size ( ) , error : : wallet_internal_error ,
" current_index= " + std : : to_string ( height ) + " , m_blockchain.size()= " + std : : to_string ( m_blockchain . size ( ) ) ) ;
//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
if ( b . timestamp + 60 * 60 * 24 > m_account . get_createtime ( ) )
{
TIME_MEASURE_START ( miner_tx_handle_time ) ;
process_new_transaction ( b . miner_tx , height , b ) ;
TIME_MEASURE_FINISH ( miner_tx_handle_time ) ;
TIME_MEASURE_START ( txs_handle_time ) ;
for ( const auto & tx_entry : bche . txs_ptr )
{
process_new_transaction ( tx_entry - > tx , height , b ) ;
}
TIME_MEASURE_FINISH ( txs_handle_time ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " 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
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Skipped block by timestamp, height: " < < height < < " , block time " < < b . timestamp < < " , account time " < < m_account . get_createtime ( ) ) ;
2018-12-27 18:50:45 +03:00
}
m_blockchain . push_back ( bl_id ) ;
+ + m_local_bc_height ;
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
m_wcallback - > on_new_block ( height , b ) ;
}
//----------------------------------------------------------------------------------------------------
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 = m_blockchain . size ( ) ;
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 ] ) ;
}
//----------------------------------------------------------------------------------------------------
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 ) ;
get_short_chain_history ( req . block_ids ) ;
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 " ) ;
2019-11-12 11:33:39 +03:00
2018-12-27 18:50:45 +03:00
if ( res . status = = CORE_RPC_STATUS_GENESIS_MISMATCH )
{
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 " ) ;
THROW_IF_TRUE_WALLET_EX ( gbd_res . status ! = CORE_RPC_STATUS_OK , error : : get_blocks_error , gbd_res . status ) ;
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 ( ) ;
m_blockchain . push_back ( 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 ) ;
2018-12-27 18:50:45 +03:00
get_short_chain_history ( req . block_ids ) ;
//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 " ) ;
}
if ( res . status = = CORE_RPC_STATUS_BUSY )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L1 ( " Core is busy, pull cancelled " ) ;
2018-12-27 18:50:45 +03:00
stop = true ;
return ;
}
THROW_IF_TRUE_WALLET_EX ( res . status ! = CORE_RPC_STATUS_OK , error : : get_blocks_error , res . status ) ;
THROW_IF_TRUE_WALLET_EX ( m_blockchain . size ( ) & & m_blockchain . size ( ) < = res . start_height , error : : wallet_internal_error ,
" wrong daemon response: m_start_height= " + std : : to_string ( res . start_height ) +
" not less than local blockchain size= " + std : : to_string ( m_blockchain . size ( ) ) ) ;
handle_pulled_blocks ( blocks_added , stop , res ) ;
}
//----------------------------------------------------------------------------------------------------
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 ;
if ( res . start_height = = 0 & & m_blockchain . size ( ) = = 1 & & ! res . blocks . empty ( ) )
{
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 ;
}
for ( const auto & bl_entry : res . blocks )
{
if ( stop )
break ;
const currency : : block & bl = bl_entry . block_ptr - > bl ;
//TODO: get_block_hash is slow
crypto : : hash bl_id = get_block_hash ( bl ) ;
if ( current_index > = m_blockchain . size ( ) )
{
process_new_blockchain_entry ( bl , bl_entry , bl_id , current_index ) ;
+ + blocks_added ;
}
else if ( bl_id ! = m_blockchain [ current_index ] )
{
//split detected here !!!
THROW_IF_TRUE_WALLET_EX ( current_index = = res . start_height , error : : wallet_internal_error ,
" wrong daemon response: split starts from the first block in response " + string_tools : : pod_to_hex ( bl_id ) +
" (height " + std : : to_string ( res . start_height ) + " ), local block id at this height: " +
string_tools : : pod_to_hex ( m_blockchain [ current_index ] ) ) ;
detach_blockchain ( current_index ) ;
process_new_blockchain_entry ( bl , bl_entry , bl_id , current_index ) ;
+ + blocks_added ;
}
else
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Block " < < bl_id < < " @ " < < current_index < < " is already in wallet's blockchain " ) ;
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 ;
}
}
}
2019-02-21 21:33:52 +03:00
WLT_LOG_L1 ( " [PULL BLOCKS] " < < res . start_height < < " --> " < < m_blockchain . size ( ) ) ;
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 ;
refresh ( blocks_fetched , received_money , m_stop ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : refresh ( std : : atomic < bool > & stop )
{
bool f ;
size_t n ;
refresh ( n , f , stop ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : transfer ( uint64_t amount , const currency : : account_public_address & acc )
{
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 ;
transaction result_tx = AUTO_VAL_INIT ( result_tx ) ;
2019-04-03 12:48:09 +03:00
this - > transfer ( dst , 0 , 0 , TX_DEFAULT_FEE , extra , attachments , tools : : detail : : ssi_digit , tools : : tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , result_tx ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
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 " ) ;
THROW_IF_TRUE_WALLET_EX ( res . status = = CORE_RPC_STATUS_BUSY , error : : daemon_busy , " getinfo " ) ;
THROW_IF_TRUE_WALLET_EX ( res . status ! = CORE_RPC_STATUS_OK , error : : get_blocks_error , res . status ) ;
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
if ( tei . m_alias . m_address . m_spend_public_key = = m_account . get_keys ( ) . m_account_address . m_spend_public_key & &
tei . m_alias . m_address . m_view_public_key = = m_account . get_keys ( ) . m_account_address . m_view_public_key )
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 ) ;
if ( res . status ! = CORE_RPC_STATUS_OK )
return false ;
if ( local_adr = = res . alias_details . address )
return true ;
}
return false ;
}
//----------------------------------------------------------------------------------------------------
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 ) ;
if ( res . status = = CORE_RPC_STATUS_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 " ) ;
2018-12-27 18:50:45 +03:00
THROW_IF_TRUE_WALLET_EX ( res . status ! = CORE_RPC_STATUS_OK , error : : get_blocks_error , res . status ) ;
//- @#@ ----- 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 ) ) ;
std : : unordered_map < crypto : : hash , std : : pair < currency : : transaction , money_transfer2_details > > unconfirmed_multisig_transfers_from_tx_pool ;
has_related_alias_in_unconfirmed = false ;
uint64_t tx_expiration_ts_median = get_tx_expiration_median ( ) ;
for ( const auto & tx_blob : res . txs )
{
currency : : transaction tx ;
money_transfer2_details td ;
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 ) ;
crypto : : hash tx_hash = currency : : get_transaction_hash ( tx ) ;
auto it = unconfirmed_in_transfers_local . find ( tx_hash ) ;
if ( it ! = unconfirmed_in_transfers_local . end ( ) )
{
m_unconfirmed_in_transfers . insert ( * it ) ;
continue ;
}
// read extra
std : : vector < size_t > outs ;
uint64_t tx_money_got_in_outs = 0 ;
crypto : : public_key tx_pub_key = null_pkey ;
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 , tx_money_got_in_outs , derivation ) ;
THROW_IF_TRUE_WALLET_EX ( ! r , error : : acc_outs_lookup_error , tx , tx_pub_key , m_account . get_keys ( ) ) ;
//collect incomes
td . receive_indices = outs ;
bool new_multisig_spend_detected = false ;
//check if we have spendings
uint64_t tx_money_spent_in_ins = 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 ) )
{
auto it = m_key_images . find ( boost : : get < currency : : txin_to_key > ( in ) . k_image ) ;
if ( it ! = m_key_images . end ( ) )
{
tx_money_spent_in_ins + = boost : : get < currency : : txin_to_key > ( in ) . amount ;
td . spent_indices . push_back ( i ) ;
spend_transfers . push_back ( it - > second ) ;
}
}
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 ( ) )
{
td . spent_indices . push_back ( i ) ;
r = unconfirmed_multisig_transfers_from_tx_pool . insert ( std : : make_pair ( multisig_id , std : : make_pair ( tx , td ) ) ) . second ;
if ( ! r )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_RED ( " Warning: Receiving the same multisig out id from tx pool more then once: " < < multisig_id , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
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
2019-02-21 21:33:52 +03:00
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 ) ;
2018-12-27 18:50:45 +03:00
new_multisig_spend_detected = true ;
}
}
}
}
if ( ( ( ! tx_money_spent_in_ins & & tx_money_got_in_outs ) | | ( currency : : is_derivation_used_to_encrypt ( tx , derivation ) & & ! tx_money_spent_in_ins ) ) & & ! is_tx_expired ( tx , tx_expiration_ts_median ) )
{
m_unconfirmed_in_transfers [ tx_hash ] = tx ;
if ( m_unconfirmed_txs . count ( tx_hash ) )
continue ;
//prepare notification about pending transaction
2019-08-27 17:36:53 +02:00
wallet_public : : wallet_transfer_info & unconfirmed_wti = misc_utils : : get_or_insert_value_initialized ( m_unconfirmed_txs , tx_hash ) ;
2018-12-27 18:50:45 +03:00
unconfirmed_wti . is_income = true ;
prepare_wti ( unconfirmed_wti , 0 , m_core_runtime_config . get_core_time ( ) , tx , tx_money_got_in_outs , td ) ;
rise_on_transfer2 ( unconfirmed_wti ) ;
}
else if ( tx_money_spent_in_ins | | new_multisig_spend_detected )
{
m_unconfirmed_in_transfers [ tx_hash ] = tx ;
if ( m_unconfirmed_txs . count ( tx_hash ) )
continue ;
//outgoing tx that was sent somehow not from this application instance
uint64_t amount = 0 ;
/*if (!new_multisig_spend_detected && tx_money_spent_in_ins < tx_money_got_in_outs+get_tx_fee(tx))
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Transaction that get more then send: tx_money_spent_in_ins= " < < tx_money_spent_in_ins
2018-12-27 18:50:45 +03:00
< < " , tx_money_got_in_outs= " < < tx_money_got_in_outs < < " , tx_id= " < < tx_hash ) ;
}
else */ if ( new_multisig_spend_detected )
{
amount = 0 ;
}
else
{
amount = tx_money_spent_in_ins - ( tx_money_got_in_outs + get_tx_fee ( tx ) ) ;
}
//prepare notification about pending transaction
2019-08-27 17:36:53 +02:00
wallet_public : : wallet_transfer_info & unconfirmed_wti = misc_utils : : get_or_insert_value_initialized ( m_unconfirmed_txs , tx_hash ) ;
2018-12-27 18:50:45 +03:00
unconfirmed_wti . is_income = false ;
prepare_wti ( unconfirmed_wti , 0 , m_core_runtime_config . get_core_time ( ) , tx , amount , td ) ;
//mark transfers as spend to get correct balance
for ( auto tr_index : spend_transfers )
{
if ( tr_index > m_transfers . size ( ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " INTERNAL ERROR: tr_index " < < tr_index < < " more then m_transfers.size()= " < < m_transfers . size ( ) ) ;
2018-12-27 18:50:45 +03:00
continue ;
}
uint32_t flags_before = m_transfers [ tr_index ] . m_flags ;
m_transfers [ tr_index ] . m_flags | = WALLET_TRANSFER_DETAIL_FLAG_SPENT ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L1 ( " wallet transfer # " < < tr_index < < " is marked as spent, flags: " < < flags_before < < " -> " < < m_transfers [ tr_index ] . m_flags < < " , reason: UNCONFIRMED tx: " < < tx_hash ) ;
2018-12-27 18:50:45 +03:00
unconfirmed_wti . selected_indicies . push_back ( tr_index ) ;
}
rise_on_transfer2 ( unconfirmed_wti ) ;
}
}
// 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 + + )
{
if ( it - > second . is_income )
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 ) ;
}
}
}
size_t sz = m_transfers . size ( ) ;
for ( size_t i = 0 ; i ! = sz ; i + + )
{
auto & t = m_transfers [ i ] ;
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 ) )
{
//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 )
{
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 ;
m_height_of_start_sync = m_blockchain . size ( ) ;
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 < < " ) " ) ;
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 ( ) ) ;
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 ( ) ;
}
2019-02-21 21:33:52 +03:00
WLT_LOG_L1 ( " Refresh done, blocks received: " < < blocks_fetched < < " , balance: " < < print_money ( balance ( ) ) < < " , unlocked: " < < print_money ( unlocked_balance ( ) ) ) ;
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 ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : detach_blockchain ( uint64_t height )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Detaching blockchain on height " < < 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
{
auto it = std : : find_if ( m_transfers . begin ( ) , m_transfers . end ( ) , [ & ] ( const transfer_details & td ) { return td . m_ptx_wallet_info - > m_block_height > = height ; } ) ;
if ( it ! = m_transfers . end ( ) )
{
size_t i_start = it - m_transfers . begin ( ) ;
for ( size_t i = i_start ; i ! = m_transfers . size ( ) ; i + + )
{
auto it_ki = m_key_images . find ( m_transfers [ i ] . m_key_image ) ;
2019-04-26 19:08:38 +03:00
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 > = height , " transfer # " < < i < < " block height is less than " < < height ) ;
2018-12-27 18:50:45 +03:00
m_key_images . erase ( it_ki ) ;
+ + transfers_detached ;
}
m_transfers . erase ( it , m_transfers . end ( ) ) ;
}
}
size_t blocks_detached = m_blockchain . end ( ) - ( m_blockchain . begin ( ) + height ) ;
m_blockchain . erase ( m_blockchain . begin ( ) + height , m_blockchain . end ( ) ) ;
m_local_bc_height - = blocks_detached ;
//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 ] ;
if ( tr . m_spent_height > = height )
{
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 ;
}
}
//rollback tranfers history
auto tr_hist_it = m_transfer_history . rend ( ) ;
for ( auto it = m_transfer_history . rbegin ( ) ; it ! = m_transfer_history . rend ( ) ; it + + )
{
if ( it - > height < height )
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 ) )
{
if ( ! it - > is_mining )
WLT_LOG_ERROR ( " is_mining flag is not consistent for tx " < < it - > tx_hash ) ;
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 ( ) ; )
{
if ( height < = it - > second . m_block_height )
it = m_payments . erase ( it ) ;
else
+ + it ;
}
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Detached blockchain on height " < < height < < " , transfers detached " < < transfers_detached < < " , blocks detached " < < blocks_detached ) ;
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 ( ) ;
currency : : block b ;
currency : : generate_genesis_block ( b ) ;
m_blockchain . push_back ( get_block_hash ( b ) ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : reset_all ( )
{
m_blockchain . clear ( ) ;
m_transfers . clear ( ) ;
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);
m_local_bc_height = 1 ;
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 ;
2018-12-27 18:50:45 +03:00
return true ;
}
//----------------------------------------------------------------------------------------------------
2019-04-08 14:16:11 +03:00
bool wallet2 : : store_keys ( std : : string & buff , const std : : string & password , 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
wallet2 : : keys_file_data keys_file_data = boost : : value_initialized < wallet2 : : keys_file_data > ( ) ;
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 ;
bool r = store_keys ( buff , m_password ) ;
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-04-12 09:48:33 +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 " ) ;
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 ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void wallet2 : : load_keys ( const std : : string & buff , const std : : string & password )
{
wallet2 : : keys_file_data keys_file_data ;
// std::string buf;
// bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
// CHECK_AND_THROW_WALLET_EX(!r, error::file_read_error, keys_file_name);
bool r = : : serialization : : parse_binary ( buff , keys_file_data ) ;
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 ;
account_data . resize ( keys_file_data . account_data . size ( ) ) ;
crypto : : chacha8 ( keys_file_data . account_data . data ( ) , keys_file_data . account_data . size ( ) , key , keys_file_data . iv , & account_data [ 0 ] ) ;
const currency : : account_keys & keys = m_account . get_keys ( ) ;
r = epee : : serialization : : load_t_from_binary ( m_account , account_data ) ;
r = r & & verify_keys ( keys . m_view_secret_key , keys . m_account_address . m_view_public_key ) ;
2019-04-03 12:48:09 +03:00
if ( keys . m_spend_secret_key = = currency : : null_skey )
m_watch_only = true ;
else
r = r & & verify_keys ( keys . m_spend_secret_key , keys . m_account_address . m_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 ( ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : generate ( const std : : wstring & path , const std : : string & pass )
{
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
check_for_free_space_and_throw_if_it_lacks ( m_wallet_file ) ;
2018-12-27 18:50:45 +03:00
m_password = pass ;
m_account . generate ( ) ;
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 ) ) ;
2019-04-08 14:16:11 +03:00
if ( m_watch_only )
{
bool stub ;
load_keys2ki ( true , stub ) ;
}
2018-12-27 18:50:45 +03:00
store ( ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : restore ( const std : : wstring & path , const std : : string & pass , const std : : string & restore_key )
{
clear ( ) ;
2019-04-03 12:48:09 +03:00
prepare_file_names ( path ) ;
2018-12-27 18:50:45 +03:00
m_password = pass ;
bool r = m_account . restore_keys_from_braindata ( restore_key ) ;
2019-02-27 00:59:02 +03:00
init_log_prefix ( ) ;
2018-12-27 18:50:45 +03:00
THROW_IF_TRUE_WALLET_EX ( ! r , error : : wallet_internal_error , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
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
check_for_free_space_and_throw_if_it_lacks ( m_wallet_file ) ;
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 ) ;
THROW_IF_TRUE_WALLET_EX ( data_file . fail ( ) , error : : file_not_found , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
wallet_file_binary_header wbh = AUTO_VAL_INIT ( wbh ) ;
data_file . read ( ( char * ) & wbh , sizeof ( wbh ) ) ;
THROW_IF_TRUE_WALLET_EX ( data_file . fail ( ) , error : : file_not_found , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
THROW_IF_TRUE_WALLET_EX ( wbh . m_signature ! = WALLET_FILE_SIGNATURE , error : : file_not_found , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
THROW_IF_TRUE_WALLET_EX ( wbh . m_cb_body > WALLET_FILE_MAX_BODY_SIZE | |
wbh . m_cb_keys > WALLET_FILE_MAX_KEYS_SIZE , error : : file_not_found , epee : : string_encoding : : convert_to_ansii ( m_wallet_file ) ) ;
keys_buff . resize ( wbh . m_cb_keys ) ;
data_file . read ( ( char * ) keys_buff . data ( ) , wbh . m_cb_keys ) ;
load_keys ( keys_buff , password ) ;
2019-04-03 12:48:09 +03:00
bool need_to_resync = ! tools : : portable_unserialize_obj_from_stream ( * this , data_file ) ;
2018-12-27 18:50:45 +03:00
2019-04-03 12:48:09 +03:00
if ( m_watch_only )
load_keys2ki ( true , need_to_resync ) ;
if ( need_to_resync )
2018-12-27 18:50:45 +03:00
{
reset_history ( ) ;
}
m_local_bc_height = m_blockchain . size ( ) ;
2019-04-03 12:48:09 +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 ) ) ;
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 ( ) ) ;
2019-04-12 09:48:33 +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 ( ) < < " ) " ) ;
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-24 08:12:28 +03:00
// check_for_free_space_and_throw_if_it_lacks(path_to_save); temporary disabled, wallet saving implemented in two-stage scheme to avoid data loss due to lack of space
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 ;
2019-04-08 14:16:11 +03:00
bool r = store_keys ( keys_buff , password ) ;
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 ) ;
2018-12-27 18:50:45 +03:00
wbh . m_signature = WALLET_FILE_SIGNATURE ;
wbh . m_cb_keys = keys_buff . size ( ) ;
//@#@ change it to proper
wbh . m_cb_body = 1000 ;
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
WLT_LOG_L0 ( " Storing to " < < tmp_file_path . string ( ) < < " ... " ) ;
2019-04-08 14:16:11 +03:00
2019-04-12 09:48:33 +03:00
r = tools : : portble_serialize_obj_to_stream ( * this , data_file ) ;
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 ( ) ;
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 ) )
boost : : filesystem : : rename ( path_to_save , tmp_old_file_path ) ;
boost : : filesystem : : rename ( tmp_file_path , path_to_save ) ;
boost : : filesystem : : remove ( tmp_old_file_path ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
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 " ) ;
bool stub = false ;
wo . load_keys2ki ( true , stub ) ; // to create outkey2ki file
// 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 ) ;
const currency : : txout_target_v & out_t = td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . target ;
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 ) ) ;
wo . m_pending_key_images_file_container . push_back ( tools : : out_key_to_ki ( out_key , td . m_key_image ) ) ;
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 ) ;
}
//----------------------------------------------------------------------------------------------------
2019-10-09 16:01:33 +03:00
void wallet2 : : check_for_free_space_and_throw_if_it_lacks ( const std : : wstring & wallet_filename , uint64_t exact_size_needed_if_known /* = UINT64_MAX */ )
{
namespace fs = boost : : filesystem ;
try
{
fs : : path wallet_file_path ( wallet_filename ) ;
fs : : path base_path = wallet_file_path . parent_path ( ) ;
2019-10-16 12:35:35 +03:00
if ( base_path . empty ( ) )
base_path = fs : : path ( " . " ) ;
2019-10-09 16:01:33 +03:00
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( fs : : is_directory ( base_path ) , " directory does not exist: " < < base_path . string ( ) ) ;
uint64_t min_free_size = exact_size_needed_if_known ;
if ( min_free_size = = UINT64_MAX )
{
// if exact size needed is unknown -- determine it as
// twice the original wallet file size or MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES, which one is bigger
min_free_size = MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES ;
if ( fs : : is_regular_file ( wallet_file_path ) )
2019-10-29 02:12:59 +03:00
min_free_size = std : : max ( min_free_size , 2 * static_cast < uint64_t > ( fs : : file_size ( wallet_file_path ) ) ) ;
2019-10-09 16:01:33 +03:00
}
else
{
min_free_size + = 1024 * 1024 * 10 ; // add a little for FS overhead and so
}
fs : : space_info si = fs : : space ( base_path ) ;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( si . available > min_free_size , " free space at " < < base_path . string ( ) < < " is too low: " < < si . available < < " , required minimum is: " < < min_free_size ) ;
}
catch ( tools : : error : : wallet_common_error & )
{
throw ;
}
catch ( std : : exception & e )
{
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( false , " failed to determine free space: " < < e . what ( ) ) ;
}
catch ( . . . )
{
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX ( false , " failed to determine free space: unknown exception " ) ;
}
}
//----------------------------------------------------------------------------------------------------
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 ) ;
}
//----------------------------------------------------------------------------------------------------
2019-05-06 19:38:06 +02:00
uint64_t wallet2 : : balance ( uint64_t & unlocked , uint64_t & awaiting_in , uint64_t & awaiting_out , uint64_t & mined ) const
2018-12-27 18:50:45 +03:00
{
2019-05-06 19:38:06 +02:00
unlocked = 0 ;
2018-12-27 18:50:45 +03:00
uint64_t balance_total = 0 ;
awaiting_in = 0 ;
awaiting_out = 0 ;
mined = 0 ;
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
{
balance_total + = td . amount ( ) ;
if ( is_transfer_unlocked ( td ) )
2019-05-06 19:38:06 +02:00
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 )
{
if ( utx . second . is_income )
{
balance_total + = utx . second . amount ;
awaiting_in + = utx . second . amount ;
}
else
{
//collect change in unconfirmed outgoing transactions
for ( auto r : utx . second . td . rcv )
balance_total + = r ;
awaiting_out + = utx . second . amount ;
}
}
return balance_total ;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : balance ( ) const
{
uint64_t stub = 0 ;
return balance ( stub , stub , stub , stub ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : get_transfers ( wallet2 : : transfer_container & incoming_transfers ) const
{
incoming_transfers = m_transfers ;
2019-11-27 03:19:10 +01:00
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : generate_packing_transaction_if_needed ( transaction & tx )
{
prepare_free_transfers_cache ( 0 ) ;
auto it = m_found_free_amounts . find ( CURRENCY_BLOCK_REWARD ) ;
if ( it = = m_found_free_amounts . end ( ) | | it - > second . size ( ) < WALLET_POS_MINT_PACKING_SIZE )
return false ;
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
{
static const char * header = " index amount g_index flags block tx out# key image " ;
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 ( ) ;
}
//----------------------------------------------------------------------------------------------------
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
std : : string decrypted_src_blob = crypto : : chacha_crypt ( tx_sources_blob , m_account . get_keys ( ) . m_view_secret_key ) ;
2019-04-12 09:48:33 +03:00
// deserialize args
finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
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
2019-04-12 09:48:33 +03:00
THROW_IF_FALSE_WALLET_EX ( ft . ftp . spend_pub_key = = m_account . get_keys ( ) . m_account_address . m_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 (
m_account . get_keys ( ) . m_account_address . m_view_public_key ,
ft . one_time_key ,
derivation ) ,
" internal error: sign_transfer: failed to generate key derivation( "
< < m_account . get_keys ( ) . m_account_address . m_view_public_key
< < " , 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 )
{
const auto & out = ft . tx . vout [ i ] ;
if ( out . target . type ( ) ! = typeid ( txout_to_key ) )
continue ;
const txout_to_key & otk = boost : : get < txout_to_key > ( out . target ) ;
crypto : : public_key ephemeral_pub = AUTO_VAL_INIT ( ephemeral_pub ) ;
if ( ! crypto : : derive_public_key ( derivation , i , m_account . get_keys ( ) . m_account_address . m_spend_public_key , ephemeral_pub ) )
{
WLT_LOG_ERROR ( " derive_public_key failed for tx " < < get_transaction_hash ( ft . tx ) < < " , out # " < < i ) ;
}
2019-04-03 12:48:09 +03:00
2019-04-05 00:42:28 +03:00
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 ( ) . m_spend_secret_key , ephemeral_sec ) ;
crypto : : key_image ki = AUTO_VAL_INIT ( ki ) ;
crypto : : generate_key_image ( ephemeral_pub , ephemeral_sec , ki ) ;
2019-04-30 22:41:01 +02:00
ft . outs_key_images . push_back ( make_serializable_pair ( static_cast < uint64_t > ( i ) , ki ) ) ;
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 ) ;
crypto : : chacha_crypt ( signed_tx_blob , m_account . get_keys ( ) . m_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
//----------------------------------------------------------------------------------------------------
void wallet2 : : submit_transfer ( const std : : string & signed_tx_blob , currency : : transaction & tx )
{
// decrypt sources
std : : string decrypted_src_blob = crypto : : chacha_crypt ( signed_tx_blob , m_account . get_keys ( ) . m_view_secret_key ) ;
// deserialize tx data
finalized_tx ft = AUTO_VAL_INIT ( ft ) ;
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
THROW_IF_FALSE_WALLET_CMN_ERR_EX ( ft . ftp . spend_pub_key = = m_account . get_keys ( ) . m_account_address . m_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 ( ) ) ;
auto & out = tx . vout [ p . first ] ;
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 ) ;
const crypto : : public_key & out_key = src . outputs [ src . real_output ] . second ;
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 ;
m_pending_key_images_file_container . push_back ( tools : : out_key_to_ki ( p . first , p . second ) ) ;
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 ( ) ;
}
//----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : get_recent_transfers_history ( std : : vector < wallet_public : : wallet_transfer_info > & trs , size_t offset , size_t count )
2018-12-27 18:50:45 +03:00
{
if ( offset > = m_transfer_history . size ( ) )
return ;
auto start = m_transfer_history . rbegin ( ) + offset ;
auto stop = m_transfer_history . size ( ) - offset > = count ? start + count : m_transfer_history . rend ( ) ;
if ( ! count )
stop = m_transfer_history . rend ( ) ;
trs . insert ( trs . end ( ) , start , stop ) ;
}
//----------------------------------------------------------------------------------------------------
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 ) ;
}
//----------------------------------------------------------------------------------------------------
2019-07-24 19:14:35 +02:00
bool wallet2 : : is_transfer_okay_for_pos ( const transfer_details & tr , uint64_t & stake_unlock_time )
2018-12-27 18:50:45 +03: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
2018-12-27 18:50:45 +03:00
if ( m_blockchain . size ( ) - tr . m_ptx_wallet_info - > m_block_height < = m_core_runtime_config . min_coinstake_age )
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 ;
}
//----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : get_mining_history ( wallet_public : : mining_history & hist )
2018-12-27 18:50:45 +03:00
{
for ( auto & tr : m_transfer_history )
{
if ( currency : : is_coinbase ( tr . tx ) & & tr . tx . vin . size ( ) = = 2 )
{
2019-08-27 17:36:53 +02:00
tools : : wallet_public : : mining_history_entry mhe = AUTO_VAL_INIT ( mhe ) ;
2018-12-27 18:50:45 +03:00
mhe . a = tr . amount ;
mhe . t = tr . timestamp ;
mhe . h = tr . height ;
hist . mined_entries . push_back ( mhe ) ;
}
}
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_pos_entries ( currency : : COMMAND_RPC_SCAN_POS : : request & req )
{
for ( size_t i = 0 ; i ! = m_transfers . size ( ) ; i + + )
{
auto & tr = m_transfers [ i ] ;
2019-07-24 19:14:35 +02:00
uint64_t stake_unlock_time = 0 ;
if ( ! is_transfer_okay_for_pos ( tr , stake_unlock_time ) )
2018-12-27 18:50:45 +03:00
continue ;
currency : : pos_entry pe = AUTO_VAL_INIT ( pe ) ;
pe . amount = tr . amount ( ) ;
pe . index = tr . m_global_output_index ;
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 ;
req . pos_entries . push_back ( pe ) ;
}
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : prepare_and_sign_pos_block ( currency : : block & b ,
const currency : : pos_entry & pos_info ,
const crypto : : public_key & source_tx_pub_key ,
uint64_t in_tx_output_index ,
const std : : vector < const crypto : : public_key * > & keys_ptrs )
{
//generate coinbase transaction
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 0 ] . type ( ) = = typeid ( currency : : txin_gen ) , false , " Wrong output input in transaction " ) ;
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 1 ] . type ( ) = = typeid ( currency : : txin_to_key ) , false , " Wrong output input in transaction " ) ;
2018-12-27 18:50:45 +03:00
auto & txin = boost : : get < currency : : txin_to_key > ( b . miner_tx . vin [ 1 ] ) ;
txin . k_image = pos_info . keyimage ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( b . miner_tx . signatures . size ( ) = = 1 & & b . miner_tx . signatures [ 0 ] . size ( ) = = txin . key_offsets . size ( ) ,
2018-12-27 18:50:45 +03:00
false , " Wrong signatures amount in coinbase transacton " ) ;
//derive secret key
crypto : : key_derivation pos_coin_derivation = AUTO_VAL_INIT ( pos_coin_derivation ) ;
bool r = crypto : : generate_key_derivation ( source_tx_pub_key ,
m_account . get_keys ( ) . m_view_secret_key ,
pos_coin_derivation ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " internal error: pos coin base generator: failed to generate_key_derivation( "
2018-12-27 18:50:45 +03:00
< < source_tx_pub_key
< < " , view secret key: " < < m_account . get_keys ( ) . m_view_secret_key < < " ) " ) ;
crypto : : secret_key derived_secret_ephemeral_key = AUTO_VAL_INIT ( derived_secret_ephemeral_key ) ;
crypto : : derive_secret_key ( pos_coin_derivation ,
in_tx_output_index ,
m_account . get_keys ( ) . m_spend_secret_key ,
derived_secret_ephemeral_key ) ;
// sign block actually in coinbase transaction
crypto : : hash block_hash = currency : : get_block_hash ( b ) ;
crypto : : generate_ring_signature ( block_hash ,
txin . k_image ,
keys_ptrs ,
derived_secret_ephemeral_key ,
0 ,
& b . miner_tx . signatures [ 0 ] [ 0 ] ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L4 ( " GENERATED RING SIGNATURE: block_id " < < block_hash
2018-12-27 18:50:45 +03:00
< < " txin.k_image " < < txin . k_image
< < " key_ptr: " < < * keys_ptrs [ 0 ]
< < " signature: " < < b . miner_tx . signatures [ 0 ] [ 0 ] ) ;
return true ;
}
//------------------------------------------------------------------
bool wallet2 : : build_kernel ( const pos_entry & pe , const stake_modifier_type & stake_modifier , stake_kernel & kernel , uint64_t & coindays_weight , uint64_t timestamp )
{
PROFILE_FUNC ( " build_kernel " ) ;
coindays_weight = 0 ;
kernel = stake_kernel ( ) ;
kernel . kimage = pe . keyimage ;
kernel . stake_modifier = stake_modifier ;
kernel . block_timestamp = timestamp ;
coindays_weight = get_coinday_weight ( pe . amount ) ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : fill_mining_context ( mining_context & ctx )
{
// #ifdef _DEBUG
// 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 = m_account.get_public_address_str();
// tmpl_req.pos_block = true;
// m_core_proxy->call_COMMAND_RPC_GETBLOCKTEMPLATE(tmpl_req, tmpl_rsp);
// #endif
bool r = get_pos_entries ( ctx . sp ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( r , false , " Failed to get_pos_entries() " ) ;
2018-12-27 18:50:45 +03:00
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 ) ;
ctx . rsp . status = CORE_RPC_STATUS_NOT_FOUND ;
m_core_proxy - > call_COMMAND_RPC_GET_POS_MINING_DETAILS ( pos_details_req , pos_details_resp ) ;
if ( pos_details_resp . status ! = CORE_RPC_STATUS_OK )
return false ;
ctx . basic_diff . assign ( pos_details_resp . pos_basic_difficulty ) ;
ctx . sm = pos_details_resp . sm ;
ctx . rsp . last_block_hash = pos_details_resp . last_block_hash ;
ctx . rsp . status = CORE_RPC_STATUS_OK ;
ctx . rsp . is_pos_allowed = pos_details_resp . pos_mining_allowed ;
ctx . rsp . starter_timestamp = pos_details_resp . starter_timestamp ;
return true ;
}
//------------------------------------------------------------------
bool wallet2 : : try_mint_pos ( )
{
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 ) ;
if ( ! ctx . rsp . is_pos_allowed )
{
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
uint64_t pos_entries_amount = 0 ;
for ( auto & ent : ctx . sp . pos_entries )
pos_entries_amount + = ent . amount ;
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 ) ;
if ( ctx . rsp . status = = CORE_RPC_STATUS_OK )
{
build_minted_block ( ctx . sp , ctx . rsp ) ;
}
2019-06-27 02:30:43 +03:00
WLT_LOG_L0 ( " PoS mining iteration finished, status: " < < ctx . rsp . status < < " , used " < < ctx . sp . pos_entries . size ( ) < < " entries with total amount: " < < print_money_brief ( pos_entries_amount ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//-------------------------------
bool wallet2 : : reset_history ( )
{
std : : string pass = m_password ;
std : : wstring file_path = m_wallet_file ;
account_base acc_tmp = m_account ;
crypto : : hash genesis_hash = m_blockchain [ 0 ] ;
clear ( ) ;
m_blockchain [ 0 ] = genesis_hash ;
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 ;
}
//-------------------------------
bool wallet2 : : build_minted_block ( const currency : : COMMAND_RPC_SCAN_POS : : request & req ,
const currency : : COMMAND_RPC_SCAN_POS : : response & rsp ,
uint64_t new_block_expected_height /* = UINT64_MAX */ )
{
return build_minted_block ( req , rsp , m_account . get_public_address ( ) , new_block_expected_height ) ;
}
bool wallet2 : : build_minted_block ( const currency : : COMMAND_RPC_SCAN_POS : : request & req ,
const currency : : COMMAND_RPC_SCAN_POS : : response & rsp ,
const currency : : account_public_address & miner_address ,
uint64_t new_block_expected_height /* UINT64_MAX */ )
{
//found a block, construct it, sign and push to daemon
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Found kernel, constructing block " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_NO_ASSERT_MES ( rsp . index < req . pos_entries . size ( ) , false , " call_COMMAND_RPC_SCAN_POS returned wrong index: " < < rsp . index < < " , expected less then " < < req . pos_entries . size ( ) ) ;
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 ;
tmpl_req . pos_amount = req . pos_entries [ rsp . index ] . amount ;
tmpl_req . pos_index = req . pos_entries [ rsp . index ] . index ;
tmpl_req . extra_text = m_miner_text_info ;
2019-07-24 19:14:35 +02:00
tmpl_req . stake_unlock_time = req . pos_entries [ rsp . index ] . stake_unlock_time ;
2018-12-27 18:50:45 +03:00
m_core_proxy - > call_COMMAND_RPC_GETBLOCKTEMPLATE ( tmpl_req , tmpl_rsp ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( tmpl_rsp . status = = CORE_RPC_STATUS_OK , false , " Failed to create block template after kernel hash found! " ) ;
2018-12-27 18:50:45 +03:00
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 ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( res , false , " Failed to create block template after kernel hash found! " ) ;
2018-12-27 18:50:45 +03:00
res = parse_and_validate_block_from_blob ( block_blob , b ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( res , false , " Failed to create block template after kernel hash found! " ) ;
2018-12-27 18:50:45 +03:00
if ( rsp . last_block_hash ! = b . prev_id )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_YELLOW ( " Kernel was found but block is behindhand, b.prev_id= " < < b . prev_id < < " , last_block_hash= " < < rsp . last_block_hash , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
std : : vector < const crypto : : public_key * > keys_ptrs ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( req . pos_entries [ rsp . index ] . wallet_index < m_transfers . size ( ) ,
2018-12-27 18:50:45 +03:00
false , " Wrong wallet_index at generating coinbase transacton " ) ;
const auto & target = m_transfers [ req . pos_entries [ rsp . index ] . wallet_index ] . m_ptx_wallet_info - > m_tx . vout [ m_transfers [ req . pos_entries [ rsp . index ] . wallet_index ] . m_internal_output_index ] . target ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( target . type ( ) = = typeid ( currency : : txout_to_key ) , false , " wrong type_id in source transaction in coinbase tx " ) ;
2018-12-27 18:50:45 +03:00
const currency : : txout_to_key & txtokey = boost : : get < currency : : txout_to_key > ( target ) ;
keys_ptrs . push_back ( & txtokey . key ) ;
//put actual time for tx block
b . timestamp = rsp . block_timestamp ;
currency : : etc_tx_time tt = AUTO_VAL_INIT ( tt ) ;
tt . v = m_core_runtime_config . get_core_time ( ) ;
b . miner_tx . extra . push_back ( tt ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_MAGENTA ( " Applying actual timestamp: " < < epee : : misc_utils : : get_time_str ( tt . v ) , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
//sign block
res = prepare_and_sign_pos_block ( b ,
req . pos_entries [ rsp . index ] ,
get_tx_pub_key_from_extra ( m_transfers [ req . pos_entries [ rsp . index ] . wallet_index ] . m_ptx_wallet_info - > m_tx ) ,
m_transfers [ req . pos_entries [ rsp . index ] . wallet_index ] . m_internal_output_index ,
keys_ptrs ) ;
2019-02-21 21:33:52 +03:00
WLT_CHECK_AND_ASSERT_MES ( res , false , " Failed to prepare_and_sign_pos_block " ) ;
2018-12-27 18:50:45 +03:00
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Block constructed < " < < get_block_hash ( b ) < < " >, sending to core... " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
currency : : COMMAND_RPC_SUBMITBLOCK : : request subm_req = AUTO_VAL_INIT ( subm_req ) ;
currency : : COMMAND_RPC_SUBMITBLOCK : : response subm_rsp = AUTO_VAL_INIT ( subm_rsp ) ;
subm_req . push_back ( epee : : string_tools : : buff_to_hex_nodelimer ( t_serializable_object_to_blob ( b ) ) ) ;
m_core_proxy - > call_COMMAND_RPC_SUBMITBLOCK ( subm_req , subm_rsp ) ;
if ( subm_rsp . status ! = CORE_RPC_STATUS_OK )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_ERROR ( " Constructed block is not accepted by core, status: " < < subm_rsp . status ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " POS block generated and accepted, congrats! " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
m_wcallback - > on_pos_block_found ( b ) ;
//@#@
//double check timestamp
if ( time ( NULL ) - get_actual_timestamp ( b ) > 5 )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_RED ( " Found block ( " < < get_block_hash ( b ) < < " ) timestamp ( " < < get_actual_timestamp ( b )
< < " ) is suspiciously less ( " < < time ( NULL ) - get_actual_timestamp ( b ) < < " ) then curren time( " < < time ( NULL ) < < " ) " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
//
return true ;
}
//----------------------------------------------------------------------------------------------------
2019-08-27 17:36:53 +02:00
void wallet2 : : get_unconfirmed_transfers ( std : : vector < wallet_public : : wallet_transfer_info > & trs )
2018-12-27 18:50:45 +03:00
{
for ( auto & u : m_unconfirmed_txs )
trs . push_back ( u . second ) ;
}
//----------------------------------------------------------------------------------------------------
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 ;
2019-07-24 19:14:35 +02:00
if ( td . m_ptx_wallet_info - > m_block_height + WALLET_DEFAULT_TX_SPENDABLE_AGE > m_blockchain . 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 ) ;
if ( for_pos_mining & & m_blockchain . size ( ) > m_core_runtime_config . hard_fork1_starts_after_height )
{
//allowed of staking locked coins with
stake_lock_time = unlock_time ;
}
else
{
if ( ! currency : : is_tx_spendtime_unlocked ( unlock_time , m_blockchain . size ( ) , m_core_runtime_config . get_core_time ( ) ) )
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 ;
tx_dest . addr . push_back ( m_account . get_keys ( ) . m_account_address ) ;
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 ) ;
2019-04-03 12:48:09 +03:00
transfer ( destinations , 0 , 0 , od . fee , extra , attachments , detail : : ssi_digit , 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 ) ;
2019-08-03 00:22:04 +02:00
transfer ( std : : vector < currency : : tx_destination_entry > ( ) , 0 , 0 , fee , extra , attachments , detail : : ssi_digit , 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 ;
tx_dest . addr . push_back ( m_account . get_keys ( ) . m_account_address ) ;
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 ) ;
2019-04-03 12:48:09 +03:00
transfer ( destinations , 0 , 0 , od . fee , extra , attachments , detail : : ssi_digit , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , res_tx ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : request_alias_registration ( const 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 ) ;
}
std : : vector < currency : : tx_destination_entry > destinations ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
extra . push_back ( ai ) ;
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 ;
destinations . push_back ( tx_dest_alias_reward ) ;
2019-04-03 12:48:09 +03:00
transfer ( destinations , 0 , 0 , fee , extra , attachments , detail : : ssi_digit , tx_dust_policy ( DEFAULT_DUST_THRESHOLD ) , res_tx , CURRENCY_TO_KEY_OUT_RELAXED , false ) ;
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 ) ;
}
bool r = currency : : sign_extra_alias_entry ( ai , m_account . get_keys ( ) . m_account_address . m_spend_public_key , m_account . get_keys ( ) . m_spend_secret_key ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Failed to sign alias update " ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Generated upodate 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
< < " signed(owner) pub key: " < < m_account . get_keys ( ) . m_account_address . m_spend_public_key < < ENDL
< < " transfered to address: " < < get_account_address_as_str ( ai . m_address ) < < ENDL
< < " signed_hash: " < < currency : : get_sign_buff_hash_for_alias_update ( ai )
) ;
std : : vector < currency : : tx_destination_entry > destinations ;
std : : vector < currency : : extra_v > extra ;
std : : vector < currency : : attachment_v > attachments ;
extra . push_back ( ai ) ;
2019-04-03 12:48:09 +03:00
transfer ( destinations , 0 , 0 , fee , extra , attachments , detail : : ssi_digit , 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 )
{
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 ;
}
//----------------------------------------------------------------------------------------------------
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
{
ss < < " index amount spent_h g_index block block_ts flg tx out# key image " < < ENDL ;
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 ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_fake_offers ( std : : list < bc_services : : offer_details_ex > & offers , uint64_t amount )
{
for ( uint64_t i = 0 ; i ! = amount ; i + + )
{
bc_services : : offer_details od ;
od . offer_type = rand ( ) % 4 ;
od . amount_primary = rand ( ) ;
od . amount_target = rand ( ) ;
od . bonus = get_random_rext ( 10 ) ;
od . target = get_random_rext ( 10 ) ;
od . location_country = get_random_rext ( 6 ) ;
od . location_city = get_random_rext ( 10 ) ;
od . contacts = get_random_rext ( 20 ) ;
od . comment = get_random_rext ( 30 ) ;
od . payment_types = get_random_rext ( 10 ) ;
od . deal_option = get_random_rext ( 10 ) ;
od . category = get_random_rext ( 4 ) ;
od . expiration_time = 3 ;
crypto : : hash tx_id = crypto : : rand < crypto : : hash > ( ) ;
offers . push_back ( bc_services : : offer_details_ex ( ) ) ;
bc_services : : offer_details_ex & odl = offers . back ( ) ;
static_cast < bc_services : : offer_details & > ( odl ) = od ;
odl . timestamp = m_core_runtime_config . get_core_time ( ) ;
odl . index_in_tx = 0 ;
odl . tx_hash = tx_id ;
odl . stopped = false ;
odl . fee = 10000 ;
}
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 ;
2019-04-03 12:48:09 +03:00
construct_params . split_strategy_id = detail : : ssi_digit ;
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
{
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
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
{
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
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 ) ;
2019-04-03 12:48:09 +03:00
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
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 ;
2019-04-03 12:48:09 +03:00
construct_params . split_strategy_id = detail : : ssi_digit ;
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 ;
ctp . split_strategy_id = detail : : ssi_digit ;
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
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
prepare_transaction ( ctp , ftp , tx ) ;
selected_transfers = ftp . selected_transfers ;
finalize_transaction ( ftp , tx , one_time_key , false ) ;
2018-12-27 18:50:45 +03:00
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : add_transfers_to_expiration_list ( const std : : vector < uint64_t > & selected_transfers , uint64_t expiration , uint64_t change_amount , const crypto : : hash & related_tx_id )
{
// 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 ( ) . change_amount = change_amount ;
m_money_expirations . back ( ) . related_tx_id = related_tx_id ;
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 < <
ss . str ( ) < <
" change_amount: " < < print_money_brief ( change_amount ) < < " , expire(s) at: " < < expiration , LOG_LEVEL_0 ) ;
}
//----------------------------------------------------------------------------------------------------
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 ;
ctp . split_strategy_id = detail : : ssi_digit ;
ctp . tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED ;
ctp . unlock_time = unlock_time ;
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
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 ) ;
}
//----------------------------------------------------------------------------------------------------
2019-11-27 03:19:10 +01:00
bool wallet2 : : prepare_tx_sources_for_packing ( uint64_t items_to_pack , size_t fake_outputs_count , std : : vector < currency : : tx_source_entry > & sources , std : : vector < uint64_t > & selected_indicies , uint64_t & found_money )
{
prepare_free_transfers_cache ( fake_outputs_count ) ;
auto it = m_found_free_amounts . find ( CURRENCY_BLOCK_REWARD ) ;
if ( it = = m_found_free_amounts . end ( ) | | it - > second . size ( ) < WALLET_POS_MINT_PACKING_SIZE )
return false ;
uint64_t found_money = 0 ;
for ( auto set_it = it - > second . begin ( ) ; set_it ! = it - > second . end ( ) ; it + + )
{
if ( is_transfer_ready_to_go ( m_transfers [ * set_it ] , fake_outputs_count ) )
{
found_money + = it - > first ;
selected_indicies . push_back ( * set_it ) ;
WLT_LOG_L2 ( " Selected index: " < < * set_it < < " , transfer_details: " < < ENDL < < epee : : serialization : : store_t_to_json ( m_transfers [ * set_it ] ) ) ;
}
it - > second . erase ( it - > second . begin ( ) ) ;
if ( ! it - > second . size ( ) )
found_free_amounts . erase ( it ) ;
}
return prepare_tx_sources ( fake_outputs_count , sources , selected_indicies , found_money ) ;
}
//----------------------------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
bool wallet2 : : prepare_tx_sources ( uint64_t needed_money , size_t fake_outputs_count , uint64_t dust_threshold , std : : vector < currency : : tx_source_entry > & sources , std : : vector < uint64_t > & selected_indicies , uint64_t & found_money )
{
found_money = select_transfers ( needed_money , fake_outputs_count , dust_threshold , selected_indicies ) ;
THROW_IF_FALSE_WALLET_EX_MES ( found_money > = needed_money , error : : not_enough_money , " wallet_dump: " < < ENDL < < dump_trunsfers ( false ) , found_money , needed_money , 0 ) ;
2019-11-27 03:19:10 +01:00
return prepare_tx_sources ( fake_outputs_count , sources , selected_indicies , found_money ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : prepare_tx_sources ( size_t fake_outputs_count , std : : vector < currency : : tx_source_entry > & sources , std : : vector < uint64_t > & selected_indicies , uint64_t & found_money )
{
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 ) ;
req . use_forced_mix_outs = false ; //add this feature to UI later
req . outs_count = fake_outputs_count + 1 ; // add one to make possible (if need) to skip real output key
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 ( ) ) ;
2018-12-27 18:50:45 +03:00
req . amounts . push_back ( it - > amount ( ) ) ;
}
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 " ) ;
THROW_IF_FALSE_WALLET_EX ( daemon_resp . status ! = CORE_RPC_STATUS_BUSY , error : : daemon_busy , " getrandom_outs.bin " ) ;
THROW_IF_FALSE_WALLET_EX ( daemon_resp . status = = CORE_RPC_STATUS_OK , error : : get_random_outs_error , daemon_resp . status ) ;
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
{
if ( amount_outs . outs . size ( ) < req . outs_count )
{
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
}
//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 ( ) ;
//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 ;
tx_output_entry oe ;
oe . first = daemon_oe . global_amount_index ;
oe . second = daemon_oe . out_key ;
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 )
{
if ( a . first . type ( ) . hash_code ( ) = = typeid ( uint64_t ) . hash_code ( ) )
return boost : : get < uint64_t > ( a . first ) > = td . m_global_output_index ;
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;
tx_output_entry real_oe ;
real_oe . first = td . m_global_output_index ; // TODO: use ref_by_id when neccessary
real_oe . second = boost : : get < txout_to_key > ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . target ) . key ;
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 ;
2019-02-21 21:33:52 +03:00
print_source_entry ( src ) ;
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() " ) ;
const tx_out & out = it - > second . m_ptx_wallet_info - > m_tx . vout [ it - > second . m_internal_output_index ] ;
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 ;
}
//----------------------------------------------------------------------------------------------------------------
uint64_t wallet2 : : get_needed_money ( uint64_t fee , const std : : vector < currency : : tx_destination_entry > & dsts )
{
uint64_t needed_money = fee ;
BOOST_FOREACH ( auto & dt , dsts )
{
THROW_IF_TRUE_WALLET_EX ( 0 = = dt . amount , error : : zero_destination ) ;
uint64_t money_to_add = dt . amount ;
if ( dt . amount_to_provide )
money_to_add = dt . amount_to_provide ;
needed_money + = money_to_add ;
THROW_IF_TRUE_WALLET_EX ( needed_money < money_to_add , error : : tx_sum_overflow , dsts , fee ) ;
}
return needed_money ;
}
//----------------------------------------------------------------------------------------------------------------
void wallet2 : : send_transaction_to_network ( const transaction & tx )
{
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 = = CORE_RPC_STATUS_BUSY , error : : daemon_busy , " sendrawtransaction " ) ;
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status = = CORE_RPC_STATUS_DISCONNECTED , error : : wallet_internal_error , " Transfer attempt while daemon offline " ) ;
THROW_IF_TRUE_WALLET_EX ( daemon_send_resp . status ! = CORE_RPC_STATUS_OK , error : : tx_rejected , tx , daemon_send_resp . status ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " transaction " < < get_transaction_hash ( tx ) < < " generated ok and sent to daemon: " < < ENDL < < currency : : obj_to_json_str ( tx ) ) ;
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 )
{
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 ( ) )
recipients . push_back ( get_account_address_as_str ( addr ) ) ;
}
}
if ( ! recipients . size ( ) )
{
//transaction send to ourself
recipients . push_back ( get_account_address_as_str ( m_account . get_public_address ( ) ) ) ;
}
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-04-12 09:48:33 +03:00
if ( throw_if_flag_already_set )
{
for ( uint64_t i : selected_transfers )
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( i < m_transfers . size ( ) , " invalid transfer index given: " < < i < < " , m_transfers.size() == " < < m_transfers . size ( ) ) ;
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 ] ) ) ;
}
}
2018-12-27 18:50:45 +03:00
for ( uint64_t i : selected_transfers )
{
2019-04-12 09:48:33 +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 ( ) ) ;
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 ( " 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 ) ;
}
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : clear_transfers_from_flag ( const std : : vector < uint64_t > & selected_transfers , uint32_t flag , const std : : string & reason /* = empty_string */ )
{
for ( uint64_t i : selected_transfers )
{
THROW_IF_TRUE_WALLET_EX ( i > = m_transfers . size ( ) , error : : wallet_internal_error , " i >= m_transfers.size() " ) ;
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 ) ;
}
}
//----------------------------------------------------------------------------------------------------
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
switch ( m_transfer_history [ i ] . tx_type )
{
case GUI_TX_TYPE_PUSH_OFFER :
{
bc_services : : offer_details od ;
if ( ! get_type_in_variant_container ( m_transfer_history [ i ] . srv_attachments , od ) )
{
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 ;
if ( ! get_type_in_variant_container ( m_transfer_history [ i ] . srv_attachments , uo ) )
{
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 ;
if ( ! get_type_in_variant_container ( m_transfer_history [ i ] . srv_attachments , co ) )
{
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 ( ) )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_L3 ( " Unable to find original tx record " < < h < < " in cancel offer " < < h ) ;
2018-12-27 18:50:45 +03:00
break ;
}
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 ;
}
//----------------------------------------------------------------------------------------------------
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 )
{
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Selecting indices for transfer found_free_amounts.size()= " < < found_free_amounts . size ( ) < < " ... " , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
uint64_t found_money = 0 ;
while ( found_money < needed_money & & found_free_amounts . size ( ) )
{
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 ( ) ] ) ) ;
2018-12-27 18:50:45 +03:00
}
it - > second . erase ( it - > second . begin ( ) ) ;
if ( ! it - > second . size ( ) )
found_free_amounts . erase ( it ) ;
}
2019-02-21 21:33:52 +03:00
WLT_LOG_GREEN ( " Selected " < < print_money ( found_money ) < < " coins, 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 ;
if ( ! currency : : is_mixattr_applicable_for_fake_outs_counter ( boost : : get < currency : : txout_to_key > ( td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . target ) . mix_attr , fake_outputs_count ) )
return false ;
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 ) )
{
m_found_free_amounts [ td . m_ptx_wallet_info - > m_tx . vout [ td . m_internal_output_index ] . amount ] . insert ( i ) ;
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 )
{
for ( auto i : indexs )
add_transfer_to_transfers_cache ( m_transfers [ i ] . m_ptx_wallet_info - > m_tx . vout [ m_transfers [ i ] . m_internal_output_index ] . amount , i ) ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : add_transfer_to_transfers_cache ( uint64_t amount , uint64_t index )
{
m_found_free_amounts [ amount ] . insert ( index ) ;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : select_transfers ( uint64_t needed_money , size_t fake_outputs_count , uint64_t dust , std : : vector < uint64_t > & selected_indicies )
{
prepare_free_transfers_cache ( fake_outputs_count ) ;
return select_indices_for_transfer ( selected_indicies , m_found_free_amounts , needed_money , fake_outputs_count ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : read_money_transfer2_details_from_tx ( const transaction & tx , const std : : vector < currency : : tx_destination_entry > & splitted_dsts ,
2019-08-27 17:36:53 +02:00
wallet_public : : wallet_transfer_info_details & wtd )
2018-12-27 18:50:45 +03:00
{
PROFILE_FUNC ( " wallet2::read_money_transfer2_details_from_tx " ) ;
for ( auto & d : splitted_dsts )
{
if ( d . addr . size ( ) & & d . addr . back ( ) . m_spend_public_key = = m_account . get_keys ( ) . m_account_address . m_spend_public_key & &
d . addr . back ( ) . m_view_public_key = = m_account . get_keys ( ) . m_account_address . m_view_public_key )
wtd . rcv . push_back ( d . amount ) ;
}
//scan key images
for ( auto & i : tx . vin )
{
if ( i . type ( ) = = typeid ( currency : : txin_to_key ) )
{
const currency : : txin_to_key & in_to_key = boost : : get < currency : : txin_to_key > ( i ) ;
if ( m_key_images . count ( in_to_key . k_image ) )
{
wtd . spn . push_back ( in_to_key . amount ) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------------------------------
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 )
{
PROFILE_FUNC ( " wallet2::add_sent_unconfirmed_tx " ) ;
2019-08-27 17:36:53 +02:00
wallet_public : : wallet_transfer_info & unconfirmed_wti = misc_utils : : get_or_insert_value_initialized ( m_unconfirmed_txs , currency : : get_transaction_hash ( tx ) ) ;
2018-12-27 18:50:45 +03:00
//unconfirmed_wti.tx = tx;
unconfirmed_wti . remote_addresses = recipients ;
for ( auto addr : recipients )
unconfirmed_wti . recipients_aliases . push_back ( get_alias_for_address ( addr ) ) ;
unconfirmed_wti . is_income = false ;
unconfirmed_wti . selected_indicies = selected_indicies ;
/*TODO: add selected_indicies to read_money_transfer2_details_from_tx in case of performance problems*/
read_money_transfer2_details_from_tx ( tx , splitted_dsts , unconfirmed_wti . td ) ;
uint64_t change_amount = 0 ;
uint64_t inputs_amount = 0 ;
for ( auto i : unconfirmed_wti . td . rcv )
change_amount + = i ;
for ( auto i : unconfirmed_wti . td . spn )
inputs_amount + = i ;
prepare_wti ( unconfirmed_wti , 0 , m_core_runtime_config . get_core_time ( ) , tx , inputs_amount - ( change_amount + get_tx_fee ( tx ) ) , money_transfer2_details ( ) ) ;
rise_on_transfer2 ( unconfirmed_wti ) ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : get_alias_for_address ( const std : : string & addr )
{
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 ) ;
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 " ) ;
2018-12-27 18:50:45 +03:00
return " " ;
}
return res . alias_info . alias ;
}
//----------------------------------------------------------------------------------------------------
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 )
{
2019-04-03 12:48:09 +03:00
transfer ( dsts , fake_outputs_count , unlock_time , fee , extra , attachments , detail : : ssi_digit , 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 ;
THROW_IF_TRUE_WALLET_EX ( m_blockchain . size ( ) > 1 , error : : wallet_internal_error , " Can't change wallet genesis block once the blockchain has been populated " ) ;
crypto : : hash genesis_hash = get_block_hash ( genesis ) ;
if ( m_blockchain . size ( ) = = 1 & & m_blockchain [ 0 ] ! = genesis_hash )
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Changing genesis block for wallet " < < m_account . get_public_address_str ( ) < < " : " < < ENDL < < " " < < m_blockchain [ 0 ] < < " -> " < < genesis_hash ) ;
2018-12-27 18:50:45 +03:00
m_blockchain . clear ( ) ;
m_blockchain . push_back ( genesis_hash ) ;
m_local_bc_height = 1 ;
m_last_bc_timestamp = genesis . timestamp ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L2 ( " Processing genesis block: " < < genesis_hash ) ;
2018-12-27 18:50:45 +03:00
process_new_transaction ( genesis . miner_tx , 0 , genesis ) ;
}
void wallet2 : : set_genesis ( const crypto : : hash & genesis_hash )
{
THROW_IF_TRUE_WALLET_EX ( m_blockchain . size ( ) ! = 1 , error : : wallet_internal_error , " Can't change wallet genesis hash once the blockchain has been populated " ) ;
2019-02-21 21:33:52 +03:00
WLT_LOG_L0 ( " Changing genesis hash for wallet " < < m_account . get_public_address_str ( ) < < " : " < < ENDL < < " " < < m_blockchain [ 0 ] < < " -> " < < genesis_hash ) ;
2018-12-27 18:50:45 +03:00
m_blockchain [ 0 ] = genesis_hash ;
}
//----------------------------------------------------------------------------------------------------
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 ) ;
if ( res . status ! = CORE_RPC_STATUS_OK )
{
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 ;
}
//----------------------------------------------------------------------------------------------------
2019-02-21 21:33:52 +03:00
void wallet2 : : print_source_entry ( const currency : : tx_source_entry & src ) const
{
std : : ostringstream indexes ;
std : : for_each ( src . outputs . begin ( ) , src . outputs . end ( ) , [ & ] ( const currency : : tx_source_entry : : output_entry & s_e ) { indexes < < s_e . first < < " " ; } ) ;
WLT_LOG_L0 ( " amount= " < < currency : : print_money ( src . amount ) < < " , real_output= " < < src . real_output < < " , real_output_in_tx_index= " < < src . real_output_in_tx_index < < " , indexes: " < < indexes . str ( ) ) ;
}
//----------------------------------------------------------------------------------------------------
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 ;
}
//----------------------------------------------------------------------------------------------------
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 ,
std : : vector < currency : : tx_destination_entry > & final_detinations )
{
currency : : tx_destination_entry change_dts = AUTO_VAL_INIT ( change_dts ) ;
if ( needed_money < found_money )
{
change_dts . addr . push_back ( m_account . get_keys ( ) . m_account_address ) ;
change_dts . amount = found_money - needed_money ;
}
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX ( found_money > = needed_money , " needed_money== " < < needed_money < < " < found_money== " < < found_money ) ;
uint64_t dust = 0 ;
bool r = detail : : apply_split_strategy_by_id ( destination_split_strategy_id , dsts , change_dts , dust_policy . dust_threshold , final_detinations , 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 ) ;
//@#@
# ifdef _DEBUG
if ( final_detinations . size ( ) > 10 )
{
WLT_LOG_L0 ( " final_detinations.size()= " < < final_detinations . size ( ) ) ;
}
# endif
//@#@
if ( 0 ! = dust & & ! dust_policy . add_to_fee )
{
final_detinations . push_back ( currency : : tx_destination_entry ( dust , dust_policy . addr_for_dust ) ) ;
}
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : prepare_transaction ( const construct_tx_param & ctp , finalize_tx_param & ftp , const currency : : transaction & tx_for_mode_separate /* = currency::transaction() */ )
{
TIME_MEASURE_START_MS ( get_needed_money_time ) ;
uint64_t needed_money = get_needed_money ( ctp . fee , ctp . dsts ) ;
if ( ctp . flags & TX_FLAG_SIGNATURE_MODE_SEPARATE & & tx_for_mode_separate . vout . size ( ) )
{
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 " ) ;
needed_money + = ( currency : : get_outs_money_amount ( tx_for_mode_separate ) - get_inputs_money_amount ( tx_for_mode_separate ) ) ;
}
TIME_MEASURE_FINISH_MS ( get_needed_money_time ) ;
uint64_t found_money = 0 ;
TIME_MEASURE_START_MS ( prepare_tx_sources_time ) ;
if ( ctp . multisig_id = = currency : : null_hash )
prepare_tx_sources ( needed_money , ctp . fake_outputs_count , ctp . dust_policy . dust_threshold , ftp . sources , ftp . selected_transfers , found_money ) ;
else
prepare_tx_sources ( ctp . multisig_id , ftp . sources , found_money ) ;
TIME_MEASURE_FINISH_MS ( prepare_tx_sources_time ) ;
TIME_MEASURE_START_MS ( prepare_tx_destinations_time ) ;
prepare_tx_destinations ( needed_money , found_money , static_cast < detail : : split_strategy_id_t > ( ctp . split_strategy_id ) , ctp . dust_policy , ctp . dsts , ftp . prepared_destinations ) ;
TIME_MEASURE_FINISH_MS ( prepare_tx_destinations_time ) ;
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 ;
/* 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 : : finalize_transaction ( const finalize_tx_param & ftp , currency : : transaction & tx , crypto : : secret_key & tx_key , bool broadcast_tx )
{
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-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 ( ) ,
ftp . sources ,
ftp . prepared_destinations ,
ftp . extra ,
ftp . attachments ,
tx ,
tx_key ,
ftp . unlock_time ,
ftp . crypt_address ,
0 , // expiration time
ftp . tx_outs_attr ,
ftp . shuffle ,
ftp . flags ) ;
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 ;
r = sign_multisig_input_in_tx ( 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)
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
m_tx_keys . insert ( std : : make_pair ( get_transaction_hash ( tx ) , tx_key ) ) ;
THROW_IF_FALSE_WALLET_EX ( get_object_blobsize ( tx ) < CURRENCY_MAX_TRANSACTION_BLOB_SIZE , error : : tx_too_big , tx , m_upper_transaction_size_limit ) ;
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 )
send_transaction_to_network ( 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 )
add_sent_tx_detailed_info ( 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 ,
std : : string * p_signed_tx_blob_str )
2019-04-03 12:48:09 +03: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-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH ( precalculation_time ) ;
TIME_MEASURE_START ( prepare_transaction_time ) ;
finalize_tx_param ftp = AUTO_VAL_INIT ( ftp ) ;
2019-04-12 09:48:33 +03:00
prepare_transaction ( ctp , ftp ) ;
2019-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH ( prepare_transaction_time ) ;
if ( m_watch_only )
{
2019-04-30 19:34:44 +02:00
TIME_MEASURE_START ( store_unsigned_tx_time ) ;
2019-04-12 09:48:33 +03:00
ftp . spend_pub_key = m_account . get_public_address ( ) . m_spend_public_key ;
blobdata bl = t_serializable_object_to_blob ( ftp ) ;
2019-04-03 12:48:09 +03:00
crypto : : chacha_crypt ( bl , m_account . get_keys ( ) . m_view_secret_key ) ;
2019-04-12 09:48:33 +03:00
epee : : file_io_utils : : save_string_to_file ( " zano_tx_unsigned " , bl ) ;
2019-04-03 12:48:09 +03:00
LOG_PRINT_L0 ( " Transaction stored to unsigned_zano_tx. You need to sign this tx using a full-access wallet. " ) ;
2019-04-08 14:16:11 +03:00
if ( p_signed_tx_blob_str ! = nullptr )
* p_signed_tx_blob_str = bl ;
2019-04-30 19:34:44 +02:00
TIME_MEASURE_FINISH ( store_unsigned_tx_time ) ;
2019-04-03 12:48:09 +03:00
// unlock transfers at the very end
TIME_MEASURE_START ( mark_transfers_as_spent_time ) ;
2019-04-12 09:48:33 +03:00
mark_transfers_with_flag ( ftp . selected_transfers , WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION , std : : string ( " cold sig reservation for money transfer " ) , true ) ;
2019-04-03 12:48:09 +03:00
TIME_MEASURE_FINISH ( mark_transfers_as_spent_time ) ;
2019-04-30 19:34:44 +02:00
WLT_LOG_GREEN ( " [wallet::transfer] "
< < " precalculation_time: " < < print_fixed_decimal_point ( precalculation_time , 3 )
< < " , prepare_transaction_time: " < < print_fixed_decimal_point ( prepare_transaction_time , 3 )
< < " , 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_0 ) ;
2019-04-03 12:48:09 +03:00
return ;
}
TIME_MEASURE_START ( mark_transfers_as_spent_time ) ;
mark_transfers_as_spent ( ftp . selected_transfers , std : : string ( " money transfer, tx: " ) + epee : : string_tools : : pod_to_hex ( get_transaction_hash ( tx ) ) ) ;
TIME_MEASURE_FINISH ( mark_transfers_as_spent_time ) ;
2019-05-06 20:45:27 +02:00
TIME_MEASURE_START ( finalize_transaction_time ) ;
try
{
crypto : : secret_key sk = AUTO_VAL_INIT ( sk ) ;
finalize_transaction ( ftp , tx , sk , send_to_network ) ;
}
catch ( . . . )
{
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 ( tx ) ) ) ;
throw ;
}
TIME_MEASURE_FINISH ( finalize_transaction_time ) ;
2019-04-30 19:34:44 +02:00
WLT_LOG_GREEN ( " [wallet::transfer] "
< < " precalculation_time: " < < print_fixed_decimal_point ( precalculation_time , 3 )
< < " , 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
2019-04-30 19:34:44 +02:00
print_tx_sent_message ( tx , std : : string ( ) + " (transfer) " , fee ) ;
2019-04-03 12:48:09 +03:00
}
2018-12-27 18:50:45 +03:00
} // namespace tools