From 28df2e744d968722ffc01878f3a33a9096be0c4a Mon Sep 17 00:00:00 2001 From: Meheboob Khan Date: Wed, 24 Aug 2022 19:55:08 +0200 Subject: [PATCH] Fix for failing Unit tests --- libraries/wallet/wallet.cpp | 594 +++++++++++++++++++++++++++++++++++- tests/cli/son.cpp | 37 ++- 2 files changed, 613 insertions(+), 18 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 09aa5bdc..aa5b371f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -134,6 +134,8 @@ public: std::string operator()(const T& op)const; std::string operator()(const transfer_operation& op)const; + std::string operator()(const transfer_from_blind_operation& op)const; + std::string operator()(const transfer_to_blind_operation& op)const; std::string operator()(const account_create_operation& op)const; std::string operator()(const account_update_operation& op)const; std::string operator()(const asset_create_operation& op)const; @@ -2156,8 +2158,8 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(url)(block_signing_key)(broadcast) ) } - signed_transaction activate_deregistered_son(const string & owner_account, - bool broadcast /* = false */) + signed_transaction activate_deregistered_son(const string & owner_account, + bool broadcast /* = false */) { try { son_object son = get_son(owner_account); @@ -2406,7 +2408,7 @@ public: op.sidechain = sidechain; op.peerplays_uid = peerplays_uid; op.peerplays_transaction_id = peerplays_transaction_id; - op.peerplays_from = peerplays_from; + op.peerplays_from = peerplays_from; op.peerplays_asset = asset(asset_val.amount * asset_price.base.amount / asset_price.quote.amount); op.withdraw_sidechain = withdraw_sidechain; op.withdraw_address = withdraw_address; @@ -2857,9 +2859,11 @@ public: { account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); + if (!son_obj) FC_THROW("Account ${son} is not registered as a SON", ("son", son)); auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); + dlog("Approving son account ${son}", ("son", son)); if (!insert_result.second) FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); } @@ -2870,13 +2874,16 @@ public: if (!son_obj) FC_THROW("Account ${son} is not registered as a SON", ("son", son)); unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); + dlog("Rejecting son account ${son}", ("son", son)); + dlog("votes_removed ${votes_removed}", ("votes_removed", votes_removed)); if (!votes_removed) FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); } - voting_account_object.options.extensions.value.num_son = desired_number_of_sons; - + voting_account_object.options.num_son = desired_number_of_sons; + dlog("desired_number_of_sons = ${desired_number_of_sons}", ("desired_number_of_sons", desired_number_of_sons)); account_update_operation account_update_op; account_update_op.account = voting_account_object.id; + dlog("account_update_op.account = ${account_update_op.account}", ("account_update_op.account", account_update_op.account)); account_update_op.new_options = voting_account_object.options; signed_transaction tx; @@ -3470,6 +3477,70 @@ public: return ss.str(); }; + + m["get_blind_balances"] = [this](variant result, const fc::variants& a) + { + auto r = result.as>( GRAPHENE_MAX_NESTED_OBJECTS ); + vector asset_recs; + std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) { + return get_asset(a.asset_id); + }); + + std::stringstream ss; + for( unsigned i = 0; i < asset_recs.size(); ++i ) + ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; + + return ss.str(); + }; + m["transfer_to_blind"] = [this](variant result, const fc::variants& a) + { + auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); + std::stringstream ss; + r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + ss << "\n"; + for( const auto& out : r.outputs ) + { + asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); + ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; + } + return ss.str(); + }; + m["blind_transfer"] = [this](variant result, const fc::variants& a) + { + auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); + std::stringstream ss; + r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + ss << "\n"; + for( const auto& out : r.outputs ) + { + asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); + ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; + } + return ss.str(); + }; + m["receive_blind_transfer"] = [this](variant result, const fc::variants& a) + { + auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); + std::stringstream ss; + asset_object as = get_asset( r.amount.asset_id ); + ss << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; + return ss.str(); + }; + m["blind_history"] = [this](variant result, const fc::variants& a) + { + auto records = result.as>( GRAPHENE_MAX_NESTED_OBJECTS ); + std::stringstream ss; + ss << "WHEN " + << " " << "AMOUNT" << " " << "FROM" << " => " << "TO" << " " << "MEMO" <<"\n"; + ss << "====================================================================================\n"; + for( auto& r : records ) + { + asset_object as = get_asset( r.amount.asset_id ); + ss << fc::get_approximate_relative_time_string( r.date ) + << " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; + } + return ss.str(); + }; m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a) { const vector tournaments = result.as >( GRAPHENE_MAX_NESTED_OBJECTS ); @@ -4330,6 +4401,26 @@ std::string operation_printer::operator()(const T& op)const out << " result: " << str_result; return ""; } +std::string operation_printer::operator()(const transfer_from_blind_operation& op)const +{ + auto a = wallet.get_asset( op.fee.asset_id ); + auto receiver = wallet.get_account( op.to ); + + out << receiver.name + << " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance"; + return ""; +} +std::string operation_printer::operator()(const transfer_to_blind_operation& op)const +{ + auto fa = wallet.get_asset( op.fee.asset_id ); + auto a = wallet.get_asset( op.amount.asset_id ); + auto sender = wallet.get_account( op.from ); + + out << sender.name + << " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"") + << " fee: " << fa.amount_to_pretty_string( op.fee ); + return ""; +} string operation_printer::operator()(const transfer_operation& op) const { out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) @@ -5324,7 +5415,7 @@ signed_transaction wallet_api::sidechain_withdrawal_transaction(const string &so const string &withdraw_amount) { return my->sidechain_withdrawal_transaction(son_name_or_id, - block_num, + block_num, sidechain, peerplays_uid, peerplays_transaction_id, @@ -5454,7 +5545,7 @@ signed_transaction wallet_api::sidechain_deposit_transaction( const string &son const string &peerplays_from_name_or_id, const string &peerplays_to_name_or_id) { - return my->sidechain_deposit_transaction(son_name_or_id, + return my->sidechain_deposit_transaction(son_name_or_id, sidechain, transaction_id, operation_index, @@ -6100,6 +6191,495 @@ bool wallet_api::set_key_label( public_key_type key, string label } return false; } +map wallet_api::get_blind_accounts()const +{ + map result; + for( const auto& item : my->_wallet.labeled_keys ) + result[item.label] = item.key; + return result; +} +map wallet_api::get_my_blind_accounts()const +{ + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); + map result; + for( const auto& item : my->_wallet.labeled_keys ) + { + if( my->_keys.find(item.key) != my->_keys.end() ) + result[item.label] = item.key; + } + return result; +} + +public_key_type wallet_api::create_blind_account( string label, string brain_key ) +{ + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); + + auto label_itr = my->_wallet.labeled_keys.get().find(label); + if( label_itr != my->_wallet.labeled_keys.get().end() ) + FC_ASSERT( !"Key with label already exists" ); + brain_key = fc::trim_and_normalize_spaces( brain_key ); + auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() ); + auto priv_key = fc::ecc::private_key::regenerate( secret ); + public_key_type pub_key = priv_key.get_public_key(); + + FC_ASSERT( set_key_label( pub_key, label ) ); + + my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key ); + + save_wallet_file(); + return pub_key; +} + +vector wallet_api::get_blind_balances( string key_or_label ) +{ + vector result; + map balances; + + vector used; + + auto pub_key = get_public_key( key_or_label ); + auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); + auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false) ); + auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true) ); + while( start != end ) + { + if( !start->used ) + { + auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} ); + if( answer.size() ) + balances[start->amount.asset_id] += start->amount.amount; + else + used.push_back( start->commitment() ); + } + ++start; + } + for( const auto& u : used ) + { + auto itr = my->_wallet.blind_receipts.get().find( u ); + my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); + } + for( auto item : balances ) + result.push_back( asset( item.second, item.first ) ); + return result; +} + +blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label, + string to_account_id_or_name, + string amount_in, + string symbol, + bool broadcast ) +{ try { + transfer_from_blind_operation from_blind; + + + auto fees = my->_remote_db->get_global_properties().parameters.current_fees; + fc::optional asset_obj = get_asset(symbol); + FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); + auto amount = asset_obj->amount_from_string(amount_in); + + from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); + + auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount ); + + + auto conf = blind_transfer_help( from_blind_account_key_or_label, + from_blind_account_key_or_label, + blind_in, symbol, false, true/*to_temp*/ ); + FC_ASSERT( conf.outputs.size() > 0 ); + + auto to_account = my->get_account( to_account_id_or_name ); + from_blind.to = to_account.id; + from_blind.amount = amount; + from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor; + from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } ); + from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); + + idump( (from_blind) ); + conf.trx.operations.push_back(from_blind); + ilog( "about to validate" ); + conf.trx.validate(); + + if( broadcast && conf.outputs.size() == 2 ) { + + // Save the change + blind_confirmation::output conf_output; + blind_confirmation::output change_output = conf.outputs[0]; + + // The wallet must have a private key for confirmation.to, this is used to decrypt the memo + public_key_type from_key = get_public_key(from_blind_account_key_or_label); + conf_output.confirmation.to = from_key; + conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key; + conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo; + conf_output.confirmation_receipt = conf_output.confirmation; + //try { + receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name ); + //} catch ( ... ){} + } + + ilog( "about to broadcast" ); + conf.trx = sign_transaction( conf.trx, broadcast ); + + return conf; +} FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) } + +blind_confirmation wallet_api::blind_transfer( string from_key_or_label, + string to_key_or_label, + string amount_in, + string symbol, + bool broadcast ) +{ + return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false ); +} +blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, + string to_key_or_label, + string amount_in, + string symbol, + bool broadcast, + bool to_temp ) +{ + blind_confirmation confirm; + try { + + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); + public_key_type from_key = get_public_key(from_key_or_label); + public_key_type to_key = get_public_key(to_key_or_label); + + fc::optional asset_obj = get_asset(symbol); + FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); + + blind_transfer_operation blind_tr; + blind_tr.outputs.resize(2); + + auto fees = my->_remote_db->get_global_properties().parameters.current_fees; + + auto amount = asset_obj->amount_from_string(amount_in); + + asset total_amount = asset_obj->amount(0); + + vector blinding_factors; + + //auto from_priv_key = my->get_private_key( from_key ); + + blind_tr.fee = fees->calculate_fee( blind_tr, asset_obj->options.core_exchange_rate ); + + vector used; + + auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); + auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false) ); + auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true) ); + while( start != end ) + { + auto result = my->_remote_db->get_blinded_balances( {start->commitment() } ); + if( result.size() == 0 ) + { + used.push_back( start->commitment() ); + } + else + { + blind_tr.inputs.push_back({start->commitment(), start->control_authority}); + blinding_factors.push_back( start->data.blinding_factor ); + total_amount += start->amount; + + if( total_amount >= amount + blind_tr.fee ) + break; + } + ++start; + } + for( const auto& u : used ) + { + auto itr = my->_wallet.blind_receipts.get().find( u ); + my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); + } + + FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficent Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) ); + + auto one_time_key = fc::ecc::private_key::generate(); + auto secret = one_time_key.get_shared_secret( to_key ); + auto child = fc::sha256::hash( secret ); + auto nonce = fc::sha256::hash( one_time_key.get_secret() ); + auto blind_factor = fc::sha256::hash( child ); + + auto from_secret = one_time_key.get_shared_secret( from_key ); + auto from_child = fc::sha256::hash( from_secret ); + auto from_nonce = fc::sha256::hash( nonce ); + + auto change = total_amount - amount - blind_tr.fee; + fc::sha256 change_blind_factor; + fc::sha256 to_blind_factor; + if( change.amount > 0 ) + { + idump(("to_blind_factor")(blind_factor) ); + blinding_factors.push_back( blind_factor ); + change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 ); + wdump(("change_blind_factor")(change_blind_factor) ); + } + else // change == 0 + { + blind_tr.outputs.resize(1); + blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); + idump(("to_sum_blind_factor")(blind_factor) ); + blinding_factors.push_back( blind_factor ); + idump(("nochange to_blind_factor")(blind_factor) ); + } + fc::ecc::public_key from_pub_key = from_key; + fc::ecc::public_key to_pub_key = to_key; + + blind_output to_out; + to_out.owner = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); + to_out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); + idump(("to_out.blind")(blind_factor)(to_out.commitment) ); + + + if( blind_tr.outputs.size() > 1 ) + { + to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); + + blind_output change_out; + change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 ); + change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value ); + change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, 0, change.amount.value ); + blind_tr.outputs[1] = change_out; + + + blind_confirmation::output conf_output; + conf_output.label = from_key_or_label; + conf_output.pub_key = from_key; + conf_output.decrypted_memo.from = from_key; + conf_output.decrypted_memo.amount = change; + conf_output.decrypted_memo.blinding_factor = change_blind_factor; + conf_output.decrypted_memo.commitment = change_out.commitment; + conf_output.decrypted_memo.check = from_secret._hash[0]; + conf_output.confirmation.one_time_key = one_time_key.get_public_key(); + conf_output.confirmation.to = from_key; + conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) ); + conf_output.auth = change_out.owner; + conf_output.confirmation_receipt = conf_output.confirmation; + + confirm.outputs.push_back( conf_output ); + } + blind_tr.outputs[0] = to_out; + + blind_confirmation::output conf_output; + conf_output.label = to_key_or_label; + conf_output.pub_key = to_key; + conf_output.decrypted_memo.from = from_key; + conf_output.decrypted_memo.amount = amount; + conf_output.decrypted_memo.blinding_factor = blind_factor; + conf_output.decrypted_memo.commitment = to_out.commitment; + conf_output.decrypted_memo.check = secret._hash[0]; + conf_output.confirmation.one_time_key = one_time_key.get_public_key(); + conf_output.confirmation.to = to_key; + conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); + conf_output.auth = to_out.owner; + conf_output.confirmation_receipt = conf_output.confirmation; + + confirm.outputs.push_back( conf_output ); + + /** commitments must be in sorted order */ + std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(), + [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); + std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(), + [&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } ); + + confirm.trx.operations.emplace_back( std::move(blind_tr) ); + ilog( "validate before" ); + confirm.trx.validate(); + confirm.trx = sign_transaction(confirm.trx, broadcast); + + if( broadcast ) + { + for( const auto& out : confirm.outputs ) + { + try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){} + } + } + + return confirm; +} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) } + + + +/** + * Transfers a public balance from @from to one or more blinded balances using a + * stealth transfer. + */ +blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, + string asset_symbol, + /** map from key or label to amount */ + vector> to_amounts, + bool broadcast ) +{ try { + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); + idump((to_amounts)); + + blind_confirmation confirm; + account_object from_account = my->get_account(from_account_id_or_name); + + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); + + transfer_to_blind_operation bop; + bop.from = from_account.id; + + vector blinding_factors; + + asset total_amount = asset_obj->amount(0); + + for( auto item : to_amounts ) + { + auto one_time_key = fc::ecc::private_key::generate(); + auto to_key = get_public_key( item.first ); + auto secret = one_time_key.get_shared_secret( to_key ); + auto child = fc::sha256::hash( secret ); + auto nonce = fc::sha256::hash( one_time_key.get_secret() ); + auto blind_factor = fc::sha256::hash( child ); + + blinding_factors.push_back( blind_factor ); + + auto amount = asset_obj->amount_from_string(item.second); + total_amount += amount; + + + fc::ecc::public_key to_pub_key = to_key; + blind_output out; + out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); + out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); + if( to_amounts.size() > 1 ) + out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); + + + blind_confirmation::output conf_output; + conf_output.label = item.first; + conf_output.pub_key = to_key; + conf_output.decrypted_memo.amount = amount; + conf_output.decrypted_memo.blinding_factor = blind_factor; + conf_output.decrypted_memo.commitment = out.commitment; + conf_output.decrypted_memo.check = secret._hash[0]; + conf_output.confirmation.one_time_key = one_time_key.get_public_key(); + conf_output.confirmation.to = to_key; + conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); + conf_output.confirmation_receipt = conf_output.confirmation; + + confirm.outputs.push_back( conf_output ); + + bop.outputs.push_back(out); + } + bop.amount = total_amount; + bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); + + /** commitments must be in sorted order */ + std::sort( bop.outputs.begin(), bop.outputs.end(), + [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); + + confirm.trx.operations.push_back( bop ); + my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.current_fees); + confirm.trx.validate(); + confirm.trx = sign_transaction(confirm.trx, broadcast); + + if( broadcast ) + { + for( const auto& out : confirm.outputs ) + { + try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){} + } + } + + return confirm; +} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) } + +blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ) +{ + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); + stealth_confirmation conf(confirmation_receipt); + FC_ASSERT( conf.to ); + + blind_receipt result; + result.conf = conf; + + auto to_priv_key_itr = my->_keys.find( *conf.to ); + FC_ASSERT( to_priv_key_itr != my->_keys.end(), "No private key for receiver", ("conf",conf) ); + + + auto to_priv_key = wif_to_key( to_priv_key_itr->second ); + FC_ASSERT( to_priv_key ); + + auto secret = to_priv_key->get_shared_secret( conf.one_time_key ); + auto child = fc::sha256::hash( secret ); + + auto child_priv_key = to_priv_key->child( child ); + //auto blind_factor = fc::sha256::hash( child ); + + auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo ); + auto memo = fc::raw::unpack( plain_memo ); + + result.to_key = *conf.to; + result.to_label = get_key_label( result.to_key ); + if( memo.from ) + { + result.from_key = *memo.from; + result.from_label = get_key_label( result.from_key ); + if( result.from_label == string() ) + { + result.from_label = opt_from; + set_key_label( result.from_key, result.from_label ); + } + } + else + { + result.from_label = opt_from; + } + result.amount = memo.amount; + result.memo = opt_memo; + + // confirm the amount matches the commitment (verify the blinding factor) + auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value ); + FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) ); + + blind_balance bal; + bal.amount = memo.amount; + bal.to = *conf.to; + if( memo.from ) bal.from = *memo.from; + bal.one_time_key = conf.one_time_key; + bal.blinding_factor = memo.blinding_factor; + bal.commitment = memo.commitment; + bal.used = false; + + auto child_pubkey = child_priv_key.get_public_key(); + auto owner = authority(1, public_key_type(child_pubkey), 1); + result.control_authority = owner; + result.data = memo; + + auto child_key_itr = owner.key_auths.find( child_pubkey ); + if( child_key_itr != owner.key_auths.end() ) + my->_keys[child_key_itr->first] = key_to_wif( child_priv_key ); + + // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal ); + + result.date = fc::time_point::now(); + my->_wallet.blind_receipts.insert( result ); + my->_keys[child_pubkey] = key_to_wif( child_priv_key ); + + save_wallet_file(); + + return result; +} + +vector wallet_api::blind_history( string key_or_account ) +{ + vector result; + auto pub_key = get_public_key( key_or_account ); + + if( pub_key == public_key_type() ) + return vector(); + + for( auto& r : my->_wallet.blind_receipts ) + { + if( r.from_key == pub_key || r.to_key == pub_key ) + result.push_back( r ); + } + std::sort( result.begin(), result.end(), [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } ); + return result; +} /////////////// // peerplays // diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 38a3799b..5174396b 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -39,7 +39,7 @@ public: fixture_(fixture) { fixture_.init_nathan(); - fixture_.generate_blocks(HARDFORK_SON3_TIME); + fixture_.generate_blocks(HARDFORK_SON_FOR_HIVE_TIME); fixture_.generate_block(); } @@ -232,6 +232,7 @@ BOOST_AUTO_TEST_CASE( son_voting ) con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); // Vote for a son1account BOOST_TEST_MESSAGE("Voting for son1account"); + BOOST_CHECK(generate_maintenance_block()); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true); BOOST_CHECK(generate_maintenance_block()); @@ -337,8 +338,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - //! Set son number as 5 (as the begining son count) - unsigned int son_number = 5; + unsigned int son_number = gpo.parameters.maximum_son_count(); flat_map sidechain_public_keys; @@ -401,7 +401,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); BOOST_CHECK(generate_maintenance_block()); - BOOST_CHECK(gpo.active_sons.size() == son_number); + BOOST_CHECK(gpo.active_sons.size() == gpo.parameters.maximum_son_count()); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -454,13 +454,20 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; + BOOST_TEST_MESSAGE("sidechain_public_keys add for bitcoin account 1"); sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + BOOST_TEST_MESSAGE("sidechain_public_keys add for hive account 1"); sth.create_son("son1account", "http://son1", sidechain_public_keys); + BOOST_TEST_MESSAGE("created son account 1"); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; + BOOST_TEST_MESSAGE("sidechain_public_keys add for bitcoin account 2"); sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + BOOST_TEST_MESSAGE("sidechain_public_keys add for hive account 2"); sth.create_son("son2account", "http://son2", sidechain_public_keys); + BOOST_TEST_MESSAGE("created son account 2"); + BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes"); @@ -472,13 +479,17 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) // Get votes at start son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_start_votes = son1_obj.total_votes; + BOOST_TEST_MESSAGE("son1_start_votes: " << son1_start_votes); son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_start_votes = son2_obj.total_votes; - + BOOST_TEST_MESSAGE("son2_start_votes: " << son2_start_votes); std::vector accepted; std::vector rejected; signed_transaction update_votes_tx; + generate_block(); + BOOST_CHECK(generate_maintenance_block()); + // Vote for both SONs accepted.clear(); rejected.clear(); @@ -493,10 +504,12 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; + BOOST_TEST_MESSAGE("son1_end_votes: " << son1_end_votes); BOOST_CHECK(son1_end_votes > son1_start_votes); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; + BOOST_TEST_MESSAGE("son2_end_votes: " << son2_end_votes); BOOST_CHECK(son2_end_votes > son2_start_votes); son2_start_votes = son2_end_votes; @@ -513,18 +526,21 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; + BOOST_TEST_MESSAGE("son1_end_votes: " << son1_end_votes); BOOST_CHECK(son1_end_votes < son1_start_votes); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); + // voice distribution changed, SON2 now has all voices son2_end_votes = son2_obj.total_votes; + BOOST_TEST_MESSAGE("son2_end_votes: " << son2_end_votes); BOOST_CHECK((son2_end_votes > son2_start_votes)); // nathan spent funds for vb, it has different voting power son2_start_votes = son2_end_votes; // Try to reject incorrect SON accepted.clear(); rejected.clear(); - rejected.push_back("son1accnt"); + rejected.push_back("son1account"); BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, 1, true), fc::exception); generate_block(); @@ -560,8 +576,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) // Try to accept and reject the same SON accepted.clear(); rejected.clear(); - rejected.push_back("son1accnt"); - accepted.push_back("son1accnt"); + rejected.push_back("son1account"); + accepted.push_back("son1account"); BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); @@ -645,8 +661,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - //! Set son number as 5 (as the begining son count) - unsigned int son_number = 5; + unsigned int son_number = gpo.parameters.maximum_son_count(); flat_map sidechain_public_keys; @@ -688,7 +703,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) map active_sons = con.wallet_api_ptr->list_active_sons(); BOOST_CHECK(active_sons.size() == son_number); - for(unsigned int i = 1; i < son_number + 1; i++) + for(unsigned int i = 1; i < son_number; i++) { std::string name = "sonaccount" + fc::to_pretty_string(i); BOOST_CHECK(active_sons.find(name) != active_sons.end());