1
0
Fork 0
forked from lthn/blockchain

Merge branch 'cryptoassets' of github.com:hyle-team/zano into cryptoassets

This commit is contained in:
cryptozoidberg 2023-06-20 21:59:32 +02:00
commit 7b228f07b4
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
22 changed files with 390 additions and 138 deletions

View file

@ -1323,7 +1323,7 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms
{
CHECK_AND_ASSERT_MES(b.miner_tx.attachment.empty(), false, "coinbase transaction has attachments; attachments are not allowed for coinbase transactions.");
CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 3, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 2");
CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 3, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 3");
CHECK_AND_ASSERT_MES(b.miner_tx.proofs[0].type() == typeid(zc_asset_surjection_proof), false, "coinbase transaction has incorrect type of proof #0 (expected: zc_asset_surjection_proof)");
CHECK_AND_ASSERT_MES(b.miner_tx.proofs[1].type() == typeid(zc_outs_range_proof), false, "coinbase transaction has incorrect type of proof #1 (expected: zc_outs_range_proof)");
CHECK_AND_ASSERT_MES(b.miner_tx.proofs[2].type() == typeid(zc_balance_proof), false, "coinbase transaction has incorrect type of proof #2 (expected: zc_balance_proof)");
@ -1518,6 +1518,8 @@ bool blockchain_storage::create_block_template(const create_block_template_param
if (!block_filled)
return false;
resp.txs_fee = fee;
/*
instead of complicated two-phase template construction and adjustment of cumulative size with block reward we
use CURRENCY_COINBASE_BLOB_RESERVED_SIZE as penalty-free coinbase transaction reservation.
@ -1528,6 +1530,7 @@ bool blockchain_storage::create_block_template(const create_block_template_param
miner_address,
stakeholder_address,
b.miner_tx,
resp.block_reward_without_fee,
get_tx_version(height, m_core_runtime_config.hard_forks),
ex_nonce,
CURRENCY_MINER_TX_MAX_OUTS,

View file

@ -147,6 +147,8 @@ namespace currency
wide_difficulty_type diffic;
uint64_t height;
tx_generation_context miner_tx_tgc; // bad design, a lot of copying, consider redesign -- sowle
uint64_t block_reward_without_fee;
uint64_t txs_fee; // sum of transactions' fee if any
};
typedef std::unordered_map<crypto::hash, transaction> transactions_map;

View file

@ -357,24 +357,24 @@ namespace currency
const account_public_address &miner_address,
const account_public_address &stakeholder_address,
transaction& tx,
uint64_t& block_reward_without_fee,
uint64_t tx_version,
const blobdata& extra_nonce /* = blobdata() */,
size_t max_outs /* = CURRENCY_MINER_TX_MAX_OUTS */,
bool pos /* = false */,
const pos_entry& pe /* = pos_entry() */, // only pe.stake_unlock_time and pe.stake_amount are used now, TODO: consider refactoring -- sowle
tx_generation_context* ogc_ptr /* = nullptr */,
tx_generation_context* ogc_ptr /* = nullptr */,
const keypair* tx_one_time_key_to_use /* = nullptr */
)
{
bool r = false;
uint64_t block_reward = 0;
if (!get_block_reward(pos, median_size, current_block_size, already_generated_coins, block_reward, height))
if (!get_block_reward(pos, median_size, current_block_size, already_generated_coins, block_reward_without_fee, height))
{
LOG_ERROR("Block is too big");
return false;
}
block_reward += fee;
uint64_t block_reward = block_reward_without_fee + fee;
//
// prepare destinations

View file

@ -190,6 +190,7 @@ namespace currency
std::string htlc_origin;
std::vector<serializable_pair<uint64_t, crypto::key_image>> outs_key_images; // pairs (out_index, key_image) for each change output
crypto::key_derivation derivation;
bool was_not_prepared = false; // true if tx was not prepared/created for some good reason (e.g. not enough outs for UTXO defragmentation tx). Because we decided not to throw exceptions for non-error cases. -- sowle
BEGIN_SERIALIZE_OBJECT()
FIELD(tx)
@ -198,6 +199,7 @@ namespace currency
FIELD(htlc_origin)
FIELD(outs_key_images)
FIELD(derivation)
FIELD(was_not_prepared)
END_SERIALIZE()
};
@ -255,7 +257,8 @@ namespace currency
uint64_t fee,
const account_public_address &miner_address,
const account_public_address &stakeholder_address,
transaction& tx,
transaction& tx,
uint64_t& block_reward_without_fee,
uint64_t tx_version,
const blobdata& extra_nonce = blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,

@ -1 +1 @@
Subproject commit 2750d12c11f6063e75f6370a4382db7f0784d624
Subproject commit 1471e71f4ff685dd080e6551773cf129f1d02c43

View file

@ -912,6 +912,8 @@ namespace currency
res.prev_hash = string_tools::pod_to_hex(resp.b.prev_id);
res.miner_tx_tgc = resp.miner_tx_tgc;
res.height = resp.height;
res.block_reward_without_fee = resp.block_reward_without_fee;
res.txs_fee = resp.txs_fee;
//calculate epoch seed
res.seed = currency::ethash_epoch_to_seed(currency::ethash_height_to_epoch(res.height));

View file

@ -861,6 +861,8 @@ namespace currency
blobdata blocktemplate_blob;
std::string prev_hash;
tx_generation_context miner_tx_tgc;
uint64_t block_reward_without_fee;
uint64_t txs_fee;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
@ -870,6 +872,8 @@ namespace currency
KV_SERIALIZE(blocktemplate_blob)
KV_SERIALIZE(prev_hash)
KV_SERIALIZE(miner_tx_tgc)
KV_SERIALIZE(block_reward_without_fee)
KV_SERIALIZE(txs_fee)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};

View file

