diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 6d4cbc1f..49455e7f 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -830,7 +830,7 @@ bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& t fee = get_tx_fee(tx_res_ptr->tx); purge_transaction_keyimages_from_blockchain(tx, true); - bool r = unprocess_blockchain_tx_extra(tx); + bool r = unprocess_blockchain_tx_extra(tx, tx_res_ptr->m_keeper_block_height); CHECK_AND_ASSERT_MES(r, false, "failed to unprocess_blockchain_tx_extra for tx " << tx_id); r = unprocess_blockchain_tx_attachments(tx, get_current_blockchain_size(), 0/*TODO: add valid timestamp here in future if need*/); @@ -3805,7 +3805,7 @@ bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx return true; } //------------------------------------------------------------------ -bool blockchain_storage::unprocess_blockchain_tx_extra(const transaction& tx) +bool blockchain_storage::unprocess_blockchain_tx_extra(const transaction& tx, const uint64_t height) { tx_extra_info ei = AUTO_VAL_INIT(ei); bool r = parse_and_validate_tx_extra(tx, ei); @@ -3818,9 +3818,7 @@ bool blockchain_storage::unprocess_blockchain_tx_extra(const transaction& tx) if (ei.m_asset_operation.operation_type != ASSET_DESCRIPTOR_OPERATION_UNDEFINED) { - crypto::public_key asset_id = currency::null_pkey; - CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ei.m_asset_operation, nullptr, &asset_id), false, "get_or_calculate_asset_id failed"); - r = pop_asset_info(asset_id); + r = pop_asset_info(ei.m_asset_operation, height); CHECK_AND_ASSERT_MES(r, false, "failed to pop_alias_info"); } return true; @@ -3862,15 +3860,16 @@ bool blockchain_storage::get_asset_info(const crypto::public_key& asset_id, asse { CRITICAL_REGION_LOCAL(m_read_lock); auto as_ptr = m_db_assets.find(asset_id); - if (as_ptr) - { - if (as_ptr->size()) - { - result = as_ptr->back().descriptor; - return true; - } - } - return false; + if (!as_ptr) + return false; + if (as_ptr->empty()) + return false; + // the last history item must have opt_descriptor, but we check it just to be sure + if (!as_ptr->back().opt_descriptor.has_value()) + return false; + + result = as_ptr->back().opt_descriptor.get(); + return true; } //------------------------------------------------------------------ uint64_t blockchain_storage::get_assets(uint64_t offset, uint64_t count, std::list& assets) const @@ -3882,15 +3881,19 @@ uint64_t blockchain_storage::get_assets(uint64_t offset, uint64_t count, std::li if (i < offset) return true; // continue - CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.size(), "asset_descriptor_history unexpectedly have 0 size, asset_id: " << asset_id); - assets.push_back(asset_descriptor_with_id()); - static_cast(assets.back()) = asset_descriptor_history.back().descriptor; - assets.back().asset_id = asset_id; + CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.size(), "unexpectedly, the asset_descriptor_history is empty; asset_id: " << asset_id); + CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.back().opt_descriptor.has_value(), "the last element of asset_descriptor_history doesn't have descriptor; asset_id: " << asset_id); + asset_descriptor_with_id& added_item = assets.emplace_back(); + asset_descriptor_base& added_item_adb = static_cast(added_item); + added_item_adb = asset_descriptor_history.back().opt_descriptor.get(); + replace_asset_ticker_and_full_name_if_invalid(added_item_adb, asset_id); + added_item.asset_id = asset_id; + if (assets.size() >= count) { - return false; + return false; // stop } - return true; + return true; // continue }); return assets.size(); } @@ -4084,21 +4087,71 @@ bool blockchain_storage::put_alias_info(const transaction & tx, extra_alias_entr return true; } //------------------------------------------------------------------ -bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id) +bool blockchain_storage::pop_asset_info(const asset_descriptor_operation& ado, const uint64_t height) { CRITICAL_REGION_LOCAL(m_read_lock); + crypto::public_key asset_id = currency::null_pkey; + CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ado, nullptr, &asset_id), false, "get_or_calculate_asset_id failed"); + auto asset_history_ptr = m_db_assets.find(asset_id); CHECK_AND_ASSERT_MES(asset_history_ptr && asset_history_ptr->size(), false, "empty name list in pop_asset_info"); - + assets_container::t_value_type local_asset_hist = *asset_history_ptr; - local_asset_hist.pop_back(); + + if (is_hardfork_active_for_height(ZANO_HARDFORK_05, height)) + { + // NEW HF5 handling + assets_container::t_value_type local_asset_hist = *asset_history_ptr; + asset_descriptor_operation& last_ado = local_asset_hist.back(); // above we made sure that the history isn't empty + CHECK_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "opt_descriptor is missing during asset pop, op: " << (int)ado.operation_type); + asset_descriptor_base& last_adb = last_ado.opt_descriptor.get(); + + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) + { + // just change the most recent history record, don't pop + CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (emit)"); + last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) - crypto::point_t(ado.opt_amount_commitment.get())).to_public_key(); + if (!last_adb.hidden_supply) + { + CHECK_AND_ASSERT_MES(last_ado.opt_amount.has_value() && ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (emit)"); + last_ado.opt_amount.get() -= ado.opt_amount.get(); + } + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + // just change the most recent history record, don't pop + CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (burn)"); + last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) + crypto::point_t(ado.opt_amount_commitment.get())).to_public_key(); + if (!last_adb.hidden_supply) + { + CHECK_AND_ASSERT_MES(last_ado.opt_amount.has_value() && ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (burn)"); + last_ado.opt_amount.get() += ado.opt_amount.get(); + } + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) + { + // just pop the most recent history record + local_asset_hist.pop_back(); + } + else + { + CHECK_AND_ASSERT_MES(false, false, "invalid operation: " << (int)ado.operation_type); + } + } + else + { + // HF4 + local_asset_hist.pop_back(); + } + + // both HF4 and HF5 if (local_asset_hist.size()) m_db_assets.set(asset_id, local_asset_hist); else m_db_assets.erase(asset_id); - LOG_PRINT_MAGENTA("[ASSET_POP]: " << asset_id << ": " << (!local_asset_hist.empty() ? "(prev)" : "(erased)"), LOG_LEVEL_1); + return true; } //------------------------------------------------------------------ @@ -4107,16 +4160,18 @@ bool blockchain_storage::validate_ado_ownership(asset_op_verification_context& a bool r = false; CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has empty history record"); const asset_descriptor_operation& last_ado = avc.asset_op_history->back(); + CHECK_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "last ado is missing opt_descriptor; asset id: " << avc.asset_id); + const asset_descriptor_base& last_adb = last_ado.opt_descriptor.get(); - if (is_hardfork_active(ZANO_HARDFORK_05)) // TODO: consider changing to height-specific check + if (is_hardfork_active_for_height(ZANO_HARDFORK_05, avc.height)) { - if (last_ado.descriptor.owner_eth_pub_key.has_value()) + if (last_adb.owner_eth_pub_key.has_value()) { - CHECK_AND_ASSERT_MES(last_ado.descriptor.owner == null_pkey, false, "owner_eth_pub_key is set but owner pubkey is nonzero"); + CHECK_AND_ASSERT_MES(last_adb.owner == null_pkey, false, "owner_eth_pub_key is set but owner pubkey is nonzero"); asset_operation_ownership_proof_eth aoop_eth{}; r = get_type_in_variant_container(avc.tx.proofs, aoop_eth); CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof_eth is missing"); - if (!crypto::verify_eth_signature(avc.tx_id, last_ado.descriptor.owner_eth_pub_key.value(), aoop_eth.eth_sig)) + if (!crypto::verify_eth_signature(avc.tx_id, last_adb.owner_eth_pub_key.value(), aoop_eth.eth_sig)) { LOG_ERROR("Failed to validate secp256k1 signature for hash: " << avc.tx_id << ", signature: " << aoop_eth.eth_sig); return false; @@ -4133,12 +4188,13 @@ bool blockchain_storage::validate_ado_ownership(asset_op_verification_context& a r = get_type_in_variant_container(avc.tx.proofs, aoop); CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof is missing"); - return crypto::verify_schnorr_sig(avc.tx_id, last_ado.descriptor.owner, aoop.gss); + return crypto::verify_schnorr_sig(avc.tx_id, last_adb.owner, aoop.gss); } //------------------------------------------------------------------ -bool blockchain_storage::validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const +bool blockchain_storage::validate_asset_operation_hf4(asset_op_verification_context& avc) const { CRITICAL_REGION_LOCAL(m_read_lock); + CHECK_AND_ASSERT_MES(!is_hardfork_active_for_height(ZANO_HARDFORK_05, avc.height), false, "validate_asset_operation_hf4 was called in HF5"); CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(avc.ado, &avc.asset_id_pt, &avc.asset_id), false, "get_or_calculate_asset_id failed"); avc.asset_op_history = m_db_assets.find(avc.asset_id); @@ -4150,16 +4206,25 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) { CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << avc.asset_id << " has already been registered"); - avc.amount_to_validate = ado.descriptor.current_supply; - if(this->is_hardfork_active(ZANO_HARDFORK_05)) - { - CHECK_AND_ASSERT_MES(validate_ado_initial(ado.descriptor), false, "validate_ado_initial failed!"); - } + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing while registering asset " << avc.asset_id); + + // CZ please review the following two added lines, do we need them _here_? I think they should go to hardfork_specific_terms... + // anyway, we musn't miss these checks for the sake of consensus + CHECK_AND_ASSERT_MES(!ado.opt_descriptor.get().owner_eth_pub_key.has_value(), false, "owner_eth_pub_key is prohibited before HF5"); + CHECK_AND_ASSERT_MES(!ado.opt_asset_id_salt.has_value(), false, "opt_asset_id_salt is prohibited before HF5"); + + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing while registering asset " << avc.asset_id); + avc.amount_to_validate = ado.opt_descriptor.get().current_supply; } else { CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size() > 0, false, "asset with id " << avc.asset_id << " has not been registered"); const asset_descriptor_operation& last_ado = avc.asset_op_history->back(); + CHECK_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "opt_descriptor is missing in last ado, op: " << (int)ado.operation_type); + const asset_descriptor_base& last_adb = last_ado.opt_descriptor.get(); + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing in ado, op: " << (int)ado.operation_type); + const asset_descriptor_base adb = ado.opt_descriptor.get(); + // check ownership permission if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) { @@ -4171,23 +4236,23 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) { //check that total current_supply haven't changed - CHECK_AND_ASSERT_MES(ado.descriptor.current_supply == last_ado.descriptor.current_supply, false, "update operation attempted to change emission, failed"); - CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "update operation modifies asset descriptor in a prohibited manner"); + CHECK_AND_ASSERT_MES(adb.current_supply == last_adb.current_supply, false, "update operation attempted to change emission, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(adb, last_adb), false, "update operation modifies asset descriptor in a prohibited manner"); need_to_validate_ao_amount_commitment = false; } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) { - CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > last_ado.descriptor.current_supply, false, "emit operation does not increase the current supply, failed"); - CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "emit operation modifies asset descriptor in a prohibited manner"); - CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "emit operation is not allowed to update meta info"); - avc.amount_to_validate = ado.descriptor.current_supply - last_ado.descriptor.current_supply; + CHECK_AND_ASSERT_MES(adb.current_supply > last_adb.current_supply, false, "emit operation does not increase the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(adb, last_adb), false, "emit operation modifies asset descriptor in a prohibited manner"); + CHECK_AND_ASSERT_MES(adb.meta_info == last_adb.meta_info, false, "emit operation is not allowed to update meta info"); + avc.amount_to_validate = adb.current_supply - last_adb.current_supply; } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) { - CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < last_ado.descriptor.current_supply, false, "burn operation does not decrease the current supply, failed"); - CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_ado.descriptor), false, "burn operation modifies asset descriptor in a prohibited manner"); - CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_ado.descriptor.meta_info, false, "burn operation is not allowed to update meta info"); - avc.amount_to_validate = last_ado.descriptor.current_supply - ado.descriptor.current_supply; + CHECK_AND_ASSERT_MES(adb.current_supply < last_adb.current_supply, false, "burn operation does not decrease the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(adb, last_adb), false, "burn operation modifies asset descriptor in a prohibited manner"); + CHECK_AND_ASSERT_MES(adb.meta_info == last_adb.meta_info, false, "burn operation is not allowed to update meta info"); + avc.amount_to_validate = last_adb.current_supply - adb.current_supply; } else { @@ -4205,35 +4270,182 @@ bool blockchain_storage::validate_asset_operation_against_current_blochain_state return true; } //------------------------------------------------------------------ -bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado) +bool blockchain_storage::validate_asset_operation(asset_op_verification_context& avc) const +{ + CRITICAL_REGION_LOCAL(m_read_lock); + CHECK_AND_ASSERT_MES(is_hardfork_active_for_height(ZANO_HARDFORK_05, avc.height), false, "validate_asset_operation was called before HF5"); + + CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(avc.ado, &avc.asset_id_pt, &avc.asset_id), false, "get_or_calculate_asset_id failed"); + avc.asset_op_history = m_db_assets.find(avc.asset_id); + + const asset_descriptor_operation& ado = avc.ado; + + bool need_to_validate_ao_amount_commitment = true; + + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) + { + CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << avc.asset_id << " has already been registered"); + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing while registering asset " << avc.asset_id); + avc.amount_to_validate = ado.opt_descriptor.get().current_supply; + // HF5 specific + CHECK_AND_ASSERT_MES(validate_ado_initial(ado.opt_descriptor.get()), false, "validate_ado_initial failed!"); + CHECK_AND_ASSERT_MES(ado.opt_amount.has_value() && avc.amount_to_validate == ado.opt_amount.get(), false, "opt_amount is missing or incorrect"); + } + else + { + CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size() > 0, false, "asset with id " << avc.asset_id << " has not been registered"); + const asset_descriptor_operation& last_ado = avc.asset_op_history->back(); + CHECK_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "opt_descriptor is missing in last ado, op: " << (int)ado.operation_type); + const asset_descriptor_base& last_adb = last_ado.opt_descriptor.get(); + + // check ownership permission + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) + { + bool r = validate_ado_ownership(avc); + CHECK_AND_ASSERT_MES(r, false, "Failed to validate ownership of asset_descriptor_operation, rejecting"); + } + + avc.amount_to_validate = 0; + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) + { + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing (update)"); + //check that total current_supply haven't changed + CHECK_AND_ASSERT_MES(ado.opt_descriptor.get().current_supply == last_adb.current_supply, false, "update operation attempted to change emission, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.opt_descriptor.get(), last_adb), false, "update operation modifies asset descriptor in a prohibited manner"); + need_to_validate_ao_amount_commitment = false; + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) + { + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > last_adb.current_supply, false, "emit operation does not increase the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_adb), false, "emit operation modifies asset descriptor in a prohibited manner"); + CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_adb.meta_info, false, "emit operation is not allowed to update meta info"); + + if (!last_adb.hidden_supply) + { + CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "opt_amount is missing (emit)"); + avc.amount_to_validate = ado.opt_amount.get(); + } + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply < last_adb.current_supply, false, "burn operation does not decrease the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, last_adb), false, "burn operation modifies asset descriptor in a prohibited manner"); + CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == last_adb.meta_info, false, "burn operation is not allowed to update meta info"); + + if (!last_adb.hidden_supply) + { + CHECK_AND_ASSERT_MES(ado.opt_amount.has_value(), false, "opt_amount is missing (burn)"); + avc.amount_to_validate = ado.opt_amount.get(); + } + } + else + { + LOG_ERROR("Unknown operation type: " << (int)ado.operation_type); + return false; + } + } + + if (need_to_validate_ao_amount_commitment) + { + bool r = validate_asset_operation_amount_commitment(avc); + CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation"); + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, const uint64_t height) { CRITICAL_REGION_LOCAL(m_read_lock); - asset_op_verification_context avc = { tx, tx_id, ado }; - CHECK_AND_ASSERT_MES(validate_asset_operation_against_current_blochain_state(avc), false, "asset operation validation failed"); - - assets_container::t_value_type local_asset_history{}; - if (avc.asset_op_history) - local_asset_history = *avc.asset_op_history; - local_asset_history.push_back(ado); - m_db_assets.set(avc.asset_id, local_asset_history); - - switch(ado.operation_type) + asset_op_verification_context avc = { tx, tx_id, ado, height }; + if (is_hardfork_active_for_height(ZANO_HARDFORK_05, height)) { - case ASSET_DESCRIPTOR_OPERATION_REGISTER: - LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(ado.descriptor.current_supply, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); - break; - case ASSET_DESCRIPTOR_OPERATION_UPDATE: - LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); - break; - case ASSET_DESCRIPTOR_OPERATION_EMIT: - LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); - break; - case ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN: - LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); - break; - default: - LOG_ERROR("Unknown operation type: " << (int)ado.operation_type); + CHECK_AND_ASSERT_MES(validate_asset_operation(avc), false, "asset operation validation failed (HF5)"); + // NEW HF5 handling here + + assets_container::t_value_type local_asset_history{}; + if (avc.asset_op_history) + local_asset_history = *avc.asset_op_history; + + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) + { + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing (register)"); // validated in validate_asset_operation(), just in case + const asset_descriptor_base& adb = ado.opt_descriptor.get(); + local_asset_history.push_back(ado); + LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(adb.current_supply, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1); + } + else + { + CHECK_AND_ASSERT_MES(!local_asset_history.empty(), false, "local_asset_history is empty (update/emit/burn)"); + asset_descriptor_operation& last_ado = local_asset_history.back(); + CHECK_AND_ASSERT_MES(last_ado.opt_descriptor.has_value(), false, "last_ado.opt_descriptor is missing (update/emit/burn)"); + asset_descriptor_base& last_adb = local_asset_history.back().opt_descriptor.get(); + + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) + { + local_asset_history.push_back(ado); + LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << last_adb.ticker << ", \"" << last_adb.full_name << "\"", LOG_LEVEL_1); + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) + { + // just change the most recent history record, don't push + CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (emit)"); + last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) + crypto::point_t(ado.opt_amount_commitment.get())).to_public_key(); + if (!last_adb.hidden_supply) + { + CHECK_AND_ASSERT_MES(last_ado.opt_amount.has_value() && ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (emit)"); + last_ado.opt_amount.get() += ado.opt_amount.get(); + } + LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, last_adb.decimal_point) << ", " << avc.asset_id << ": " << last_adb.ticker << ", \"" << last_adb.full_name << "\"", LOG_LEVEL_1); + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + // just change the most recent history record, don't push + CHECK_AND_ASSERT_MES(last_ado.opt_amount_commitment.has_value() && ado.opt_amount_commitment.has_value(), false, "last_ado.opt_amount_commitment or ado.opt_amount_commitment is missing (burn)"); + last_ado.opt_amount_commitment.get() = (crypto::point_t(last_ado.opt_amount_commitment.get()) - crypto::point_t(ado.opt_amount_commitment.get())).to_public_key(); + if (!last_adb.hidden_supply) + { + CHECK_AND_ASSERT_MES(last_ado.opt_amount.has_value() && ado.opt_amount.has_value(), false, "last_ado.opt_amount or ado.opt_amount is missing (burn)"); + last_ado.opt_amount.get() -= ado.opt_amount.get(); + } + LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, last_adb.decimal_point) << ", " << avc.asset_id << ": " << last_adb.ticker << ", \"" << last_adb.full_name << "\"", LOG_LEVEL_1); + } + else + CHECK_AND_ASSERT_MES(false, false, "Unknown operation type: " << (int)ado.operation_type); + } + + m_db_assets.set(avc.asset_id, local_asset_history); + } + else + { + // HF4 + CHECK_AND_ASSERT_MES(validate_asset_operation_hf4(avc), false, "asset operation validation failed (HF4)"); + + assets_container::t_value_type local_asset_history{}; + if (avc.asset_op_history) + local_asset_history = *avc.asset_op_history; + local_asset_history.push_back(ado); + m_db_assets.set(avc.asset_id, local_asset_history); + + const asset_descriptor_base& adb = ado.opt_descriptor.get(); // in HF4 descriptor must always be present, validated in validate_asset_operation_hf4() + switch(ado.operation_type) + { + case ASSET_DESCRIPTOR_OPERATION_REGISTER: + LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(adb.current_supply, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1); + break; + case ASSET_DESCRIPTOR_OPERATION_UPDATE: + LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1); + break; + case ASSET_DESCRIPTOR_OPERATION_EMIT: + LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1); + break; + case ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN: + LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, adb.decimal_point) << ", " << avc.asset_id << ": " << adb.ticker << ", \"" << adb.full_name << "\"", LOG_LEVEL_1); + break; + default: + LOG_ERROR("Unknown operation type: " << (int)ado.operation_type); + } } return true; @@ -4309,7 +4521,7 @@ bool blockchain_storage::prevalidate_alias_info(const transaction& tx, const ext return true; } //------------------------------------------------------------------ -bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id) +bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id, const uint64_t height) { //check transaction extra tx_extra_info ei = AUTO_VAL_INIT(ei); @@ -4325,7 +4537,7 @@ bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, cons } if (ei.m_asset_operation.operation_type != ASSET_DESCRIPTOR_OPERATION_UNDEFINED) { - r = put_asset_info(tx, tx_id, ei.m_asset_operation); + r = put_asset_info(tx, tx_id, ei.m_asset_operation, height); CHECK_AND_ASSERT_MES(r, false, "failed to put_asset_info"); } @@ -4525,7 +4737,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const CHECK_AND_ASSERT_MES(validate_tx_for_hardfork_specific_terms(tx, tx_id, bl_height), false, "tx " << tx_id << ": hardfork-specific validation failed"); TIME_MEASURE_START_PD(tx_process_extra); - bool r = process_blockchain_tx_extra(tx, tx_id); + bool r = process_blockchain_tx_extra(tx, tx_id, bl_height); CHECK_AND_ASSERT_MES(r, false, "failed to process_blockchain_tx_extra"); TIME_MEASURE_FINISH_PD_COND(need_to_profile, tx_process_extra); @@ -4541,7 +4753,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const { LOG_ERROR("critical internal error: add_transaction_input_visitor failed. but key_images should be already checked"); purge_transaction_keyimages_from_blockchain(tx, false); - bool r = unprocess_blockchain_tx_extra(tx); + bool r = unprocess_blockchain_tx_extra(tx, bl_height); CHECK_AND_ASSERT_MES(r, false, "failed to unprocess_blockchain_tx_extra"); unprocess_blockchain_tx_attachments(tx, bl_height, timestamp); @@ -4562,7 +4774,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const { LOG_ERROR("critical internal error: tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain"); purge_transaction_keyimages_from_blockchain(tx, true); - bool r = unprocess_blockchain_tx_extra(tx); + bool r = unprocess_blockchain_tx_extra(tx, bl_height); CHECK_AND_ASSERT_MES(r, false, "failed to unprocess_blockchain_tx_extra"); unprocess_blockchain_tx_attachments(tx, bl_height, timestamp); @@ -6967,7 +7179,7 @@ bool blockchain_storage::is_hardfork_active(size_t hardfork_id) const //------------------------------------------------------------------ bool blockchain_storage::is_hardfork_active_for_height(size_t hardfork_id, uint64_t height) const { - return m_core_runtime_config.is_hardfork_active_for_height(hardfork_id, height); + return m_core_runtime_config.is_hardfork_active_for_height(hardfork_id, height != UINT64_MAX ? height : m_db_blocks.size()); } //------------------------------------------------------------------ bool blockchain_storage::prevalidate_block(const block& bl) diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 9d7cba3a..e6b30edf 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -381,7 +381,8 @@ namespace currency const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0)const; bool validate_ado_ownership(asset_op_verification_context& avc) const; - bool validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const; + bool validate_asset_operation_hf4(asset_op_verification_context& avc) const; + bool validate_asset_operation(asset_op_verification_context& avc) const; void set_core_runtime_config(const core_runtime_config& pc) const; const core_runtime_config& get_core_runtime_config()const; @@ -679,14 +680,14 @@ namespace currency uint64_t get_adjusted_time()const; bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); bool update_next_comulative_size_limit(); - bool process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id); - bool unprocess_blockchain_tx_extra(const transaction& tx); + bool process_blockchain_tx_extra(const transaction& tx, const crypto::hash& tx_id, const uint64_t height); + bool unprocess_blockchain_tx_extra(const transaction& tx, const uint64_t height); 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 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); - bool put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado); + bool pop_asset_info(const asset_descriptor_operation& ado, const uint64_t height); + bool put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, const uint64_t height); void fill_addr_to_alias_dict(); //bool resync_spent_tx_flags(); bool prune_ring_signatures_and_attachments_if_need(); diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 271b8706..d2abec65 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -803,12 +803,13 @@ namespace currency { uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED; uint8_t version = 1; - - boost::optional opt_amount_commitment; // premultiplied by 1/8 - boost::optional opt_asset_id; // target asset_id - for update/emit - boost::optional opt_descriptor; //used in deploy/update - boost::optional opt_amount; //used in burn/emit - std::vector etc; //reserved for future use + // register emit burn update + boost::optional opt_amount_commitment; // + + + - (premultiplied by 1/8) + boost::optional opt_asset_id; // - + + + + boost::optional opt_descriptor; // + - - + + boost::optional opt_amount; // ? ? ? - (only for non-hidden supply) + boost::optional opt_asset_id_salt; // ? - - - (optional) + std::vector etc; // (reserved for future use) BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_OPERATION_LAST_VER, version) @@ -819,6 +820,7 @@ namespace currency FIELD(opt_asset_id) FIELD(opt_descriptor) FIELD(opt_amount) + FIELD(opt_asset_id_salt) FIELD(etc) END_SERIALIZE() @@ -832,16 +834,18 @@ namespace currency BOOST_SERIALIZE(opt_asset_id) BOOST_SERIALIZE(opt_descriptor) BOOST_SERIALIZE(opt_amount) + BOOST_SERIALIZE(opt_asset_id_salt) BOOST_SERIALIZE(etc) END_BOOST_SERIALIZATION_TOTAL_FIELDS(7) BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(version) DOC_DSCR("Asset operation type struct version") DOC_EXMP(2) DOC_END KV_SERIALIZE(operation_type) DOC_DSCR("Asset operation type identifier") DOC_EXMP(1) DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(opt_amount_commitment) DOC_DSCR("Asset operation amount commitment(optional)") DOC_EXMP("5688b56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(opt_asset_id) DOC_DSCR("ID of an asset.(optional)") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END - KV_SERIALIZE(opt_descriptor) DOC_DSCR("Asset operation amount commitment(optional)") DOC_EXMP_AUTO() DOC_END - KV_SERIALIZE(opt_amount) DOC_DSCR("Asset operation amount(optional, needed only for burn/emit)") DOC_EXMP_AUTO() DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(opt_amount_commitment) DOC_DSCR("(optional) Asset operation amount commitment (register/emit/burn).") DOC_EXMP("5688b56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(opt_asset_id) DOC_DSCR("(optional) ID of an asset (emit/burn/update).") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END + KV_SERIALIZE(opt_descriptor) DOC_DSCR("(optional) Asset operation descriptor (register/update).") DOC_EXMP_AUTO() DOC_END + KV_SERIALIZE(opt_amount) DOC_DSCR("(optional) Asset operation amount (register/emit/burn when supply is non-hidden).") DOC_EXMP_AUTO() DOC_END + KV_SERIALIZE(opt_asset_id_salt) DOC_DSCR("(optional) Asset ID salt. May only be used for asset registration.") DOC_EXMP_AUTO() DOC_END //KV_SERIALIZE(etc) DOC_DSCR("Extra operations") DOC_EXMP_AUTO() DOC_END <---- serialization for variant not supported yet END_KV_SERIALIZE_MAP() diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index fc3cc932..5765e7c0 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -619,7 +619,8 @@ namespace currency { // make sure that amount commitment corresponds to opt_amount_commitment_g_proof 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(context.ado.amount_commitment).modify_mul8() - context.amount_to_validate * context.asset_id_pt; + CHECK_AND_ASSERT_MES(context.ado.opt_amount_commitment.has_value(), false, "amount_commitment is absent"); + crypto::point_t A = crypto::point_t(context.ado.opt_amount_commitment.get()).modify_mul8() - context.amount_to_validate * context.asset_id_pt; 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"); @@ -675,12 +676,14 @@ namespace currency { if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) { + CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is missing"); // amount_commitment supposed to be validated earlier in validate_asset_operation_amount_commitment() - sum_of_pseudo_out_amount_commitments += crypto::point_t(ado.amount_commitment); // *1/8 + sum_of_pseudo_out_amount_commitments += crypto::point_t(ado.opt_amount_commitment.get()); // *1/8 } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) { - outs_commitments_sum += crypto::point_t(ado.amount_commitment); // *1/8 + CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is missing"); + outs_commitments_sum += crypto::point_t(ado.opt_amount_commitment.get()); // *1/8 } } size_t zc_sigs_count = 0; @@ -2186,18 +2189,23 @@ namespace currency // otherwise, it must be a register operation CHECK_AND_ASSERT_MES(ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER, false, "unexpected asset operation type: " << (int)ado.operation_type << ", " << get_asset_operation_type_string(ado.operation_type)); + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing: " << (int)ado.operation_type << ", " << get_asset_operation_type_string(ado.operation_type)); + const asset_descriptor_base &adb = ado.opt_descriptor.get(); // calculate asset id crypto::hash_helper_t::hs_t hsc; hsc.add_32_chars(CRYPTO_HDS_ASSET_ID); - hsc.add_hash(crypto::hash_helper_t::h(ado.descriptor.ticker)); - hsc.add_hash(crypto::hash_helper_t::h(ado.descriptor.full_name)); - hsc.add_hash(crypto::hash_helper_t::h(ado.descriptor.meta_info)); - hsc.add_scalar(crypto::scalar_t(ado.descriptor.total_max_supply)); - hsc.add_scalar(crypto::scalar_t(ado.descriptor.decimal_point)); - hsc.add_pub_key(ado.descriptor.owner); - if (ado.descriptor.owner_eth_pub_key.has_value()) - hsc.add_eth_pub_key(ado.descriptor.owner_eth_pub_key.value()); + hsc.add_hash(crypto::hash_helper_t::h(adb.ticker)); + hsc.add_hash(crypto::hash_helper_t::h(adb.full_name)); + hsc.add_hash(crypto::hash_helper_t::h(adb.meta_info)); + hsc.add_scalar(crypto::scalar_t(adb.total_max_supply)); + hsc.add_scalar(crypto::scalar_t(adb.decimal_point)); + hsc.add_pub_key(adb.owner); + if (adb.owner_eth_pub_key.has_value()) + hsc.add_eth_pub_key(adb.owner_eth_pub_key.value()); + if (ado.opt_asset_id_salt.has_value()) + hsc.add_scalar(crypto::scalar_t(ado.opt_asset_id_salt.get())); + crypto::hash h = hsc.calc_hash_no_reduce(); // this hash function needs to be computationally expensive (s.a. the whitepaper) @@ -2242,16 +2250,11 @@ 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"); - // - // old: - // asset_control_key = Hs(CRYPTO_HDS_ASSET_CONTROL_KEY, 8 * tx_key.sec * sender_account_keys.account_address.spend_public_key, 0) - // ado.descriptor.owner = asset_control_key * G + CHECK_AND_ASSERT_MES(ado.opt_descriptor.has_value(), false, "opt_descriptor is missing (register)"); + asset_descriptor_base& adb = ado.opt_descriptor.get(); - if (!ado.descriptor.owner_eth_pub_key.has_value()) - ado.descriptor.owner = sender_account_keys.account_address.spend_public_key; + if (!adb.owner_eth_pub_key.has_value()) + adb.owner = sender_account_keys.account_address.spend_public_key; CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(ado, &gen_context.ao_asset_id_pt, &gen_context.ao_asset_id), false, "get_or_calculate_asset_id failed"); @@ -2268,10 +2271,11 @@ namespace currency amount_of_emitted_asset += item.amount; } } - ado.descriptor.current_supply = amount_of_emitted_asset; // TODO: consider setting current_supply beforehand, not setting it hear in ad-hoc manner -- sowle + adb.current_supply = amount_of_emitted_asset; + ado.opt_amount = amount_of_emitted_asset; // TODO: support hidden supply -- 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; - ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); + ado.opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) { @@ -2298,10 +2302,10 @@ namespace currency amount_of_burned_assets -= item.amount; } } - ado.descriptor.current_supply -= amount_of_burned_assets; + ado.opt_amount = amount_of_burned_assets; // TODO: support hidden supply -- sowle 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(); + ado.opt_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_burn{ &ado }); @@ -2325,10 +2329,10 @@ namespace currency item.asset_id = gen_context.ao_asset_id; } } - ado.descriptor.current_supply += amount_of_emitted_asset; + ado.opt_amount = amount_of_emitted_asset; // TODO: support hidden supply -- 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; - ado.amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); + ado.opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index f1706b54..2f4e1783 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -259,6 +259,7 @@ namespace currency const transaction& tx; const crypto::hash& tx_id; const asset_descriptor_operation& ado; + uint64_t height = UINT64_MAX; // default value means the height of the upcoming block (top_block + 1) crypto::public_key asset_id = currency::null_pkey; crypto::point_t asset_id_pt = crypto::c_point_0; uint64_t amount_to_validate = 0; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 305bd6d4..afd527b3 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1316,6 +1316,13 @@ namespace tools { WALLET_RPC_BEGIN_TRY_ENTRY(); + if (!currency::validate_asset_ticker_and_full_name(req.asset_descriptor)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; + er.message = "asset ticker or full_name is invalid"; + return false; + } + std::vector currency_destinations; rpc_destinations_to_currency_destinations(req.destinations, true, !req.do_not_split_destinations, currency_destinations);