1
0
Fork 0
forked from lthn/blockchain

Merge commit 'e58b20ae5b62745513b8451af1ae195c9c6a8e20' into release

This commit is contained in:
sowle 2024-10-12 03:04:17 +02:00
commit 0a055e892c
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
20 changed files with 1021 additions and 16 deletions

View file

@ -25,6 +25,8 @@
//
#pragma once
#include <type_traits>
#include <optional>
#include "misc_language.h"
namespace epee
{
@ -51,24 +53,73 @@ namespace epee
}
};
template<typename T>
struct is_std_optional : std::false_type {};
template<typename T>
struct is_std_optional<std::optional<T>> : std::true_type {};
template<typename T>
struct is_std_optional<boost::optional<T>> : std::true_type {};
//basic helpers for pod-to-hex serialization
template<class t_pod_type>
std::string transform_t_pod_to_str_internal(const t_pod_type& a)
{
return epee::string_tools::pod_to_hex(a);
}
template<class t_pod_type>
std::string transform_t_pod_to_str_internal(const std::optional<t_pod_type>& a)
{
if (a.has_value())
return epee::string_tools::pod_to_hex(*a);
else
return "";
}
template<class t_pod_type>
std::string transform_t_pod_to_str_internal(const boost::optional<t_pod_type>& a)
{
if (a.has_value())
return epee::string_tools::pod_to_hex(*a);
else
return "";
}
//basic helpers for pod-to-hex serialization
template<class t_pod_type>
std::string transform_t_pod_to_str(const t_pod_type & a)
{
return epee::string_tools::pod_to_hex(a);
return transform_t_pod_to_str_internal(a);
}
template<class t_pod_type>
template<class t_pod_type>
t_pod_type transform_str_to_t_pod(const std::string& a)
{
t_pod_type res = AUTO_VAL_INIT(res);
t_pod_type res = AUTO_VAL_INIT(res);
if (a.empty())
return res;
if constexpr (is_std_optional<t_pod_type>::value)
{
typename t_pod_type::value_type v = AUTO_VAL_INIT(v);
if (!epee::string_tools::hex_to_pod(a, v))
throw std::runtime_error(std::string("Unable to transform \"") + a + "\" to pod type " + typeid(typename t_pod_type::value_type).name());
return v;
}
if (!epee::string_tools::hex_to_pod(a, res))
throw std::runtime_error(std::string("Unable to transform \"") + a + "\" to pod type " + typeid(t_pod_type).name());
return res;
}
//basic helpers for blob-to-hex serialization
inline std::string transform_binbuf_to_hexstr(const std::string& a)

View file

@ -157,6 +157,10 @@ namespace crypto
{
return o << epee::string_tools::pod_to_hex(v);
}
std::ostream& operator<<(std::ostream& o, const eth_signature& v)
{
return o << epee::string_tools::pod_to_hex(v);
}
} // namespace crypto

View file

@ -62,5 +62,6 @@ namespace crypto
std::ostream& operator<<(std::ostream& o, const eth_secret_key& v);
std::ostream& operator<<(std::ostream& o, const eth_public_key& v);
std::ostream& operator<<(std::ostream& o, const eth_signature& v);
} // namespace crypto

View file

@ -4116,7 +4116,15 @@ bool blockchain_storage::validate_ado_ownership(asset_op_verification_context& a
asset_operation_ownership_proof_eth aoop_eth{};
r = get_type_in_variant_container(avc.tx.proofs, aoop_eth);
CHECK_AND_ASSERT_MES(r, false, "Ownership validation failed: asset_operation_ownership_proof_eth is missing");
return crypto::verify_eth_signature(avc.tx_id, last_ado.descriptor.owner_eth_pub_key.value(), aoop_eth.eth_sig);
if (!crypto::verify_eth_signature(avc.tx_id, last_ado.descriptor.owner_eth_pub_key.value(), aoop_eth.eth_sig))
{
LOG_ERROR("Failed to validate secp256k1 signature for hash: " << avc.tx_id << ", signature: " << aoop_eth.eth_sig);
return false;
}
else
{
return true;
}
}
// owner_eth_pub_key has no value -- fallback to default
}

View file