@ -1808,11 +1808,13 @@ bool simple_wallet::deploy_new_asset(const std::vector<std::string> &args)
if (!args.size() || args.size() > 1)
{
fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1";
return true;
}
bool r = epee::serialization::load_t_from_json_file(adb, args[0]);
if (!r)
{
fail_msg_writer() << "Failed to load json file with asset specification: " << args[0];
return true;
}
tx_destination_entry td = AUTO_VAL_INIT(td);
td.addr.push_back(m_wallet->get_account().get_public_address());
@ -1842,6 +1844,7 @@ bool simple_wallet::add_custom_asset_id(const std::vector<std::string> &args)
if (!args.size() || args.size() > 1)
{
fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1";
return true;
}
crypto::public_key asset_id = currency::null_pkey;
if (!epee::string_tools::parse_tpod_from_hex_string(args[0], asset_id))
@ -1877,6 +1880,7 @@ bool simple_wallet::generate_ionic_swap_proposal(const std::vector<std::string>
if (args.size() != 2)
{
fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1";
return true;
}
view::ionic_swap_proposal_info proposal_info = AUTO_VAL_INIT(proposal_info);
@ -1884,6 +1888,7 @@ bool simple_wallet::generate_ionic_swap_proposal(const std::vector<std::string>
if (!r)
{
fail_msg_writer() << "Failed to load json file with asset specification: " << args[0];
return true;
}
currency::account_public_address destination_addr = AUTO_VAL_INIT(destination_addr);
currency::payment_id_t integrated_payment_id;
@ -1921,6 +1926,7 @@ bool simple_wallet::get_ionic_swap_proposal_info(const std::vector<std::string>
if (args.size() != 1)
{
fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1";
return true;
}
std::string raw_proposal;
@ -1953,6 +1959,7 @@ bool simple_wallet::accept_ionic_swap_proposal(const std::vector<std::string> &a
if (args.size() != 1)
{
fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1";
return true;
}
std::string raw_proposal;
@ -1983,6 +1990,7 @@ bool simple_wallet::remove_custom_asset_id(const std::vector<std::string> &args)
if (!args.size() || args.size() > 1)
{
fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1";
return true;
}
crypto::public_key asset_id = currency::null_pkey;
if (!epee::string_tools::parse_tpod_from_hex_string(args[0], asset_id))

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 206
#define PROJECT_VERSION_BUILD_NO 208
#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

@ -51,6 +51,10 @@ using namespace currency;
#define MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES (100*1024*1024) // 100 MB
#define WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX 10 // TODO @#@# change to default decoy set number
#define WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 3 // TODO: @#@# consider descreasing to mimic normal tx
#define WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 10 // TODO: @#@# consider descreasing to mimic normal tx
#undef LOG_DEFAULT_CHANNEL
#define LOG_DEFAULT_CHANNEL "wallet"
ENABLE_CHANNEL_BY_DEFAULT("wallet")
@ -69,7 +73,9 @@ namespace tools
, m_watch_only(false)
, m_last_pow_block_h(0)
, m_minimum_height(WALLET_MINIMUM_HEIGHT_UNSET_CONST)
, m_pos_mint_packing_size(WALLET_DEFAULT_POS_MINT_PACKING_SIZE)
, m_min_utxo_count_for_defragmentation_tx(WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX)
, m_max_utxo_count_for_defragmentation_tx(WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX)
, m_decoys_count_for_defragmentation_tx(WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX)
, m_current_wallet_file_size(0)
, m_use_deffered_global_outputs(false)
#ifdef DISABLE_TOR
@ -203,9 +209,15 @@ bool wallet2::set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy)
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::set_pos_mint_packing_size(uint64_t new_size)
void wallet2::set_pos_utxo_count_limits_for_defragmentation_tx(uint64_t min_outs, uint64_t max_outs)
{
m_pos_mint_packing_size = new_size;
m_min_utxo_count_for_defragmentation_tx = min_outs;
m_max_utxo_count_for_defragmentation_tx = max_outs;
}
//----------------------------------------------------------------------------------------------------
void wallet2::set_pos_decoys_count_for_defragmentation_tx(size_t decoys_count)
{
m_decoys_count_for_defragmentation_tx = decoys_count;
}
//----------------------------------------------------------------------------------------------------
std::shared_ptr<i_core_proxy> wallet2::get_core_proxy()
@ -3363,33 +3375,18 @@ void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) con
incoming_transfers = m_transfers;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number)
bool wallet2::generate_utxo_defragmentation_transaction_if_needed(currency::transaction& tx)
{
prepare_free_transfers_cache(0);
auto it = m_found_free_amounts[currency::native_coin_asset_id].find(CURRENCY_BLOCK_REWARD);
if (it == m_found_free_amounts[currency::native_coin_asset_id].end() || it->second.size() <= m_pos_mint_packing_size)
return false;
//let's check if we have at least WALLET_POS_MINT_PACKING_SIZE transactions which is ready to go
size_t count = 0;
for (auto it_ind = it->second.begin(); it_ind != it->second.end() && count <= m_pos_mint_packing_size; it_ind++)
{
if (is_transfer_ready_to_go(m_transfers[*it_ind], fake_outputs_number))
++count;
}
if (count <= m_pos_mint_packing_size)
return false;
construct_tx_param ctp = get_default_construct_tx_param();
currency::tx_destination_entry de = AUTO_VAL_INIT(de);
de.addr.push_back(m_account.get_public_address());
de.amount = m_pos_mint_packing_size*CURRENCY_BLOCK_REWARD;
ctp.dsts.push_back(de);
ctp.perform_packing = true;
TRY_ENTRY();
transfer(ctp, tx, false, nullptr);
CATCH_ENTRY2(false);
ctp.create_utxo_defragmentation_tx = true;
finalized_tx ftp{};
transfer(ctp, ftp, false, nullptr);
if (ftp.was_not_prepared)
return false; // no such UTXO were found, not an error
tx = ftp.tx;
return true;
}
//----------------------------------------------------------------------------------------------------
@ -3682,7 +3679,7 @@ bool enum_container(iterator_t it_begin, iterator_t it_end, callback_t cb)
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_consolidating_transaction(const wallet_public::wallet_transfer_info& wti)
bool wallet2::is_defragmentation_transaction(const wallet_public::wallet_transfer_info& wti)
{
if (!wti.is_income)
{
@ -3705,7 +3702,7 @@ void wallet2::get_recent_transfers_history(std::vector<wallet_public::wallet_tra
if (exclude_mining_txs)
{
if (currency::is_coinbase(wti.tx) || is_consolidating_transaction(wti))
if (currency::is_coinbase(wti.tx) || is_defragmentation_transaction(wti))
return true;
}
trs.push_back(wti);
@ -3912,7 +3909,7 @@ bool wallet2::is_in_hardfork_zone(uint64_t hardfork_index) const
return m_core_runtime_config.is_hardfork_active_for_height(hardfork_index, get_blockchain_current_size());
}
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const pos_entry& pe, currency::tx_generation_context& miner_tx_tgc) const
bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, uint64_t full_block_reward, const currency::pos_entry& pe, currency::tx_generation_context& miner_tx_tgc, currency::block& b) const
{
bool r = false;
WLT_CHECK_AND_ASSERT_MES(pe.wallet_index < m_transfers.size(), false, "invalid pe.wallet_index: " << pe.wallet_index);
@ -4096,9 +4093,8 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
b.miner_tx.proofs.emplace_back(std::move(range_proofs));
// balance proof
uint64_t block_reward = COIN; // TODO @#@# move it somewhere -- sowle
currency::zc_balance_proof balance_proof{};
r = generate_tx_balance_proof(b.miner_tx, miner_tx_id, miner_tx_tgc, block_reward, balance_proof);
r = generate_tx_balance_proof(b.miner_tx, miner_tx_id, miner_tx_tgc, full_block_reward, balance_proof);
WLT_CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
b.miner_tx.proofs.emplace_back(std::move(balance_proof));
@ -4228,12 +4224,21 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco
tmpl_req.pe.tx_out_index = td.m_internal_output_index;
tmpl_req.pe.wallet_index = cxt.index;
//generate packing tx
transaction pack_tx = AUTO_VAL_INIT(pack_tx);
if (generate_packing_transaction_if_needed(pack_tx, 0))
// mark stake source as spent and make sure it will be restored in case of error
const std::vector<uint64_t> stake_transfer_idx_vec{ cxt.index };
mark_transfers_as_spent(stake_transfer_idx_vec, "stake source");
bool gracefull_leaving = false;
auto stake_transfer_spent_flag_restorer = epee::misc_utils::create_scope_leave_handler([&](){
if (!gracefull_leaving)
clear_transfers_from_flag(stake_transfer_idx_vec, WALLET_TRANSFER_DETAIL_FLAG_SPENT, "stake source");
});
// generate UTXO Defragmentation Transaction - to reduce the UTXO set size
transaction udtx{};
if (generate_utxo_defragmentation_transaction_if_needed(udtx))
{
tx_to_blob(pack_tx, tmpl_req.explicit_transaction);
WLT_LOG_GREEN("Packing inputs: " << pack_tx.vin.size() << " inputs consolidated in tx " << get_transaction_hash(pack_tx), LOG_LEVEL_0);
tx_to_blob(udtx, tmpl_req.explicit_transaction);
WLT_LOG_GREEN("Note: " << udtx.vin.size() << " inputs were aggregated into UTXO defragmentation tx " << get_transaction_hash(udtx), LOG_LEVEL_0);
}
m_core_proxy->call_COMMAND_RPC_GETBLOCKTEMPLATE(tmpl_req, tmpl_rsp);
WLT_CHECK_AND_ASSERT_MES(tmpl_rsp.status == API_RETURN_CODE_OK, false, "Failed to create block template after kernel hash found!");
@ -4257,11 +4262,12 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco
set_block_datetime(current_timestamp, b);
WLT_LOG_MAGENTA("Applying actual timestamp: " << current_timestamp, LOG_LEVEL_0);
res = prepare_and_sign_pos_block(cxt, b, tmpl_req.pe, tmpl_rsp.miner_tx_tgc);
uint64_t full_block_reward = tmpl_rsp.block_reward_without_fee + tmpl_rsp.txs_fee;
res = prepare_and_sign_pos_block(cxt, full_block_reward, tmpl_req.pe, tmpl_rsp.miner_tx_tgc, b);
WLT_CHECK_AND_ASSERT_MES(res, false, "Failed to prepare_and_sign_pos_block");
crypto::hash block_hash = get_block_hash(b);
WLT_LOG_GREEN("Block " << print16(block_hash) << " has been constructed, sending to core...", LOG_LEVEL_0);
WLT_LOG_GREEN("Block " << print16(block_hash) << " @ " << get_block_height(b) << " has been constructed, sending to core...", LOG_LEVEL_0);
currency::COMMAND_RPC_SUBMITBLOCK2::request subm_req = AUTO_VAL_INIT(subm_req);
currency::COMMAND_RPC_SUBMITBLOCK2::response subm_rsp = AUTO_VAL_INIT(subm_rsp);
@ -4278,6 +4284,7 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco
WLT_LOG_GREEN("PoS block " << print16(block_hash) << " generated and accepted, congrats!", LOG_LEVEL_0);
m_wcallback->on_pos_block_found(b);
gracefull_leaving = true; // to prevent source transfer flags be cleared in scope leave handler
return true;
}
//----------------------------------------------------------------------------------------------------
@ -4492,6 +4499,7 @@ void wallet2::deploy_new_asset(const currency::asset_descriptor_base& asset_info
construct_tx_param ctp = get_default_construct_tx_param();
ctp.dsts = destinations;
ctp.extra.push_back(asset_reg_info);
ctp.need_at_least_1_zc = true;
finalized_tx ft = AUTO_VAL_INIT(ft);
this->transfer(ctp, ft, true, nullptr);
@ -5376,33 +5384,40 @@ bool wallet2::decrypt_buffer(const std::string& buff, std::string& res_buff)
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money)
bool wallet2::prepare_tx_sources_for_defragmentation_tx(std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money)
{
prepare_free_transfers_cache(fake_outputs_count);
//prepare_free_transfers_cache(fake_outputs_count);
//free_amounts_cache_type& free_amounts_for_native_coin = m_found_free_amounts[currency::native_coin_asset_id];
free_amounts_cache_type& free_amounts_for_native_coin = m_found_free_amounts[currency::native_coin_asset_id];
auto it = free_amounts_for_native_coin.find(CURRENCY_BLOCK_REWARD);
if (it == free_amounts_for_native_coin.end() || it->second.size() < m_pos_mint_packing_size)
return false;
for (auto set_it = it->second.begin(); set_it != it->second.end() && selected_indicies.size() <= m_pos_mint_packing_size; )
std::stringstream ss;
if (epee::log_space::log_singletone::get_log_detalisation_level() >= LOG_LEVEL_2)
ss << "preparing sources for utxo defragmentation tx:";
for (size_t i = 0, size = m_transfers.size(); i < size && selected_indicies.size() < m_max_utxo_count_for_defragmentation_tx; ++i)
{
if (is_transfer_ready_to_go(m_transfers[*set_it], fake_outputs_count))
{
found_money += it->first;
selected_indicies.push_back(*set_it);
WLT_LOG_L2("Selected index: " << *set_it << ", transfer_details: " << ENDL << epee::serialization::store_t_to_json(m_transfers[*set_it]));
it->second.erase(set_it++);
}
else
set_it++;
}
if (!it->second.size())
free_amounts_for_native_coin.erase(it);
const auto& td = m_transfers[i];
if (!td.is_native_coin() || td.m_amount > CURRENCY_BLOCK_REWARD)
continue;
return prepare_tx_sources(fake_outputs_count, sources, selected_indicies);
if (is_transfer_ready_to_go(td, m_decoys_count_for_defragmentation_tx))
{
found_money += td.m_amount;
selected_indicies.push_back(i);
if (epee::log_space::log_singletone::get_log_detalisation_level() >= LOG_LEVEL_2)
ss << " selected transfer #" << i << ", amount: " << print_money_brief(td.m_amount) << ", height: " << td.m_ptx_wallet_info->m_block_height << ", " << (td.is_zc() ? "ZC" : " ");
}
}
if (selected_indicies.size() < m_min_utxo_count_for_defragmentation_tx)
{
// too few outputs were found, hence don't create a defragmentation tx
selected_indicies.clear();
found_money = 0;
return false;
}
WLT_LOG(ss.str(), LOG_LEVEL_2);
return prepare_tx_sources(m_decoys_count_for_defragmentation_tx, sources, selected_indicies);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_tx_sources(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t dust_threshold, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies)
@ -6416,7 +6431,7 @@ void wallet2::prepare_tx_destinations(uint64_t needed_money,
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const mode_separate_context& msc)
bool wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const mode_separate_context& msc)
{
SET_CONTEXT_OBJ_FOR_SCOPE(pconstruct_tx_param, ctp);
@ -6448,9 +6463,14 @@ void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx
//uint64_t found_money = 0;
TIME_MEASURE_START_MS(prepare_tx_sources_time);
if (ctp.perform_packing)
if (ctp.create_utxo_defragmentation_tx)
{
prepare_tx_sources_for_packing(WALLET_DEFAULT_POS_MINT_PACKING_SIZE, 0, ftp.sources, ftp.selected_transfers, needed_money_map[currency::native_coin_asset_id].found_amount);
try
{
if (!prepare_tx_sources_for_defragmentation_tx(ftp.sources, ftp.selected_transfers, needed_money_map[currency::native_coin_asset_id].found_amount))
return false;
}
catch(const error::not_enough_outs_to_mix&) { return false; } // if there's not enough decoys, return false to indicate minor non-fatal error
}
else if (ctp.htlc_tx_id != currency::null_hash)
{
@ -6506,6 +6526,7 @@ void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx
<< ", construct_tx_time: " << construct_tx_time << " ms"
<< ", sign_ms_input_time: " << sign_ms_input_time << " ms",
LOG_LEVEL_0);*/
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::finalize_transaction(const currency::finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key /* = true */)
@ -6706,6 +6727,10 @@ void wallet2::transfer(construct_tx_param& ctp,
check_and_throw_if_self_directed_tx_with_payment_id_requested(ctp);
bool asset_operation_requested = count_type_in_variant_container<asset_descriptor_operation>(ctp.extra) != 0;
bool dont_have_zero_asset_ids_in_destinations = std::count_if(ctp.dsts.begin(), ctp.dsts.end(), [](const tx_destination_entry& de) { return de.asset_id == null_pkey; }) == 0;
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(asset_operation_requested || dont_have_zero_asset_ids_in_destinations, "zero asset id is used errounesly (no asset operation was requested)");
if (ctp.crypt_address.spend_public_key == currency::null_pkey)
{
ctp.crypt_address = currency::get_crypt_address_from_destinations(m_account.get_keys(), ctp.dsts);
@ -6714,7 +6739,11 @@ void wallet2::transfer(construct_tx_param& ctp,
TIME_MEASURE_START(prepare_transaction_time);
currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
ftp.tx_version = this->get_current_tx_version();
prepare_transaction(ctp, ftp);
if (!prepare_transaction(ctp, ftp))
{
result.was_not_prepared = true;
return;
}
TIME_MEASURE_FINISH(prepare_transaction_time);
if (m_watch_only)

View file

@ -50,8 +50,6 @@
#define WALLET_DEFAULT_TX_SPENDABLE_AGE 10
#define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1
#define WALLET_DEFAULT_POS_MINT_PACKING_SIZE 100
#define WALLET_TRANSFER_DETAIL_FLAG_SPENT uint32_t(1 << 0)
#define WALLET_TRANSFER_DETAIL_FLAG_BLOCKED uint32_t(1 << 1)
#define WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION uint32_t(1 << 2)
@ -299,7 +297,7 @@ namespace tools
currency::account_public_address crypt_address;
uint8_t tx_outs_attr = 0;
bool shuffle = false;
bool perform_packing = false;
bool create_utxo_defragmentation_tx = false;
bool need_at_least_1_zc = false;
};
@ -574,7 +572,7 @@ namespace tools
const currency::account_base& get_account() const { return m_account; }
void get_recent_transfers_history(std::vector<wallet_public::wallet_transfer_info>& trs, size_t offset, size_t count, uint64_t& total, uint64_t& last_item_index, bool exclude_mining_txs = false, bool start_from_end = true);
bool is_consolidating_transaction(const wallet_public::wallet_transfer_info& wti);
bool is_defragmentation_transaction(const wallet_public::wallet_transfer_info& wti);
uint64_t get_recent_transfers_total_count();
uint64_t get_transfer_entries_count();
void get_unconfirmed_transfers(std::vector<wallet_public::wallet_transfer_info>& trs, bool exclude_mining_txs = false);
@ -609,7 +607,8 @@ namespace tools
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);
bool set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy);
void set_pos_mint_packing_size(uint64_t new_size);
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
void set_pos_decoys_count_for_defragmentation_tx(size_t decoys_count);
void set_minimum_height(uint64_t h);
std::shared_ptr<i_core_proxy> get_core_proxy();
uint64_t balance() const;
@ -885,7 +884,7 @@ namespace tools
//next functions in public area only becausce of test_generator
//TODO: Need refactoring - remove it back to private zone
void set_genesis(const crypto::hash& genesis_hash);
bool prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const currency::pos_entry& pe, currency::tx_generation_context& miner_tx_tgc) const;
bool prepare_and_sign_pos_block(const mining_context& cxt, uint64_t full_block_reward, const currency::pos_entry& pe, currency::tx_generation_context& miner_tx_tgc, currency::block& b) const;
void process_new_blockchain_entry(const currency::block& b,
const currency::block_direct_data_entry& bche,
const crypto::hash& bl_id,
@ -933,7 +932,7 @@ namespace tools
const std::list<expiration_entry_info>& get_expiration_entries() const { return m_money_expirations; };
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
void prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const mode_separate_context& emode_separate = mode_separate_context());
bool prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const mode_separate_context& emode_separate = mode_separate_context());
void finalize_transaction(const currency::finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key = true);
void finalize_transaction(const currency::finalize_tx_param& ftp, currency::finalized_tx& result, bool broadcast_tx, bool store_tx_secret_key = true );
@ -1042,7 +1041,7 @@ private:
bool prepare_tx_sources(size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, const std::vector<uint64_t>& selected_indicies);
bool prepare_tx_sources(crypto::hash multisig_id, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
bool prepare_tx_sources_htlc(crypto::hash htlc_tx_id, const std::string& origin, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
bool prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
bool prepare_tx_sources_for_defragmentation_tx(std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
void prefetch_global_indicies_if_needed(const std::vector<uint64_t>& selected_indicies);
assets_selection_context get_needed_money(uint64_t fee, const std::vector<currency::tx_destination_entry>& dsts);
void prepare_tx_destinations(const assets_selection_context& needed_money_map,
@ -1110,7 +1109,7 @@ private:
void exception_handler();
void exception_handler() const;
uint64_t get_minimum_allowed_fee_for_contract(const crypto::hash& ms_id);
bool generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number);
bool generate_utxo_defragmentation_transaction_if_needed(currency::transaction& tx);
bool store_unsigned_tx_to_file_and_reserve_transfers(const currency::finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str = nullptr);
void check_and_throw_if_self_directed_tx_with_payment_id_requested(const construct_tx_param& ctp);
void push_new_block_id(const crypto::hash& id, uint64_t height);
@ -1146,7 +1145,9 @@ private:
std::atomic<uint64_t> m_last_bc_timestamp;
bool m_do_rise_transfer;
uint64_t m_pos_mint_packing_size;
uint64_t m_min_utxo_count_for_defragmentation_tx;
uint64_t m_max_utxo_count_for_defragmentation_tx;
size_t m_decoys_count_for_defragmentation_tx;
transfer_container m_transfers;
multisig_transfer_container m_multisig_transfers;

View file

@ -299,6 +299,7 @@ bool test_generator::construct_block(currency::block& blk,
blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx);
size_t target_block_size = txs_size + 0; // zero means no cost for ordinary coinbase
tx_generation_context miner_tx_tgc{};
uint64_t block_reward_without_fee = 0;
while (true)
{
r = construct_miner_tx(height, misc_utils::median(block_sizes),
@ -308,6 +309,7 @@ bool test_generator::construct_block(currency::block& blk,
miner_acc.get_keys().account_address,
miner_acc.get_keys().account_address,
blk.miner_tx,
block_reward_without_fee,
get_tx_version(height, m_hardforks),
blobdata(),
test_generator::get_test_gentime_settings().miner_tx_max_outs,
@ -354,7 +356,7 @@ bool test_generator::construct_block(currency::block& blk,
else
{
//need to build pos block
r = sign_block(wallets[won_walled_index].mining_context, pe, *wallets[won_walled_index].wallet, miner_tx_tgc, blk);
r = sign_block(wallets[won_walled_index].mining_context, pe, block_reward_without_fee + total_fee, *wallets[won_walled_index].wallet, miner_tx_tgc, blk);
CHECK_AND_ASSERT_MES(r, false, "Failed to find_kernel_and_sign()");
}
@ -373,11 +375,12 @@ bool test_generator::construct_block(currency::block& blk,
bool test_generator::sign_block(const tools::wallet2::mining_context& mining_context,
const pos_entry& pe,
uint64_t full_block_reward,
const tools::wallet2& w,
tx_generation_context& miner_tx_tgc,
currency::block& b)
{
bool r = w.prepare_and_sign_pos_block(mining_context, b, pe, miner_tx_tgc);
bool r = w.prepare_and_sign_pos_block(mining_context, full_block_reward, pe, miner_tx_tgc, b);
CHECK_AND_ASSERT_MES(r, false, "prepare_and_sign_pos_block failed");
return true;
}
@ -933,9 +936,10 @@ bool test_generator::construct_block(int64_t manual_timestamp_adjustment,
}
else
{
uint64_t base_block_reward = 0;
size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx);
// TODO: This will work, until size of constructed block is less then CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_public_address(), miner_acc.get_public_address(), blk.miner_tx, get_tx_version(height, m_hardforks), blobdata(), 1))
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_public_address(), miner_acc.get_public_address(), blk.miner_tx, base_block_reward, get_tx_version(height, m_hardforks), blobdata(), 1))
return false;
}

View file

@ -488,6 +488,7 @@ public:
bool sign_block(const tools::wallet2::mining_context& mining_context,
const currency::pos_entry& pe,
uint64_t full_block_reward,
const tools::wallet2& w,
currency::tx_generation_context& miner_tx_tgc,
currency::block& b);
@ -951,7 +952,9 @@ bool test_generator::construct_block_gentime_with_coinbase_cb(const currency::bl
size_t height = get_block_height(prev_block) + 1;
//size_t current_block_size = get_object_blobsize(miner_tx);
r = construct_miner_tx(height, epee::misc_utils::median(block_sizes), already_generated_coins, 0 /* current_block_size !HACK! */, 0, acc.get_public_address(), acc.get_public_address(), miner_tx, get_tx_version(height, m_hardforks), currency::blobdata(), 1);
uint64_t block_reward_without_fee = 0;
r = construct_miner_tx(height, epee::misc_utils::median(block_sizes), already_generated_coins, 0 /* current_block_size !HACK! */, 0, acc.get_public_address(), acc.get_public_address(), miner_tx, block_reward_without_fee, get_tx_version(height, m_hardforks), currency::blobdata(), 1);
CHECK_AND_ASSERT_MES(r, false, "construct_miner_tx failed");
if (!cb(miner_tx))

View file

@ -1076,7 +1076,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(wallet_outputs_with_same_key_image);
GENERATE_AND_PLAY(wallet_unconfirmed_tx_expiration);
GENERATE_AND_PLAY(wallet_unconfimed_tx_balance);
GENERATE_AND_PLAY_HF(packing_outputs_on_pos_minting_wallet, "3");
GENERATE_AND_PLAY_HF(packing_outputs_on_pos_minting_wallet, "3-*");
GENERATE_AND_PLAY(wallet_watch_only_and_chain_switch);
GENERATE_AND_PLAY(wallet_rpc_integrated_address);
@ -1256,6 +1256,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(zarcanum_in_alt_chain);
GENERATE_AND_PLAY(assets_and_explicit_native_coins_in_outs);
GENERATE_AND_PLAY(zarcanum_block_with_txs);
GENERATE_AND_PLAY(asset_depoyment_and_few_zc_utxos);
// GENERATE_AND_PLAY(gen_block_reward);

View file

@ -332,3 +332,129 @@ bool assets_and_explicit_native_coins_in_outs::c2_alice_deploys_asset(currency::
return true;
}
//------------------------------------------------------------------------------
asset_depoyment_and_few_zc_utxos::asset_depoyment_and_few_zc_utxos()
{
REGISTER_CALLBACK_METHOD(asset_depoyment_and_few_zc_utxos, c1);
m_hardforks.clear();
m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
}
bool asset_depoyment_and_few_zc_utxos::generate(std::vector<test_event_entry>& events) const
{
bool r = false;
uint64_t ts = test_core_time::get_time();
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts);
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks
// HF4 requires tests_random_split_strategy (for 2 outputs minimum)
test_gentime_settings tgts = generator.get_test_gentime_settings();
tgts.split_strategy = tests_random_split_strategy;
generator.set_test_gentime_settings(tgts);
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
DO_CALLBACK_PARAMS(events, "check_hardfork_inactive", static_cast<size_t>(ZANO_HARDFORK_04_ZARCANUM));
// tx_0: miner -> Alice
// make tx_0 before HF4, so Alice will have only bare outs
transaction tx_0{};
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
for(size_t i = 0; i < 100; ++i)
destinations.emplace_back(TESTS_DEFAULT_FEE, m_accounts[ALICE_ACC_IDX].get_public_address());
m_alice_initial_balance = TESTS_DEFAULT_FEE * 100;
r = fill_tx_sources(sources, events, blk_0r, miner_acc.get_keys(), m_alice_initial_balance + TESTS_DEFAULT_FEE, 0);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_attachment, tx_0, get_tx_version_from_events(events), 0);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
ADD_CUSTOM_EVENT(events, tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
// make sure HF4 has been activated
DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast<size_t>(ZANO_HARDFORK_04_ZARCANUM));
// tx_1: miner -> Alice
// send less than min fee. This output will be the only ZC UTXO in Alice's wallet
//destinations.clear();
MAKE_TX(events, tx_1, miner_acc, alice_acc, TESTS_DEFAULT_FEE * 0.5, blk_1);
m_alice_initial_balance += TESTS_DEFAULT_FEE * 0.5;
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1, miner_acc, tx_1);
// rewind blocks
REWIND_BLOCKS_N_WITH_TIME(events, blk_2r, blk_2, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// check Alice's balance and make sure she can deploy an asset
DO_CALLBACK(events, "c1");
return true;
}
bool asset_depoyment_and_few_zc_utxos::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]);
alice_wlt->refresh();
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", m_alice_initial_balance, 0, m_alice_initial_balance, 0, 0), false, "");
// make sure Alice has correct UTXO wallet structure
tools::wallet2::transfer_container transfers{};
alice_wlt->get_transfers(transfers);
size_t zc_unspent_outs = 0, unspent_outs = 0;
for(auto& td : transfers)
{
if (!td.is_spent())
{
++unspent_outs;
if (td.is_zc())
++zc_unspent_outs;
}
}
CHECK_AND_ASSERT_MES(unspent_outs == 101 && zc_unspent_outs == 1, false, "incorrect UTXO structure: " << unspent_outs << ", " << zc_unspent_outs);
asset_descriptor_base adb{};
adb.total_max_supply = 100 * 1000000000000;
adb.full_name = "very confidential asset";
adb.ticker = "VCA";
adb.decimal_point = 12;
std::vector<tx_destination_entry> destinations;
destinations.emplace_back(adb.total_max_supply, m_accounts[MINER_ACC_IDX].get_public_address(), null_pkey);
destinations.emplace_back(adb.total_max_supply / 2, m_accounts[MINER_ACC_IDX].get_public_address(), null_pkey);
transaction asset_emission_tx{};
crypto::public_key asset_id = null_pkey;
alice_wlt->deploy_new_asset(adb, destinations, asset_emission_tx, asset_id);
// make sure the emission tx is correct
CHECK_AND_ASSERT_MES(asset_emission_tx.vout.size() > 2, false, "Unexpected vout size: " << asset_emission_tx.vout.size());
for(auto& out : asset_emission_tx.vout)
{
CHECK_AND_ASSERT_MES(out.type() == typeid(tx_out_zarcanum), false, "invalid out type");
const tx_out_zarcanum& out_zc = boost::get<tx_out_zarcanum>(out);
// as soon as this is the asset emmiting transaction, no output has an obvious asset id
// make sure it is so
CHECK_AND_ASSERT_MES(out_zc.blinded_asset_id != native_coin_asset_id_1div8, false, "One of outputs has explicit native asset id, which is unexpected");
}
// get this tx confirmed
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_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count());
return true;
}

