@ -122,6 +122,7 @@ using namespace cryptonote;
# define FIRST_REFRESH_GRANULARITY 1024
static const std : : string MULTISIG_SIGNATURE_MAGIC = " SigMultisigPkV1 " ;
static const std : : string MULTISIG_EXTRA_INFO_MAGIC = " MultisigxV1 " ;
namespace
{
@ -133,6 +134,42 @@ namespace
dir / = " .shared-ringdb " ;
return dir . string ( ) ;
}
std : : string pack_multisignature_keys ( const std : : string & prefix , const std : : vector < crypto : : public_key > & keys , const crypto : : secret_key & signer_secret_key )
{
std : : string data ;
crypto : : public_key signer ;
CHECK_AND_ASSERT_THROW_MES ( crypto : : secret_key_to_public_key ( signer_secret_key , signer ) , " Failed to derive public spend key " ) ;
data + = std : : string ( ( const char * ) & signer , sizeof ( crypto : : public_key ) ) ;
for ( const auto & key : keys )
{
data + = std : : string ( ( const char * ) & key , sizeof ( crypto : : public_key ) ) ;
}
data . resize ( data . size ( ) + sizeof ( crypto : : signature ) ) ;
crypto : : hash hash ;
crypto : : cn_fast_hash ( data . data ( ) , data . size ( ) - sizeof ( crypto : : signature ) , hash ) ;
crypto : : signature & signature = * ( crypto : : signature * ) & data [ data . size ( ) - sizeof ( crypto : : signature ) ] ;
crypto : : generate_signature ( hash , signer , signer_secret_key , signature ) ;
return MULTISIG_EXTRA_INFO_MAGIC + tools : : base58 : : encode ( data ) ;
}
std : : vector < crypto : : public_key > secret_keys_to_public_keys ( const std : : vector < crypto : : secret_key > & keys )
{
std : : vector < crypto : : public_key > public_keys ;
public_keys . reserve ( keys . size ( ) ) ;
std : : transform ( keys . begin ( ) , keys . end ( ) , std : : back_inserter ( public_keys ) , [ ] ( const crypto : : secret_key & k ) - > crypto : : public_key {
crypto : : public_key p ;
CHECK_AND_ASSERT_THROW_MES ( crypto : : secret_key_to_public_key ( k , p ) , " Failed to derive public spend key " ) ;
return p ;
} ) ;
return public_keys ;
}
}
namespace
@ -776,6 +813,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_callback ( 0 ) ,
m_trusted_daemon ( false ) ,
m_nettype ( nettype ) ,
m_multisig_rounds_passed ( 0 ) ,
m_always_confirm_transfers ( true ) ,
m_print_ring_members ( false ) ,
m_store_tx_info ( true ) ,
@ -2918,6 +2956,7 @@ bool wallet2::clear()
m_address_book . clear ( ) ;
m_subaddresses . clear ( ) ;
m_subaddress_labels . clear ( ) ;
m_multisig_rounds_passed = 0 ;
return true ;
}
@ -2932,6 +2971,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
{
std : : string account_data ;
std : : string multisig_signers ;
std : : string multisig_derivations ;
cryptonote : : account_base account = m_account ;
crypto : : chacha_key key ;
@ -2984,6 +3024,14 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
CHECK_AND_ASSERT_MES ( r , false , " failed to serialize wallet multisig signers " ) ;
value . SetString ( multisig_signers . c_str ( ) , multisig_signers . length ( ) ) ;
json . AddMember ( " multisig_signers " , value , json . GetAllocator ( ) ) ;
r = : : serialization : : dump_binary ( m_multisig_derivations , multisig_derivations ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to serialize wallet multisig derivations " ) ;
value . SetString ( multisig_derivations . c_str ( ) , multisig_derivations . length ( ) ) ;
json . AddMember ( " multisig_derivations " , value , json . GetAllocator ( ) ) ;
value2 . SetUint ( m_multisig_rounds_passed ) ;
json . AddMember ( " multisig_rounds_passed " , value2 , json . GetAllocator ( ) ) ;
}
value2 . SetInt ( m_always_confirm_transfers ? 1 : 0 ) ;
@ -3156,6 +3204,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_multisig = false ;
m_multisig_threshold = 0 ;
m_multisig_signers . clear ( ) ;
m_multisig_rounds_passed = 0 ;
m_multisig_derivations . clear ( ) ;
m_always_confirm_transfers = false ;
m_print_ring_members = false ;
m_default_mixin = 0 ;
@ -3214,6 +3264,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_multisig = field_multisig ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , multisig_threshold , unsigned int , Uint , m_multisig , 0 ) ;
m_multisig_threshold = field_multisig_threshold ;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , multisig_rounds_passed , unsigned int , Uint , false , 0 ) ;
m_multisig_rounds_passed = field_multisig_rounds_passed ;
if ( m_multisig )
{
if ( ! json . HasMember ( " multisig_signers " ) )
@ -3234,6 +3286,24 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
LOG_ERROR ( " Field multisig_signers found in JSON, but failed to parse " ) ;
return false ;
}
//previous version of multisig does not have this field
if ( json . HasMember ( " multisig_derivations " ) )
{
if ( ! json [ " multisig_derivations " ] . IsString ( ) )
{
LOG_ERROR ( " Field multisig_derivations found in JSON, but not String " ) ;
return false ;
}
const char * field_multisig_derivations = json [ " multisig_derivations " ] . GetString ( ) ;
std : : string multisig_derivations = std : : string ( field_multisig_derivations , field_multisig_derivations + json [ " multisig_derivations " ] . GetStringLength ( ) ) ;
r = : : serialization : : parse_binary ( multisig_derivations , m_multisig_derivations ) ;
if ( ! r )
{
LOG_ERROR ( " Field multisig_derivations found in JSON, but failed to parse " ) ;
return false ;
}
}
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR ( json , always_confirm_transfers , int , Int , false , true ) ;
m_always_confirm_transfers = field_always_confirm_transfers ;
@ -3869,12 +3939,12 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
CHECK_AND_ASSERT_THROW_MES ( ! view_keys . empty ( ) , " empty view keys " ) ;
CHECK_AND_ASSERT_THROW_MES ( view_keys . size ( ) = = spend_keys . size ( ) , " Mismatched view/spend key sizes " ) ;
CHECK_AND_ASSERT_THROW_MES ( threshold > 1 & & threshold < = spend_keys . size ( ) + 1 , " Invalid threshold " ) ;
CHECK_AND_ASSERT_THROW_MES ( threshold = = spend_keys . size ( ) | | threshold = = spend_keys . size ( ) + 1 , " Unsupported threshold case " ) ;
std : : string extra_multisig_info ;
crypto : : hash hash ;
clear ( ) ;
std : : vector < crypto : : secret_key > multisig_keys ;
rct : : key spend_pkey = rct : : identity ( ) ;
rct : : key spend_skey ;
std : : vector < crypto : : public_key > multisig_signers ;
// decrypt keys
epee : : misc_utils : : auto_scope_leave_caller keys_reencryptor ;
@ -3887,43 +3957,78 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
keys_reencryptor = epee : : misc_utils : : create_scope_leave_handler ( [ & , this , chacha_key ] ( ) { m_account . encrypt_keys ( chacha_key ) ; m_account . decrypt_viewkey ( chacha_key ) ; } ) ;
}
MINFO ( " Creating spend key... " ) ;
std : : vector < crypto : : secret_key > multisig_keys ;
rct : : key spend_pkey , spend_skey ;
// In common multisig scheme there are 4 types of key exchange rounds:
// 1. First round is exchange of view secret keys and public spend keys.
// 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key,
// M - public multisig key (in first round it equals to public spend key), K - new public multisig key.
// 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key,
// k - secret multisig key used to sign transactions. k and M are sets of keys, of course.
// And secret spend key as the sum of all participant's secret multisig keys
// 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys
// and calculate common spend public key as sum of all unique participants' public multisig keys.
// Note that N/N scheme has only first round. N-1/N has 2 rounds: first and last. Common M/N has all 4 rounds.
// IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G!
// Wallet's public spend key is the sum of unique public multisig keys of all participants.
// secret_spend_key * G = public signer key
if ( threshold = = spend_keys . size ( ) + 1 )
{
// In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key
MINFO ( " Creating spend key... " ) ;
// Calculates all multisig keys and spend key
cryptonote : : generate_multisig_N_N ( get_account ( ) . get_keys ( ) , spend_keys , multisig_keys , spend_skey , spend_pkey ) ;
// Our signer key is b * G, where b is secret spend key.
multisig_signers = spend_keys ;
multisig_signers . push_back ( get_multisig_signer_public_key ( get_account ( ) . get_keys ( ) . m_spend_secret_key ) ) ;
}
else if ( threshold = = spend_keys . size ( ) )
else
{
cryptonote : : generate_multisig_N1_N ( get_account ( ) . get_keys ( ) , spend_keys , multisig_keys , spend_skey , spend_pkey ) ;
// We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi).
// note that derivations are public keys as DH exchange suppose it to be
auto derivations = cryptonote : : generate_multisig_derivations ( get_account ( ) . get_keys ( ) , spend_keys ) ;
spend_pkey = rct : : identity ( ) ;
multisig_signers = std : : vector < crypto : : public_key > ( spend_keys . size ( ) + 1 , crypto : : null_pkey ) ;
if ( threshold = = spend_keys . size ( ) )
{
// N - 1 / N case
// We need an extra step, so we package all the composite public keys
// we know about, and make a signed string out of them
std : : string data ;
crypto : : public_key signer ;
CHECK_AND_ASSERT_THROW_MES ( crypto : : secret_key_to_public_key ( rct : : rct2sk ( spend_skey ) , signer ) , " Failed to derive public spend key " ) ;
data + = std : : string ( ( const char * ) & signer , sizeof ( crypto : : public_key ) ) ;
MINFO ( " Creating spend key... " ) ;
for ( const auto & msk : multisig_keys )
{
rct : : key pmsk = rct : : scalarmultBase ( rct : : sk2rct ( msk ) ) ;
data + = std : : string ( ( const char * ) & pmsk , sizeof ( crypto : : public_key ) ) ;
}
// Calculating set of our secret multisig keys as follows: mi = H(Mi),
// where mi - secret multisig key, Mi - others' participants public multisig key
multisig_keys = cryptonote : : calculate_multisig_keys ( derivations ) ;
data . resize ( data . size ( ) + sizeof ( crypto : : signature ) ) ;
crypto : : cn_fast_hash ( data . data ( ) , data . size ( ) - sizeof ( signature ) , hash ) ;
crypto : : signature & signature = * ( crypto : : signature * ) & data [ data . size ( ) - sizeof ( crypto : : signature ) ] ;
crypto : : generate_signature ( hash , signer , rct : : rct2sk ( spend_skey ) , signature ) ;
// calculating current participant's spend secret key as sum of all secret multisig keys for current participant.
// IMPORTANT: participant's secret spend key is not an entire wallet's secret spend!
// Entire wallet's secret spend is sum of all unique secret multisig keys
// among all of participants and is not held by anyone!
spend_skey = rct : : sk2rct ( cryptonote : : calculate_multisig_signer_key ( multisig_keys ) ) ;
extra_multisig_info = std : : string ( " MultisigxV1 " ) + tools : : base58 : : encode ( data ) ;
// Preparing data for the last round to calculate common public spend key. The data contains public multisig keys.
extra_multisig_info = pack_multisignature_keys ( MULTISIG_EXTRA_INFO_MAGIC , secret_keys_to_public_keys ( multisig_keys ) , rct : : rct2sk ( spend_skey ) ) ;
}
else
{
CHECK_AND_ASSERT_THROW_MES ( false , " Unsupported threshold case " ) ;
// M / N case
MINFO ( " Preparing keys for next exchange round... " ) ;
// Preparing data for middle round - packing new public multisig keys to exchage with others.
extra_multisig_info = pack_multisignature_keys ( MULTISIG_EXTRA_INFO_MAGIC , derivations , m_account . get_keys ( ) . m_spend_secret_key ) ;
spend_skey = rct : : sk2rct ( m_account . get_keys ( ) . m_spend_secret_key ) ;
// Need to store middle keys to be able to proceed in case of wallet shutdown.
m_multisig_derivations = derivations ;
}
}
// the multisig view key is shared by all, make one all can derive
clear ( ) ;
MINFO ( " Creating view key... " ) ;
crypto : : secret_key view_skey = cryptonote : : generate_multisig_view_secret_key ( get_account ( ) . get_keys ( ) . m_view_secret_key , view_keys ) ;
@ -3935,39 +4040,165 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
m_account_public_address = m_account . get_keys ( ) . m_account_address ;
m_watch_only = false ;
m_multisig = true ;
m_multisig_threshold = threshold ;
m_key_device_type = hw : : device : : device_type : : SOFTWARE ;
m_multisig_threshold = threshold ;
m_multisig_signers = multisig_signers ;
+ + m_multisig_rounds_passed ;
if ( threshold = = spend_keys . size ( ) + 1 )
// re-encrypt keys
keys_reencryptor = epee : : misc_utils : : auto_scope_leave_caller ( ) ;
create_keys_file ( m_wallet_file , false , password , boost : : filesystem : : exists ( m_wallet_file + " .address.txt " ) ) ;
setup_new_blockchain ( ) ;
if ( ! m_wallet_file . empty ( ) )
store ( ) ;
return extra_multisig_info ;
}
std : : string wallet2 : : exchange_multisig_keys ( const epee : : wipeable_string & password ,
const std : : vector < std : : string > & info )
{
THROW_WALLET_EXCEPTION_IF ( info . empty ( ) ,
error : : wallet_internal_error , " Empty multisig info " ) ;
if ( info [ 0 ] . substr ( 0 , MULTISIG_EXTRA_INFO_MAGIC . size ( ) ) ! = MULTISIG_EXTRA_INFO_MAGIC )
{
m_multisig_signers = spend_keys ;
m_multisig_signers . push_back ( get_multisig_signer_public_key ( ) ) ;
THROW_WALLET_EXCEPTION_IF( false ,
error : : wallet_internal_error , " Unsupported info string " ) ;
}
else
std : : vector < crypto : : public_key > signers ;
std : : unordered_set < crypto : : public_key > pkeys ;
THROW_WALLET_EXCEPTION_IF ( ! unpack_extra_multisig_info ( info , signers , pkeys ) ,
error : : wallet_internal_error , " Bad extra multisig info " ) ;
return exchange_multisig_keys ( password , pkeys , signers ) ;
}
std : : string wallet2 : : exchange_multisig_keys ( const epee : : wipeable_string & password ,
std : : unordered_set < crypto : : public_key > derivations ,
std : : vector < crypto : : public_key > signers )
{
CHECK_AND_ASSERT_THROW_MES ( ! derivations . empty ( ) , " empty pkeys " ) ;
CHECK_AND_ASSERT_THROW_MES ( ! signers . empty ( ) , " empty signers " ) ;
bool ready = false ;
CHECK_AND_ASSERT_THROW_MES ( multisig ( & ready ) , " The wallet is not multisig " ) ;
CHECK_AND_ASSERT_THROW_MES ( ! ready , " Multisig wallet creation process has already been finished " ) ;
// keys are decrypted
epee : : misc_utils : : auto_scope_leave_caller keys_reencryptor ;
if ( m_ask_password = = AskPasswordToDecrypt & & ! m_unattended & & ! m_watch_only )
{
m_multisig_signers = std : : vector < crypto : : public_key > ( spend_keys . size ( ) + 1 , crypto : : null_pkey ) ;
crypto : : chacha_key chacha_key ;
crypto : : generate_chacha_key ( password . data ( ) , password . size ( ) , chacha_key , m_kdf_rounds ) ;
m_account . encrypt_viewkey ( chacha_key ) ;
m_account . decrypt_keys ( chacha_key ) ;
keys_reencryptor = epee : : misc_utils : : create_scope_leave_handler ( [ & , this , chacha_key ] ( ) { m_account . encrypt_keys ( chacha_key ) ; m_account . decrypt_viewkey ( chacha_key ) ; } ) ;
}
// re-encrypt keys
if ( m_multisig_rounds_passed = = multisig_rounds_required ( m_multisig_signers . size ( ) , m_multisig_threshold ) - 1 )
{
// the last round is passed and we have to calculate spend public key
// add ours if not included
crypto : : public_key local_signer = get_multisig_signer_public_key ( ) ;
if ( std : : find ( signers . begin ( ) , signers . end ( ) , local_signer ) = = signers . end ( ) )
{
signers . push_back ( local_signer ) ;
for ( const auto & msk : get_account ( ) . get_multisig_keys ( ) )
{
derivations . insert ( rct : : rct2pk ( rct : : scalarmultBase ( rct : : sk2rct ( msk ) ) ) ) ;
}
}
CHECK_AND_ASSERT_THROW_MES ( signers . size ( ) = = m_multisig_signers . size ( ) , " Bad signers size " ) ;
// Summing all of unique public multisig keys to calculate common public spend key
crypto : : public_key spend_public_key = cryptonote : : generate_multisig_M_N_spend_public_key ( std : : vector < crypto : : public_key > ( derivations . begin ( ) , derivations . end ( ) ) ) ;
m_account_public_address . m_spend_public_key = spend_public_key ;
m_account . finalize_multisig ( spend_public_key ) ;
m_multisig_signers = signers ;
std : : sort ( m_multisig_signers . begin ( ) , m_multisig_signers . end ( ) , [ ] ( const crypto : : public_key & e0 , const crypto : : public_key & e1 ) { return memcmp ( & e0 , & e1 , sizeof ( e0 ) ) ; } ) ;
+ + m_multisig_rounds_passed ;
m_multisig_derivations . clear ( ) ;
// keys are encrypted again
keys_reencryptor = epee : : misc_utils : : auto_scope_leave_caller ( ) ;
create_keys_file ( m_wallet_file , false , password , boost : : filesystem : : exists ( m_wallet_file + " .address.txt " ) ) ;
if ( ! m_wallet_file . empty ( ) )
{
bool r = store_keys ( m_keys_file , password , false ) ;
THROW_WALLET_EXCEPTION_IF ( ! r , error : : file_save_error , m_keys_file ) ;
setup_new_blockchain ( ) ;
if ( boost : : filesystem : : exists ( m_wallet_file + " .address.txt " ) )
{
r = file_io_utils : : save_string_to_file ( m_wallet_file + " .address.txt " , m_account . get_public_address_str ( m_nettype ) ) ;
if ( ! r ) MERROR ( " String with address text not saved " ) ;
}
}
m_subaddresses . clear ( ) ;
m_subaddress_labels . clear ( ) ;
add_subaddress_account ( tr ( " Primary account " ) ) ;
if ( ! m_wallet_file . empty ( ) )
store ( ) ;
return { } ;
}
// Below are either middle or secret spend key establishment rounds
for ( const auto & key : m_multisig_derivations )
derivations . erase ( key ) ;
// Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys.
auto new_derivations = cryptonote : : generate_multisig_derivations ( get_account ( ) . get_keys ( ) , std : : vector < crypto : : public_key > ( derivations . begin ( ) , derivations . end ( ) ) ) ;
std : : string extra_multisig_info ;
if ( m_multisig_rounds_passed = = multisig_rounds_required ( m_multisig_signers . size ( ) , m_multisig_threshold ) - 2 ) // next round is last
{
// Next round is last therefore we are performing secret spend establishment round as described above.
MINFO ( " Creating spend key... " ) ;
// Calculating our secret multisig keys by hashing our public multisig keys.
auto multisig_keys = cryptonote : : calculate_multisig_keys ( std : : vector < crypto : : public_key > ( new_derivations . begin ( ) , new_derivations . end ( ) ) ) ;
// And summing it to get personal secret spend key
crypto : : secret_key spend_skey = cryptonote : : calculate_multisig_signer_key ( multisig_keys ) ;
m_account . make_multisig ( m_account . get_keys ( ) . m_view_secret_key , spend_skey , rct : : rct2pk ( rct : : identity ( ) ) , multisig_keys ) ;
// Packing public multisig keys to exchange with others and calculate common public spend key in the last round
extra_multisig_info = pack_multisignature_keys ( MULTISIG_EXTRA_INFO_MAGIC , secret_keys_to_public_keys ( multisig_keys ) , spend_skey ) ;
}
else
{
// This is just middle round
MINFO ( " Preparing keys for next exchange round... " ) ;
extra_multisig_info = pack_multisignature_keys ( MULTISIG_EXTRA_INFO_MAGIC , new_derivations , m_account . get_keys ( ) . m_spend_secret_key ) ;
m_multisig_derivations = new_derivations ;
}
+ + m_multisig_rounds_passed ;
create_keys_file ( m_wallet_file , false , password , boost : : filesystem : : exists ( m_wallet_file + " .address.txt " ) ) ;
return extra_multisig_info ;
}
std : : string wallet2 : : make_multisig ( const epee : : wipeable_string & password ,
const std : : vector < std : : string > & info ,
uint32_t threshold )
void wallet2 : : unpack_multisig_info ( const std : : vector < std : : string > & info ,
std : : vector < crypto: : public_key > & public_keys ,
std : : vector < crypto : : secret_key > & secret_keys ) const
{
// parse all multisig info
std : : vector < crypto : : secret_key > secret_keys ( info . size ( ) ) ;
std : : vector < crypto : : public_key > public_keys ( info . size ( ) ) ;
public_keys. resize ( info . size ( ) ) ;
s ecret_keys. resize ( info . size ( ) ) ;
for ( size_t i = 0 ; i < info . size ( ) ; + + i )
{
THROW_WALLET_EXCEPTION_IF ( ! verify_multisig_info ( info [ i ] , secret_keys [ i ] , public_keys [ i ] ) ,
@ -4011,75 +4242,51 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
" Found local spend public key, but not local view secret key - something very weird " ) ;
}
}
}
std : : string wallet2 : : make_multisig ( const epee : : wipeable_string & password ,
const std : : vector < std : : string > & info ,
uint32_t threshold )
{
std : : vector < crypto : : secret_key > secret_keys ( info . size ( ) ) ;
std : : vector < crypto : : public_key > public_keys ( info . size ( ) ) ;
unpack_multisig_info ( info , public_keys , secret_keys ) ;
return make_multisig ( password , secret_keys , public_keys , threshold ) ;
}
bool wallet2 : : finalize_multisig ( const epee : : wipeable_string & password , std : : unordered_set < crypto : : public_key > pkeys , std : : vector < crypto : : public_key > signers )
{
CHECK_AND_ASSERT_THROW_MES ( ! pkeys . empty ( ) , " empty pkeys " ) ;
// keys are decrypted
epee : : misc_utils : : auto_scope_leave_caller keys_reencryptor ;
if ( m_ask_password = = AskPasswordToDecrypt & & ! m_unattended & & ! m_watch_only )
{
crypto : : chacha_key chacha_key ;
crypto : : generate_chacha_key ( password . data ( ) , password . size ( ) , chacha_key , m_kdf_rounds ) ;
m_account . encrypt_viewkey ( chacha_key ) ;
m_account . decrypt_keys ( chacha_key ) ;
keys_reencryptor = epee : : misc_utils : : create_scope_leave_handler ( [ & , this , chacha_key ] ( ) { m_account . encrypt_keys ( chacha_key ) ; m_account . decrypt_viewkey ( chacha_key ) ; } ) ;
}
exchange_multisig_keys ( password , pkeys , signers ) ;
return true ;
}
// add ours if not included
crypto : : public_key local_signer ;
CHECK_AND_ASSERT_THROW_MES ( crypto : : secret_key_to_public_key ( get_account ( ) . get_keys ( ) . m_spend_secret_key , local_signer ) ,
" Failed to derive public spend key " ) ;
if ( std : : find ( signers . begin ( ) , signers . end ( ) , local_signer ) = = signers . end ( ) )
bool wallet2 : : unpack_extra_multisig_info ( const std : : vector < std : : string > & info ,
std : : vector < crypto : : public_key > & signers ,
std : : unordered_set < crypto : : public_key > & pkeys ) const
{
// parse all multisig info
signers . resize ( info . size ( ) , crypto : : null_pkey ) ;
for ( size_t i = 0 ; i < info . size ( ) ; + + i )
{
signers . push_back ( local_signer ) ;
for ( const auto & msk : get_account ( ) . get_multisig_keys ( ) )
if ( ! verify_extra_multisig_info ( info [ i ] , pkeys , signers [ i ] ) )
{
pkeys . insert ( rct : : rct2pk ( rct : : scalarmultBase ( rct : : sk2rct ( msk ) ) ) ) ;
return false ;
}
}
CHECK_AND_ASSERT_THROW_MES ( signers . size ( ) = = m_multisig_signers . size ( ) , " Bad signers size " ) ;
crypto : : public_key spend_public_key = cryptonote : : generate_multisig_N1_N_spend_public_key ( std : : vector < crypto : : public_key > ( pkeys . begin ( ) , pkeys . end ( ) ) ) ;
m_account_public_address . m_spend_public_key = spend_public_key ;
m_account . finalize_multisig ( spend_public_key ) ;
m_multisig_signers = signers ;
std : : sort ( m_multisig_signers . begin ( ) , m_multisig_signers . end ( ) , [ ] ( const crypto : : public_key & e0 , const crypto : : public_key & e1 ) { return memcmp ( & e0 , & e1 , sizeof ( e0 ) ) ; } ) ;
// keys are encrypted again
keys_reencryptor = epee : : misc_utils : : auto_scope_leave_caller ( ) ;
create_keys_file ( m_wallet_file , false , password , boost : : filesystem : : exists ( m_wallet_file + " .address.txt " ) ) ;
m_subaddresses . clear ( ) ;
m_subaddress_labels . clear ( ) ;
add_subaddress_account ( tr ( " Primary account " ) ) ;
if ( ! m_wallet_file . empty ( ) )
store ( ) ;
return true ;
}
bool wallet2 : : finalize_multisig ( const epee : : wipeable_string & password , const std : : vector < std : : string > & info )
{
// parse all multisig info
std : : unordered_set < crypto : : public_key > public_keys ;
std : : vector < crypto : : public_key > signers ( info . size ( ) , crypto : : null_pkey ) ;
for ( size_t i = 0 ; i < info . size ( ) ; + + i )
{
if ( ! verify_extra_multisig_info ( info [ i ] , public_keys , signers [ i ] ) )
std : : vector < crypto : : public_key > signers ;
if ( ! unpack_extra_multisig_info ( info , signers , public_keys ) )
{
MERROR ( " Bad multisig info " ) ;
return false ;
}
}
return finalize_multisig ( password , public_keys , signers ) ;
}
@ -4142,14 +4349,13 @@ bool wallet2::verify_multisig_info(const std::string &data, crypto::secret_key &
bool wallet2 : : verify_extra_multisig_info ( const std : : string & data , std : : unordered_set < crypto : : public_key > & pkeys , crypto : : public_key & signer )
{
const size_t header_len = strlen ( " MultisigxV1 " ) ;
if ( data . size ( ) < header_len | | data . substr ( 0 , header_len ) ! = " MultisigxV1 " )
if ( data . size ( ) < MULTISIG_EXTRA_INFO_MAGIC . size ( ) | | data . substr ( 0 , MULTISIG_EXTRA_INFO_MAGIC . size ( ) ) ! = MULTISIG_EXTRA_INFO_MAGIC )
{
MERROR ( " Multisig info header check error " ) ;
return false ;
}
std : : string decoded ;
if ( ! tools : : base58 : : decode ( data . substr ( header_len ) , decoded ) )
if ( ! tools : : base58 : : decode ( data . substr ( MULTISIG_EXTRA_INFO_MAGIC. size ( ) ) , decoded ) )
{
MERROR ( " Multisig info decoding error " ) ;
return false ;