1
0
Fork 0
forked from lthn/blockchain

Merge branch 'develop' into secp256k1

# Conflicts:
#	src/gui/qt-daemon/layout
#	src/simplewallet/simplewallet.cpp
#	src/version.h.in
#	tests/core_tests/chaingen_main.cpp
#	tests/core_tests/multiassets_test.cpp
#	tests/core_tests/multiassets_test.h
#	tests/unit_tests/multiassets_test.cpp
This commit is contained in:
sowle 2024-09-23 14:24:00 +02:00
commit 08281059fb
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
18 changed files with 2427 additions and 69 deletions

View file

@ -60,7 +60,7 @@ namespace currency
return m_keys;
}
//-----------------------------------------------------------------
void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password)
void account_base::crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password)
{
crypto::chacha8_key key = AUTO_VAL_INIT(key);
crypto::generate_chacha8_key(password, key);
@ -71,16 +71,23 @@ namespace currency
crypto::chacha8(scr_data, src_length, key, iv, (char*)dst_data);
}
//-----------------------------------------------------------------
std::string account_base::get_seed_phrase(const std::string& password) const
std::string account_base::get_seed_phrase(const std::string& password) const
{
if (m_keys_seed_binary.empty())
return "";
return get_seed_phrase(password, m_keys_seed_binary);
}
//-----------------------------------------------------------------
std::string account_base::get_seed_phrase(const std::string& password, const std::vector<unsigned char>& keys_seed_binary) const
{
if (keys_seed_binary.empty())
return "";
std::vector<unsigned char> processed_seed_binary = m_keys_seed_binary;
std::vector<unsigned char> processed_seed_binary = keys_seed_binary;
if (!password.empty())
{
//encrypt seed phrase binary data
crypt_with_pass(&m_keys_seed_binary[0], m_keys_seed_binary.size(), &processed_seed_binary[0], password);
crypt_with_pass(&keys_seed_binary[0], keys_seed_binary.size(), &processed_seed_binary[0], password);
}
std::string keys_seed_text = tools::mnemonic_encoding::binary2text(processed_seed_binary);
@ -92,7 +99,7 @@ namespace currency
CHECK_AND_ASSERT_THROW_MES(self_check_is_password_used == !password.empty(), "Account seed phrase internal error: password flag encoded wrong");
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
std::string binary_for_check_sum((const char*)&m_keys_seed_binary[0], m_keys_seed_binary.size());
std::string binary_for_check_sum((const char*)&keys_seed_binary[0], keys_seed_binary.size());
binary_for_check_sum.append(password);
crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size());
*reinterpret_cast<uint64_t*>(&h) = creation_timestamp_rounded;

View file

@ -56,6 +56,7 @@ namespace currency
std::string get_public_address_str() const;
std::string get_seed_phrase(const std::string& seed_password) const;
std::string get_seed_phrase(const std::string& password, const std::vector<unsigned char>& keys_seed_binary) const;
std::string get_tracking_seed() const;
bool restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password);
bool restore_from_tracking_seed(const std::string& tracking_seed);
@ -82,6 +83,8 @@ namespace currency
static std::vector<unsigned char> string_to_vector_of_chars(const std::string& v) { return std::vector<unsigned char>(v.begin(), v.end()); }
static bool is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected);
static bool is_seed_tracking(const std::string& seed_phrase);
static void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password);
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_keys)

View file

