From 87d5bb092f84a78eb44811f461c3b741872ef351 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Mon, 12 Feb 2024 22:56:47 +0400 Subject: [PATCH] assets: implemented ownership transfer --- src/currency_core/blockchain_storage.cpp | 2 +- src/currency_core/currency_format_utils.cpp | 93 +++++++++------- src/currency_core/currency_format_utils.h | 25 +++-- src/wallet/wallet2.cpp | 113 ++++++++++++++------ src/wallet/wallet2_base.h | 11 +- 5 files changed, 159 insertions(+), 85 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index febc64c9..ea0d350d 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -4126,7 +4126,7 @@ bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::has CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size(), false, "asset with id " << avc.asset_id << " has not been registered"); // check ownership permission - if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE /*|| ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN*/) { bool r = validate_ado_ownership(avc); CHECK_AND_ASSERT_MES(r, false, "Faild to validate ownership of asset_descriptor_operation, rejecting"); diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index c94e74c4..f91fbe5f 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -2154,14 +2154,16 @@ namespace currency { if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) { - crypto::secret_key asset_control_key{}; - bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, tx_key.sec, asset_control_key, ado.descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY); - CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed"); + //crypto::secret_key asset_control_key{}; + //bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, tx_key.sec, asset_control_key, ado.descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY); + //CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed"); + + ado.descriptor.owner = sender_account_keys.account_address.spend_public_key; calculate_asset_id(ado.descriptor.owner, &gen_context.ao_asset_id_pt, &gen_context.ao_asset_id); // calculate amount blinding mask - gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, asset_control_key, tx_key.pub); + gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, tx_key.sec, tx_key.pub); // set correct asset_id to the corresponding destination entries uint64_t amount_of_emitted_asset = 0; @@ -2178,6 +2180,38 @@ namespace currency gen_context.ao_amount_commitment = amount_of_emitted_asset * gen_context.ao_asset_id_pt + gen_context.ao_amount_blinding_mask * crypto::c_point_G; ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + CHECK_AND_ASSERT_MES(ado.opt_asset_id, false, "ado.opt_asset_id is not found at ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN"); + + gen_context.ao_asset_id = *ado.opt_asset_id; + gen_context.ao_asset_id_pt.from_public_key(gen_context.ao_asset_id); + // calculate amount blinding mask + gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, tx_key.sec, tx_key.pub); + gen_context.ao_commitment_in_outputs = true; + + // set correct asset_id to the corresponding destination entries + uint64_t amount_of_burned_assets = 0; + for (auto& item : ftp.sources) + { + if (item.asset_id == gen_context.ao_asset_id) + { + amount_of_burned_assets += item.amount; + } + } + for (auto& item : ftp.prepared_destinations) + { + if (item.asset_id == gen_context.ao_asset_id) + { + CHECK_AND_ASSERT_THROW_MES(amount_of_burned_assets >= item.amount, "Failed to find burn amount, failed condition: amount_of_burned_assets(" << amount_of_burned_assets << ") >= item.amount(" << item.amount << ")"); + amount_of_burned_assets -= item.amount; + } + } + ado.descriptor.current_supply -= amount_of_burned_assets; + + gen_context.ao_amount_commitment = amount_of_burned_assets * gen_context.ao_asset_id_pt + gen_context.ao_amount_blinding_mask * crypto::c_point_G; + ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); + } else { if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) @@ -2187,7 +2221,7 @@ namespace currency gen_context.ao_asset_id = *ado.opt_asset_id; gen_context.ao_asset_id_pt.from_public_key(gen_context.ao_asset_id); // calculate amount blinding mask - gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, ftp.asset_control_key, tx_key.pub); + gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, tx_key.sec, tx_key.pub); // set correct asset_id to the corresponding destination entries uint64_t amount_of_emitted_asset = 0; @@ -2212,46 +2246,23 @@ namespace currency //fields that not supposed to be changed? } - else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) - { - CHECK_AND_ASSERT_MES(ado.opt_asset_id, false, "ado.opt_asset_id is not found at ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN"); - - gen_context.ao_asset_id = *ado.opt_asset_id; - gen_context.ao_asset_id_pt.from_public_key(gen_context.ao_asset_id); - // calculate amount blinding mask - gen_context.ao_amount_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_ASSET_CONTROL_ABM, ftp.asset_control_key, tx_key.pub); - gen_context.ao_commitment_in_outputs = true; - - // set correct asset_id to the corresponding destination entries - uint64_t amount_of_burned_assets = 0; - for (auto& item: ftp.sources) - { - if (item.asset_id == gen_context.ao_asset_id) - { - amount_of_burned_assets += item.amount; - } - } - for (auto& item : ftp.prepared_destinations) - { - if (item.asset_id == gen_context.ao_asset_id ) - { - CHECK_AND_ASSERT_THROW_MES(amount_of_burned_assets >= item.amount, "Failed to find burn amount, failed condition: amount_of_burned_assets(" << amount_of_burned_assets << ") >= item.amount("<< item.amount << ")"); - amount_of_burned_assets -= item.amount; - } - } - ado.descriptor.current_supply -= amount_of_burned_assets; - - gen_context.ao_amount_commitment = amount_of_burned_assets * gen_context.ao_asset_id_pt + gen_context.ao_amount_blinding_mask * crypto::c_point_G; - ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); - } - if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_handle_asset_descriptor_operation_before_seal{ &ado }); //seal it with owners signature crypto::signature sig = currency::null_sig; - crypto::public_key pub_k = currency::null_pkey; - crypto::secret_key_to_public_key(ftp.asset_control_key, pub_k); - crypto::generate_signature(get_signature_hash_for_asset_operation(ado), pub_k, ftp.asset_control_key, sig); + crypto::hash h = get_signature_hash_for_asset_operation(ado) + if (ftp.pthirdparty_sign_handler) + { + bool r = ftp.pthirdparty_sign_handler->sign(h, ftp.ado_current_asset_owner, sig); + CHECK_AND_ASSERT_MES(r, false, "asset thirparty sign failed"); + } + else + { + crypto::public_key pub_k = currency::null_pkey; + crypto::secret_key_to_public_key(sender_account_keys.spend_secret_key, pub_k); + CHECK_AND_ASSERT_MES(ftp.ado_current_asset_owner == pub_k, false, "asset owner key not matched with provided private key for asset operation signing"); + crypto::generate_signature(h, pub_k, account_keys.spend_secret_key, sig); + } ado.opt_proof = sig; } return true; diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 7896ae5d..41cda480 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -134,15 +134,18 @@ namespace currency END_KV_SERIALIZE_MAP() }; - struct htlc_info - { - bool hltc_our_out_is_before_expiration; - }; + struct htlc_info + { + bool hltc_our_out_is_before_expiration; + }; + struct thirdparty_sign_handler + { + virtual bool sign(const crypto::hash& h, const crypto::public_key& owner_public_key, crypto::signature& sig); + }; struct finalize_tx_param { - uint64_t unlock_time; std::vector extra; std::vector attachments; @@ -158,10 +161,14 @@ namespace currency crypto::public_key spend_pub_key; // only for validations uint64_t tx_version; uint64_t mode_separate_fee = 0; - crypto::secret_key asset_control_key = currency::null_skey; + epee::misc_utils::events_dispatcher* pevents_dispatcher; - tx_generation_context gen_context{}; // solely for consolidated txs + + //crypto::secret_key asset_control_key = currency::null_skey; + crypto::public_key ado_current_asset_owner = null_pkey; + thirdparty_sign_handler* pthirdparty_sign_handler = nullptr; + BEGIN_SERIALIZE_OBJECT() FIELD(unlock_time) @@ -179,9 +186,11 @@ namespace currency FIELD(spend_pub_key) FIELD(tx_version) FIELD(mode_separate_fee) - FIELD(asset_control_key) if (flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) + { FIELD(gen_context); + } + FIELD(ado_current_asset_owner) END_SERIALIZE() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d53f1f88..578e3ec1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -402,20 +402,8 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op { if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) { - crypto::public_key self_check = AUTO_VAL_INIT(self_check); - crypto::secret_key asset_control_key = AUTO_VAL_INIT(asset_control_key); - bool r = derive_key_pair_from_key_pair(ptc.tx_pub_key, m_account.get_keys().spend_secret_key, asset_control_key, self_check, CRYPTO_HDS_ASSET_CONTROL_KEY); - if (!r) + if (ado.descriptor.owner != m_account.get_public_address().spend_public_key) { - //not critical error, continue to work - LOG_ERROR("Failed to derive_key_pair_from_key_pair for asset_descriptor_operation in tx " << ptc.tx_hash()); - break; - } - - if (self_check != ado.descriptor.owner) - { - //still not critical error - LOG_ERROR("Public key from asset_descriptor_operation(" << ado.descriptor.owner << ") not much with derived public key(" << self_check << "), for tx" << ptc.tx_hash()); break; } crypto::public_key asset_id{}; @@ -423,7 +411,6 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(m_own_asset_descriptors.count(asset_id) == 0, "asset with asset_id " << asset_id << " has already been registered in the wallet as own asset"); wallet_own_asset_context& asset_context = m_own_asset_descriptors[asset_id]; asset_context.asset_descriptor = ado.descriptor; - asset_context.control_key = asset_control_key; std::stringstream ss; ss << "New Asset Registered:" @@ -440,22 +427,83 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op if (m_wcallback) m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str()); } - else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(ado.opt_asset_id, get_asset_operation_type_string(ado.operation_type) << " failed with empty opt_asset_id"); + auto it = m_own_asset_descriptors.find(*ado.opt_asset_id); + if (it == m_own_asset_descriptors.end()) + break; + //asset had been updated + add_rollback_event(ptc.height, asset_update_event{ it->first, it->second }); + it->second.asset_descriptor = ado.descriptor; + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE ) { WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(ado.opt_asset_id, get_asset_operation_type_string(ado.operation_type) << " failed with empty opt_asset_id"); auto it = m_own_asset_descriptors.find(*ado.opt_asset_id); - WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(it != m_own_asset_descriptors.end(), "asset with asset_id " << *ado.opt_asset_id << " not found during " << get_asset_operation_type_string(ado.operation_type)); - if (it->second.asset_descriptor.owner != ado.descriptor.owner) + if (it == m_own_asset_descriptors.end()) { - //ownership of the asset had been transfered - add_rollback_event(ptc.height, asset_unown_event{ it->first, it->second }); - m_own_asset_descriptors.erase(it); + if (ado.descriptor.owner == m_account.get_public_address().spend_public_key) + { + // ownership of the asset acquired + + wallet_own_asset_context& asset_context = m_own_asset_descriptors[*ado.opt_asset_id]; + asset_context.asset_descriptor = ado.descriptor; + + std::stringstream ss; + ss << "Asset ownership acquired:" + << ENDL << "asset id: " << *ado.opt_asset_id + << ENDL << "Name: " << ado.descriptor.full_name + << ENDL << "Ticker: " << ado.descriptor.ticker + << ENDL << "Total Max Supply: " << print_asset_money(ado.descriptor.total_max_supply, ado.descriptor.decimal_point) + << ENDL << "Current Supply: " << print_asset_money(ado.descriptor.current_supply, ado.descriptor.decimal_point) + << ENDL << "Decimal Point: " << ado.descriptor.decimal_point; + + + add_rollback_event(ptc.height, asset_register_event{ *ado.opt_asset_id }); + WLT_LOG_MAGENTA(ss.str(), LOG_LEVEL_0); + if (m_wcallback) + m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str()); + } + else + { + // update event of the asset that we not control, skip + break; + } } else { - //asset had been updated - add_rollback_event(ptc.height, asset_update_event{ it->first, it->second }); - it->second.asset_descriptor = ado.descriptor; + //update event for asset that we control, check if ownership is still ours + if (ado.descriptor.owner != m_account.get_public_address().spend_public_key) + { + //ownership of the asset had been transfered + add_rollback_event(ptc.height, asset_unown_event{ it->first, it->second }); + m_own_asset_descriptors.erase(it); + + std::stringstream ss; + ss << "Asset ownership lost:" + << ENDL << "asset id: " << *ado.opt_asset_id + << ENDL << "New owner: " << ado.descriptor.owner + << ENDL << "Name: " << ado.descriptor.full_name + << ENDL << "Ticker: " << ado.descriptor.ticker + << ENDL << "Total Max Supply: " << print_asset_money(ado.descriptor.total_max_supply, ado.descriptor.decimal_point) + << ENDL << "Current Supply: " << print_asset_money(ado.descriptor.current_supply, ado.descriptor.decimal_point) + << ENDL << "Decimal Point: " << ado.descriptor.decimal_point; + + add_rollback_event(ptc.height, asset_register_event{ *ado.opt_asset_id }); + WLT_LOG_MAGENTA(ss.str(), LOG_LEVEL_0); + if (m_wcallback) + m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str()); + + + } + else + { + //just an update of the asset + add_rollback_event(ptc.height, asset_update_event{ it->first, it->second }); + it->second.asset_descriptor = ado.descriptor; + + } } } } while (false); @@ -869,7 +917,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t WLT_LOG_L2("Payment found, id (hex): " << epee::string_tools::buff_to_hex_nodelimer(payment_id) << ", tx: " << payment.m_tx_hash << ", amount: " << print_money_brief(payment.m_amount) << "subtransfers = " << payment.subtransfers.size()); } - if (ptc.spent_own_native_inputs) + if (ptc.employed_entries.receive.size() || ptc.employed_entries.spent.size()) { //check if there are asset_registration that belong to this wallet asset_descriptor_operation ado = AUTO_VAL_INIT(ado); @@ -4882,7 +4930,8 @@ void wallet2::emmit_asset(const crypto::public_key asset_id, std::vectorsecond.control_key; + ctp.ado_current_asset_owner = rsp.asset_descriptor.owner; + //ctp.asset_deploy_control_key = own_asset_entry_it->second.control_key; finalized_tx ft = AUTO_VAL_INIT(ft); this->transfer(ctp, ft, true, nullptr); @@ -4901,7 +4950,10 @@ void wallet2::update_asset(const crypto::public_key asset_id, const currency::as construct_tx_param ctp = get_default_construct_tx_param(); ctp.extra.push_back(asset_update_info); ctp.need_at_least_1_zc = true; - ctp.asset_deploy_control_key = own_asset_entry_it->second.control_key; + currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb); + bool r = this->daemon_get_asset_info(asset_id, adb); + CHECK_AND_ASSERT_THROW_MES(r, "Failed to get asset info from daemon"); + ctp.ado_current_asset_owner = adb.owner; finalized_tx ft = AUTO_VAL_INIT(ft); this->transfer(ctp, ft, true, nullptr); @@ -4910,8 +4962,8 @@ void wallet2::update_asset(const crypto::public_key asset_id, const currency::as //---------------------------------------------------------------------------------------------------- void wallet2::burn_asset(const crypto::public_key asset_id, uint64_t amount_to_burn, currency::transaction& result_tx) { - auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id); - CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Failed find asset_id " << asset_id << " in own assets list"); + //auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id); + //CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Failed find asset_id " << asset_id << " in own assets list"); COMMAND_RPC_GET_ASSET_INFO::request req; req.asset_id = asset_id; COMMAND_RPC_GET_ASSET_INFO::response rsp; @@ -4934,7 +4986,7 @@ void wallet2::burn_asset(const crypto::public_key asset_id, uint64_t amount_to_b construct_tx_param ctp = get_default_construct_tx_param(); ctp.extra.push_back(asset_burn_info); ctp.need_at_least_1_zc = true; - ctp.asset_deploy_control_key = own_asset_entry_it->second.control_key; + ctp.ado_current_asset_owner = rsp.asset_descriptor.owner; ctp.dsts.push_back(dst_to_burn); finalized_tx ft = AUTO_VAL_INIT(ft); @@ -6927,7 +6979,8 @@ bool wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx const currency::transaction& tx_for_mode_separate = msc.tx_for_mode_separate; assets_selection_context needed_money_map = get_needed_money(ctp.fee, ctp.dsts); - ftp.asset_control_key = ctp.asset_deploy_control_key; + ftp.ado_current_asset_owner = ctp.ado_current_asset_owner; + ftp.pthirdparty_sign_handler = ctp.pthirdparty_sign_handler; // // TODO @#@# need to do refactoring over this part to support hidden amounts and asset_id // diff --git a/src/wallet/wallet2_base.h b/src/wallet/wallet2_base.h index 52da0c59..d9c9589c 100644 --- a/src/wallet/wallet2_base.h +++ b/src/wallet/wallet2_base.h @@ -199,6 +199,7 @@ namespace tools END_SERIALIZE() }; + struct construct_tx_param { // preparing data for tx @@ -223,7 +224,9 @@ namespace tools bool shuffle = false; bool create_utxo_defragmentation_tx = false; bool need_at_least_1_zc = false; - crypto::secret_key asset_deploy_control_key = currency::null_skey; + //crypto::secret_key asset_deploy_control_key = currency::null_skey; + currency::thirdparty_sign_handler* pthirdparty_sign_handler = nullptr; + crypto::public_key ado_current_asset_owner = currency::null_pkey; }; struct mode_separate_context @@ -255,13 +258,11 @@ namespace tools struct wallet_own_asset_context { currency::asset_descriptor_base asset_descriptor; - crypto::secret_key control_key; - //uint64_t height = 0; + bool thirdparty_custody = false; BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(asset_descriptor) - BOOST_SERIALIZE(control_key) - //BOOST_SERIALIZE(height) + BOOST_SERIALIZE(thirdparty_custody) END_BOOST_SERIALIZATION() };