@ -749,7 +749,7 @@ namespace currency
KV_SERIALIZE(meta_info) DOC_DSCR("Any other information associated with the asset in free form.") DOC_EXMP("Stable and private") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used only for EMIT and UPDATE validation, can be changed by transferring asset ownership.") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
KV_SERIALIZE(hidden_supply) DOC_DSCR("This field is reserved for future use and will be documented later.") DOC_END
KV_SERIALIZE(owner_eth_pub_key) DOC_DSCR("[Optional] Owner's key in the case when ETH signature is used.") DOC_END
KV_SERIALIZE_POD_AS_HEX_STRING(owner_eth_pub_key) DOC_DSCR("[Optional] Owner's key in the case when ETH signature is used.") DOC_END
END_KV_SERIALIZE_MAP()
};

@ -1 +1 @@
Subproject commit 6d212d4eefaf6d13c72799cb89be2c80b1813d38
Subproject commit 5c878005ace55484eafe2985d204cd51e90b203b

View file

@ -8,6 +8,6 @@
#define PROJECT_REVISION "1"
#define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION
#define PROJECT_VERSION_BUILD_NO 348
#define PROJECT_VERSION_BUILD_NO 349
#define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO)
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]"

View file

@ -395,7 +395,7 @@ 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)
{
auto print_ado_owner = [ado](std::ostream& o){
ado.descriptor.owner_eth_pub_key.has_value() ? o << ado.descriptor.owner_eth_pub_key.get() << " (ETH)" : o << ado.descriptor.owner;
ado.descriptor.owner_eth_pub_key.has_value() ? o << ado.descriptor.owner_eth_pub_key.value() << " (ETH)" : o << ado.descriptor.owner;
};
do
@ -3754,13 +3754,10 @@ bool wallet2::balance(std::unordered_map<crypto::public_key, wallet_public::asse
for (const auto& emp_entry : utx.second.employed_entries.receive)
{
auto it_employed_entry = subtransfers_by_assets_map.find(emp_entry.asset_id);
if (it_employed_entry == subtransfers_by_assets_map.end())
{
LOG_ERROR("Intenral error, check the wallet code at give location");
continue;
}
if (!(it_employed_entry->second)) // if is_incoming == false, then we need to check for change and add it to total
if (it_employed_entry == subtransfers_by_assets_map.end() || !(it_employed_entry->second)) // if is_incoming == false, then we need to check for change and add it to total
{
//it_employed_entry == subtransfers_by_assets_map.end() is a case when amount sent exactly equal amount received (someone producing more outputs for example)
//still need to add to total as it is a change
wallet_public::asset_balance_entry_base& e = balances[emp_entry.asset_id];
e.total += emp_entry.amount;
}

View file

@ -1433,7 +1433,7 @@ namespace tools
res.status = e.what();
return true;
}
res.status = API_RETURN_CODE_OK;
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}

View file

@ -1296,6 +1296,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY_HF(asset_operation_and_hardfork_checks, "4-*");
GENERATE_AND_PLAY_HF(eth_signed_asset_basics, "5-*"); // TODO: make HF4 version
GENERATE_AND_PLAY_HF(eth_signed_asset_via_rpc, "5-*"); // TODO: make HF4 version
//GENERATE_AND_PLAY_HF(asset_current_and_total_supplies_comparative_constraints, "4-*"); <-- temporary disabled, waiting for Stepan's fix -- sowle
GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*");

View file

@ -1952,3 +1952,369 @@ bool eth_signed_asset_via_rpc::c1(currency::core& c, size_t ev_index, const std:
return true;
}
asset_current_and_total_supplies_comparative_constraints::asset_current_and_total_supplies_comparative_constraints()
{
{
auto& adb{m_adbs.at(asset_position::alpha)};
adb.full_name = "Alpha";
adb.ticker = "ALPH";
adb.current_supply = 1;
adb.total_max_supply = 0;
}
{
auto& adb{m_adbs.at(asset_position::beta)};
adb.full_name = "Beta";
adb.ticker = "BETA";
adb.current_supply = adb.total_max_supply = 1;
}
{
auto &adb{m_adbs.at(asset_position::gamma)};
adb.full_name = "Gamma";
adb.ticker = "GAMM";
adb.current_supply = adb.total_max_supply = 0;
}
m_ados_register.at(asset_position::alpha).operation_type = m_ados_register.at(asset_position::beta).operation_type = m_ados_register.at(asset_position::gamma).operation_type =
ASSET_DESCRIPTOR_OPERATION_REGISTER;
m_ado_emit.operation_type = ASSET_DESCRIPTOR_OPERATION_EMIT;
REGISTER_CALLBACK_METHOD(asset_current_and_total_supplies_comparative_constraints, assert_asset_gamma_registered);
REGISTER_CALLBACK_METHOD(asset_current_and_total_supplies_comparative_constraints, assert_asset_alpha_not_registered);
REGISTER_CALLBACK_METHOD(asset_current_and_total_supplies_comparative_constraints, assert_asset_beta_registered);
REGISTER_CALLBACK_METHOD(asset_current_and_total_supplies_comparative_constraints, emit_asset_beta_with_incorrect_supply);
REGISTER_CALLBACK_METHOD(asset_current_and_total_supplies_comparative_constraints, assert_asset_beta_not_emitted);
REGISTER_CALLBACK_METHOD(asset_current_and_total_supplies_comparative_constraints, public_burn_asset_beta_with_incorrect_supply);
}
bool asset_current_and_total_supplies_comparative_constraints::generate(std::vector<test_event_entry>& events) const
{
/* Test ideas:
* ensure that it's possible to register asset with .current_supply = .total_max_supply = 0;
* ensure that asset operations in which .current_supply is greater than .total_max_supply are not performed. */
bool success{};
GENERATE_ACCOUNT(miner);
GENERATE_ACCOUNT(alice);
transaction tx_0{}, tx_1{}, tx_2{}, tx_3{}, tx_4{};
m_accounts.push_back(miner);
m_accounts.push_back(alice);
m_adbs.at(asset_position::alpha).owner = m_adbs.at(asset_position::beta).owner = m_adbs.at(asset_position::gamma).owner = alice.get_public_address().spend_public_key;
m_ados_register.at(asset_position::alpha).descriptor = m_adbs.at(asset_position::alpha);
m_ados_register.at(asset_position::beta).descriptor = m_ado_emit.descriptor = m_adbs.at(asset_position::beta);
m_ados_register.at(asset_position::gamma).descriptor = m_adbs.at(asset_position::gamma);
CHECK_AND_ASSERT(m_ado_emit.descriptor.current_supply <= m_ado_emit.descriptor.total_max_supply, false);
++m_ado_emit.descriptor.current_supply;
CHECK_AND_ASSERT(m_ado_emit.descriptor.current_supply > m_ado_emit.descriptor.total_max_supply, false);
MAKE_GENESIS_BLOCK(events, blk_0, miner, test_core_time::get_time());
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
{
const auto& top{blk_0r};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
success = fill_tx_sources_and_destinations(events, top, miner.get_keys(), alice.get_public_address(), MK_TEST_COINS(8), TESTS_DEFAULT_FEE, 0, sources, destinations);
CHECK_AND_ASSERT_EQ(success, true);
success = construct_tx(miner.get_keys(), sources, destinations, empty_attachment, tx_0, get_tx_version(get_block_height(top), m_hardforks), 0);
CHECK_AND_ASSERT_EQ(success, true);
}
ADD_CUSTOM_EVENT(events, tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner, tx_0);
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// Alice registers the asset GAMM with .current_supply = 0, .total_max_supply = 0.
{
const auto& top{blk_1r};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
const auto& ado{m_ados_register.at(asset_position::gamma)};
crypto::secret_key one_time{};
success = fill_tx_sources_and_destinations(events, top, alice.get_keys(), alice.get_public_address(), MK_TEST_COINS(2), TESTS_DEFAULT_FEE, 0, sources, destinations);
CHECK_AND_ASSERT_EQ(success, true);
destinations.emplace_back(ado.descriptor.current_supply, alice.get_public_address(), null_pkey);
CHECK_AND_ASSERT_EQ(ado.descriptor.total_max_supply, 0);
CHECK_AND_ASSERT_EQ(ado.descriptor.total_max_supply, ado.descriptor.current_supply);
success = construct_tx(alice.get_keys(), sources, destinations, {ado}, empty_attachment, tx_1, get_tx_version(get_block_height(top), m_hardforks), one_time, 0);
CHECK_AND_ASSERT_EQ(success, true);
}
// tx_1 is valid and must be accepted.
ADD_CUSTOM_EVENT(events, tx_1);
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, alice, tx_1);
REWIND_BLOCKS_N(events, blk_2r, blk_2, alice, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
DO_CALLBACK(events, "assert_asset_gamma_registered");
// Alice registers asset ALPH. Transaction is invalid, because .current_supply > .total_max_supply in the asset base descriptor.
{
const auto& top{blk_2r};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
crypto::secret_key one_time{};
const auto& ado{m_ados_register.at(asset_position::alpha)};
success = fill_tx_sources_and_destinations(events, top, alice.get_keys(), alice.get_public_address(), MK_TEST_COINS(2), TESTS_DEFAULT_FEE, 0, sources, destinations);
CHECK_AND_ASSERT_EQ(success, true);
destinations.emplace_back(ado.descriptor.current_supply, alice.get_public_address(), null_pkey);
CHECK_AND_ASSERT_MES(ado.descriptor.current_supply > ado.descriptor.total_max_supply, false, "current_supply <= total_max_supply");
success = construct_tx(alice.get_keys(), sources, destinations, {ado}, empty_attachment, tx_2, get_tx_version(get_block_height(top), m_hardforks), one_time, 0);
CHECK_AND_ASSERT_EQ(success, true);
}
/* TODO: tx_1 is invalid and mustn't be accepted.
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx_2);
DO_CALLBACK(events, "assert_asset_alpha_not_registered"); */
// Alice registers asset BETA. In the asset base descriptor .current_supply <= .total_max_supply.
{
const auto& top{blk_2r};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
crypto::secret_key one_time{};
const auto& ado{m_ados_register.at(asset_position::beta)};
success = fill_tx_sources_and_destinations(events, top, alice.get_keys(), alice.get_public_address(), MK_TEST_COINS(2), TESTS_DEFAULT_FEE, 0, sources, destinations);
CHECK_AND_ASSERT_EQ(success, true);
destinations.emplace_back(ado.descriptor.current_supply, alice.get_public_address(), null_pkey);
CHECK_AND_ASSERT(ado.descriptor.current_supply <= ado.descriptor.total_max_supply, false);
success = construct_tx(alice.get_keys(), sources, destinations, {ado}, empty_attachment, tx_3, get_tx_version(get_block_height(top), m_hardforks), one_time, 0);
CHECK_AND_ASSERT_EQ(success, true);
}
// tx_3 is valid and must be accepted.
ADD_CUSTOM_EVENT(events, tx_3);
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2r, alice, tx_3);
REWIND_BLOCKS_N(events, blk_3r, blk_3, alice, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
DO_CALLBACK(events, "assert_asset_beta_registered");
{
crypto::public_key beta_asset_id{};
crypto::point_t point_beta_asset_id{};
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(m_ados_register.at(asset_position::beta), &point_beta_asset_id, &beta_asset_id), false, "failed to calculate asset id");
m_ado_emit.opt_asset_id = beta_asset_id;
}
CHECK_AND_ASSERT_GREATER(m_ado_emit.descriptor.current_supply, m_ado_emit.descriptor.total_max_supply);
// Alice emits asset BETA. The emission is performed through the wallet object. There is no emission, because .current_supply > .total_max_supply in the asset base descriptor.
DO_CALLBACK(events, "emit_asset_beta_with_incorrect_supply");
/* Alice emits asset BETA. A transaction is constructed through finalize_tx_param object. This is low-level transaction construction. Transaction muse be rejected by the core, because
.current_supply > .total_max_supply in the asset base descriptor. */
{
const auto& top{blk_3r};
const auto& ado_register{m_ados_register.at(asset_position::beta)};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
crypto::secret_key one_time{};
tx_source_entry source{};
finalize_tx_param ftp{};
finalized_tx ftx{};
success = fill_tx_sources_and_destinations(events, top, alice.get_keys(), alice.get_public_address(), MK_TEST_COINS(2), TESTS_DEFAULT_FEE, 0, sources, destinations);
CHECK_AND_ASSERT_EQ(success, true);
CHECK_AND_ASSERT_GREATER(m_ado_emit.descriptor.current_supply, ado_register.descriptor.current_supply);
destinations.emplace_back(m_ado_emit.descriptor.current_supply - ado_register.descriptor.current_supply, alice.get_public_address(), null_pkey);
ftp.sources = sources;
ftp.prepared_destinations = destinations;
ftp.tx_version = get_tx_version(get_block_height(top), m_hardforks);
ftp.extra = {m_ado_emit};
ftp.shuffle = true;
CHECK_AND_ASSERT_GREATER(m_ado_emit.descriptor.current_supply, m_ado_emit.descriptor.total_max_supply);
success = construct_tx(alice.get_keys(), ftp, ftx);
CHECK_AND_ASSERT_EQ(success, true);
tx_4 = ftx.tx;
}
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx_4);
DO_CALLBACK(events, "assert_asset_beta_not_emitted");
// Alice burns asset BETA. The public burn is performed through the wallet object. Burn mustn't be performed, because .current_supply > .total_max_supply in the asset base descriptor.
DO_CALLBACK(events, "public_burn_asset_beta_with_incorrect_supply");
return true;
}
bool asset_current_and_total_supplies_comparative_constraints::assert_asset_alpha_not_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const
{
const std::shared_ptr alice_wallet{init_playtime_test_wallet_t<tools::wallet2>(events, c, ALICE_ACC_IDX)};
crypto::public_key alpha_asset_id{};
const std::string ticker{m_ados_register.at(asset_position::alpha).descriptor.ticker};
alice_wallet->refresh();
{
crypto::point_t point_alpha_asset_id{};
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(m_ados_register.at(asset_position::alpha), &point_alpha_asset_id, &alpha_asset_id), false, "failed to calculate asset " + ticker + " id");
}
{
asset_descriptor_base alpha_adb{};
CHECK_AND_ASSERT_MES(!c.get_blockchain_storage().get_asset_info(alpha_asset_id, alpha_adb), false, "the asset " + ticker + " must not be registered");
}
return true;
}
bool asset_current_and_total_supplies_comparative_constraints::assert_asset_beta_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const
{
const std::shared_ptr alice_wallet{init_playtime_test_wallet_t<tools::wallet2>(events, c, ALICE_ACC_IDX)};
crypto::public_key key_beta_asset_id{};
const std::string ticker{m_ados_register.at(asset_position::beta).descriptor.ticker};
alice_wallet->refresh();
{
crypto::point_t point_beta_asset_id{};
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(m_ados_register.at(asset_position::beta), &point_beta_asset_id, &key_beta_asset_id), false, "failed to calculate asset id");
}
{
asset_descriptor_base beta_adb{};
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().get_asset_info(key_beta_asset_id, beta_adb), false, "the asset " + ticker + " must not be registered");
}
CHECK_AND_ASSERT_MES(alice_wallet->balance(key_beta_asset_id) == 1, false, "Alice has got not exactly 1 " + ticker);
return true;
}
bool asset_current_and_total_supplies_comparative_constraints::emit_asset_beta_with_incorrect_supply(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const
{
const std::shared_ptr alice_wallet{init_playtime_test_wallet_t<tools::wallet2>(events, c, ALICE_ACC_IDX)};
std::vector<tx_destination_entry> destinations{};
crypto::public_key beta_asset_id{};
{
asset_descriptor_base adb{};
CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().get_asset_info(*m_ado_emit.opt_asset_id, adb), true);
}
{
crypto::point_t point_beta_asset_id{};
CHECK_AND_ASSERT_EQ(get_or_calculate_asset_id(m_ado_emit, &point_beta_asset_id, &beta_asset_id), true);
}
CHECK_AND_ASSERT_EQ(*m_ado_emit.opt_asset_id, beta_asset_id);
alice_wallet->refresh();
{
const auto& ado_register{m_ados_register.at(asset_position::beta)};
CHECK_AND_ASSERT_GREATER(m_ado_emit.descriptor.current_supply, ado_register.descriptor.current_supply);
destinations.emplace_back(m_ado_emit.descriptor.current_supply - ado_register.descriptor.current_supply, alice_wallet->get_account().get_public_address(), beta_asset_id);
}
CHECK_AND_ASSERT_GREATER(m_ado_emit.descriptor.current_supply, m_ado_emit.descriptor.total_max_supply);
try
{
transaction tx{};
alice_wallet->emit_asset(beta_asset_id, destinations, tx);
}
catch (const tools::error::tx_rejected&)
{
CHECK_AND_ASSERT_EQ(c.get_pool_transactions_count(), 0);
return true;
}
catch (...)
{
return false;
}
return false;
}
bool asset_current_and_total_supplies_comparative_constraints::assert_asset_beta_not_emitted(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const
{
const std::shared_ptr alice_wallet{init_playtime_test_wallet_t<tools::wallet2>(events, c, ALICE_ACC_IDX)};
crypto::public_key beta_asset_id{};
const auto& register_ado{m_ados_register.at(asset_position::beta)};
alice_wallet->refresh();
{
crypto::point_t point_beta_asset_id{};
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(register_ado, &point_beta_asset_id, &beta_asset_id), false, "failed to calculate asset id");
}
{
const uint64_t& current_supply{register_ado.descriptor.current_supply};
CHECK_AND_ASSERT_MES(alice_wallet->balance(beta_asset_id) == current_supply, false, "Alice has got not exactly " + std::to_string(current_supply) + ' ' + register_ado.descriptor.ticker);
}
}
bool asset_current_and_total_supplies_comparative_constraints::public_burn_asset_beta_with_incorrect_supply(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const
{
const std::shared_ptr alice_wallet{init_playtime_test_wallet_t<tools::wallet2>(events, c, ALICE_ACC_IDX)};
crypto::public_key beta_asset_id{};
alice_wallet->refresh();
{
crypto::point_t point_beta_asset_id{};
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(m_ados_register.at(asset_position::beta), &point_beta_asset_id, &beta_asset_id), false, "failed to calculate asset id");
}
try
{
transaction tx{};
alice_wallet->burn_asset(beta_asset_id, m_ado_emit.descriptor.current_supply, tx);
}
catch (const std::runtime_error&)
{
CHECK_AND_ASSERT_EQ(c.get_pool_transactions_count(), 0);
return true;
}
return false;
}
bool asset_current_and_total_supplies_comparative_constraints::assert_asset_gamma_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const
{
const std::shared_ptr alice_wallet{init_playtime_test_wallet_t<tools::wallet2>(events, c, ALICE_ACC_IDX)};
crypto::public_key key_gamma_asset_id{};
const std::string ticker{m_ados_register.at(asset_position::gamma).descriptor.ticker};
alice_wallet->refresh();
{
crypto::point_t point_gamma_asset_id{};
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(m_ados_register.at(asset_position::gamma), &point_gamma_asset_id, &key_gamma_asset_id), false, "failed to calculate asset " + ticker + " id");
}
{
asset_descriptor_base gamma_adb{};
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().get_asset_info(key_gamma_asset_id, gamma_adb), false, "the asset " + ticker + " must be registered");
}
CHECK_AND_ASSERT_EQ(alice_wallet->balance(key_gamma_asset_id), m_ados_register.at(asset_position::gamma).descriptor.current_supply);
return true;
}

