@ -47,6 +47,7 @@
# include "common/command_line.h"
# include "common/util.h"
# include "common/dns_utils.h"
# include "common/base58.h"
# include "p2p/net_node.h"
# include "cryptonote_protocol/cryptonote_protocol_handler.h"
# include "simplewallet.h"
@ -714,6 +715,8 @@ simple_wallet::simple_wallet()
m_cmd_binder . set_handler ( " rescan_spent " , boost : : bind ( & simple_wallet : : rescan_spent , this , _1 ) , tr ( " Rescan blockchain for spent outputs " ) ) ;
m_cmd_binder . set_handler ( " get_tx_key " , boost : : bind ( & simple_wallet : : get_tx_key , this , _1 ) , tr ( " Get transaction key (r) for a given <txid> " ) ) ;
m_cmd_binder . set_handler ( " check_tx_key " , boost : : bind ( & simple_wallet : : check_tx_key , this , _1 ) , tr ( " Check amount going to <address> in <txid> " ) ) ;
m_cmd_binder . set_handler ( " get_tx_proof " , boost : : bind ( & simple_wallet : : get_tx_proof , this , _1 ) , tr ( " Generate a signature to prove payment to <address> in <txid> using the transaction secret key (r) without revealing it " ) ) ;
m_cmd_binder . set_handler ( " check_tx_proof " , boost : : bind ( & simple_wallet : : check_tx_proof , this , _1 ) , tr ( " Check tx proof for payment going to <address> in <txid> " ) ) ;
m_cmd_binder . set_handler ( " show_transfers " , boost : : bind ( & simple_wallet : : show_transfers , this , _1 ) , tr ( " show_transfers [in|out|pending|failed|pool] [<min_height> [<max_height>]] - Show incoming/outgoing transfers within an optional height range " ) ) ;
m_cmd_binder . set_handler ( " unspent_outputs " , boost : : bind ( & simple_wallet : : unspent_outputs , this , _1 ) , tr ( " unspent_outputs [<min_amount> <max_amount>] - Show unspent outputs within an optional amount range) " ) ) ;
m_cmd_binder . set_handler ( " rescan_bc " , boost : : bind ( & simple_wallet : : rescan_blockchain , this , _1 ) , tr ( " Rescan blockchain from scratch " ) ) ;
@ -3232,6 +3235,85 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
}
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : get_tx_proof ( const std : : vector < std : : string > & args )
{
if ( args . size ( ) ! = 2 & & args . size ( ) ! = 3 ) {
fail_msg_writer ( ) < < tr ( " usage: get_tx_proof <txid> <dest_address> [<tx_key>] " ) ;
return true ;
}
if ( m_wallet - > ask_password ( ) & & ! get_and_verify_password ( ) ) { return true ; }
cryptonote : : blobdata txid_data ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( args [ 0 ] , txid_data ) | | txid_data . size ( ) ! = sizeof ( crypto : : hash ) )
{
fail_msg_writer ( ) < < tr ( " failed to parse txid " ) ;
return true ;
}
crypto : : hash txid = * reinterpret_cast < const crypto : : hash * > ( txid_data . data ( ) ) ;
cryptonote : : account_public_address address ;
bool has_payment_id ;
crypto : : hash8 payment_id ;
if ( ! cryptonote : : get_account_address_from_str_or_url ( address , has_payment_id , payment_id , m_wallet - > testnet ( ) , args [ 1 ] ) )
{
fail_msg_writer ( ) < < tr ( " failed to parse address " ) ;
return true ;
}
LOCK_IDLE_SCOPE ( ) ;
crypto : : secret_key tx_key , tx_key2 ;
bool r = m_wallet - > get_tx_key ( txid , tx_key ) ;
cryptonote : : blobdata tx_key_data ;
if ( args . size ( ) = = 3 )
{
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( args [ 2 ] , tx_key_data ) | | tx_key_data . size ( ) ! = sizeof ( crypto : : secret_key ) )
{
fail_msg_writer ( ) < < tr ( " failed to parse tx_key " ) ;
return true ;
}
tx_key2 = * reinterpret_cast < const crypto : : secret_key * > ( tx_key_data . data ( ) ) ;
}
if ( r )
{
if ( args . size ( ) = = 3 & & tx_key ! = rct : : sk2rct ( tx_key2 ) )
{
fail_msg_writer ( ) < < tr ( " Tx secret key was found for the given txid, but you've also provided another tx secret key which doesn't match the found one. " ) ;
return true ;
}
}
else
{
if ( tx_key_data . empty ( ) )
{
fail_msg_writer ( ) < < tr ( " Tx secret key wasn't found in the wallet file. Provide it as the optional third parameter if you have it elsewhere. " ) ;
return true ;
}
tx_key = tx_key2 ;
}
crypto : : public_key R ;
crypto : : secret_key_to_public_key ( tx_key , R ) ;
crypto : : public_key rA = rct : : rct2pk ( rct : : scalarmultKey ( rct : : pk2rct ( address . m_view_public_key ) , rct : : sk2rct ( tx_key ) ) ) ;
crypto : : signature sig ;
try
{
crypto : : generate_tx_proof ( txid , R , address . m_view_public_key , rA , tx_key , sig ) ;
}
catch ( const std : : runtime_error & e )
{
fail_msg_writer ( ) < < e . what ( ) ;
return true ;
}
std : : string sig_str = std : : string ( " ProofV1 " ) +
tools : : base58 : : encode ( std : : string ( ( const char * ) & rA , sizeof ( crypto : : public_key ) ) ) +
tools : : base58 : : encode ( std : : string ( ( const char * ) & sig , sizeof ( crypto : : signature ) ) ) ;
success_msg_writer ( ) < < tr ( " Signature: " ) < < sig_str ;
return true ;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : check_tx_key ( const std : : vector < std : : string > & args_ )
{
std : : vector < std : : string > local_args = args_ ;
@ -3278,6 +3360,18 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
return true ;
}
crypto : : key_derivation derivation ;
if ( ! crypto : : generate_key_derivation ( address . m_view_public_key , tx_key , derivation ) )
{
fail_msg_writer ( ) < < tr ( " failed to generate key derivation from supplied parameters " ) ;
return true ;
}
return check_tx_key_helper ( txid , address , derivation ) ;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : check_tx_key_helper ( const crypto : : hash & txid , const cryptonote : : account_public_address & address , const crypto : : key_derivation & derivation )
{
COMMAND_RPC_GET_TRANSACTIONS : : request req ;
COMMAND_RPC_GET_TRANSACTIONS : : response res ;
req . txs_hashes . push_back ( epee : : string_tools : : pod_to_hex ( txid ) ) ;
@ -3311,13 +3405,6 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
return true ;
}
crypto : : key_derivation derivation ;
if ( ! crypto : : generate_key_derivation ( address . m_view_public_key , tx_key , derivation ) )
{
fail_msg_writer ( ) < < tr ( " failed to generate key derivation from supplied parameters " ) ;
return true ;
}
uint64_t received = 0 ;
try {
for ( size_t n = 0 ; n < tx . vout . size ( ) ; + + n )
@ -3340,14 +3427,6 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
{
rct : : key Ctmp ;
//rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
crypto : : key_derivation derivation ;
bool r = crypto : : generate_key_derivation ( address . m_view_public_key , tx_key , derivation ) ;
if ( ! r )
{
LOG_ERROR ( " Failed to generate key derivation to decode rct output " < < n ) ;
amount = 0 ;
}
else
{
crypto : : secret_key scalar1 ;
crypto : : derivation_to_scalar ( derivation , n , scalar1 ) ;
@ -3404,6 +3483,129 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
return true ;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet : : check_tx_proof ( const std : : vector < std : : string > & args )
{
if ( args . size ( ) ! = 3 ) {
fail_msg_writer ( ) < < tr ( " usage: check_tx_proof <txid> <address> <signature> " ) ;
return true ;
}
if ( ! try_connect_to_daemon ( ) )
return true ;
// parse txid
cryptonote : : blobdata txid_data ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( args [ 0 ] , txid_data ) | | txid_data . size ( ) ! = sizeof ( crypto : : hash ) )
{
fail_msg_writer ( ) < < tr ( " failed to parse txid " ) ;
return true ;
}
crypto : : hash txid = * reinterpret_cast < const crypto : : hash * > ( txid_data . data ( ) ) ;
// parse address
cryptonote : : account_public_address address ;
bool has_payment_id ;
crypto : : hash8 payment_id ;
if ( ! cryptonote : : get_account_address_from_str_or_url ( address , has_payment_id , payment_id , m_wallet - > testnet ( ) , args [ 1 ] ) )
{
fail_msg_writer ( ) < < tr ( " failed to parse address " ) ;
return true ;
}
// parse pubkey r*A & signature
std : : string sig_str = args [ 2 ] ;
const size_t header_len = strlen ( " ProofV1 " ) ;
if ( sig_str . size ( ) < header_len | | sig_str . substr ( 0 , header_len ) ! = " ProofV1 " )
{
fail_msg_writer ( ) < < tr ( " Signature header check error " ) ;
return true ;
}
crypto : : public_key rA ;
crypto : : signature sig ;
const size_t rA_len = tools : : base58 : : encode ( std : : string ( ( const char * ) & rA , sizeof ( crypto : : public_key ) ) ) . size ( ) ;
const size_t sig_len = tools : : base58 : : encode ( std : : string ( ( const char * ) & sig , sizeof ( crypto : : signature ) ) ) . size ( ) ;
std : : string rA_decoded ;
std : : string sig_decoded ;
if ( ! tools : : base58 : : decode ( sig_str . substr ( header_len , rA_len ) , rA_decoded ) )
{
fail_msg_writer ( ) < < tr ( " Signature decoding error " ) ;
return true ;
}
if ( ! tools : : base58 : : decode ( sig_str . substr ( header_len + rA_len , sig_len ) , sig_decoded ) )
{
fail_msg_writer ( ) < < tr ( " Signature decoding error " ) ;
return true ;
}
if ( sizeof ( crypto : : public_key ) ! = rA_decoded . size ( ) | | sizeof ( crypto : : signature ) ! = sig_decoded . size ( ) )
{
fail_msg_writer ( ) < < tr ( " Signature decoding error " ) ;
return true ;
}
memcpy ( & rA , rA_decoded . data ( ) , sizeof ( crypto : : public_key ) ) ;
memcpy ( & sig , sig_decoded . data ( ) , sizeof ( crypto : : signature ) ) ;
// fetch tx pubkey from the daemon
COMMAND_RPC_GET_TRANSACTIONS : : request req ;
COMMAND_RPC_GET_TRANSACTIONS : : response res ;
req . txs_hashes . push_back ( epee : : string_tools : : pod_to_hex ( txid ) ) ;
if ( ! net_utils : : invoke_http_json ( " /gettransactions " , req , res , m_http_client ) | |
( res . txs . size ( ) ! = 1 & & res . txs_as_hex . size ( ) ! = 1 ) )
{
fail_msg_writer ( ) < < tr ( " failed to get transaction from daemon " ) ;
return true ;
}
cryptonote : : blobdata tx_data ;
bool ok ;
if ( res . txs . size ( ) = = 1 )
ok = string_tools : : parse_hexstr_to_binbuff ( res . txs . front ( ) . as_hex , tx_data ) ;
else
ok = string_tools : : parse_hexstr_to_binbuff ( res . txs_as_hex . front ( ) , tx_data ) ;
if ( ! ok )
{
fail_msg_writer ( ) < < tr ( " failed to parse transaction from daemon " ) ;
return true ;
}
crypto : : hash tx_hash , tx_prefix_hash ;
cryptonote : : transaction tx ;
if ( ! cryptonote : : parse_and_validate_tx_from_blob ( tx_data , tx , tx_hash , tx_prefix_hash ) )
{
fail_msg_writer ( ) < < tr ( " failed to validate transaction from daemon " ) ;
return true ;
}
if ( tx_hash ! = txid )
{
fail_msg_writer ( ) < < tr ( " failed to get the right transaction from daemon " ) ;
return true ;
}
crypto : : public_key R = get_tx_pub_key_from_extra ( tx ) ;
if ( R = = null_pkey )
{
fail_msg_writer ( ) < < tr ( " Tx pubkey was not found " ) ;
return true ;
}
// check signature
if ( crypto : : check_tx_proof ( txid , R , address . m_view_public_key , rA , sig ) )
{
success_msg_writer ( ) < < tr ( " Good signature " ) ;
}
else
{
fail_msg_writer ( ) < < tr ( " Bad signature " ) ;
return true ;
}
// obtain key derivation by multiplying scalar 1 to the pubkey r*A included in the signature
crypto : : key_derivation derivation ;
if ( ! crypto : : generate_key_derivation ( rA , rct : : rct2sk ( rct : : I ) , derivation ) )
{
fail_msg_writer ( ) < < tr ( " failed to generate key derivation " ) ;
return true ;
}
return check_tx_key_helper ( txid , address , derivation ) ;
}
//----------------------------------------------------------------------------------------------------
static std : : string get_human_readable_timestamp ( uint64_t ts )
{
char buffer [ 64 ] ;