View file

@ -26,3 +26,12 @@ struct assets_and_explicit_native_coins_in_outs : public wallet_test
mutable uint64_t m_alice_initial_balance = 0;
};
struct asset_depoyment_and_few_zc_utxos : public wallet_test
{
asset_depoyment_and_few_zc_utxos();
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);
mutable uint64_t m_alice_initial_balance = 0;
};

View file

@ -166,9 +166,10 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
pe.amount = m_context.stake_amount;
// generate miner tx using incorrect current_block_size only for size estimation
uint64_t block_reward_without_fee = 0;
size_t estimated_block_size = m_txs_total_size;
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, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use);
reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, 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");
estimated_block_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx);
@ -176,7 +177,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
for (size_t try_count = 0; try_count != 10; ++try_count)
{
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, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use);
reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, 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");
cumulative_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx);

View file

@ -1085,7 +1085,7 @@ bool pos_minting_tx_packing::c1(currency::core& c, size_t ev_index, const std::v
m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size // unlocked
), false, "");
alice_wlt->set_pos_mint_packing_size(m_pos_mint_packing_size);
alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(m_pos_mint_packing_size + 1, m_pos_mint_packing_size + 1); // +1 because previous implementation () had an error with this limit
// no coinbase tx outputs should be packed
r = alice_wlt->try_mint_pos();

