diff --git a/src/common/boost_serialization_maps.h b/src/common/boost_serialization_maps.h index 12a10d74..8c91d331 100644 --- a/src/common/boost_serialization_maps.h +++ b/src/common/boost_serialization_maps.h @@ -19,6 +19,9 @@ template struct TAssertEquality { #define BOOST_SERIALIZE(x) _arch & x; +#define BOOST_END_VERSION_UNDER(x) \ + if(ver < x ) {return true;} + #define END_BOOST_SERIALIZATION() } diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 12f7ed06..69d4f316 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -3812,27 +3812,90 @@ bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id) return true; } //------------------------------------------------------------------ +bool blockchain_storage::validate_ado_ownership(asset_op_verification_context& avc) +{ +// asset_id = AUTO_VAL_INIT(asset_id); +// CHECK_AND_ASSERT_MES(validate_asset_operation_balance_proof(tx, tx_id, ado, asset_id), false, "asset operation validation failed!"); + CHECK_AND_ASSERT_MES(ado.op_proof.has_value(), false, "Ownership validation failed - missing signature"); + + + CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << asset_id << " has invalid history size() == 0"); + + crypto::public_key owner_key = avc.asset_op_history->back().descriptor.owner; + asset_descriptor_operation ado_local = ado; + normalize_asset_operation_for_hashing(ado_local); + std::string buff = t_serializable_object_to_blob(ado_local); + crypto::hash h = crypto::cn_fast_hash(buff.data(), buff.size()); + return crypto::check_signature(h, owner_key, *ado.op_proof); +} +//------------------------------------------------------------------ bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado) { CRITICAL_REGION_LOCAL(m_read_lock); + + asset_op_verification_context avc = { tx , tx_id, ado }; + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) { - crypto::public_key asset_id{}; - CHECK_AND_ASSERT_MES(validate_asset_operation(tx, tx_id, ado, asset_id), false, "asset operation validation failed!"); - auto asset_history_ptr = m_db_assets.find(asset_id); - CHECK_AND_ASSERT_MES(!asset_history_ptr, false, "asset with id " << asset_id << " has already been registered"); + calculate_asset_id(avc.ado.descriptor.owner, &avc.asset_id_pt, &avc.asset_id); + avc.asset_op_history = m_db_assets.find(avc.asset_id); + CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << asset_id << " has already been registered"); + + avc.amout_to_validate = ado.descriptor.current_supply; + CHECK_AND_ASSERT_MES(validate_asset_operation_balance_proof(avc), false, "asset operation validation failed!"); + assets_container::t_value_type local_asset_history = AUTO_VAL_INIT(local_asset_history); local_asset_history.push_back(ado); m_db_assets.set(asset_id, local_asset_history); LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << asset_id << ": " << ado.descriptor.full_name, LOG_LEVEL_1); //TODO: //rise_core_event(CORE_EVENT_ADD_ASSET, alias_info_to_rpc_alias_info(ai)); - } - else - { - //TODO: implement other operations - CHECK_AND_ASSERT_THROW(false, "asset operation not implemented yet"); + }else + { + CHECK_AND_ASSERT_MES(avc.ado.opt_asset_id, false, "asset_id not provided for asset altering operation"); + avc.asset_op_history = m_db_assets.find(*avc.ado.opt_asset_id); + + CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size(), false, "asset with id " << asset_id << " has not been registered"); + // check ownership permission + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMMIT || 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"); + } + + avc.amout_to_validate = 0; + //validate balance proof + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) + { + //check that total current_supply haven't changed + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply != avc.asset_op_history->back().descriptor.current_supply); + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMMIT) + { + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > avc.asset_op_history->back().descriptor.current_supply); + avc.amout_to_validate = ado.descriptor.current_supply - avc.asset_op_history->back().descriptor.current_supply; + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < avc.asset_op_history->back().descriptor.current_supply); + avc.amout_to_validate = avc.asset_op_history->back().descriptor.current_supply - ado.descriptor.current_supply; + } + else + { + LOG_ERROR("Unknown operation type: " << ado.operation_type); + return false; + } + bool r = validate_asset_operation_balance_proof(avc); + CHECK_AND_ASSERT(r, false, "Balance proof validation failed for asset_descriptor_operation"); + assets_container::t_value_type local_asset_history = *avc.asset_op_history; + + local_asset_history.push_back(ado); + m_db_assets.set(asset_id, local_asset_history); + LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << asset_id << ": " << ado.descriptor.full_name, LOG_LEVEL_1); + + //TODO: + //rise_core_event(CORE_EVENT_ADD_ASSET, alias_info_to_rpc_alias_info(ai)); } return true; diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index f9dfa437..b9dde3d9 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -654,6 +654,7 @@ namespace currency bool unprocess_blockchain_tx_extra(const transaction& tx); bool process_blockchain_tx_attachments(const transaction& tx, uint64_t h, const crypto::hash& bl_id, uint64_t timestamp); bool unprocess_blockchain_tx_attachments(const transaction& tx, uint64_t h, uint64_t timestamp); + bool validate_ado_ownership(asset_op_verification_context& avc); bool pop_alias_info(const extra_alias_entry& ai); bool put_alias_info(const transaction& tx, extra_alias_entry& ai); bool pop_asset_info(const crypto::public_key& asset_id); diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 7a013ba2..7e2b928f 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -813,17 +813,26 @@ namespace currency uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED; asset_descriptor_base descriptor; boost::optional opt_amount_commitment; // premultiplied by 1/8 + boost::optional opt_proof; // operation proof - for update/emit + boost::optional opt_asset_id; // target asset_id - for update/emit BEGIN_VERSIONED_SERIALIZE() + CURRENT_VERSION(1) FIELD(operation_type) FIELD(descriptor) FIELD(opt_amount_commitment) + END_VERSION_UNDER(1) + FIELD(opt_proof) + FIELD(opt_asset_id) END_SERIALIZE() BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(operation_type) BOOST_SERIALIZE(descriptor) BOOST_SERIALIZE(opt_amount_commitment) + BOOST_END_VERSION_UNDER(1) + BOOST_SERIALIZE(opt_proof) + BOOST_SERIALIZE(opt_asset_id) END_BOOST_SERIALIZATION() }; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index f7e39eca..2034f3c5 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -557,15 +557,19 @@ namespace currency return true; } //----------------------------------------------------------------------------------------------- - bool validate_asset_operation(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, crypto::public_key& asset_id) + bool validate_asset_operation_balance_proof(asset_op_verification_context& context)// const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, crypto::public_key& asset_id) { - crypto::point_t asset_id_pt = crypto::c_point_0; - calculate_asset_id(ado.descriptor.owner, &asset_id_pt, &asset_id); - CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.proofs) == 1, false, "asset_operation_proof not present or present more than once"); - const asset_operation_proof& aop = get_type_in_variant_container_by_ref(tx.proofs); + CHECK_AND_ASSERT_MES(count_type_in_variant_container(context.tx.proofs) == 1, false, "asset_operation_proof not present or present more than once"); + const asset_operation_proof& aop = get_type_in_variant_container_by_ref(context.tx.proofs); - if (ado.descriptor.hidden_supply) + bool is_burn = false; + if (context.ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + is_burn = true; + } + + if (context.ado.descriptor.hidden_supply) { CHECK_AND_ASSERT_MES(aop.opt_amount_commitment_composition_proof.has_value(), false, "opt_amount_commitment_composition_proof is absent"); // TODO @#@# if asset is hidden -- theck composition proof @@ -574,11 +578,15 @@ namespace currency else { // make sure that amount commitment corresponds to opt_amount_commitment_g_proof - CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is absent"); + CHECK_AND_ASSERT_MES(context.ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is absent"); CHECK_AND_ASSERT_MES(aop.opt_amount_commitment_g_proof.has_value(), false, "opt_amount_commitment_g_proof is absent"); - crypto::point_t A = crypto::point_t(ado.opt_amount_commitment.get()).modify_mul8() - ado.descriptor.current_supply * asset_id_pt; + crypto::point_t A = is_burn ? + crypto::point_t(context.ado.opt_amount_commitment.get()).modify_mul8() + context.amount * asset_id_pt + : + crypto::point_t(context.ado.opt_amount_commitment.get()).modify_mul8() - context.amount * asset_id_pt + ; - bool r = crypto::check_signature(tx_id, A.to_public_key(), aop.opt_amount_commitment_g_proof.get()); + bool r = crypto::check_signature(context.tx_id, A.to_public_key(), aop.opt_amount_commitment_g_proof.get()); CHECK_AND_ASSERT_MES(r, false, "opt_amount_commitment_g_proof check failed"); } @@ -1105,6 +1113,14 @@ namespace currency string_tools::append_pod_to_strbuff(origin_blob, origin_hs); return origin_blob; } + + //--------------------------------------------------------------- + + void normalize_asset_operation_for_hashing(asset_descriptor_operation& op) + { + op.op_proof = boost::none; + } + //--------------------------------------------------------------- bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr /* = CURRENCY_TO_KEY_OUT_RELAXED */) { @@ -2309,31 +2325,34 @@ namespace currency pado = get_type_in_variant_container(tx.extra); if (pado) { - // only operation register is supported atm - CHECK_AND_ASSERT_MES(pado->operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER, false, "unsupported asset operation: " << (int)pado->operation_type); - crypto::secret_key asset_control_key{}; - bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, one_time_tx_secret_key, asset_control_key, pado->descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY); - CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed"); - - calculate_asset_id(pado->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); - - // set correct asset_id to the corresponding destination entries - uint64_t amount_of_emitted_asset = 0; - for (auto& item : shuffled_dsts) + if (pado->operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) { - if (item.asset_id == currency::null_pkey) - { - item.asset_id = gen_context.ao_asset_id; // set calculated asset_id to the asset's outputs, if this asset is being emitted within this tx - amount_of_emitted_asset += item.amount; - } - } - pado->descriptor.current_supply = amount_of_emitted_asset; // TODO: consider setting current_supply beforehand, not setting it hear in ad-hoc manner -- sowle + CHECK_AND_ASSERT_MES(pado->operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER, false, "unsupported asset operation: " << (int)pado->operation_type); + crypto::secret_key asset_control_key{}; + bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, one_time_tx_secret_key, asset_control_key, pado->descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY); + CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed"); - 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; - pado->opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); + calculate_asset_id(pado->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); + + // set correct asset_id to the corresponding destination entries + uint64_t amount_of_emitted_asset = 0; + for (auto& item : shuffled_dsts) + { + if (item.asset_id == currency::null_pkey) + { + item.asset_id = gen_context.ao_asset_id; // set calculated asset_id to the asset's outputs, if this asset is being emitted within this tx + amount_of_emitted_asset += item.amount; + } + } + pado->descriptor.current_supply = amount_of_emitted_asset; // TODO: consider setting current_supply beforehand, not setting it hear in ad-hoc manner -- sowle + + 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; + pado->opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); + + } //LOG_PRINT_CYAN("AO " << ": " << crypto::scalar_t(amount_of_emitted_asset) << " x " << gen_context.ao_asset_id_pt << " + " << gen_context.ao_amount_blinding_mask << " x G", LOG_LEVEL_0); //LOG_PRINT_CYAN(" == " << gen_context.ao_amount_commitment << ", x 1/8 == " << pado->opt_amount_commitment.get(), LOG_LEVEL_0); } diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index cef2f29a..2788de96 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -242,6 +242,17 @@ namespace currency std::vector amount_commitments; }; + struct asset_op_verification_context + { + const transaction& tx; + const crypto::hash& tx_id; + const asset_descriptor_operation& ado; + crypto::public_key asset_id = currency::null_pkey; + crypto::point_t asset_id_pt = crypto::c_point_0; + uint64_t amout_to_validate = 0; + std::shared_ptr< const std::list > asset_op_history; + }; + bool verify_multiple_zc_outs_range_proofs(const std::vector& range_proofs); bool generate_asset_surjection_proof(const crypto::hash& context_hash, bool has_non_zc_inputs, tx_generation_context& ogc, zc_asset_surjection_proof& result); bool verify_asset_surjection_proof(const transaction& tx, const crypto::hash& tx_id); @@ -250,7 +261,7 @@ namespace currency const std::vector& vouts, zc_outs_range_proof& result); bool check_tx_bare_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0); bool check_tx_balance(const transaction& tx, const crypto::hash& tx_id, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0); - bool validate_asset_operation(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, crypto::public_key& asset_id); + bool validate_asset_operation_balance_proof(asset_op_verification_context& context); //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t current_block_size, @@ -433,6 +444,8 @@ namespace currency uint64_t get_timstamp_from_word(std::string word, bool& password_used); std::string generate_origin_for_htlc(const txout_htlc& htlc, const account_keys& acc_keys); + void normalize_asset_operation_for_hashing(asset_descriptor_operation& op); + template typename std::conditional::value, const std::vector, std::vector >::type& get_txin_etc_options(t_txin_v& in) { diff --git a/src/gui/qt-daemon/layout b/src/gui/qt-daemon/layout index 0419d666..47d40574 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit 0419d66684dd044d56681d75359d4afd859ff2ea +Subproject commit 47d405747b7c04e69b4a7fd7a4a5cf866e098627 diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index d2e8d029..407f4576 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -113,6 +113,8 @@ do { \ if (_ser_ar.is_saving_arch()) { s_version = v; } \ } while (0); +#define END_VERSION_UNDER(x) \ + if(s_version < x ) {return true;} #define BEGIN_VERSIONED_SERIALIZE() \ diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index edba7e67..8d685b3a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4648,6 +4648,34 @@ void wallet2::deploy_new_asset(const currency::asset_descriptor_base& asset_info m_custom_assets[new_asset_id] = ado.descriptor; } //---------------------------------------------------------------------------------------------------- +void wallet2::update_emmit_asset(const crypto::public_key asset_id, std::vector& destinations, 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"); + + asset_descriptor_operation asset_reg_info = AUTO_VAL_INIT(asset_reg_info); + asset_reg_info.descriptor = asset_info; + asset_reg_info.operation_type = ASSET_DESCRIPTOR_OPERATION_EMMIT; + asset_reg_info.asset_id = asset_id; + construct_tx_param ctp = get_default_construct_tx_param(); + ctp.dsts = destinations; + ctp.extra.push_back(asset_reg_info); + ctp.need_at_least_1_zc = true; + ctp.control_key = own_asset_entry_it->second.control_key; + + finalized_tx ft = AUTO_VAL_INIT(ft); + this->transfer(ctp, ft, true, nullptr); + result_tx = ft.tx; + //get generated asset id + currency::asset_descriptor_operation ado = AUTO_VAL_INIT(ado); + bool r = get_type_in_variant_container(result_tx.extra, ado); + CHECK_AND_ASSERT_THROW_MES(r, "Failed find asset info in tx"); + calculate_asset_id(ado.descriptor.owner, nullptr, &new_asset_id); + + m_custom_assets[new_asset_id] = ado.descriptor; +} +//---------------------------------------------------------------------------------------------------- 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)) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 85871bbb..2df6e548 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -292,6 +292,7 @@ namespace tools bool shuffle = false; bool create_utxo_defragmentation_tx = false; bool need_at_least_1_zc = false; + crypto::secret_key control_key = currency::null_pkey; }; struct mode_separate_context diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index bdd04695..04e21245 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1031,7 +1031,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_HF(gen_no_attchments_in_coinbase, "3"); GENERATE_AND_PLAY(gen_no_attchments_in_coinbase_gentime); - GENERATE_AND_PLAY(gen_alias_tests); + //GENERATE_AND_PLAY(gen_alias_tests); + GENERATE_AND_PLAY_HF(gen_alias_tests, "4,1"); GENERATE_AND_PLAY(gen_alias_strange_data); GENERATE_AND_PLAY(gen_alias_concurrency_with_switch); GENERATE_AND_PLAY(gen_alias_same_alias_in_tx_pool);