1
0
Fork 0
forked from lthn/blockchain

Merge branch 'asset_update' into cryptoassets

# Conflicts:
#	tests/core_tests/multiassets_test.cpp
This commit is contained in:
sowle 2023-08-29 14:13:15 +02:00
commit 3402da561c
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
21 changed files with 1078 additions and 185 deletions

View file

@ -34,6 +34,7 @@
#include <algorithm>
#include <functional>
#include <boost/thread.hpp>
#include <boost/any.hpp>
#include "include_base_utils.h"
#include "auto_val_init.h"
@ -372,6 +373,11 @@ namespace misc_utils
virtual void do_call(){};
};
template<typename param_t>
struct call_basic_param
{
virtual void do_call(param_t& p) {};
};
template<typename t_callback>
struct call_specific: public call_basic
@ -386,12 +392,34 @@ namespace misc_utils
t_callback m_cb;
};
template<typename param_t, typename t_callback>
struct call_specific_param : public call_basic_param<param_t>
{
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<typename t_callback>
auto build_abstract_callback(t_callback cb) -> std::shared_ptr<call_basic>
{
return std::shared_ptr<call_basic>(new call_specific<t_callback>(cb));
}
template<typename param_t, typename t_callback>
auto build_abstract_callback_param(t_callback cb) -> std::shared_ptr<call_basic_param<param_t>>
{
return std::shared_ptr<call_basic_param<param_t>>(new call_specific_param<param_t, t_callback>(cb));
}
template<class callback_type>
@ -427,6 +455,67 @@ namespace misc_utils
return res.first;
}
class events_dispatcher
{
public:
template<typename param_t>
struct callback_entry
{
std::shared_ptr<epee::misc_utils::call_basic_param<const param_t> > m_cb;
};
std::map<std::type_index, boost::any> m_callbacks;
template<typename param_t, typename callback_t>
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<const param_t> cb_entry = { epee::misc_utils::build_abstract_callback_param<const param_t>(cb) };
m_callbacks[ti] = cb_entry;
}
template<typename param_t>
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<typename param_t>
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<const param_t >* pcallback_entry = boost::any_cast<callback_entry<const param_t >>(&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

View file

@ -19,6 +19,9 @@ template<size_t A, size_t B> struct TAssertEquality {
#define BOOST_SERIALIZE(x) _arch & x;
#define BOOST_END_VERSION_UNDER(x) \
if(ver < x ) {return;}
#define END_BOOST_SERIALIZATION() }

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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<crypto::public_key> opt_amount_commitment; // premultiplied by 1/8
boost::optional<crypto::signature> opt_proof; // operation proof - for update/emit
boost::optional<crypto::public_key> 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<crypto::linear_composition_proof_s> opt_amount_commitment_composition_proof; // for hidden supply
boost::optional<crypto::signature> 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()

View file

@ -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<crypto::gt_X>(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<asset_operation_proof>(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<const asset_operation_proof>(context.tx.proofs);
CHECK_AND_ASSERT_MES(count_type_in_variant_container<asset_operation_proof>(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<const asset_operation_proof>(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<tx_out_zarcanum>(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<uint16_t>& 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<tx_destination_entry>& 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<tx_source_entry>& sources = ftp.sources;
@ -2309,33 +2469,9 @@ namespace currency
pado = get_type_in_variant_container<asset_descriptor_operation>(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<const crypto::public_key*>& output_keys_ptrs, const std::vector<crypto::signature>& sig)
{
std::stringstream s;

View file

@ -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<crypto::point_t> 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_descriptor_operation> > asset_op_history;
};
bool verify_multiple_zc_outs_range_proofs(const std::vector<zc_outs_range_proofs_with_commitments>& 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<tx_out_v>& 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<txout_ref_v> relative_output_offsets_to_absolute(const std::vector<txout_ref_v>& off);
// DEPRECATED: consider using prepare_outputs_entries_for_key_offsets and absolute_sorted_output_offsets_to_relative_in_place instead
std::vector<txout_ref_v> absolute_output_offsets_to_relative(const std::vector<txout_ref_v>& off);
[[deprecated]] std::vector<txout_ref_v> absolute_output_offsets_to_relative(const std::vector<txout_ref_v>& off);
bool absolute_sorted_output_offsets_to_relative_in_place(std::vector<txout_ref_v>& 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<class t_txin_v>
typename std::conditional<std::is_const<t_txin_v>::value, const std::vector<txin_etc_details_v>, std::vector<txin_etc_details_v> >::type& get_txin_etc_options(t_txin_v& in)

View file

@ -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:

View file

@ -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

View file

@ -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; }

View file

@ -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<uint64_t>* pglobal_indexes)
{
//check for transaction spends
@ -498,15 +569,15 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
std::vector<wallet_out_info> 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> 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<crypto::public_key, wallet_public::asset_balance_entry_base> 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<crypto::public_key, wallet_public::asset_balance_entry_base>& 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<currency::tx_destination_entry>& 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 */)

View file

@ -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<crypto::public_key, selection_for_amount> 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<void>
{
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<asset_register_event, asset_update_event, asset_unown_event> wallet_event_t;
struct transaction_wallet_info
{
@ -578,6 +624,7 @@ namespace tools
std::vector<std::string> recipients;
std::vector<std::string> 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<uint64_t>& amounts);
void deploy_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector<currency::tx_destination_entry>& destinations, currency::transaction& result_tx, crypto::public_key& new_asset_id);
void emmit_asset(const crypto::public_key asset_id, std::vector<currency::tx_destination_entry>& 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<i_core_proxy>& 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<crypto::public_key, wallet_public::asset_balance_entry_base>& balances, uint64_t& mined) const;
bool balance(std::list<wallet_public::asset_balance_entry>& 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<uint64_t>& selected_transfers, const std::vector<payment_details_subtransfer>& 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<uint64_t>* pglobal_indexes);
void fetch_tx_global_indixes(const currency::transaction& tx, std::vector<uint64_t>& goutputs_indexes);
void fetch_tx_global_indixes(const std::list<std::reference_wrapper<const currency::transaction>>& txs, std::vector<std::vector<uint64_t>>& 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<std::pair<uint64_t, wallet_event_t>> m_rollback_events;
}; // class wallet2

View file

@ -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;
};

View file

@ -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()

View file

@ -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>([&](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<test_event_entry>& events) con
bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
std::shared_ptr<debug_wallet2> miner_wlt = init_playtime_test_wallet_t<debug_wallet2>(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<wde_construct_tx_handle_asset_descriptor_operation>([&](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<wde_construct_tx_handle_asset_descriptor_operation>();
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<wde_construct_tx_handle_asset_descriptor_operation_before_seal>([&](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<wde_construct_tx_handle_asset_descriptor_operation_before_seal>();
miner_wlt->get_debug_events_dispatcher().SUBSCIRBE_DEBUG_EVENT<wde_construct_tx_handle_asset_descriptor_operation_before_seal>([&](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;
}

View file

@ -7,7 +7,6 @@
#include "chaingen.h"
#include "wallet_tests_basic.h"
struct multiassets_basic_test : public wallet_test
{
static uint64_t ts_starter;

View file

@ -86,18 +86,11 @@ bool wallet_test::check_balance(currency::core& c, size_t ev_index, const std::v
}
}
std::shared_ptr<tools::wallet2> wallet_test::init_playtime_test_wallet(const std::vector<test_event_entry>& 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<block>(events[0]));
std::shared_ptr<tools::wallet2> 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<tools::wallet2>(events, c, acc);
}
std::shared_ptr<tools::wallet2> wallet_test::init_playtime_test_wallet(const std::vector<test_event_entry>& events, currency::core& c, size_t account_index) const

View file

@ -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<typename wallet_t>
std::shared_ptr<wallet_t> init_playtime_test_wallet_t(const std::vector<test_event_entry>& 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<currency::block>(events[0]));
std::shared_ptr<wallet_t> 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<typename wallet_t>
std::shared_ptr<wallet_t> init_playtime_test_wallet_t(const std::vector<test_event_entry>& 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<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;
}
};

View file

@ -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

View file

@ -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<std::string> 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;