View file

@ -95,3 +95,22 @@ struct eth_signed_asset_via_rpc : public wallet_test
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct asset_current_and_total_supplies_comparative_constraints : public wallet_test
{
public:
asset_current_and_total_supplies_comparative_constraints();
bool generate(std::vector<test_event_entry>& events) const;
bool assert_asset_alpha_not_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool assert_asset_beta_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool emit_asset_beta_with_incorrect_supply(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool assert_asset_beta_not_emitted(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool public_burn_asset_beta_with_incorrect_supply(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
bool assert_asset_gamma_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events) const;
private:
enum asset_position { alpha = 0, beta = 1, gamma = 2 };
mutable std::array<currency::asset_descriptor_base, 3> m_adbs{};
mutable std::array<currency::asset_descriptor_operation, 3> m_ados_register{};
mutable currency::asset_descriptor_operation m_ado_emit{};
};

View file

@ -9,7 +9,7 @@
#include "offers_tests_common.h"
#include "tx_builder.h"
#include "chaingen_helpers.h"
#include "..\..\src\currency_core\tx_semantic_validation.h"
#include "../../src/currency_core/tx_semantic_validation.h"
using namespace epee;
using namespace crypto;

2
utils/JS/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/node_modules*
.vscode

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

192
utils/JS/package-lock.json generated Normal file
View file

@ -0,0 +1,192 @@
{
"name": "JS",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"axios": "^1.7.7",
"ethers": "^6.13.2"
}
},
"node_modules/@adraffy/ens-normalize": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
"integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw=="
},
"node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@types/node": {
"version": "18.15.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz",
"integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q=="
},
"node_modules/aes-js": {
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q=="
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ethers": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz",
"integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/ethers-io/"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@adraffy/ens-normalize": "1.10.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.2",
"@types/node": "18.15.13",
"aes-js": "4.0.0-beta.5",
"tslib": "2.4.0",
"ws": "8.17.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

6
utils/JS/package.json Normal file
View file

@ -0,0 +1,6 @@
{
"dependencies": {
"axios": "^1.7.7",
"ethers": "^6.13.2"
}
}

View file

@ -0,0 +1,8 @@
{
"id": 0,
"jsonrpc": "2.0",
"result": {
"status": "",
"transfers_were_unlocked": false
}
}

248
utils/JS/test_eth_sig.js Normal file
View file

@ -0,0 +1,248 @@
const axios = require('axios');
const { ethers } = require("ethers");
const { exit } = require('process');
const fs = require('fs');
/// Define an async function that takes method name and parameters
async function callJsonRpc(requestData, port = 22222) {
try {
const response = await axios.post('http://127.0.0.1:' + port +'/json_rpc', requestData, {
headers: {
'Content-Type': 'application/json',
},
});
// Return the result from the JSON-RPC response
return response.data;
} catch (error) {
console.error('Error:', error);
throw error; // Optionally rethrow the error to handle it in the calling function
}
}
function reverseHexBytes(hexString) {
// Ensure the hex string length is even
if (hexString.length % 2 !== 0) {
throw new Error("Invalid hex string length");
}
// Split the hex string into chunks of 2 characters (1 byte)
const bytes = hexString.match(/.{1,2}/g);
// Reverse the array of bytes and join them back into a string
const reversedHex = bytes.reverse().join('');
return reversedHex;
}
async function deploy_asset()
{
try {
//Generated Private Key: 0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154
//Generated Address: 0x0886bA9F5b117D2A3C1ce18106F2Ce759f5D34C8
const loadedWallet = new ethers.Wallet("0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154");
console.log("Loaded Address:", loadedWallet.address);
console.log("Public key:", loadedWallet.signingKey.compressedPublicKey);
const owner_eth_pub_key = loadedWallet.signingKey.compressedPublicKey.substring(2);
console.log("Generated Public key HEX:", owner_eth_pub_key);
const jsonObject = {
id: 0,
jsonrpc: "2.0",
method: "deploy_asset",
params: {
asset_descriptor: {
//current_supply: 1000000000000000,
decimal_point: 12,
full_name: "Zano wrapped ABC",
hidden_supply: false,
meta_info: "Stable and private",
owner: "",
ticker: "ZABC",
total_max_supply: 1000000000000000000,
owner_eth_pub_key: owner_eth_pub_key
},
destinations: [
{
address: "ZxC1U6hoCRM9PBSwrBTrWD8XgcHqLNJN9NWqXs9o994eZuHHBvSAyBpQ4TbWSNoabUDPdD8iEM5ZjPoMM7jE48mp2iKcVHLSK",
amount: 1000000000000000,
asset_id: ""
},
{
address: "ZxC1U6hoCRM9PBSwrBTrWD8XgcHqLNJN9NWqXs9o994eZuHHBvSAyBpQ4TbWSNoabUDPdD8iEM5ZjPoMM7jE48mp2iKcVHLSK",
amount: 1000000000000000,
asset_id: ""
}
],
do_not_split_destinations: false
}
};
const res = await callJsonRpc(jsonObject);
console.log("deploy_asset response: " + JSON.stringify(res, null, 2));
/*
deploy_asset response:
{
"id": 0,
"jsonrpc": "2.0",
"result": {
"new_asset_id": "7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf",
"tx_id": "73ff52bf4d85153f2b25033dd76e9e92e63214ed983682182e6e2b2ce0ecf46c"
}
}
*/
}
catch (error) {
console.error('Error occurred:', error);
}
}
async function emmit_asset()
{
try {
//Generated Private Key: 0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154
//Generated Address: 0x0886bA9F5b117D2A3C1ce18106F2Ce759f5D34C8
// asset_id 7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf
//var use_pregenerated_files = false;
const loadedWallet = new ethers.Wallet("0x17a938099954cee510d7fc9eb2366f0762b093d9be547acabf8be85f774ef154");
console.log("Loaded Address:", loadedWallet.address);
console.log("Public key:", loadedWallet.signingKey.compressedPublicKey);
const owner_eth_pub_key = loadedWallet.signingKey.compressedPublicKey.substring(2);
console.log("Generated Public key HEX:", owner_eth_pub_key);
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed on coordinator node:
var res_emmit;
//if(!use_pregenerated_files)
//{
const requestDataEmit = {
id: 0,
jsonrpc: "2.0",
method: "emit_asset",
params: {
asset_id: "7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf",
destinations: [{
address: "ZxC1U6hoCRM9PBSwrBTrWD8XgcHqLNJN9NWqXs9o994eZuHHBvSAyBpQ4TbWSNoabUDPdD8iEM5ZjPoMM7jE48mp2iKcVHLSK",
amount: 100000000000,
asset_id: ""
}],
do_not_split_destinations: false
}
};
res_emmit = await callJsonRpc(requestDataEmit);
fs.writeFileSync('emmit_response.json', JSON.stringify(res_emmit, null, 2));
console.log("emmit_response response: " + JSON.stringify(res_emmit, null, 2));
//}else
//{
// const data = fs.readFileSync('emmit_response.json', 'utf8');
// res_emmit = JSON.parse(data);
//}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed on validator node:
var res_decrypt;
//if(!use_pregenerated_files)
//{
const requestDataDecrypt = {
id: 0,
jsonrpc: "2.0",
method: "decrypt_tx_details",
params: {
outputs_addresses: res_emmit.result.data_for_external_signing.outputs_addresses,
tx_blob: res_emmit.result.data_for_external_signing.unsigned_tx,
tx_id: "",
tx_secret_key: res_emmit.result.data_for_external_signing.tx_secret_key
}
};
res_decrypt = await callJsonRpc(requestDataDecrypt, 12111); //request to daemon
fs.writeFileSync('decrypt_response.json', JSON.stringify(res_decrypt, null, 2));
console.log("decrypt_response : " + JSON.stringify(res_decrypt, null, 2));
//TODO: response holds all information about what this transaction actually transfer and to what addresses
//}else
//{
// const data = fs.readFileSync('decrypt_response.json', 'utf8');
// res_decrypt = JSON.parse(data);
//}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed with TSS scheme:
const bytesToSign = ethers.getBytes('0x' + res_decrypt.result.verified_tx_id);
const signature = loadedWallet.signingKey.sign(bytesToSign).serialized;
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//this part is performed on coordinator node with given signature:
const signature_without_0x = signature.substring(2);
console.log("Generated signature: " + signature_without_0x);
// Strip the last byte (recovery parameter) to get 64 bytes
const strippedSignature = signature_without_0x.slice(0, -2); // Remove the last byte (2 hex chars)
const requestSendSigned = {
id: 0,
jsonrpc: "2.0",
method: "send_ext_signed_asset_tx",
params: {
eth_sig: strippedSignature,
expected_tx_id: res_decrypt.result.verified_tx_id,
finalized_tx: res_emmit.result.data_for_external_signing.finalized_tx,
unlock_transfers_on_fail: false,
unsigned_tx: res_emmit.result.data_for_external_signing.unsigned_tx
}
}
const res_sign = await callJsonRpc(requestSendSigned);
fs.writeFileSync('sign_response.json', JSON.stringify(res_sign, null, 2));
console.log("sign_response response: " + JSON.stringify(res_sign, null, 2));
}
catch (error) {
console.error('Error occurred:', error);
}
}
async function main()
{
try {
/*
await deploy_asset();
TODO: wait for 10 confirmations
//wait for 10 confirmations
//asset id 7d51ecaad2e3458e0d62b146f33079c6ea307841b09a44b777e0c01eb11b98bf
*/
await emmit_asset();
} catch (error) {
console.error('Error occurred:', error);
}
}
main();