View file

@ -34,23 +34,24 @@ bool test_transaction_generation_and_ring_signature()
std::string add_str = miner_acc3.get_public_address_str();
uint64_t block_reward_without_fee = 0;
account_base rv_acc;
rv_acc.generate();
account_base rv_acc2;
rv_acc2.generate();
transaction tx_mine_1;
construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().account_address, miner_acc1.get_keys().account_address, tx_mine_1, TRANSACTION_VERSION_PRE_HF4);
construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().account_address, miner_acc1.get_keys().account_address, tx_mine_1, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4);
transaction tx_mine_2;
construct_miner_tx(0, 0, 0, 0, 0, miner_acc2.get_keys().account_address, miner_acc2.get_keys().account_address, tx_mine_2, TRANSACTION_VERSION_PRE_HF4);
construct_miner_tx(0, 0, 0, 0, 0, miner_acc2.get_keys().account_address, miner_acc2.get_keys().account_address, tx_mine_2, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4);
transaction tx_mine_3;
construct_miner_tx(0, 0, 0, 0, 0, miner_acc3.get_keys().account_address, miner_acc3.get_keys().account_address, tx_mine_3, TRANSACTION_VERSION_PRE_HF4);
construct_miner_tx(0, 0, 0, 0, 0, miner_acc3.get_keys().account_address, miner_acc3.get_keys().account_address, tx_mine_3, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4);
transaction tx_mine_4;
construct_miner_tx(0, 0, 0, 0, 0, miner_acc4.get_keys().account_address, miner_acc4.get_keys().account_address, tx_mine_4, TRANSACTION_VERSION_PRE_HF4);
construct_miner_tx(0, 0, 0, 0, 0, miner_acc4.get_keys().account_address, miner_acc4.get_keys().account_address, tx_mine_4, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4);
transaction tx_mine_5;
construct_miner_tx(0, 0, 0, 0, 0, miner_acc5.get_keys().account_address, miner_acc5.get_keys().account_address, tx_mine_5, TRANSACTION_VERSION_PRE_HF4);
construct_miner_tx(0, 0, 0, 0, 0, miner_acc5.get_keys().account_address, miner_acc5.get_keys().account_address, tx_mine_5, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4);
transaction tx_mine_6;
construct_miner_tx(0, 0, 0, 0, 0, miner_acc6.get_keys().account_address, miner_acc6.get_keys().account_address, tx_mine_6, TRANSACTION_VERSION_PRE_HF4);
construct_miner_tx(0, 0, 0, 0, 0, miner_acc6.get_keys().account_address, miner_acc6.get_keys().account_address, tx_mine_6, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4);
//fill inputs entry
typedef tx_source_entry::output_entry tx_output_entry;
@ -134,8 +135,9 @@ bool test_block_creation()
account_public_address adr;
bool r = get_account_address_from_str(adr, "ZxDLGBGXbjo5w51tJkvxEPHFRr7Xft4hf33N8EkJPndoGCqocQF1mzpZqYwXByx5gMbfQuPAAB9vj79EFR6Jwkgu1o3aMQPwJ");
CHECK_AND_ASSERT_MES(r, false, "failed to import");
uint64_t block_reward_without_fee = 0;
block b;
r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, TRANSACTION_VERSION_PRE_HF4);
r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4);
return r;
}