@ -116,7 +116,7 @@ namespace currency
secret_index = ring.size() - 1;
}
CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": can't find a corresponding asset id in inputs, asset id: " << H);
CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": cannot find a corresponding asset id in inputs or asset operations; asset id: " << H);
result.bge_proofs.emplace_back(crypto::BGE_proof_s{});
uint8_t err = 0;
@ -322,7 +322,7 @@ namespace currency
CHECK_AND_ASSERT_MES(ogc.asset_id_blinding_mask_x_amount_sum.is_zero(), false, "it's expected that all asset ids for this tx are obvious and thus explicit"); // because this tx has no ZC inputs => all outs clearly have native asset id
CHECK_AND_ASSERT_MES(ogc.ao_amount_blinding_mask.is_zero(), false, "asset emmission is not allowed for txs without ZC inputs");
// (sum(bare inputs' amounts) - fee) * H + sum(pseudo out amount commitments) - sum(outputs' commitments) = lin(G)
// (sum(bare inputs' amounts) - fee) * H - sum(outputs' commitments) = lin(G)
crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt - ogc.amount_commitments_sum;
crypto::scalar_t secret_x = -ogc.amount_blinding_masks_sum;
@ -336,6 +336,8 @@ 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
// (sum(bare inputs' amounts) - fee) * H + sum(pseudo out amount commitments) + asset_op_commitment - sum(outputs' commitments) = lin(X)
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;
@ -472,7 +474,7 @@ namespace currency
}
CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS || height == 0, false, "Too many outs (" << destinations.size() << ")! Miner tx can't be constructed.");
tx = AUTO_VAL_INIT_T(transaction);
// tx is not cleared intentionally to allow passing additional args in the extra/attachments
tx.version = tx_version;
tx_generation_context tx_gen_context{};

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Boolberry developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -714,9 +714,12 @@ bool MainWindow::show_inital()
{
TRY_ENTRY();
if (load_app_config())
{
restore_pos(true);
}
else
{
LOG_PRINT_L1("set defaults values to config");
m_config = AUTO_VAL_INIT(m_config);
this->show();
QSize sz = AUTO_VAL_INIT(sz);
@ -976,6 +979,11 @@ QString MainWindow::start_backend(const QString& params)
CATCH_ENTRY_FAIL_API_RESPONCE();
}
void MainWindow::show_notification(const QString& title, const QString& message)
{
show_notification(title.toStdString(), message.toStdString());
}
QString MainWindow::sync_call(const QString& func_name, const QString& params)
{
if (func_name == "test_call")
@ -1107,6 +1115,7 @@ bool MainWindow::get_is_disabled_notifications(const QString& param)
}
bool MainWindow::set_is_disabled_notifications(const bool& param)
{
LOG_PRINT_L1("set_is_disabled_notifications: notifications were " << (m_config.disable_notifications ? "DISABLED" : "ENABLED") << " -> now " << (param ? "DISABLED" : "ENABLED"));
m_config.disable_notifications = param;
return m_config.disable_notifications;
}
@ -2468,6 +2477,10 @@ QString MainWindow::print_log(const QString& param)
void MainWindow::show_notification(const std::string& title, const std::string& message)
{
TRY_ENTRY();
if (m_config.disable_notifications)
return;
LOG_PRINT_L1("system notification: \"" << title << "\", \"" << message << "\"");
// it's expected that title and message are utf-8 encoded!
@ -2482,5 +2495,3 @@ void MainWindow::show_notification(const std::string& title, const std::string&
#endif
CATCH_ENTRY2(void());
}

View file

@ -190,6 +190,7 @@ public:
void on_menu_show(const QString& param);
QString is_remnotenode_mode_preconfigured(const QString& param);
QString start_backend(const QString& params);
void show_notification(const QString& title, const QString& message);
QString async_call(const QString& func_name, const QString& params);
QString sync_call(const QString& func_name, const QString& params);

View file

@ -124,6 +124,7 @@ namespace
{
const command_line::arg_descriptor<std::string> arg_wallet_file ("wallet-file", "Use wallet <arg>", "");
const command_line::arg_descriptor<std::string> arg_generate_new_wallet ("generate-new-wallet", "Generate new wallet and save it to <arg> or <address>.wallet by default", "");
const command_line::arg_descriptor<bool> arg_derive_custom_seed("derive_custom_seed", "Derive seed phrase from custom 24-words secret(advanced option, do it on your own risk)", "");
const command_line::arg_descriptor<std::string> arg_generate_new_auditable_wallet ("generate-new-auditable-wallet", "Generate new auditable wallet and store it to <arg>", "");
const command_line::arg_descriptor<std::string> arg_daemon_address ("daemon-address", "Use daemon instance at <host>:<port>", "");
const command_line::arg_descriptor<std::string> arg_daemon_host ("daemon-host", "Use daemon instance at host <arg> instead of localhost", "");
@ -2870,6 +2871,266 @@ bool search_for_wallet_file(const std::wstring &search_here/*, const std::string
return false;
}
int custom_seed_builder()
{
success_msg_writer() <<
"**********************************************************************\n" <<
"This is an experimental tool that helps you create a custom seed phrase \n"
"based on your own 24 words. It can be extremely unsafe, so only use it \n"
"if you're confident in what you're doing.\n"
"**********************************************************************";
success_msg_writer() << "Please enter 24 words that you want to use as base for the seed:";
std::string seed_24;
std::getline(std::cin, seed_24);
std::list<std::string> words;
std::string trimed_seed_24 = epee::string_tools::trim(seed_24);
boost::split(words, trimed_seed_24, boost::is_space(), boost::token_compress_on);
seed_24 = boost::algorithm::join(words, " ");
std::string passphrase;
success_msg_writer() << "Please enter seed passphrase(it's highly recommended to use passphrase for custom seed):";
std::getline(std::cin, passphrase);
if (passphrase.empty())
{
success_msg_writer() << "Using unsecured seed(no passphrase)";
}
else
{
std::string passphrase_confirmation;
success_msg_writer() << "Please confirm passphrase:";
std::getline(std::cin, passphrase_confirmation);
if (passphrase_confirmation != passphrase)
{
success_msg_writer() << "Passphrase mismatched, try again";
return EXIT_FAILURE;
}
}
account_base acc;
acc.generate();
std::string pass_protected_or_not = "";
std::vector<unsigned char> binary_from_seed = tools::mnemonic_encoding::text2binary(seed_24);
std::vector<unsigned char> processed_binary_from_seed = binary_from_seed;
if (!passphrase.empty())
{
//encrypt seed phrase binary data
account_base::crypt_with_pass(&binary_from_seed[0], binary_from_seed.size(), &processed_binary_from_seed[0], passphrase);
pass_protected_or_not = "(secured with passphrase)";
}
{
pass_protected_or_not = "(!without passphrase!)";
}
const std::string new_seed = acc.get_seed_phrase(passphrase, processed_binary_from_seed);
success_msg_writer() << "Here is your seed" << pass_protected_or_not << "\n " << new_seed;
return EXIT_SUCCESS;
}
int seed_doctor()
{
success_msg_writer() <<
"**********************************************************************\n" <<
"This is experimental tool that might help you to recover your wallet's corrupted seed phrase. \n" <<
"It might help to sort out only trivial situations where one word was written wrong or \n" <<
"two word was written in wrong order\n" <<
"**********************************************************************";
success_msg_writer() << "Please enter problematic seed phrase:";
std::string seed;
std::getline(std::cin, seed);
success_msg_writer() << "Please enter wallet address if you have it(press enter if you don't know it):";
std::string address;
std::getline(std::cin, address);
address = epee::string_tools::trim(address);
std::string passphrase;
bool check_summ_available = false;
//cut the last timestamp word from restore_dats
std::vector<std::string> words;
boost::split(words, seed, boost::is_space());
std::set<size_t> failed_words;
size_t i = 0;
//let's validate each word
for (const auto& w : words)
{
if (!tools::mnemonic_encoding::valid_word(w))
{
success_msg_writer() << "Word " << i << " '" << w << "' is invalid, attempting to restore it";
failed_words.insert(i);
}
i++;
}
if (words.size() == SEED_PHRASE_V1_WORDS_COUNT)
{
// 24 seed words + one timestamp word = 25 total
success_msg_writer() << "SEED_PHRASE_V1_WORDS_COUNT, checksum is unavailable";
if (address.empty())
{
success_msg_writer() << "With SEED_PHRASE_V1_WORDS_COUNT and address unknown it's not enough information to recover it, sorry";
return EXIT_FAILURE;
}
}
else if (words.size() == SEED_PHRASE_V2_WORDS_COUNT)
{
// 24 seed words + one timestamp word + one flags & checksum = 26 total
success_msg_writer() << "SEED_PHRASE_V2_WORDS_COUNT, checksum is available";
check_summ_available = true;
}
else if (words.size() == 24)
{
//seed only with no date, add date to it
std::string zero_word = tools::mnemonic_encoding::word_by_num(0); //zero_word is likely to be "like"
words.push_back(zero_word); //
}
else
{
success_msg_writer() << "Impossible to recover something with " << words.size() << " words only";
return EXIT_FAILURE;
}
bool pass_protected = false;
bool success = account_base::is_seed_password_protected(seed, pass_protected);
success_msg_writer() << "SECURED_SEED: " << (pass_protected ? "true" : "false");
if (pass_protected)
{
success_msg_writer() << "Please enter seed passphrase(if passphrase is wrong then chances to correct seed recovery is nearly zero):";
std::getline(std::cin, passphrase);
}
size_t global_candidates_count = 0;
auto brute_force_func = [&](size_t word_index) -> bool {
const map<string, uint32_t>& all_words = tools::mnemonic_encoding::get_words_map();
success_msg_writer() << "Brute forcing word " << word_index << " ....";
size_t candidates_count = 0;
std::vector<std::string> words_local = words;
for (auto it = all_words.begin(); it != all_words.end(); it++)
{
words_local[word_index] = it->first;
std::string result = boost::algorithm::join(words_local, " ");
account_base acc;
bool r = acc.restore_from_seed_phrase(result, passphrase);
if (r)
{
if (!address.empty())
{
//check against address
if (acc.get_public_address_str() == address)
{
success_msg_writer(true) << "!!!SUCCESS!!!";
success_msg_writer() << "Seed recovered, please write down recovered seed and use it to restore the wallet:\n" << result;
return true;
}
}
else
{
success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str();
candidates_count++;
}
}
}
global_candidates_count += candidates_count;
return false;
};
auto swap_func = [&](size_t word_index) -> bool {
size_t candidates_count = 0;
std::vector<std::string> words_local = words;
std::string tmp = words_local[word_index];
words_local[word_index] = words_local[word_index + 1];
words_local[word_index + 1] = tmp;
std::string result = boost::algorithm::join(words_local, " ");
account_base acc;
bool r = acc.restore_from_seed_phrase(result, passphrase);
if (r)
{
if (!address.empty())
{
//check against address
if (acc.get_public_address_str() == address)
{
success_msg_writer(true) << "!!!SUCCESS!!!";
success_msg_writer() << "Seed recovered, please write down recovered seed and use it to restore the wallet:\n" << result;
return true;
}
}
else
{
success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str();
candidates_count++;
}
}
global_candidates_count += candidates_count;
return false;
};
if (failed_words.size())
{
if (failed_words.size() > 1)
{
success_msg_writer() << "Restoring more then 1 broken words not implemented yet, sorry";
return EXIT_FAILURE;
}
if (!check_summ_available && address.empty())
{
success_msg_writer() << "No address and no checksum, recovery is impossible, sorry";
return EXIT_FAILURE;
}
size_t broken_word_index = *failed_words.begin();
bool r = brute_force_func(broken_word_index);
success_msg_writer() << "Brute forcing finished, " << global_candidates_count << " potential candidates found";
return r ? EXIT_SUCCESS : EXIT_FAILURE;
}
else
{
if (!check_summ_available && address.empty())
{
success_msg_writer() << "No address and no checksum, recovery is limited only to date reset";
std::string result = boost::algorithm::join(words, " ");
account_base acc;
bool r = acc.restore_from_seed_phrase(result, passphrase);
success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str();
return EXIT_FAILURE;
}
success_msg_writer() << "Brute forcing all each word";
for (size_t i = 0; i != words.size() && i != 24; i++)
{
bool r = brute_force_func(i);
if(r)
return EXIT_SUCCESS;
}
}
success_msg_writer() << "Brute forcing finished, " << global_candidates_count << " potential candidates found";
success_msg_writer() << "Swap check...";
for (size_t i = 0; i != words.size() && i != 23; i++)
{
bool r = swap_func(i);
if (r)
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
//----------------------------------------------------------------------------------------------------
#ifdef WIN32
int wmain( int argc, wchar_t* argv_w[ ], wchar_t* envp[ ] )
@ -2934,7 +3195,10 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_set_timeout);
command_line::add_arg(desc_params, arg_voting_config_file);
command_line::add_arg(desc_params, arg_no_password_confirmations);
command_line::add_arg(desc_params, command_line::arg_generate_rpc_autodoc);
command_line::add_arg(desc_params, command_line::arg_generate_rpc_autodoc);
command_line::add_arg(desc_params, arg_seed_doctor);
command_line::add_arg(desc_params, arg_derive_custom_seed);
tools::wallet_rpc_server::init_options(desc_params);
@ -3012,6 +3276,16 @@ int main(int argc, char* argv[])
bool offline_mode = command_line::get_arg(vm, arg_offline_mode);
if (command_line::has_arg(vm, arg_seed_doctor))
{
return seed_doctor();
}
if (command_line::has_arg(vm, arg_derive_custom_seed))
{
return custom_seed_builder();
}
if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port))
{
// runs wallet as RPC server

View file

@ -8,6 +8,6 @@
#define PROJECT_REVISION "0"
#define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION
#define PROJECT_VERSION_BUILD_NO 336
#define PROJECT_VERSION_BUILD_NO 341
#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

@ -817,3 +817,241 @@ bool chain_switching_when_out_spent_in_alt_chain_ref_id::generate(std::vector<te
return true;
}
alt_chain_and_block_tx_fee_median::alt_chain_and_block_tx_fee_median()
{
REGISTER_CALLBACK_METHOD(alt_chain_and_block_tx_fee_median, check_after_hf4);
REGISTER_CALLBACK_METHOD(alt_chain_and_block_tx_fee_median, check_before_hf4);
}
bool alt_chain_and_block_tx_fee_median::generate(
std::vector<test_event_entry>& events) const
{
/* Test idea: check chain switching rules.
Rules before and after HF4 for PoW blocks are different. There're only PoW
blocks in the test situation. If the last blocks contain transactions (non
empty blocks), then the chain with the largest this_block_tx_fee_median on its
head becomes the main.
0 10 11 21 22
(0 ) - ... - (0r) - (1 ) - ... - (1r) - (2 )
| main | \
| | [tx_0]
| |
| | main
\ - (1a) \ - (2a)
\
[tx_1]
Chain with head blk_1 versus chain with head blk_1a: chain with head blk_1
is the main, because blocks 1, 1a are empty.
Chain with head blk_2 versus chain with head blk_2a: chain with head blk_2a
is the main, because blocks 2, 2a aren't empty and the fee of tx_1 is larger
than the fee of tx_0.
*/
bool success{};
bool hf4_active{};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
transaction tx_0{}, tx_1{};
uint64_t tx_version{};
crypto::hash top_block{};
GENERATE_ACCOUNT(miner);
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);
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner);
MAKE_NEXT_BLOCK(events, blk_1a, blk_0r, miner);
/* It is decided which chain will be the main: with the head blk_1 or with the
head blk_1a.
0 10 11
/ - (1 )
|
(0 ) - ... - (0r)
|
\ - (1a)
*/
CHECK_AND_ASSERT_EQ(is_pos_block(blk_1), false);
CHECK_AND_ASSERT_EQ(is_pos_block(blk_1a), false);
CHECK_AND_ASSERT_EQ(get_block_height(blk_1), 11);
CHECK_AND_ASSERT_EQ(get_block_height(blk_1), get_block_height(blk_1a));
/* Blocks blk_1, blk_1a do not contain transactions (they are empty blocks).
Switching to the alternative chain with head blk_1a will not occur. The main
chain is the chain with the head blk_1. */
DO_CALLBACK_PARAMS(events,
"check_top_block",
params_top_block(get_block_height(blk_1),
get_block_hash(blk_1)));
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner,
CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// Transaction tx_0 is constructed and placed in block blk_2.
success = fill_tx_sources_and_destinations(
events,
/* head = */ blk_1r,
/* from = */ miner.get_keys(),
/* to = */ miner.get_public_address(),
/* amount = */ MK_TEST_COINS(10),
/* fee = */ m_fee_tx_0_blk_2,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
tx_version = get_tx_version(get_block_height(blk_1r),
m_hardforks);
success = construct_tx(miner.get_keys(),
sources,
destinations,
empty_attachment,
tx_0,
tx_version,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_0");
ADD_CUSTOM_EVENT(events, tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner, tx_0);
sources.clear();
destinations.clear();
// Transaction tx_1 is constructed and placed in block blk_2a.
tx_version = get_tx_version(get_block_height(blk_1r),
m_hardforks);
success = fill_tx_sources_and_destinations(
events,
/* head = */ blk_1r,
/* from = */ miner.get_keys(),
/* to = */ miner.get_public_address(),
/* amount = */ MK_TEST_COINS(10),
/* fee = */ m_fee_tx_1_blk_2a,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
success = construct_tx(miner.get_keys(),
sources,
destinations,
empty_attachment,
tx_1,
tx_version,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_1");
ADD_CUSTOM_EVENT(events, tx_1);
MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner, tx_1);
/* It is decided which chain will be the main: with the head blk_2 or with the
head blk_2a.
0 21 22
/ - (2 )
| \
| [tx_0]
|
(0 ) - ... - (1r)
|
|
|
\ - (2a)
\
[tx_1]
*/
CHECK_AND_ASSERT_EQ(is_pos_block(blk_2), false);
CHECK_AND_ASSERT_EQ(is_pos_block(blk_2a), false);
CHECK_AND_ASSERT_GREATER(m_fee_tx_1_blk_2a, m_fee_tx_0_blk_2);
CHECK_AND_ASSERT_EQ(get_block_height(blk_2), 22);
CHECK_AND_ASSERT_EQ(get_block_height(blk_2), get_block_height(blk_2a));
hf4_active =
m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM,
get_block_height(blk_2) + 1);
if (hf4_active)
{
/* With HF4 active, the chain with head blk_2a wins because transaction tx_1
has a greater fee than transaction tx_0. The main chain is the chain with
the head blk_2a. */
DO_CALLBACK(events, "check_after_hf4");
top_block = get_block_hash(blk_2a);
}
else
{
/* The chains have the same commulative difficulty. Therefore, with HF4
inactive, switching to the chain with the blk_2a head will not occur. The
main chain is the chain with the head blk_2. */
DO_CALLBACK(events, "check_before_hf4");
top_block = get_block_hash(blk_2);
}
DO_CALLBACK_PARAMS(events,
"check_top_block",
params_top_block(/* height = */ 22, top_block));
return true;
}
bool alt_chain_and_block_tx_fee_median::check_after_hf4(
currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events)
{
block_extended_info bei{};
const uint64_t height_block{22};
CHECK_AND_ASSERT_EQ(
m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM,
height_block),
true);
c.get_blockchain_storage().get_block_extended_info_by_height(height_block,
bei);
CHECK_AND_ASSERT_EQ(bei.this_block_tx_fee_median, m_fee_tx_1_blk_2a);
return true;
}
bool alt_chain_and_block_tx_fee_median::check_before_hf4(
currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events)
{
block_extended_info bei{};
const uint64_t height_block{22};
CHECK_AND_ASSERT_EQ(
m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM,
height_block),
false);
c.get_blockchain_storage().get_block_extended_info_by_height(height_block,
bei);
CHECK_AND_ASSERT_EQ(bei.this_block_tx_fee_median, m_fee_tx_0_blk_2);
return true;
}

View file

@ -82,3 +82,21 @@ struct chain_switching_when_out_spent_in_alt_chain_ref_id : public test_chain_un
{
bool generate(std::vector<test_event_entry>& events) const;
};
struct alt_chain_and_block_tx_fee_median : public test_chain_unit_enchanced
{
alt_chain_and_block_tx_fee_median();
bool generate(std::vector<test_event_entry>& events) const;
bool check_after_hf4(currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events);
bool check_before_hf4(currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events);
private:
const uint64_t m_fee_tx_0_blk_2{TESTS_DEFAULT_FEE};
const uint64_t m_fee_tx_1_blk_2a{2 * m_fee_tx_0_blk_2};
};

View file

@ -1814,6 +1814,48 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s
return construct_tx(keys_from, sources, destinations, empty_attachment, tx, tx_version, 0);
}
bool construct_tx(const account_keys& sender_account_keys,
const std::vector<tx_source_entry>& sources,
const std::vector<tx_destination_entry>& destinations,
const std::vector<extra_v>& extra,
const std::vector<attachment_v>& attachments,
transaction& tx,
uint64_t tx_version,
crypto::secret_key& one_time_secret_key,
uint64_t unlock_time,
uint64_t expiration_time,
uint8_t tx_outs_attr,
bool shuffle,
uint64_t flags,
uint64_t explicit_consolidated_tx_fee,
tx_generation_context& gen_context)
{
// extra copy operation, but creating transaction is not sensitive to this
finalize_tx_param ftp {};
ftp.tx_version = tx_version;
ftp.sources = sources;
ftp.prepared_destinations = destinations;
ftp.extra = extra;
ftp.attachments = attachments;
ftp.unlock_time = unlock_time;
// ftp.crypt_address = crypt_destination_addr;
ftp.expiration_time = expiration_time;
ftp.tx_outs_attr = tx_outs_attr;
ftp.shuffle = shuffle;
ftp.flags = flags;
ftp.mode_separate_fee = explicit_consolidated_tx_fee;
finalized_tx ft = AUTO_VAL_INIT(ft);
ft.tx = tx;
ft.one_time_key = one_time_secret_key;
ftp.gen_context = gen_context; // ftp, not ft here, this is UGLY -- sowle
bool r = construct_tx(sender_account_keys, ftp, ft);
tx = ft.tx;
one_time_secret_key = ft.one_time_key;
gen_context = ft.ftp.gen_context;
return r;
}
uint64_t get_balance(const currency::account_keys& addr, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, bool dbg_log)
{
uint64_t res = 0;

View file

@ -673,6 +673,22 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s
const currency::account_keys& keys_from, const currency::account_public_address& addr_to,
uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx, bool use_ref_by_id = false);
bool construct_tx(const currency::account_keys& sender_account_keys,
const std::vector<currency::tx_source_entry>& sources,
const std::vector<currency::tx_destination_entry>& destinations,
const std::vector<currency::extra_v>& extra,
const std::vector<currency::attachment_v>& attachments,
currency::transaction& tx,
uint64_t tx_version,
crypto::secret_key& one_time_secret_key,
uint64_t unlock_time,
uint64_t expiration_time,
uint8_t tx_outs_attr,
bool shuffle,
uint64_t flags,
uint64_t explicit_consolidated_tx_fee,
currency::tx_generation_context& gen_context);
void get_confirmed_txs(const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs);
bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<currency::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head);
bool fill_tx_sources(std::vector<currency::tx_source_entry>& sources, const std::vector<test_event_entry>& events,

View file

@ -1140,7 +1140,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY_HF(alt_blocks_with_the_same_txs, "3-*");
GENERATE_AND_PLAY_HF(chain_switching_when_out_spent_in_alt_chain_mixin, "3-*");
GENERATE_AND_PLAY_HF(chain_switching_when_out_spent_in_alt_chain_ref_id, "3-*");
GENERATE_AND_PLAY_HF(alt_chain_and_block_tx_fee_median, "3-*");
// miscellaneous tests
GENERATE_AND_PLAY(test_blockchain_vs_spent_keyimges);
@ -1291,15 +1291,16 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(asset_depoyment_and_few_zc_utxos);
GENERATE_AND_PLAY_HF(assets_and_pos_mining, "4-*");
GENERATE_AND_PLAY_HF(asset_emission_and_unconfirmed_balance, "4-*");
GENERATE_AND_PLAY_HF(asset_operation_in_consolidated_tx, "4-*");
GENERATE_AND_PLAY_HF(asset_operation_and_hardfork_checks, "4-*");
GENERATE_AND_PLAY_HF(eth_signed_asset_basics, "4-*"); // TODO
GENERATE_AND_PLAY_HF(eth_signed_asset_via_rpc, "4-*");
GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*");
GENERATE_AND_PLAY_HF(attachment_isolation_test, "4-*");
// GENERATE_AND_PLAY(gen_block_reward);
// END OF TESTS */

View file

@ -7,51 +7,7 @@
using namespace currency;
namespace currency
{
bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources,
const std::vector<tx_destination_entry>& destinations,
const std::vector<extra_v>& extra,
const std::vector<attachment_v>& attachments,
transaction& tx,
uint64_t tx_version,
crypto::secret_key& one_time_secret_key,
uint64_t unlock_time,
uint64_t expiration_time,
uint8_t tx_outs_attr,
bool shuffle,
uint64_t flags,
uint64_t explicit_consolidated_tx_fee,
tx_generation_context& gen_context)
{
//extra copy operation, but creating transaction is not sensitive to this
finalize_tx_param ftp{};
ftp.tx_version = tx_version;
ftp.sources = sources;
ftp.prepared_destinations = destinations;
ftp.extra = extra;
ftp.attachments = attachments;
ftp.unlock_time = unlock_time;
// ftp.crypt_address = crypt_destination_addr;
ftp.expiration_time = expiration_time;
ftp.tx_outs_attr = tx_outs_attr;
ftp.shuffle = shuffle;
ftp.flags = flags;
ftp.mode_separate_fee = explicit_consolidated_tx_fee;
finalized_tx ft = AUTO_VAL_INIT(ft);
ft.tx = tx;
ft.one_time_key = one_time_secret_key;
ftp.gen_context = gen_context; // ftp, not ft here, this is UGLY -- sowle
bool r = construct_tx(sender_account_keys, ftp, ft);
tx = ft.tx;
one_time_secret_key = ft.one_time_key;
gen_context = ft.ftp.gen_context;
return r;
}
} // namespace currency
void add_flags_to_all_destination_entries(const uint64_t flags, std::vector<currency::tx_destination_entry>& destinations)
static void add_flags_to_all_destination_entries(const uint64_t flags, std::vector<currency::tx_destination_entry>& destinations)
{
for(auto& de : destinations)
de.flags |= flags;

View file

@ -892,6 +892,445 @@ bool asset_emission_and_unconfirmed_balance::c1(currency::core& c, size_t ev_ind
//------------------------------------------------------------------------------
asset_operation_and_hardfork_checks::asset_operation_and_hardfork_checks()
{
m_adb_hello.total_max_supply = 1'000'000'000'000'000'000;
m_adb_hello.current_supply = 1'000'000'000'000'000'000;
m_adb_hello.ticker = "HLO";
m_adb_hello.full_name = "HELLO_WORLD";
m_adb_hello.meta_info = "Hello, world!";
m_adb_hello.hidden_supply = false;
m_ado_hello.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER;
m_ado_hello.opt_asset_id = currency::null_pkey;
m_adb_bye.total_max_supply = 1'000'000'000'000'000'000;
m_adb_bye.current_supply = 1'000'000'000'000'000'000;
m_adb_bye.ticker = "BYE";
m_adb_bye.full_name = "BYE_WORLD";
m_adb_bye.meta_info = "Bye, world!";
m_adb_bye.hidden_supply = false;
m_ado_bye.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER;
m_ado_hello.opt_asset_id = currency::null_pkey;
REGISTER_CALLBACK_METHOD(asset_operation_and_hardfork_checks, c1);
REGISTER_CALLBACK_METHOD(asset_operation_and_hardfork_checks, c2);
}
bool asset_operation_and_hardfork_checks::generate(
std::vector<test_event_entry>& events) const
{
/*
0 10 11 21 22
( 0) - ... - (0r) - ( 1) - ... - (1r) - ( 2)
\ \
[tx_0] [tx_1]
*/
bool success{false};
std::vector<tx_source_entry> sources{};
std::vector<tx_destination_entry> destinations{};
transaction tx_0{}, tx_1{}, tx_2{}, tx_3{}, tx_4{};
uint64_t tx_version{};
crypto::secret_key stub{};
m_accounts.resize(2);
account_base& miner{m_accounts[MINER_ACC_IDX]};
miner.generate();
account_base& alice{m_accounts[ALICE_ACC_IDX]};
alice.generate();
m_adb_hello.owner = alice.get_public_address().spend_public_key;
m_ado_hello.descriptor = m_adb_hello;
m_adb_bye.owner = alice.get_public_address().spend_public_key;
m_ado_bye.descriptor = m_adb_bye;
MAKE_GENESIS_BLOCK(events,
blk_0,
miner,
test_core_time::get_time());
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events,
blk_0r,
blk_0,
miner,
CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
success = fill_tx_sources_and_destinations(events,
/* head = */ blk_0r,
/* from = */ miner.get_keys(),
/* to = */ alice.get_public_address(),
/* amount = */ MK_TEST_COINS(12),
/* fee = */ TESTS_DEFAULT_FEE,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
tx_version = get_tx_version(get_block_height(blk_0r),
m_hardforks);
success = construct_tx(miner.get_keys(),
sources,
destinations,
empty_attachment,
tx_0,
tx_version,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_0");
ADD_CUSTOM_EVENT(events, tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner, tx_0);
REWIND_BLOCKS_N_WITH_TIME(events,
blk_1r,
blk_1,
miner,
CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
sources.clear();
destinations.clear();
success = fill_tx_sources_and_destinations(events,
/* head = */ blk_1r,
/* from = */ alice,
/* to = */ alice,
/* amount = */ MK_TEST_COINS(5),
/* fee = */ TESTS_DEFAULT_FEE,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
destinations.emplace_back(/* amount = */ 1'000'000'000'000'000'000,
/* to = */ alice.get_public_address(),
/* asset_id = */ currency::null_pkey);
tx_version = get_tx_version(get_block_height(blk_1r), m_hardforks);
success = construct_tx(alice.get_keys(),
sources,
destinations,
/* extra = */ {m_ado_hello},
empty_attachment,
tx_1,
tx_version,
stub,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_1");
ADD_CUSTOM_EVENT(events, tx_1);
MAKE_NEXT_BLOCK_TX1(events,
blk_2,
blk_1r,
alice,
tx_1);
REWIND_BLOCKS_N_WITH_TIME(events,
blk_2r,
blk_2,
miner,
CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
/* A transaction that has at least 2 registration operation descriptors in its
extra is rejected by the core. */
sources.clear();
destinations.clear();
success = fill_tx_sources_and_destinations(events,
/* head = */ blk_2r,
/* from = */ alice,
/* to = */ alice,
/* amount = */ MK_TEST_COINS(2),
/* fee = */ TESTS_DEFAULT_FEE,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
tx_version = get_tx_version(get_block_height(blk_2r),
m_hardforks);
success = construct_tx(alice.get_keys(),
sources,
destinations,
/* extra = */ {m_ado_hello, m_ado_hello},
empty_attachment,
tx_2,
tx_version,
stub,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_2");
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx_2);
sources.clear();
destinations.clear();
/* A transaction that contains a registration operation descriptor in its
attachement, but extra is empty, is valid, but doesn't register the asset. The
fact that the asset is not registered is checked in the assertions in the
callback с2. */
success = fill_tx_sources_and_destinations(events,
/* head = */ blk_2r,
/* from = */ alice,
/* to = */ alice,
/* amount = */ MK_TEST_COINS(2),
/* fee = */ TESTS_DEFAULT_FEE,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
tx_version = get_tx_version(get_block_height(blk_2r),
m_hardforks);
success = construct_tx(alice.get_keys(),
sources,
destinations,
/* attachments = */ {m_ado_bye},
tx_3,
tx_version,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_3");
ADD_CUSTOM_EVENT(events, tx_3);
DO_CALLBACK(events, "c2");
sources.clear();
destinations.clear();
/* A transaction that contains a registration operation descriptor in its
attachement, but extra is empty, is valid, but doesn't register the asset. In
this case a different definition of the function construct_tx is used. */
success = fill_tx_sources_and_destinations(events,
/* head = */ blk_2r,
/* from = */ alice,
/* to = */ alice,
/* amount = */ MK_TEST_COINS(2),
/* fee = */ TESTS_DEFAULT_FEE,
/* nmix = */ 0,
sources,
destinations);
CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations");
tx_version = get_tx_version(get_block_height(blk_2r),
m_hardforks);
success = construct_tx(alice.get_keys(),
sources,
destinations,
empty_extra,
/* attachments = */ {m_ado_bye},
tx_4,
tx_version,
stub,
0);
CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_4");
ADD_CUSTOM_EVENT(events, tx_4);
DO_CALLBACK(events, "c2");
DO_CALLBACK(events, "c1");
return true;
}
bool asset_operation_and_hardfork_checks::c1(
currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> wallet{
init_playtime_test_wallet_t<tools::wallet2>(events, c, ALICE_ACC_IDX)
};
wallet->refresh();
crypto::point_t asset_id_point{};
crypto::public_key asset_id_public_key{};
currency::get_or_calculate_asset_id(m_ado_hello,
&asset_id_point,
&asset_id_public_key);
CHECK_AND_ASSERT_MES(
check_balance_via_wallet(*wallet,
/* name = */ "Alice",
/* expected_total = */ 1'000'000'000'000'000'000,
/* expected_mined = */ 2 * COIN,
/* expected_unlocked = */ 1'000'000'000'000'000'000,
/* expected_awaiting_in = */ INVALID_BALANCE_VAL,
/* expected_awaiting_out = */ INVALID_BALANCE_VAL,
asset_id_public_key),
false,
"balance check failed");
return true;
}
bool asset_operation_and_hardfork_checks::c2(
currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events)
{
crypto::point_t asset_id_pt{};
crypto::public_key asset_id{};
currency::asset_descriptor_base stub{};
CHECK_AND_ASSERT_MES(
get_or_calculate_asset_id(m_ado_bye,
&asset_id_pt,
&asset_id),
false,
"fail to calculate asset id");
CHECK_AND_ASSERT_MES(
!c.get_blockchain_storage().get_asset_info(asset_id,
stub),
false,
"unregistered asset has info");
return true;
}
asset_operation_in_consolidated_tx::asset_operation_in_consolidated_tx()
{
m_adb_alice_currency.total_max_supply = 1'000'000'000'000'000'000;
m_adb_alice_currency.current_supply = 1'000'000'000'000'000'000;
m_adb_alice_currency.ticker = "ALC";
m_adb_alice_currency.full_name = "ALICE";
m_adb_alice_currency.meta_info = "Currency created by Alice";
m_adb_alice_currency.hidden_supply = false;
m_ado_alice_currency.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER;
m_ado_alice_currency.opt_asset_id = currency::null_pkey;
REGISTER_CALLBACK_METHOD(asset_operation_in_consolidated_tx, assert_balances);
REGISTER_CALLBACK_METHOD(asset_operation_in_consolidated_tx, assert_alice_currency_not_registered);
}
bool asset_operation_in_consolidated_tx::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure that the core rule prohibiting operations with assets in TX_FLAG_SIGNATURE_MODE_SEPARATE transactions works.
bool success {};
transaction tx_2 {};
uint64_t tx_version {};
crypto::secret_key one_time {};
tx_generation_context context_tx_2 {};
GENERATE_ACCOUNT(miner);
GENERATE_ACCOUNT(alice);
GENERATE_ACCOUNT(bob);
m_accounts.push_back(miner);
m_accounts.push_back(alice);
m_accounts.push_back(bob);
m_adb_alice_currency.owner = m_accounts.at(ALICE_ACC_IDX).get_public_address().spend_public_key;
m_ado_alice_currency.descriptor = m_adb_alice_currency;
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);
MAKE_TX(events, tx_0, miner, bob, MK_TEST_COINS(10), blk_0r);
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);
MAKE_TX(events, tx_1, miner, alice, MK_TEST_COINS(10), blk_1r);
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner, tx_1);
REWIND_BLOCKS_N(events, blk_2r, blk_2, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// Miner sent 10 coins to Alice, 10 coins to Bob.
DO_CALLBACK(events, "assert_balances");
{
std::vector<tx_source_entry> sources {};
std::vector<tx_destination_entry> destinations {};
success = fill_tx_sources(sources, events, blk_2r, alice.get_keys(), MK_TEST_COINS(5), 1);
CHECK_AND_ASSERT_MES(success, false, "failed to fill transaction sources on step 1");
destinations.emplace_back(MK_TEST_COINS(5), bob.get_public_address());
destinations.emplace_back(MK_TEST_COINS(/* 10 - 5 - 1 = */ 4), alice.get_public_address());
tx_version = get_tx_version(get_block_height(blk_2r), m_hardforks);
success = construct_tx(alice.get_keys(), sources, destinations, empty_extra, empty_attachment, tx_2, tx_version, one_time, 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE, TESTS_DEFAULT_FEE,
context_tx_2);
CHECK_AND_ASSERT_MES(success, false, "failed to construct transaction tx_2 on step 1");
}
// Transaction tx_2 hasn't been constructed completely yet. Core rejects tx_2.
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx_2);
{
std::vector<tx_source_entry> sources {};
std::vector<tx_destination_entry> destinations {};
success = fill_tx_sources(sources, events, blk_2r, bob.get_keys(), MK_TEST_COINS(5), 0);
CHECK_AND_ASSERT_MES(success, false, "failed to fill transaction sources on step 2");
for(tx_source_entry& source : sources)
{
source.separately_signed_tx_complete = true;
}
destinations.emplace_back(MK_TEST_COINS(5), alice.get_public_address());
destinations.emplace_back(MK_TEST_COINS(/* 10 - 5 - 0 = */ 5), bob.get_public_address());
destinations.emplace_back(m_adb_alice_currency.current_supply, alice.get_public_address(), null_pkey);
tx_version = get_tx_version(get_block_height(blk_2r), m_hardforks);
success = construct_tx(bob.get_keys(), sources, destinations, { m_ado_alice_currency }, empty_attachment, tx_2, tx_version, one_time, 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE,
/* fee = */ 0, context_tx_2);
CHECK_AND_ASSERT_MES(success, false, "failed to construct transaction tx_2 on step 2");
}
DO_CALLBACK(events, "mark_invalid_tx");
ADD_CUSTOM_EVENT(events, tx_2);
// Core rejects transaction tx_2. The balances of Alice, Bob haven't changed: Alice has 10 coins, Bob has 10 coins.
DO_CALLBACK(events, "assert_balances");
// Alice's asset hasn't registered, because transaction tx_2 was rejected.
DO_CALLBACK(events, "assert_alice_currency_not_registered");
return true;
}
bool asset_operation_in_consolidated_tx::assert_balances(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> alice_wallet{init_playtime_test_wallet(events, c, ALICE_ACC_IDX)};
std::shared_ptr<tools::wallet2> bob_wallet{init_playtime_test_wallet(events, c, BOB_ACC_IDX)};
alice_wallet->refresh();
bob_wallet->refresh();
CHECK_AND_ASSERT_EQ(alice_wallet->balance(currency::native_coin_asset_id), MK_TEST_COINS(10));
CHECK_AND_ASSERT_EQ(bob_wallet->balance(currency::native_coin_asset_id), MK_TEST_COINS(10));
return true;
}
bool asset_operation_in_consolidated_tx::assert_alice_currency_not_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
crypto::point_t asset_id_point{};
crypto::public_key asset_id{};
currency::asset_descriptor_base stub{};
CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(m_ado_alice_currency, &asset_id_point, &asset_id), false, "fail to calculate asset id");
CHECK_AND_ASSERT_MES(!c.get_blockchain_storage().get_asset_info(asset_id, stub), false, "unregistered asset has info");
return true;
}
//------------------------------------------------------------------------------
eth_signed_asset_basics::eth_signed_asset_basics()
{
REGISTER_CALLBACK_METHOD(eth_signed_asset_basics, c1);
@ -938,7 +1377,7 @@ std::shared_ptr<eth_sign_cb_helper<T>> construct_eth_sign_cb_helper(T&& cb)
bool eth_signed_asset_basics::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
crypto::eth_secret_key eth_sk{};
crypto::eth_public_key eth_pk{};
r = crypto::generate_eth_key_pair(eth_sk, eth_pk);
@ -1020,7 +1459,7 @@ bool eth_signed_asset_basics::c1(currency::core& c, size_t ev_index, const std::
auto signer = construct_eth_sign_cb_helper([&](const crypto::hash& h, const crypto::eth_public_key& asset_owner, crypto::eth_signature& sig)->bool {
CHECK_AND_ASSERT_EQ(asset_owner, eth_pk);
return crypto::generate_eth_signature(h, eth_sk, sig);
});
});
// TODO reconsider this ===>> ctp.p_eth_signer = signer.get();

View file

@ -48,6 +48,39 @@ struct asset_emission_and_unconfirmed_balance : public wallet_test
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct asset_operation_and_hardfork_checks : public wallet_test
{
public:
asset_operation_and_hardfork_checks();
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);
bool c2(currency::core& c,
size_t ev_index,
const std::vector<test_event_entry>& events);
private:
mutable currency::asset_descriptor_base m_adb_hello{};
mutable currency::asset_descriptor_operation m_ado_hello{};
mutable currency::asset_descriptor_base m_adb_bye{};
mutable currency::asset_descriptor_operation m_ado_bye{};
};
struct asset_operation_in_consolidated_tx : public wallet_test
{
public:
asset_operation_in_consolidated_tx();
bool generate(std::vector<test_event_entry>& events) const;
bool assert_balances(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool assert_alice_currency_not_registered(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
private:
mutable currency::asset_descriptor_base m_adb_alice_currency{};
mutable currency::asset_descriptor_operation m_ado_alice_currency{};
};
struct eth_signed_asset_basics : public wallet_test
{

View file

@ -169,6 +169,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
uint64_t block_reward_without_fee = 0;
m_block_reward = 0;
size_t estimated_block_size = m_txs_total_size;
m_block.miner_tx = transaction{};
bool r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee,
reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, m_block_reward, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use);
CHECK_AND_ASSERT_THROW_MES(r, "construct_miner_tx failed");
@ -177,6 +178,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
size_t cumulative_size = 0;
for (size_t try_count = 0; try_count != 10; ++try_count)
{
m_block.miner_tx = transaction{};
r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee,
reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, m_block_reward, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use);
CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed");

File diff suppressed because it is too large Load diff

View file

@ -757,30 +757,30 @@ struct A
struct A_v1 : public A
{
std::vector<std::string> vector_two;
uint8_t current_version = 1;
BEGIN_SERIALIZE()
FIELD(one)
FIELD(two)
FIELD(vector_one)
VERSION(1)
if (s_version < 1) return true;
VERSION_TO_MEMBER(1, current_version)
FIELD(vector_two)
END_SERIALIZE()
};
struct A_v2 : public A_v1
{
std::vector<std::string> vector_3;
std::vector<std::string> vector_4;
uint8_t current_version = 2;
BEGIN_SERIALIZE()
//CURRENT_VERSION(2)
FIELD(one)
FIELD(two)
FIELD(vector_one)
VERSION(2)
VERSION_TO_MEMBER(2, current_version)
if (s_version < 1) return true;
FIELD(vector_two)
if (s_version < 2) return true;
@ -792,13 +792,14 @@ struct A_v2 : public A_v1
struct A_v3 : public A_v2
{
std::vector<std::string> vector_5;
uint8_t current_version = 3;
BEGIN_SERIALIZE()
//CURRENT_VERSION(3)
FIELD(one)
FIELD(two)
FIELD(vector_one)
VERSION(3)
VERSION_TO_MEMBER(3, current_version)
if (s_version < 1) return true;
FIELD(vector_two)
if (s_version < 2) return true;