diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h index 1389ba03..e607044b 100644 --- a/contrib/epee/include/misc_language.h +++ b/contrib/epee/include/misc_language.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "include_base_utils.h" #include "auto_val_init.h" @@ -372,6 +373,11 @@ namespace misc_utils virtual void do_call(){}; }; + template + struct call_basic_param + { + virtual void do_call(param_t& p) {}; + }; template struct call_specific: public call_basic @@ -386,12 +392,34 @@ namespace misc_utils t_callback m_cb; }; + template + struct call_specific_param : public call_basic_param + { + call_specific_param(t_callback cb) :m_cb(cb) + {} + virtual void do_call(const param_t& p) + { + m_cb(p); + } + private: + t_callback m_cb; + }; + + + template auto build_abstract_callback(t_callback cb) -> std::shared_ptr { return std::shared_ptr(new call_specific(cb)); } + + template + auto build_abstract_callback_param(t_callback cb) -> std::shared_ptr> + { + return std::shared_ptr>(new call_specific_param(cb)); + } + template @@ -427,6 +455,67 @@ namespace misc_utils return res.first; } + + class events_dispatcher + { + + public: + + template + struct callback_entry + { + std::shared_ptr > m_cb; + }; + + std::map m_callbacks; + + template + void SUBSCIRBE_DEBUG_EVENT(callback_t cb) + { + std::type_index ti = typeid(param_t); + auto it = m_callbacks.find(ti); + if (it != m_callbacks.end()) + { + throw std::runtime_error("Handler for this type already registered"); + } + + callback_entry cb_entry = { epee::misc_utils::build_abstract_callback_param(cb) }; + + m_callbacks[ti] = cb_entry; + } + + template + void UNSUBSCRIBE_DEBUG_EVENT() + { + std::type_index ti = typeid(param_t); + auto it = m_callbacks.find(ti); + if (it != m_callbacks.end()) + { + m_callbacks.erase(it); + } + } + + + template + void RAISE_DEBUG_EVENT(const param_t& p) + { + std::type_index ti = typeid(param_t); + auto it = m_callbacks.find(ti); + if (it != m_callbacks.end()) + { + callback_entry* pcallback_entry = boost::any_cast>(&it->second); + if (!pcallback_entry) + { + throw std::runtime_error("Unexpected error: registered tipe holding something else in boost::eny"); + } + pcallback_entry->m_cb->do_call(p); + } + } + + }; + + + } // namespace misc_utils } // namespace epee diff --git a/src/common/boost_serialization_maps.h b/src/common/boost_serialization_maps.h index 12a10d74..805febee 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;} + #define END_BOOST_SERIALIZATION() } diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index 30a97135..6ecdf713 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -727,6 +727,15 @@ namespace crypto return result; } + point_t operator-() const + { + point_t result = *this; + fe zero = {0}; + fe_sub(result.m_p3.Y, zero, result.m_p3.Y); + fe_sub(result.m_p3.Z, zero, result.m_p3.Z); + return result; + } + point_t& modify_mul8() { ge_mul8_p3(&m_p3, &m_p3); diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index f6c6ae81..d8815803 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -3803,27 +3803,107 @@ 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(avc.ado.opt_proof.has_value(), false, "Ownership validation failed - missing signature"); + + + CHECK_AND_ASSERT_MES(avc.asset_op_history->size() != 0, false, "asset with id " << avc.asset_id << " has invalid history size() == 0"); + + crypto::public_key owner_key = avc.asset_op_history->back().descriptor.owner; + return crypto::check_signature(get_signature_hash_for_asset_operation(avc.ado), owner_key, *avc.ado.opt_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!"); + 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 " << avc.asset_id << " has already been registered"); + + avc.amount_to_validate = ado.descriptor.current_supply; + CHECK_AND_ASSERT_MES(validate_asset_operation_amount_proof(avc), 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"); 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)); + m_db_assets.set(avc.asset_id, local_asset_history); + 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); } else - { - //TODO: implement other operations - CHECK_AND_ASSERT_THROW(false, "asset operation not implemented yet"); + { + 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); + avc.asset_id = *avc.ado.opt_asset_id; // consider redisign + avc.asset_id_pt.from_public_key(avc.asset_id); + + 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) + { + bool r = validate_ado_ownership(avc); + CHECK_AND_ASSERT_MES(r, false, "Faild to validate ownership of asset_descriptor_operation, rejecting"); + } + + avc.amount_to_validate = 0; + bool need_to_validate_balance_proof = true; + 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, false, "update operation attempted to change emission, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "update operation attempted to change fileds that shouldn't be modified, failed"); + need_to_validate_balance_proof = false; + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) + { + CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > avc.asset_op_history->back().descriptor.current_supply, false, "emit operation does not increase the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "emit operation is not allowed to update fields"); + CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "emit operation is not allowed to update meta info"); + avc.amount_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, false, "burn operation does not decrease the current supply, failed"); + CHECK_AND_ASSERT_MES(validate_ado_update_allowed(ado.descriptor, avc.asset_op_history->back().descriptor), false, "burn operation is not allowed to update fields"); + CHECK_AND_ASSERT_MES(ado.descriptor.meta_info == avc.asset_op_history->back().descriptor.meta_info, false, "burn operation is not allowed to update meta info"); + avc.amount_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; + } + + if (need_to_validate_balance_proof) + { + bool r = validate_asset_operation_amount_proof(avc); + CHECK_AND_ASSERT_MES(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(*avc.ado.opt_asset_id, local_asset_history); + + switch(ado.operation_type) + { + 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: " << ado.operation_type); + } } return true; diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 992ab6a9..6b28166c 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -653,6 +653,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 3ad4cf45..b9b85f20 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -749,7 +749,7 @@ namespace currency crypto::public_key owner = currency::null_pkey; // consider premultipling by 1/8 bool hidden_supply = false; - BEGIN_VERSIONED_SERIALIZE() + BEGIN_VERSIONED_SERIALIZE(0) FIELD(total_max_supply) FIELD(current_supply) FIELD(decimal_point) @@ -769,6 +769,7 @@ namespace currency BOOST_SERIALIZE(full_name) BOOST_SERIALIZE(meta_info) BOOST_SERIALIZE(owner) + BOOST_SERIALIZE(hidden_supply) END_BOOST_SERIALIZATION() BEGIN_KV_SERIALIZE_MAP() @@ -779,6 +780,7 @@ namespace currency KV_SERIALIZE(full_name) KV_SERIALIZE(meta_info) KV_SERIALIZE_POD_AS_HEX_STRING(owner) + KV_SERIALIZE(hidden_supply) END_KV_SERIALIZE_MAP() }; @@ -803,7 +805,7 @@ namespace currency #define ASSET_DESCRIPTOR_OPERATION_UNDEFINED 0 #define ASSET_DESCRIPTOR_OPERATION_REGISTER 1 -#define ASSET_DESCRIPTOR_OPERATION_EMMIT 2 +#define ASSET_DESCRIPTOR_OPERATION_EMIT 2 #define ASSET_DESCRIPTOR_OPERATION_UPDATE 3 #define ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN 4 @@ -813,17 +815,25 @@ 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() + BEGIN_VERSIONED_SERIALIZE(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() }; @@ -833,7 +843,7 @@ namespace currency boost::optional opt_amount_commitment_composition_proof; // for hidden supply boost::optional opt_amount_commitment_g_proof; // for non-hidden supply, proofs that amount_commitment - supply * asset_id = lin(G) - BEGIN_VERSIONED_SERIALIZE() + BEGIN_VERSIONED_SERIALIZE(0) FIELD(opt_amount_commitment_composition_proof) FIELD(opt_amount_commitment_g_proof) END_SERIALIZE() diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index e1bfe6d1..4a9be162 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -33,6 +33,8 @@ using namespace epee; #include "common/mnemonic-encoding.h" #include "crypto/bitcoin/sha256_helper.h" #include "crypto_config.h" +#include "wallet/wallet_debug_events_definitions.h" + #define DBG_VAL_PRINT(x) ((void)0) // LOG_PRINT_CYAN(std::setw(42) << std::left << #x ":" << x, LOG_LEVEL_0) @@ -106,8 +108,8 @@ namespace currency secret_index = ring.size() - 1; } - // additional ring member for asset emitting operation - if (!ogc.ao_amount_blinding_mask.is_zero()) + // additional ring member for asset emitting operation (which has asset operation commitment in the inputs part) + if (!ogc.ao_amount_blinding_mask.is_zero() && !ogc.ao_commitment_in_outputs) { ring.emplace_back(ogc.ao_asset_id_pt - T); if (secret_index == SIZE_MAX && H == ogc.ao_asset_id) @@ -307,12 +309,13 @@ namespace currency { // there're ZC inputs => in main balance equation we only need to cancel out X-component, because G-component cancelled out by choosing blinding mask for the last pseudo out amount commitment - crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt + ogc.pseudo_out_amount_commitments_sum + ogc.ao_amount_commitment - ogc.amount_commitments_sum; + crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt + ogc.pseudo_out_amount_commitments_sum + (ogc.ao_commitment_in_outputs ? -ogc.ao_amount_commitment : ogc.ao_amount_commitment) - ogc.amount_commitments_sum; crypto::scalar_t secret_x = ogc.real_in_asset_id_blinding_mask_x_amount_sum - ogc.asset_id_blinding_mask_x_amount_sum; DBG_VAL_PRINT(bare_inputs_sum); DBG_VAL_PRINT(fee); DBG_VAL_PRINT(ogc.pseudo_out_amount_commitments_sum); + DBG_VAL_PRINT((int)ogc.ao_commitment_in_outputs); DBG_VAL_PRINT(ogc.ao_amount_commitment); DBG_VAL_PRINT(ogc.amount_commitments_sum); DBG_VAL_PRINT(ogc.real_in_asset_id_blinding_mask_x_amount_sum); @@ -321,7 +324,8 @@ namespace currency DBG_VAL_PRINT(secret_x); #ifndef NDEBUG - CHECK_AND_ASSERT_MES(commitment_to_zero == secret_x * crypto::c_point_X, false, "internal error: commitment_to_zero is malformed (X)"); + bool commitment_to_zero_is_sane = commitment_to_zero == secret_x * crypto::c_point_X; + CHECK_AND_ASSERT_MES(commitment_to_zero_is_sane, false, "internal error: commitment_to_zero is malformed (X)"); #endif r = crypto::generate_schnorr_sig(tx_id, commitment_to_zero, secret_x, proof.ss); CHECK_AND_ASSERT_MES(r, false, "generate_schnorr_sig (X) failed"); @@ -557,28 +561,25 @@ 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_amount_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(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); - 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); - - if (ado.descriptor.hidden_supply) + 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 + // TODO @#@# if asset is hidden -- check composition proof return false; } 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 = crypto::point_t(context.ado.opt_amount_commitment.get()).modify_mul8() - context.amount_to_validate * context.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"); } @@ -616,7 +617,6 @@ namespace currency const tx_out_zarcanum& ozc = boost::get(vout); outs_commitments_sum += crypto::point_t(ozc.amount_commitment); // amount_commitment premultiplied by 1/8 } - outs_commitments_sum.modify_mul8(); uint64_t fee = 0; CHECK_AND_ASSERT_MES(get_tx_fee(tx, fee) || additional_inputs_amount_and_fees_for_mining_tx > 0, false, "unable to get fee for a non-mining tx"); @@ -625,13 +625,21 @@ namespace currency ", additional inputs + fees = " << print_money_brief(additional_inputs_amount_and_fees_for_mining_tx)); crypto::point_t sum_of_pseudo_out_amount_commitments = crypto::c_point_0; - // take into account newly emitted assets + // take into account generated/burnt assets asset_descriptor_operation ado = AUTO_VAL_INIT(ado); - if (get_type_in_variant_container(tx.extra, ado) && ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) // @#@ TODO: Support other asset operations + if (get_type_in_variant_container(tx.extra, ado)) { - // opt_amount_commitment supposed to be validated earlier in validate_asset_operation() - CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is not set"); - sum_of_pseudo_out_amount_commitments += crypto::point_t(ado.opt_amount_commitment.get()); // *1/8 + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) + { + // opt_amount_commitment supposed to be validated earlier in validate_asset_operation() + CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is not set"); + 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) + { + CHECK_AND_ASSERT_MES(ado.opt_amount_commitment.has_value(), false, "opt_amount_commitment is not set"); + outs_commitments_sum += crypto::point_t(ado.opt_amount_commitment.get()); // *1/8 + } } size_t zc_sigs_count = 0; for(auto& sig_v : tx.signatures) @@ -645,6 +653,8 @@ namespace currency ++zc_sigs_count; VARIANT_SWITCH_END(); } + + outs_commitments_sum.modify_mul8(); sum_of_pseudo_out_amount_commitments.modify_mul8(); // (sum(bare inputs' amounts) - fee) * H + sum(pseudo outs commitments for ZC inputs) - sum(outputs' commitments) = lin(X) OR = lin(G) @@ -1105,6 +1115,35 @@ namespace currency string_tools::append_pod_to_strbuff(origin_blob, origin_hs); return origin_blob; } + //--------------------------------------------------------------- + bool validate_ado_update_allowed(const asset_descriptor_base& a, const asset_descriptor_base& b) + { + if (a.total_max_supply != b.total_max_supply) return false; + //if (a.current_supply != b.current_supply) return false; + if (a.decimal_point != b.decimal_point) return false; + if (a.ticker != b.ticker) return false; + if (a.full_name != b.full_name) return false; + //a.meta_info; + if (a.owner != b.owner) return false; + if (a.hidden_supply != b.hidden_supply) return false; + + return true; + } + //--------------------------------------------------------------- + crypto::hash get_signature_hash_for_asset_operation(const asset_descriptor_operation& ado) + { + asset_descriptor_operation ado_local = ado; + normalize_asset_operation_for_hashing(ado_local); + std::string buff = t_serializable_object_to_blob(ado_local); + return crypto::cn_fast_hash(buff.data(), buff.size()); + } + + //--------------------------------------------------------------- + void normalize_asset_operation_for_hashing(asset_descriptor_operation& op) + { + op.opt_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 */) { @@ -1934,7 +1973,7 @@ namespace currency if ((last_output && (tx_flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) == 0) || se.separately_signed_tx_complete) { // either normal tx or the last signature of consolidated tx -- in both cases we need to calculate non-random blinding mask for pseudo output commitment - pseudo_out_amount_blinding_mask = ogc.amount_blinding_masks_sum - ogc.pseudo_out_amount_blinding_masks_sum - ogc.ao_amount_blinding_mask; // A_1 - A^p_0 = (f_1 - f'_1) * G => f'_{i-1} = sum{y_j} - sum{f'_i} + pseudo_out_amount_blinding_mask = ogc.amount_blinding_masks_sum - ogc.pseudo_out_amount_blinding_masks_sum + (ogc.ao_commitment_in_outputs ? ogc.ao_amount_blinding_mask : -ogc.ao_amount_blinding_mask); // A_1 - A^p_0 = (f_1 - f'_1) * G => f'_{i-1} = sum{y_j} - sum{f'_i} } else { @@ -2056,6 +2095,127 @@ namespace currency p_result_point->to_public_key(*p_result_pub_key); } + bool construct_tx_handle_ado(const account_keys& sender_account_keys, + const finalize_tx_param& ftp, + asset_descriptor_operation& ado, + tx_generation_context& gen_context, + const crypto::secret_key& one_time_tx_secret_key, + std::vector& shuffled_dsts) + { + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) + { + //CHECK_AND_ASSERT_MES(ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER, false, "unsupported asset operation: " << (int)ado.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, ado.descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY); + CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed"); + + 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); + + // 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; + } + } + ado.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; + ado.opt_amount_commitment = (crypto::c_scalar_1div8 * gen_context.ao_amount_commitment).to_public_key(); + + } + else { + if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) + { + + //bool r = derive_key_pair_from_key_pair(sender_account_keys.account_address.spend_public_key, one_time_tx_secret_key, asset_control_key, ado.descriptor.owner, CRYPTO_HDS_ASSET_CONTROL_KEY); + //CHECK_AND_ASSERT_MES(r, false, "derive_key_pair_from_key_pair failed"); + + //calculate_asset_id(ado.descriptor.owner, &gen_context.ao_asset_id_pt, &gen_context.ao_asset_id); + CHECK_AND_ASSERT_MES(ado.opt_asset_id, false, "ado.opt_asset_id is not found at ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT/UPDATE"); + + 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); + + // 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) + { + amount_of_emitted_asset += item.amount; + item.asset_id = gen_context.ao_asset_id; + } + } + ado.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; + 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) + { + CHECK_AND_ASSERT_MES(ado.opt_asset_id, false, "ado.opt_asset_id is not found at ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT/UPDATE"); + //CHECK_AND_ASSERT_MES(ado.opt_proof, false, "ado.opt_asset_id is not found at ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMMIT/UPDATE"); + CHECK_AND_ASSERT_MES(!ado.opt_amount_commitment, false, "ado.opt_asset_id is not found at ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT/UPDATE"); + + //fields that not supposed to be changed? + } + else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + { + + //calculate_asset_id(ado.descriptor.owner, &gen_context.ao_asset_id_pt, &gen_context.ao_asset_id); + CHECK_AND_ASSERT_MES(ado.opt_asset_id, false, "ado.opt_asset_id is not found at ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT/UPDATE"); + + 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); + 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; // TODO: consider setting current_supply beforehand, not setting it hear in ad-hoc manner -- 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.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_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); + ado.opt_proof = sig; + } + return true; + } + bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& ftp, finalized_tx& result) { const std::vector& sources = ftp.sources; @@ -2309,33 +2469,9 @@ 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 (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); + bool r = construct_tx_handle_ado(sender_account_keys, ftp, *pado, gen_context, one_time_tx_secret_key, shuffled_dsts); + CHECK_AND_ASSERT_MES(r, false, "Failed to construct_tx_handle_ado()"); + if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_handle_asset_descriptor_operation{ pado }); } } @@ -3128,15 +3264,16 @@ namespace currency //--------------------------------------------------------------- - std::string print_money_brief(uint64_t amount) + std::string print_money_brief(uint64_t amount, size_t decimal_point /* = CURRENCY_DISPLAY_DECIMAL_POINT */) { - uint64_t remainder = amount % COIN; - amount /= COIN; + uint64_t coin = decimal_point == CURRENCY_DISPLAY_DECIMAL_POINT ? COIN : crypto::constexpr_pow(decimal_point, 10); + uint64_t remainder = amount % coin; + amount /= coin; if (remainder == 0) return std::to_string(amount) + ".0"; std::string r = std::to_string(remainder); - if (r.size() < CURRENCY_DISPLAY_DECIMAL_POINT) - r.insert(0, CURRENCY_DISPLAY_DECIMAL_POINT - r.size(), '0'); + if (r.size() < decimal_point) + r.insert(0, decimal_point - r.size(), '0'); return std::to_string(amount) + '.' + r.substr(0, r.find_last_not_of('0') + 1); } //--------------------------------------------------------------- @@ -4147,6 +4284,25 @@ namespace currency return tx_hash_for_signature; } //------------------------------------------------------------------ + const char* get_asset_operation_type_string(size_t asset_operation_type, bool short_name /* = false */) + { + switch(asset_operation_type) + { + case ASSET_DESCRIPTOR_OPERATION_UNDEFINED: + return short_name ? "undefined" : "ASSET_DESCRIPTOR_OPERATION_UNDEFINED"; + case ASSET_DESCRIPTOR_OPERATION_REGISTER: + return short_name ? "register" : "ASSET_DESCRIPTOR_OPERATION_REGISTER"; + case ASSET_DESCRIPTOR_OPERATION_UPDATE: + return short_name ? "update" : "ASSET_DESCRIPTOR_OPERATION_UPDATE"; + case ASSET_DESCRIPTOR_OPERATION_EMIT: + return short_name ? "emit" : "ASSET_DESCRIPTOR_OPERATION_EMIT"; + case ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN: + return short_name ? "burn" : "ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN"; + default: + return "unknown"; + } + } + //------------------------------------------------------------------ std::string dump_ring_sig_data(const crypto::hash& hash_for_sig, const crypto::key_image& k_image, const std::vector& output_keys_ptrs, const std::vector& sig) { std::stringstream s; diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index fa269eaa..aa9e800e 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -158,6 +158,8 @@ 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 @@ -177,6 +179,7 @@ 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); END_SERIALIZE() @@ -242,6 +245,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 amount_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 +264,8 @@ 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_amount_proof(asset_op_verification_context& context); + const char* get_asset_operation_type_string(size_t asset_operation_type, bool short_name = false); //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t current_block_size, @@ -390,13 +405,13 @@ namespace currency uint64_t get_block_height(const block& b); std::vector relative_output_offsets_to_absolute(const std::vector& off); // DEPRECATED: consider using prepare_outputs_entries_for_key_offsets and absolute_sorted_output_offsets_to_relative_in_place instead - std::vector absolute_output_offsets_to_relative(const std::vector& off); + [[deprecated]] std::vector absolute_output_offsets_to_relative(const std::vector& off); bool absolute_sorted_output_offsets_to_relative_in_place(std::vector& offsets) noexcept; // prints amount in format "3.14", "0.0" - std::string print_money_brief(uint64_t amount); + std::string print_money_brief(uint64_t amount, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT); uint64_t get_actual_timestamp(const block& b); // obsolete and depricated, use get_block_datetime uint64_t get_block_datetime(const block& b); void set_block_datetime(uint64_t datetime, block& b); @@ -432,6 +447,11 @@ namespace currency std::string get_word_from_timstamp(uint64_t timestamp, bool use_password); 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); + bool validate_ado_update_allowed(const asset_descriptor_base& a, const asset_descriptor_base& b); + + + void normalize_asset_operation_for_hashing(asset_descriptor_operation& op); + crypto::hash get_signature_hash_for_asset_operation(const asset_descriptor_operation& ado); template typename std::conditional::value, const std::vector, std::vector >::type& get_txin_etc_options(t_txin_v& in) diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index 2b5850fe..d4650dfa 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -333,11 +333,10 @@ namespace currency if (!get_type_in_variant_container(tx.extra, *p_ado)) return false; - // TODO @#@# change to ASSET_DESCRIPTOR_OPERATION_EMMIT ! - if (p_ado->operation_type != ASSET_DESCRIPTOR_OPERATION_REGISTER) - return false; + if (p_ado->operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER || p_ado->operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) + return true; - return true; + return false; } //--------------------------------------------------------------- // Prepapres vector of output_entry to be used in key_offsets in a transaction input: diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index c5c9cfb0..087e19be 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -272,6 +272,7 @@ namespace currency crypto::point_t ao_asset_id_pt = crypto::c_point_0; crypto::point_t ao_amount_commitment = crypto::c_point_0; crypto::scalar_t ao_amount_blinding_mask {}; // generate_tx_balance_proof generate_ZC_sig + bool ao_commitment_in_outputs = false; // consider redesign, some data may possibly be excluded from kv serialization -- sowle BEGIN_KV_SERIALIZE_MAP() @@ -295,12 +296,12 @@ namespace currency KV_SERIALIZE_POD_AS_HEX_STRING(ao_asset_id_pt) KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_commitment) KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_blinding_mask) + KV_SERIALIZE_POD_AS_HEX_STRING(ao_commitment_in_outputs) END_KV_SERIALIZE_MAP() // solely for consolidated txs, asset opration fields are not serialized BEGIN_SERIALIZE_OBJECT() - VERSION() - CURRENT_VERSION(0) + VERSION(0) FIELD(asset_ids) FIELD(blinded_asset_ids) FIELD(amount_commitments) @@ -323,6 +324,7 @@ namespace currency //ao_asset_id_pt //ao_amount_commitment //ao_amount_blinding_mask + //ao_commitment_in_outputs END_SERIALIZE() }; // struct tx_generation_context diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index d2e8d029..df2ab1f7 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -98,26 +98,31 @@ do { \ if (!_ser_ar.stream().good()) return false; \ } while (0); -#define VERSION() \ +#define VERSION(ver) \ do { \ _ser_ar.tag("VERSION"); \ if (!_ser_ar.stream().good()){break;} \ + s_version = s_current_version = ver; \ _ser_ar.serialize_varint(s_version); \ if (!_ser_ar.stream().good()) return false; \ if(s_version > s_current_version) return false; \ } while (0); +/* #define CURRENT_VERSION(v) \ do { \ s_current_version = v; \ 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() \ +#define BEGIN_VERSIONED_SERIALIZE(ver) \ BEGIN_SERIALIZE() \ - VERSION() + VERSION(ver) #define DEFINE_SERIALIZATION_VERSION(v) inline static uint32_t get_serialization_version() { return v; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1cd051f9..67e1764a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -42,6 +42,7 @@ using namespace epee; #include "common/variant_helper.h" #include "currency_core/crypto_config.h" #include "crypto/zarcanum.h" +#include "wallet_debug_events_definitions.h" using namespace currency; @@ -416,6 +417,76 @@ const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out } } //---------------------------------------------------------------------------------------------------- +void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_operation& ado, process_transaction_context& ptc) +{ + do + { + 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) + { + //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{}; + calculate_asset_id(ado.descriptor.owner, nullptr, &asset_id); + 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:" + << ENDL << "asset id: " << asset_id + << ENDL << "Name: " << asset_context.asset_descriptor.full_name + << ENDL << "Ticker: " << asset_context.asset_descriptor.ticker + << ENDL << "Total Max Supply: " << print_asset_money(asset_context.asset_descriptor.total_max_supply, asset_context.asset_descriptor.decimal_point) + << ENDL << "Current Supply: " << print_asset_money(asset_context.asset_descriptor.current_supply, asset_context.asset_descriptor.decimal_point) + << ENDL << "Decimal Point: " << asset_context.asset_descriptor.decimal_point; + + + add_rollback_event(ptc.height, asset_register_event{ 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 if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE || 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); + 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) + { + //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); + } + else + { + //asset had been updated + add_rollback_event(ptc.height, asset_update_event{ it->first, it->second }); + it->second.asset_descriptor = ado.descriptor; + } + } + } while (false); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::add_rollback_event(uint64_t h, const wallet_event_t& ev) +{ + m_rollback_events.emplace_back(h, ev); +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes) { //check for transaction spends @@ -498,15 +569,15 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t std::vector outs; //uint64_t sum_of_native_outs = 0; // TODO: @#@# correctly calculate tx_money_got_in_outs for post-HF4 - crypto::public_key tx_pub_key = null_pkey; - bool r = parse_and_validate_tx_extra(tx, tx_pub_key); + ptc.tx_pub_key = null_pkey; + bool r = parse_and_validate_tx_extra(tx, ptc.tx_pub_key); THROW_IF_TRUE_WALLET_EX(!r, error::tx_extra_parse_error, tx); //check for transaction income crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); std::list htlc_info_list; - r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, derivation, htlc_info_list); - THROW_IF_TRUE_WALLET_EX(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); + r = lookup_acc_outs(m_account.get_keys(), tx, ptc.tx_pub_key, outs, derivation, htlc_info_list); + THROW_IF_TRUE_WALLET_EX(!r, error::acc_outs_lookup_error, tx, ptc.tx_pub_key, m_account.get_keys()); if (!outs.empty()) { @@ -580,7 +651,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t { // normal wallet, calculate and store key images for own outs currency::keypair in_ephemeral = AUTO_VAL_INIT(in_ephemeral); - currency::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, ki); + currency::generate_key_image_helper(m_account.get_keys(), ptc.tx_pub_key, o, in_ephemeral, ki); WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(in_ephemeral.pub == out_key, "key_image generated ephemeral public key that does not match with output_key"); } @@ -804,45 +875,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t asset_descriptor_operation ado = AUTO_VAL_INIT(ado); if (get_type_in_variant_container(tx.extra, ado)) { - 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(tx_pub_key, m_account.get_keys().spend_secret_key, asset_control_key, self_check, CRYPTO_HDS_ASSET_CONTROL_KEY); - if (!r) - { - //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()); - }else - { - 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()); - } - else - { - crypto::public_key asset_id{}; - calculate_asset_id(ado.descriptor.owner, nullptr, &asset_id); - 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.height = height; - std::stringstream ss; - ss << "New Asset Registered:" - << ENDL << "asset id: " << asset_id - << ENDL << "Name: " << asset_context.asset_descriptor.full_name - << ENDL << "Ticker: " << asset_context.asset_descriptor.ticker - << ENDL << "Total Max Supply: " << print_asset_money(asset_context.asset_descriptor.total_max_supply, asset_context.asset_descriptor.decimal_point) - << ENDL << "Current Supply: " << print_asset_money(asset_context.asset_descriptor.current_supply, asset_context.asset_descriptor.decimal_point) - << ENDL << "Decimal Point: " << asset_context.asset_descriptor.decimal_point; - - WLT_LOG_MAGENTA(ss.str(), LOG_LEVEL_0); - if (m_wcallback) - m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str()); - } - } - } + process_ado_in_new_transaction(ado, ptc); } } @@ -2550,7 +2583,7 @@ bool wallet2::handle_expiration_list(uint64_t tx_expiration_ts_median) { for (auto it = m_money_expirations.begin(); it != m_money_expirations.end(); ) { - if (tx_expiration_ts_median > it->expiration_time - TX_EXPIRATION_MEDIAN_SHIFT) + if (it->expiration_time < TX_EXPIRATION_MEDIAN_SHIFT || tx_expiration_ts_median > it->expiration_time - TX_EXPIRATION_MEDIAN_SHIFT) { for (auto tr_ind : it->selected_transfers) { @@ -2740,17 +2773,39 @@ void wallet2::detach_blockchain(uint64_t including_height) } //asset descriptors - for (auto it = m_own_asset_descriptors.begin(); it != m_own_asset_descriptors.end(); ) - { - if (including_height <= it->second.height) - it = m_own_asset_descriptors.erase(it); - else - ++it; - } + handle_rollback_events(including_height); WLT_LOG_L0("Detached blockchain on height " << including_height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); } //---------------------------------------------------------------------------------------------------- +void wallet2::operator()(const asset_register_event& e) +{ + auto it = m_own_asset_descriptors.find(e.asset_id); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_own_asset_descriptors.end(), "asset_id " << e.asset_id << "not found during rolling asset_register_event"); + m_own_asset_descriptors.erase(it); +} +void wallet2::operator()(const asset_update_event& e) +{ + auto it = m_own_asset_descriptors.find(e.asset_id); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_own_asset_descriptors.end(), "asset_id " << e.asset_id << "not found during rolling asset_update_event"); + it->second = e.own_context; +} +void wallet2::operator()(const asset_unown_event& e) +{ + auto it = m_own_asset_descriptors.find(e.asset_id); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it == m_own_asset_descriptors.end(), "asset_id " << e.asset_id << "unexpectedly found during rolling asset_unown_event"); + m_own_asset_descriptors[e.asset_id] = e.own_context; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::handle_rollback_events(uint64_t including_height) +{ + while (m_rollback_events.size() && m_rollback_events.back().first >= including_height) + { + boost::apply_visitor(*this, m_rollback_events.back().second); + m_rollback_events.pop_back(); + } +} +//---------------------------------------------------------------------------------------------------- bool wallet2::deinit() { m_wcallback.reset(); @@ -3282,6 +3337,20 @@ uint64_t wallet2::balance(uint64_t& unlocked, uint64_t& awaiting_in, uint64_t& a return total; } //---------------------------------------------------------------------------------------------------- +uint64_t wallet2::balance(crypto::public_key asset_id, uint64_t& unlocked) const +{ + std::unordered_map balances; + uint64_t dummy; + balance(balances, dummy); + auto it = balances.find(asset_id); + if (it == balances.end()) + { + return 0; + } + unlocked = it->second.unlocked; + return it->second.total; +} +//---------------------------------------------------------------------------------------------------- bool wallet2::balance(std::unordered_map& balances, uint64_t& mined) const { mined = 0; @@ -4658,6 +4727,85 @@ void wallet2::deploy_new_asset(const currency::asset_descriptor_base& asset_info m_custom_assets[new_asset_id] = ado.descriptor; } //---------------------------------------------------------------------------------------------------- +void wallet2::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"); + COMMAND_RPC_GET_ASSET_INFO::request req; + req.asset_id = asset_id; + COMMAND_RPC_GET_ASSET_INFO::response rsp; + bool r = m_core_proxy->call_COMMAND_RPC_GET_ASSET_INFO(req, rsp); + CHECK_AND_ASSERT_THROW_MES(r, "Failed to call_COMMAND_RPC_GET_ASSET_INFO"); + + asset_descriptor_operation asset_emmit_info = AUTO_VAL_INIT(asset_emmit_info); + asset_emmit_info.descriptor = rsp.asset_descriptor; + asset_emmit_info.operation_type = ASSET_DESCRIPTOR_OPERATION_EMIT; + asset_emmit_info.opt_asset_id = asset_id; + construct_tx_param ctp = get_default_construct_tx_param(); + ctp.dsts = destinations; + ctp.extra.push_back(asset_emmit_info); + ctp.need_at_least_1_zc = true; + 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); + result_tx = ft.tx; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::update_asset(const crypto::public_key asset_id, const currency::asset_descriptor_base new_descriptor, 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_update_info = AUTO_VAL_INIT(asset_update_info); + asset_update_info.descriptor = new_descriptor; + asset_update_info.operation_type = ASSET_DESCRIPTOR_OPERATION_UPDATE; + asset_update_info.opt_asset_id = asset_id; + 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; + + finalized_tx ft = AUTO_VAL_INIT(ft); + this->transfer(ctp, ft, true, nullptr); + result_tx = ft.tx; +} +//---------------------------------------------------------------------------------------------------- +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"); + COMMAND_RPC_GET_ASSET_INFO::request req; + req.asset_id = asset_id; + COMMAND_RPC_GET_ASSET_INFO::response rsp; + bool r = m_core_proxy->call_COMMAND_RPC_GET_ASSET_INFO(req, rsp); + CHECK_AND_ASSERT_THROW_MES(r, "Failed to call_COMMAND_RPC_GET_ASSET_INFO"); + + + + asset_descriptor_operation asset_burn_info = AUTO_VAL_INIT(asset_burn_info); + asset_burn_info.descriptor = rsp.asset_descriptor; + + CHECK_AND_ASSERT_THROW_MES(asset_burn_info.descriptor.current_supply > amount_to_burn, "Wrong amount to burn (current_supply" << asset_burn_info.descriptor.current_supply << " is less then " << amount_to_burn << ")"); + + currency::tx_destination_entry dst_to_burn = AUTO_VAL_INIT(dst_to_burn); + dst_to_burn.amount = amount_to_burn; + dst_to_burn.asset_id = asset_id; + + asset_burn_info.operation_type = ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN; + asset_burn_info.opt_asset_id = asset_id; + 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.dsts.push_back(dst_to_burn); + + finalized_tx ft = AUTO_VAL_INIT(ft); + this->transfer(ctp, ft, true, nullptr); + result_tx = ft.tx; +} +//---------------------------------------------------------------------------------------------------- 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)) @@ -5257,7 +5405,7 @@ bool wallet2::build_ionic_swap_template(const wallet_public::ionic_swap_proposal selected_transfers = ftp.selected_transfers; currency::finalized_tx finalize_result = AUTO_VAL_INIT(finalize_result); finalize_transaction(ftp, finalize_result, false); - add_transfers_to_expiration_list(selected_transfers, for_expiration_list, proposal_detais.expiration_time, currency::null_hash); + add_transfers_to_expiration_list(selected_transfers, for_expiration_list, this->get_core_runtime_config().get_core_time() + proposal_detais.expiration_time, currency::null_hash); //wrap it all proposal.tx_template = finalize_result.tx; @@ -6461,6 +6609,19 @@ void wallet2::prepare_tx_destinations(const assets_selection_context& needed_mon if (dst.asset_id == currency::null_pkey) final_destinations.emplace_back(dst.amount, dst.addr, dst.asset_id); + //exclude destinations that supposed to be burned (for ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) + for (size_t i = 0; i < final_destinations.size(); ) + { + if (final_destinations[i].addr.size() == 0) + { + final_destinations.erase(final_destinations.begin() + i); + } + else + { + i++; + } + } + if (final_destinations.empty()) { // if there's no destinations -- make CURRENCY_TX_MIN_ALLOWED_OUTS empty destinations @@ -6534,7 +6695,7 @@ 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; // // TODO @#@# need to do refactoring over this part to support hidden amounts and asset_id // @@ -6830,6 +6991,7 @@ void wallet2::transfer(construct_tx_param& ctp, TIME_MEASURE_START(prepare_transaction_time); currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + ftp.pevents_dispatcher = &m_debug_events_dispatcher; ftp.tx_version = this->get_current_tx_version(); if (!prepare_transaction(ctp, ftp)) { @@ -6872,6 +7034,7 @@ void wallet2::transfer(construct_tx_param& ctp, print_tx_sent_message(result.tx, std::string() + "(transfer)", ctp.fee); } + //---------------------------------------------------------------------------------------------------- void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id, uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, uint64_t& amount_swept, currency::transaction* p_result_tx /* = nullptr */, std::string* p_filename_or_unsigned_tx_blob_str /* = nullptr */) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 66cdffe3..277d887c 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 asset_deploy_control_key = currency::null_skey; }; struct mode_separate_context @@ -310,7 +311,7 @@ namespace tools }; typedef std::unordered_map assets_selection_context; - class wallet2: public tools::tor::t_transport_state_notifier + class wallet2: public tools::tor::t_transport_state_notifier, public boost::static_visitor { wallet2(const wallet2&) = delete; public: @@ -320,7 +321,52 @@ namespace tools static std::string transform_tx_to_str(const currency::transaction& tx); static currency::transaction transform_str_to_tx(const std::string& tx_str); + //general rollback mechanism + struct asset_register_event + { + crypto::public_key asset_id = currency::null_pkey; + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(asset_id) + END_BOOST_SERIALIZATION() + }; + + struct wallet_own_asset_context + { + currency::asset_descriptor_base asset_descriptor; + crypto::secret_key control_key; + //uint64_t height = 0; + + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(asset_descriptor) + BOOST_SERIALIZE(control_key) + //BOOST_SERIALIZE(height) + END_BOOST_SERIALIZATION() + }; + + struct asset_update_event + { + crypto::public_key asset_id = currency::null_pkey; + wallet_own_asset_context own_context; + + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(asset_id) + BOOST_SERIALIZE(own_context) + END_BOOST_SERIALIZATION() + }; + + struct asset_unown_event + { + crypto::public_key asset_id = currency::null_pkey; + wallet_own_asset_context own_context; + + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(asset_id) + BOOST_SERIALIZE(own_context) + END_BOOST_SERIALIZATION() + }; + + typedef boost::variant wallet_event_t; struct transaction_wallet_info { @@ -578,6 +624,7 @@ namespace tools std::vector recipients; std::vector remote_aliases; multisig_entries_map* pmultisig_entries = nullptr; + crypto::public_key tx_pub_key = currency::null_pkey; uint64_t tx_expiration_ts_median = 0; const crypto::hash& tx_hash() const @@ -593,18 +640,7 @@ namespace tools }; - struct wallet_own_asset_context - { - currency::asset_descriptor_base asset_descriptor; - crypto::secret_key control_key; - uint64_t height = 0; - BEGIN_BOOST_SERIALIZATION() - BOOST_SERIALIZE(asset_descriptor) - BOOST_SERIALIZE(control_key) - BOOST_SERIALIZE(height) - END_BOOST_SERIALIZATION() - }; void assign_account(const currency::account_base& acc); void generate(const std::wstring& path, const std::string& password, bool auditable_wallet); @@ -655,6 +691,9 @@ namespace tools bool check_available_sources(std::list& amounts); void deploy_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector& destinations, currency::transaction& result_tx, crypto::public_key& new_asset_id); + void emmit_asset(const crypto::public_key asset_id, std::vector& destinations, currency::transaction& result_tx); + void update_asset(const crypto::public_key asset_id, const currency::asset_descriptor_base new_descriptor, currency::transaction& result_tx); + void burn_asset(const crypto::public_key asset_id, uint64_t amount_to_burn, currency::transaction& result_tx); bool set_core_proxy(const std::shared_ptr& proxy); void set_pos_utxo_count_limits_for_defragmentation_tx(uint64_t min_outs, uint64_t max_outs); // don't create UTXO defrag. tx if there are less than 'min_outs' outs; don't put more than 'max_outs' outs @@ -665,6 +704,7 @@ namespace tools uint64_t balance(uint64_t& unloked, uint64_t& awaiting_in, uint64_t& awaiting_out, uint64_t& mined, const crypto::public_key& asset_id = currency::native_coin_asset_id) const; bool balance(std::unordered_map& balances, uint64_t& mined) const; bool balance(std::list& balances, uint64_t& mined) const; + uint64_t balance(crypto::public_key asset_id, uint64_t& unloked) const; uint64_t balance(uint64_t& unloked) const; @@ -998,14 +1038,25 @@ namespace tools construct_tx_param get_default_construct_tx_param(); + //---------- m_rollback_events visitor ------------------------------------------------ + void operator()(const asset_register_event& e); + void operator()(const asset_update_event& e); + void operator()(const asset_unown_event& e); + +protected: + epee::misc_utils::events_dispatcher m_debug_events_dispatcher; private: // -------- t_transport_state_notifier ------------------------------------------------ virtual void notify_state_change(const std::string& state_code, const std::string& details = std::string()); + + void add_rollback_event(uint64_t h, const wallet_event_t& ev); + void handle_rollback_events(uint64_t including_height); // ------------------------------------------------------------------------------------ void add_transfers_to_expiration_list(const std::vector& selected_transfers, const std::vector& received, uint64_t expiration, const crypto::hash& related_tx_id); void remove_transfer_from_expiration_list(uint64_t transfer_index); void load_keys(const std::string& keys_file_name, const std::string& password, uint64_t file_signature, keys_file_data& kf_data); + void process_ado_in_new_transaction(const currency::asset_descriptor_operation& ado, process_transaction_context& ptc); void process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes); void fetch_tx_global_indixes(const currency::transaction& tx, std::vector& goutputs_indexes); void fetch_tx_global_indixes(const std::list>& txs, std::vector>& goutputs_indexes); @@ -1203,6 +1254,9 @@ private: mutable current_operation_context m_current_context; //this needed to access wallets state in coretests, for creating abnormal blocks and tranmsactions friend class test_generator; + + std::list> m_rollback_events; + }; // class wallet2 diff --git a/src/wallet/wallet_debug_events_definitions.h b/src/wallet/wallet_debug_events_definitions.h new file mode 100644 index 00000000..c9845a9b --- /dev/null +++ b/src/wallet/wallet_debug_events_definitions.h @@ -0,0 +1,21 @@ +// Copyright (c) 2014-2023 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + + + +//Wallet Debug Events +struct wde_construct_tx_handle_asset_descriptor_operation +{ + currency::asset_descriptor_operation* pado; +}; + + +//Wallet Debug Events +struct wde_construct_tx_handle_asset_descriptor_operation_before_seal +{ + currency::asset_descriptor_operation* pado; +}; + diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 5ca0502e..c5ec61cd 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -1347,8 +1347,7 @@ namespace wallet_public crypto::secret_key one_time_skey; BEGIN_SERIALIZE_OBJECT() - VERSION() - CURRENT_VERSION(0) + VERSION(0) FIELD(gen_context) FIELD(one_time_skey) END_SERIALIZE() @@ -1361,8 +1360,7 @@ namespace wallet_public BEGIN_SERIALIZE_OBJECT() - VERSION() - CURRENT_VERSION(0) + VERSION(0) FIELD(tx_template) FIELD(encrypted_context) END_SERIALIZE() diff --git a/tests/core_tests/multiassets_test.cpp b/tests/core_tests/multiassets_test.cpp index cb2c33b7..6a92499d 100644 --- a/tests/core_tests/multiassets_test.cpp +++ b/tests/core_tests/multiassets_test.cpp @@ -8,9 +8,68 @@ #include "wallet_test_core_proxy.h" #include "random_helper.h" - +#include "wallet/wallet_debug_events_definitions.h" using namespace currency; + + + + + +/* + + +struct debug_context_event_1 +{ + int& i; + std::string& s; +}; + + + +//#define RAISE_DEBUG_EVENT dw.handle_type + + +void test_test() +{ + epee::misc_utils::events_dispatcher ed; + + //-------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------- + //thus code will be called in the tests + ed.SUBSCIRBE_DEBUG_EVENT([&](debug_context_event_1& d) + { + //here some operations + LOG_PRINT_L0("lala: " << d.i << d.s); + // + d.i = 10; + d.s = "33333"; + + }); + + + //-------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------- + //this code will be in the wallet and helper functions + + int i = 22; + std::string sss = "11111"; + + ed.RAISE_DEBUG_EVENT(debug_context_event_1{i, sss }); + + + LOG_PRINT_L0("lala: " << i << sss); +} +*/ + + + + + + + //------------------------------------------------------------------------------ #define AMOUNT_TO_TRANSFER_MULTIASSETS_BASIC (TESTS_DEFAULT_FEE) @@ -48,7 +107,7 @@ bool multiassets_basic_test::generate(std::vector& events) con bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::vector& events) { bool r = false; - std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + std::shared_ptr miner_wlt = init_playtime_test_wallet_t(events, c, MINER_ACC_IDX); miner_wlt->get_account().set_createtime(0); miner_wlt->refresh(); @@ -114,27 +173,196 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); alice_wlt->refresh(); - balances.clear(); - alice_wlt->balance(balances, mined); + uint64_t last_alice_balances = alice_wlt->balance(asset_id, mined); + CHECK_AND_ASSERT_MES(last_alice_balances == AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC + AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC/2, false, "Failed to find needed asset in result balances"); - it_asset = balances.find(asset_id); + - CHECK_AND_ASSERT_MES(it_asset != balances.end(), false, "Failed to find needed asset in result balances"); - CHECK_AND_ASSERT_MES(it_asset->second.total == AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC + AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC/2, false, "Failed to find needed asset in result balances"); - - try { - - miner_wlt->transfer(AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC / 2, alice_wlt->get_account().get_public_address(), asset_id); - //pass over hardfork - CHECK_AND_ASSERT_MES(false, false, "Transfer with 0 Zano worked(fail)"); - } - catch (...) { - return true; + try { + + miner_wlt->transfer(AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC / 2, alice_wlt->get_account().get_public_address(), asset_id); + //pass over hardfork + CHECK_AND_ASSERT_MES(false, false, "Transfer with 0 Zano worked(fail)"); + } + catch (...) + { + LOG_PRINT_L0("Transfer failed as planned"); + //return true; + } } + miner_wlt->refresh(); + uint64_t last_miner_balance = miner_wlt->balance(asset_id, mined); + asset_descriptor_base asset_info = AUTO_VAL_INIT(asset_info); + /* + adb.total_max_supply = 1000000000000000000; //1M coins + adb.full_name = "Test coins"; + adb.ticker = "TCT"; + adb.decimal_point = 12 + */ + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + + CHECK_AND_ASSERT_MES(asset_info.current_supply == AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC*2, false, "Failed to find needed asset in result balances"); + + //test update function + asset_info.meta_info = "{\"some\": \"info\"}"; + miner_wlt->update_asset(asset_id, asset_info, tx); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 2); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + asset_descriptor_base asset_info2 = AUTO_VAL_INIT(asset_info2); + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info2); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + + CHECK_AND_ASSERT_MES(asset_info2.meta_info == asset_info.meta_info, false, "Failed to find needed asset in result balances"); + + //test emmit function + //use same destinations as we used before + miner_wlt->emmit_asset(asset_id, destinations, tx); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + miner_wlt->refresh(); + alice_wlt->refresh(); + CHECK_AND_ASSERT_MES(miner_wlt->balance(asset_id, mined) == last_miner_balance + destinations[0].amount, false, "Miner balance wrong"); + CHECK_AND_ASSERT_MES(alice_wlt->balance(asset_id, mined) == last_alice_balances + destinations[1].amount, false, "Alice balance wrong"); + + asset_descriptor_base asset_info3 = AUTO_VAL_INIT(asset_info3); + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info3); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + CHECK_AND_ASSERT_MES(asset_info3.current_supply == asset_info2.current_supply + destinations[1].amount + destinations[0].amount, false, "Failed to find needed asset in result balances"); + + + + miner_wlt->burn_asset(asset_id, last_miner_balance, tx); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); + + miner_wlt->refresh(); + CHECK_AND_ASSERT_MES(miner_wlt->balance(asset_id, mined) == destinations[0].amount, false, "Miner balance wrong"); + + asset_descriptor_base asset_info4 = AUTO_VAL_INIT(asset_info4); + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info4); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + CHECK_AND_ASSERT_MES(asset_info4.current_supply == asset_info3.current_supply - last_miner_balance, false, "Failed to find needed asset in result balances"); + + + //------------------- tests that trying to break stuff ------------------- + //tests that trying to break stuff + miner_wlt->get_debug_events_dispatcher().SUBSCIRBE_DEBUG_EVENT([&](const wde_construct_tx_handle_asset_descriptor_operation& o) + { + crypto::signature s = currency::null_sig; + o.pado->opt_proof = s; + }); + + + //test update function with broken ownership + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + + asset_info.meta_info = "{\"some2\": \"info2\"}"; + miner_wlt->update_asset(asset_id, asset_info, tx); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 2); + CHECK_AND_ASSERT_MES(!r, false, "Test failed, broken ownership passed"); + c.get_tx_pool().purge_transactions(); + miner_wlt->refresh(); + + miner_wlt->get_debug_events_dispatcher().UNSUBSCRIBE_DEBUG_EVENT(); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + //------------------- tests that trying to break stuff ------------------- + // check update_asset() with modified 'ticker' + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + + asset_info.ticker = "XXX"; + miner_wlt->update_asset(asset_id, asset_info, tx); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().purge_transactions(); + + + // check update_asset() with modified 'full_name' + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + + asset_info.full_name = "XXX"; + miner_wlt->update_asset(asset_id, asset_info, tx); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().purge_transactions(); + miner_wlt->refresh(); + + + // check update_asset() with modified 'decimal_point' + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + + asset_info.decimal_point = 3; + miner_wlt->update_asset(asset_id, asset_info, tx); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().purge_transactions(); + miner_wlt->refresh(); + + + // check update_asset() with modified 'owner' + r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info); + CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); + + asset_info.owner = currency::keypair::generate().pub; + miner_wlt->update_asset(asset_id, asset_info, tx); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().purge_transactions(); + miner_wlt->refresh(); + + + // check emmit_asset() with modified 'current_supply' + miner_wlt->get_debug_events_dispatcher().SUBSCIRBE_DEBUG_EVENT([&](const wde_construct_tx_handle_asset_descriptor_operation_before_seal& o) + { + o.pado->descriptor.current_supply += 1000000; + }); + //test emmit function but re-adjust current_supply to wrong amount + miner_wlt->emmit_asset(asset_id, destinations, tx); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().purge_transactions(); + miner_wlt->refresh(); + + //------------------- tests that trying to break stuff ------------------- + //test burn that burns more than tx has + miner_wlt->get_debug_events_dispatcher().UNSUBSCRIBE_DEBUG_EVENT(); + + miner_wlt->get_debug_events_dispatcher().SUBSCIRBE_DEBUG_EVENT([&](const wde_construct_tx_handle_asset_descriptor_operation_before_seal& o) + { + o.pado->descriptor.current_supply -= 1000000; + }); + + miner_wlt->burn_asset(asset_id, 10000000000000, tx); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().purge_transactions(); + miner_wlt->refresh(); + + // + return true; } diff --git a/tests/core_tests/multiassets_test.h b/tests/core_tests/multiassets_test.h index 6ea169f6..12fefcce 100644 --- a/tests/core_tests/multiassets_test.h +++ b/tests/core_tests/multiassets_test.h @@ -7,7 +7,6 @@ #include "chaingen.h" #include "wallet_tests_basic.h" - struct multiassets_basic_test : public wallet_test { static uint64_t ts_starter; diff --git a/tests/core_tests/wallet_tests_basic.cpp b/tests/core_tests/wallet_tests_basic.cpp index c4cca86d..511de148 100644 --- a/tests/core_tests/wallet_tests_basic.cpp +++ b/tests/core_tests/wallet_tests_basic.cpp @@ -86,18 +86,11 @@ bool wallet_test::check_balance(currency::core& c, size_t ev_index, const std::v } } + + std::shared_ptr wallet_test::init_playtime_test_wallet(const std::vector& events, currency::core& c, const account_base& acc) const { - CHECK_AND_ASSERT_THROW_MES(events.size() > 0 && events[0].type() == typeid(currency::block), "Invalid events queue, can't find genesis block at the beginning"); - crypto::hash genesis_hash = get_block_hash(boost::get(events[0])); - - std::shared_ptr w(new tools::wallet2); - w->set_core_runtime_config(c.get_blockchain_storage().get_core_runtime_config()); - w->assign_account(acc); - w->set_genesis(genesis_hash); - w->set_core_proxy(m_core_proxy); - w->set_disable_tor_relay(true); - return w; + return init_playtime_test_wallet_t(events, c, acc); } std::shared_ptr wallet_test::init_playtime_test_wallet(const std::vector& events, currency::core& c, size_t account_index) const diff --git a/tests/core_tests/wallet_tests_basic.h b/tests/core_tests/wallet_tests_basic.h index 0329e0a2..bc45bcc8 100644 --- a/tests/core_tests/wallet_tests_basic.h +++ b/tests/core_tests/wallet_tests_basic.h @@ -21,6 +21,30 @@ struct wallet_test : virtual public test_chain_unit_enchanced static std::string get_test_account_name_by_id(size_t acc_id); + template + std::shared_ptr init_playtime_test_wallet_t(const std::vector& events, currency::core& c, const currency::account_base& acc) const + { + CHECK_AND_ASSERT_THROW_MES(events.size() > 0 && events[0].type() == typeid(currency::block), "Invalid events queue, can't find genesis block at the beginning"); + crypto::hash genesis_hash = get_block_hash(boost::get(events[0])); + + std::shared_ptr w(new wallet_t); + w->set_core_runtime_config(c.get_blockchain_storage().get_core_runtime_config()); + w->assign_account(acc); + w->set_genesis(genesis_hash); + w->set_core_proxy(m_core_proxy); + w->set_disable_tor_relay(true); + return w; + } + + + template + std::shared_ptr init_playtime_test_wallet_t(const std::vector& events, currency::core& c, size_t account_index) const + { + CHECK_AND_ASSERT_THROW_MES(account_index < m_accounts.size(), "Invalid account index"); + return init_playtime_test_wallet_t(events, c, m_accounts[account_index]); + } + + protected: struct params_check_balance @@ -118,3 +142,12 @@ struct wlt_lambda_on_transfer2_wrapper : public tools::i_wallet2_callback bool m_result; Func m_callback; }; + +class debug_wallet2: public tools::wallet2 +{ +public: + epee::misc_utils::events_dispatcher& get_debug_events_dispatcher() + { + return this->m_debug_events_dispatcher; + } +}; \ No newline at end of file diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index eafa34ed..41ea0a03 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1588,6 +1588,37 @@ TEST(crypto, schnorr_sig) return true; } +TEST(crypto, point_negation) +{ + ASSERT_EQ(c_point_0, -c_point_0); + ASSERT_NEQ(c_point_G, -c_point_G); + ASSERT_EQ(c_point_G, -(-c_point_G)); + ASSERT_EQ(-c_point_G, c_scalar_Lm1 * c_point_G); + ASSERT_EQ(-c_point_G, c_point_0 - c_point_G); + ASSERT_EQ(0 * (-c_point_G), c_point_0); + scalar_t a = scalar_t::random(), b = scalar_t::random(); + ASSERT_EQ(a * (-c_point_G) + b * c_point_G + a * c_point_H + b * (-c_point_H), (b - a) * c_point_G + (a - b) * c_point_H); + ASSERT_EQ(a * (-c_point_G), (a * c_scalar_Lm1) * c_point_G); + + for(size_t i = 0, sz = sizeof(canonical_torsion_elements) / sizeof(canonical_torsion_elements[0]); i < sz; ++i) + { + point_t el{}; + ASSERT_TRUE(el.from_string(canonical_torsion_elements[i].string)); + ASSERT_EQ(el, -(-el)); + ASSERT_EQ((-scalar_t(1)) * el, (c_scalar_0 - c_scalar_1) * el); + ASSERT_NEQ(-el, (-scalar_t(1)) * el); // because torsion elements have order != L + ASSERT_NEQ(-el, c_scalar_Lm1 * el); // because torsion elements have order != L + ASSERT_EQ(-el, (scalar_t(canonical_torsion_elements[i].order) - 1) * el); // they rather have order == canonical_torsion_elements[i].order + ASSERT_EQ(-el, c_point_0 - el); + ASSERT_EQ((-el) + (el), c_point_0); + ASSERT_EQ((-el) - (-el), c_point_0); + scalar_t x = scalar_t::random(); + ASSERT_EQ(x * (-el) + x * el, c_point_0); + } + + return true; +} + // // test's runner diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 43df9c0e..47e0d4c4 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -760,11 +760,10 @@ struct A_v1 : public A BEGIN_SERIALIZE() - CURRENT_VERSION(1) FIELD(one) FIELD(two) FIELD(vector_one) - VERSION() + VERSION(1) if (s_version < 1) return true; FIELD(vector_two) END_SERIALIZE() @@ -777,11 +776,11 @@ struct A_v2 : public A_v1 BEGIN_SERIALIZE() - CURRENT_VERSION(2) + //CURRENT_VERSION(2) FIELD(one) FIELD(two) FIELD(vector_one) - VERSION() + VERSION(2) if (s_version < 1) return true; FIELD(vector_two) if (s_version < 2) return true; @@ -795,11 +794,11 @@ struct A_v3 : public A_v2 std::vector vector_5; BEGIN_SERIALIZE() - CURRENT_VERSION(3) + //CURRENT_VERSION(3) FIELD(one) FIELD(two) FIELD(vector_one) - VERSION() + VERSION(3) if (s_version < 1) return true; FIELD(vector_two) if (s_version < 2) return true;