View file

@ -3355,15 +3355,15 @@ packing_outputs_on_pos_minting_wallet::packing_outputs_on_pos_minting_wallet()
bool packing_outputs_on_pos_minting_wallet::generate(std::vector<test_event_entry>& events) const
{
// Test ideas:
// 1) UTXO defragmentation tx cannot select stake output by accident;
// 2) if UTXO defragmentation tx cannot be constructed because of too few decoy outputs, PoS can still be created;
// 3) UTXO defragmentation tx limits works as expected.
// 0 10 11 21 22 <- blockchain height (assuming CURRENCY_MINED_MONEY_UNLOCK_WINDOW == 10)
// (0 )... (0r)- (1 )... (1r)- <- main chain
// tx_0 <- txs
GENERATE_ACCOUNT(miner_acc);
m_accounts.push_back(miner_acc);
//GENERATE_ACCOUNT(alice_acc);
//m_accounts.push_back(alice_acc);
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate();
// don't use MAKE_GENESIS_BLOCK here because it will mask 'generator'
currency::block blk_0 = AUTO_VAL_INIT(blk_0);
@ -3372,21 +3372,32 @@ bool packing_outputs_on_pos_minting_wallet::generate(std::vector<test_event_entr
DO_CALLBACK(events, "configure_core");
CREATE_TEST_WALLET(miner_wlt, miner_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, miner_wlt, blk_0, 0);
uint64_t unlocked, awaiting_in, awaiting_out, mined;
m_premine_amount = miner_wlt->balance(unlocked, awaiting_in, awaiting_out, mined);
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
size_t n_blocks = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5;
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, n_blocks);
m_mined_amount = n_blocks * COIN;
m_single_amount = TESTS_DEFAULT_FEE * 5;
REFRESH_TEST_WALLET_AT_GEN_TIME(events, miner_wlt, blk_0r, n_blocks);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(miner_wlt, m_premine_amount + m_mined_amount);
MAKE_TX(events, tx_0, miner_acc, alice_acc, m_single_amount, blk_0r);
MAKE_TX(events, tx_1, miner_acc, alice_acc, m_single_amount, blk_0r);
MAKE_TX(events, tx_2, miner_acc, alice_acc, m_single_amount, blk_0r);
MAKE_TX(events, tx_3, miner_acc, alice_acc, m_single_amount, blk_0r);
MAKE_TX(events, tx_4, miner_acc, alice_acc, m_single_amount, blk_0r);
MAKE_TX(events, tx_5, miner_acc, bob_acc, m_single_amount, blk_0r);
MAKE_TX(events, tx_6, miner_acc, bob_acc, m_single_amount, blk_0r);
//MAKE_TX_FEE(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(2000), TESTS_DEFAULT_FEE, blk_0r);
//MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
//REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
m_alice_initial_balance = m_single_amount * 5;
m_bob_initial_balance = m_single_amount * 2;
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({tx_0, tx_1, tx_2, tx_3, tx_4, tx_5, tx_6}));
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_1r, CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 1);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, m_alice_initial_balance);
CREATE_TEST_WALLET(bob_wlt, bob_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, bob_wlt, blk_1r, CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 1);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(bob_wlt, m_bob_initial_balance);
DO_CALLBACK(events, "c1");
@ -3395,30 +3406,72 @@ bool packing_outputs_on_pos_minting_wallet::generate(std::vector<test_event_entr
bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX);
size_t blocks_fetched = 0;
bool received_money;
std::atomic<bool> atomic_false = ATOMIC_VAR_INIT(false);
miner_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5, false, "Incorrect numbers of blocks fetched");
alice_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance, 0, m_alice_initial_balance, 0, 0), false, "");
bob_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "bob_wlt", m_bob_initial_balance, 0, m_bob_initial_balance, 0, 0), false, "");
// 1. Try to defragment the same UTXO that is used for staking
// (Bob has two: one UTXO is for staking, other is being defragmented)
bob_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(1, 10);
bob_wlt->set_pos_decoys_count_for_defragmentation_tx(0);
bob_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 3, false, "Incorrect blockchain height:" << c.get_current_blockchain_size());
bob_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
// (also make sure that unlocked balance is zero)
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "bob_wlt", m_bob_initial_balance + CURRENCY_BLOCK_REWARD, CURRENCY_BLOCK_REWARD + TESTS_DEFAULT_FEE, 0), false, "");
miner_wlt->set_pos_mint_packing_size(4);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner_wlt", m_premine_amount + m_mined_amount, uint64_max, uint64_max, 0, 0), false, "");
// 2. Try to mine a PoS block and defragment some of UTXO
alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(2, 2);
alice_wlt->set_pos_decoys_count_for_defragmentation_tx(0);
alice_wlt->try_mint_pos();
miner_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 4, false, "Incorrect blockchain height:" << c.get_current_blockchain_size());
alice_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == 2, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 7, false, "Incorrect blockchain height:" << c.get_current_blockchain_size());
miner_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched");
// (also make sure that only two UTXOs is unlocked)
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD, CURRENCY_BLOCK_REWARD + TESTS_DEFAULT_FEE, m_single_amount * 2), false, "");
block top_block = AUTO_VAL_INIT(top_block);
bool r = c.get_blockchain_storage().get_top_block(top_block);
CHECK_AND_ASSERT_MES(r && is_pos_block(top_block), false, "get_top_block failed or smth goes wrong");
uint64_t top_block_reward = get_outs_money_amount(top_block.miner_tx);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner_wlt", m_premine_amount + m_mined_amount + COIN), false, "");
miner_wlt->reset_password(g_wallet_password);
miner_wlt->store(g_wallet_filename);
// 3. Try to mine a PoS block and defragment with huge decoy set. Make sure block is mined successfully without a defragmentation tx
// Alice has one UTXO
alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(1, 1);
alice_wlt->set_pos_decoys_count_for_defragmentation_tx(80);
alice_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 5, false, "Incorrect blockchain height:" << c.get_current_blockchain_size());
alice_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
// Alice's unlocked balance should consist only of one UTXO
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD * 2, CURRENCY_BLOCK_REWARD * 2 + TESTS_DEFAULT_FEE, m_single_amount), false, "");
// 4. Finally mine a PoS and defragment the last one unlocked UTXO
alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(1, 1);
alice_wlt->set_pos_decoys_count_for_defragmentation_tx(0);
alice_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 6, false, "Incorrect blockchain height:" << c.get_current_blockchain_size());
alice_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
// Alice's unlocked balance should be zero
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD * 3, CURRENCY_BLOCK_REWARD * 3 + TESTS_DEFAULT_FEE, 0), false, "");
return true;
}

View file

@ -257,8 +257,9 @@ struct packing_outputs_on_pos_minting_wallet : 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);
mutable uint64_t m_premine_amount = 0;
mutable uint64_t m_mined_amount = 0;
mutable uint64_t m_single_amount = 0;
mutable uint64_t m_alice_initial_balance = 0;
mutable uint64_t m_bob_initial_balance = 0;
};
struct wallet_sending_to_integrated_address : public wallet_test

View file

@ -807,7 +807,7 @@ bool zarcanum_block_with_txs::generate(std::vector<test_event_entry>& events) co
// make sure Alice received both block reward and the fee
uint64_t mined_amount_2 = COIN + fee;
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, m_alice_balance + mined_amount_2, 0, mined_amount + mined_amount_2, 0, 0));
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, m_alice_balance + mined_amount_2, UINT64_MAX, mined_amount + mined_amount_2, 0, 0));
m_alice_balance += mined_amount_2;