1
0
Fork 0
forked from lthn/blockchain

Added PoS grinding attack fuse

This commit is contained in:
cryptozoidberg 2024-05-13 16:51:13 +04:00
parent 7237867f15
commit 10c451f3b7
No known key found for this signature in database
GPG key ID: 2E10CC61CAC8F36D
9 changed files with 201 additions and 33 deletions

View file

@ -67,6 +67,8 @@ using namespace currency;
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_LAST_WORKED_VERSION 2
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION 3 //DON'T CHANGE THIS, if you need to resync db change BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MINOR_COMPATIBILITY_VERSION 4 //mismatch here means some reinitializations
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_MAJOR_FAILURE 5 //if not blocks should ever be added with this condition
#define TARGETDATA_CACHE_SIZE DIFFICULTY_WINDOW + 10
@ -96,6 +98,7 @@ blockchain_storage::blockchain_storage(tx_memory_pool& tx_pool) :m_db(nullptr, m
m_db_last_worked_version(BLOCKCHAIN_STORAGE_OPTIONS_ID_LAST_WORKED_VERSION, m_db_solo_options),
m_db_storage_major_compatibility_version(BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION, m_db_solo_options),
m_db_storage_minor_compatibility_version(BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MINOR_COMPATIBILITY_VERSION, m_db_solo_options),
m_db_major_failure(BLOCKCHAIN_STORAGE_OPTIONS_ID_MAJOR_FAILURE, m_db_solo_options),
m_db_per_block_gindex_incs(m_db),
m_tx_pool(tx_pool),
m_is_in_checkpoint_zone(false),
@ -507,7 +510,8 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro
<< " last block: " << m_db_blocks.size() - 1 << ", " << misc_utils::get_time_interval_string(timestamp_diff) << " ago" << ENDL
<< " current pos difficulty: " << get_next_diff_conditional(true) << ENDL
<< " current pow difficulty: " << get_next_diff_conditional(false) << ENDL
<< " total transactions: " << m_db_transactions.size(),
<< " total transactions: " << m_db_transactions.size() << ENDL
<< " major failure: " << (m_db_major_failure ? "true" : "false"),
LOG_LEVEL_0);
return true;
@ -1181,14 +1185,9 @@ bool blockchain_storage::switch_to_alternative_blockchain(alt_chain_type& alt_ch
return true;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) const
void blockchain_storage::collect_timestamps_and_c_difficulties_main(std::vector<uint64_t>& timestamps, std::vector<wide_difficulty_type>& commulative_difficulties, bool pos) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
std::vector<uint64_t> timestamps;
std::vector<wide_difficulty_type> commulative_difficulties;
if (!m_db_blocks.size())
return DIFFICULTY_POW_STARTER;
//skip genesis timestamp
TIME_MEASURE_START_PD(target_calculating_enum_blocks);
CRITICAL_REGION_BEGIN(m_targetdata_cache_lock);
std::list<std::pair<wide_difficulty_type, uint64_t>>& targetdata_cache = pos ? m_pos_targetdata_cache : m_pow_targetdata_cache;
@ -1203,11 +1202,14 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) con
++count;
}
CRITICAL_REGION_END();
wide_difficulty_type& dif = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty;
TIME_MEASURE_FINISH_PD(target_calculating_enum_blocks);
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::calc_diff_at_h_from_timestamps(std::vector<uint64_t>& timestamps, std::vector<wide_difficulty_type>& commulative_difficulties, uint64_t h, bool pos) const
{
wide_difficulty_type dif;
TIME_MEASURE_START_PD(target_calculating_calc);
if (m_core_runtime_config.is_hardfork_active_for_height(1, m_db_blocks.size()))
if (m_core_runtime_config.is_hardfork_active_for_height(1, h))
{
dif = next_difficulty_2(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter);
}
@ -1215,22 +1217,36 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) con
{
dif = next_difficulty_1(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter);
}
TIME_MEASURE_FINISH_PD(target_calculating_calc);
return dif;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const
wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
{
//skip genesis timestamp
CRITICAL_REGION_LOCAL(m_read_lock);
if (!m_db_blocks.size())
return DIFFICULTY_POW_STARTER;
}
std::vector<uint64_t> timestamps;
std::vector<wide_difficulty_type> commulative_difficulties;
size_t count = 0;
if (!m_db_blocks.size())
return DIFFICULTY_POW_STARTER;
collect_timestamps_and_c_difficulties_main(timestamps, commulative_difficulties, pos);
auto cb = [&](const block_extended_info& bei, bool is_main){
wide_difficulty_type& dif = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty;
dif = calc_diff_at_h_from_timestamps(timestamps, commulative_difficulties, m_db_blocks.size(), pos);
return dif;
}
//------------------------------------------------------------------
void blockchain_storage::collect_timestamps_and_c_difficulties_alt(std::vector<uint64_t>& timestamps, std::vector<wide_difficulty_type>& commulative_difficulties, bool pos, const alt_chain_type& alt_chain, uint64_t split_height) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
size_t count = 0;
auto cb = [&](const block_extended_info& bei, bool is_main) {
if (!bei.height)
return false;
bool is_pos_bl = is_pos_block(bei.bl);
@ -1242,15 +1258,22 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional2(bool pos, co
if (count >= DIFFICULTY_WINDOW)
return false;
return true;
};
};
enum_blockchain(cb, alt_chain, split_height);
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_next_diff_conditional_alt(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const
{
{
CRITICAL_REGION_LOCAL(m_read_lock);
if (!m_db_blocks.size())
return DIFFICULTY_POW_STARTER;
}
std::vector<uint64_t> timestamps;
std::vector<wide_difficulty_type> commulative_difficulties;
collect_timestamps_and_c_difficulties_alt(timestamps, commulative_difficulties, pos, alt_chain, split_height);
wide_difficulty_type diff = 0;
if(m_core_runtime_config.is_hardfork_active_for_height(1, abei.height))
diff = next_difficulty_2(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter);
else
diff = next_difficulty_1(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter);
return diff;
return calc_diff_at_h_from_timestamps(timestamps, commulative_difficulties, abei.height, pos);
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_cached_next_difficulty(bool pos) const
@ -1865,7 +1888,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
}
// PoW / PoS validation (heavy checks)
wide_difficulty_type current_diff = get_next_diff_conditional2(pos_block, alt_chain, connection_height, abei);
wide_difficulty_type current_diff = get_next_diff_conditional_alt(pos_block, alt_chain, connection_height, abei);
CHECK_AND_ASSERT_MES_CUSTOM(current_diff, false, bvc.m_verification_failed = true, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
crypto::hash proof_of_work = null_hash;
@ -5751,6 +5774,12 @@ void blockchain_storage::get_pos_mining_estimate(uint64_t amount_coins,
//------------------------------------------------------------------
bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transaction& tx, const crypto::hash& tx_id) const
{
if (m_db_major_failure)
{
LOG_ERROR("MAJOR FAILURE: POS DIFFICULTY IS GOT TO HIGH! Contact the team immediately if you see this error in logs and watch them having panic attack.");
return false;
}
uint64_t block_height = m_db_blocks.size();
return validate_tx_for_hardfork_specific_terms(tx, tx_id, block_height);
}
@ -6780,6 +6809,13 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
<< range_proofs_agregated.size()
<< ")"
<< "))");
if (is_pos_bl && current_diffic > m_core_runtime_config.max_pos_difficulty)
{
m_db_major_failure = true; //burn safety fuse
LOG_ERROR("MAJOR FAILURE: POS DIFFICULTY IS GOT TO HIGH! Contact the team immediately if you see this error in logs and watch them having panic attack."
<< ENDL << "Block id:" << id);
}
{
static epee::math_helper::average<uint64_t, 30> blocks_processing_time_avg_pos, blocks_processing_time_avg_pow;
@ -6956,6 +6992,15 @@ bool blockchain_storage::add_new_block(const block& bl, block_verification_conte
{
try
{
if (m_db_major_failure)
{
LOG_PRINT_RED_L0("Block processing is stoped due to MAJOR FAILURE fuse burned");
bvc.m_added_to_main_chain = false;
bvc.m_verification_failed = true;
return false;
}
m_db.begin_transaction();
//block bl = bl_;
@ -6979,10 +7024,6 @@ bool blockchain_storage::add_new_block(const block& bl, block_verification_conte
m_db.commit_transaction();
return false;
}
//check that block refers to chain tail
if (!(bl.prev_id == get_top_block_id()))
{

View file

@ -266,8 +266,12 @@ namespace currency
crypto::hash get_top_block_id(uint64_t& height) const;
bool get_top_block(block& b) const;
wide_difficulty_type get_next_diff_conditional(bool pos) const;
wide_difficulty_type get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const;
wide_difficulty_type get_next_diff_conditional_alt(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const;
wide_difficulty_type get_cached_next_difficulty(bool pos) const;
wide_difficulty_type calc_diff_at_h_from_timestamps(std::vector<uint64_t>& timestamps, std::vector<wide_difficulty_type>& commulative_difficulties, uint64_t h, bool pos) const;
void collect_timestamps_and_c_difficulties_main(std::vector<uint64_t>& timestamps, std::vector<wide_difficulty_type>& commulative_difficulties, bool pos) const;
void collect_timestamps_and_c_difficulties_alt(std::vector<uint64_t>& timestamps, std::vector<wide_difficulty_type>& commulative_difficulties, bool pos, const alt_chain_type& alt_chain, uint64_t split_height) const;
bool create_block_template(const account_public_address& miner_address, const blobdata& ex_nonce, block& b, wide_difficulty_type& di, uint64_t& height) const;
@ -551,6 +555,8 @@ namespace currency
tools::db::solo_db_value<uint64_t, std::string, solo_options_container, true> m_db_last_worked_version;
tools::db::solo_db_value<uint64_t, uint64_t, solo_options_container> m_db_storage_major_compatibility_version;
tools::db::solo_db_value<uint64_t, uint64_t, solo_options_container> m_db_storage_minor_compatibility_version;
tools::db::solo_db_value<uint64_t, bool, solo_options_container> m_db_major_failure; //safety fuse
outputs_container m_db_outputs;
multisig_outs_container m_db_multisig_outs;
aliases_container m_db_aliases;

View file

@ -106,6 +106,7 @@ namespace currency
crypto::public_key alias_validation_pubkey;
core_time_func_t get_core_time;
uint64_t hf4_minimum_mixins;
wide_difficulty_type max_pos_difficulty;
hard_forks_descriptor hard_forks;
@ -129,6 +130,7 @@ namespace currency
pc.tx_default_fee = TX_DEFAULT_FEE;
pc.max_alt_blocks = CURRENCY_ALT_BLOCK_MAX_COUNT;
pc.hf4_minimum_mixins = CURRENCY_HF4_MANDATORY_DECOY_SET_SIZE;
pc.max_pos_difficulty = wide_difficulty_type(POS_MAX_DIFFICULTY_ALLOWED);
// TODO: refactor the following
pc.hard_forks.set_hardfork_height(1, ZANO_HARDFORK_01_AFTER_HEIGHT);

View file

@ -157,6 +157,8 @@
#define POS_MODFIFIER_INTERVAL 10
#define POS_WALLET_MINING_SCAN_INTERVAL POS_SCAN_STEP //seconds
#define POS_MINIMUM_COINSTAKE_AGE 10 // blocks count
#define POS_MAX_DIFFICULTY_ALLOWED "25000000000000000000000" // maximum expected PoS difficuty (need to change it probaly in 20 years)
#ifndef TESTNET
# define BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION 57000

View file

@ -4887,15 +4887,16 @@ bool wallet2::try_mint_pos(const currency::account_public_address& miner_address
return true;
}, m_core_runtime_config);
bool res = true;
if (ctx.status == API_RETURN_CODE_OK)
{
build_minted_block(ctx, miner_address);
res = build_minted_block(ctx, miner_address);
}
TIME_MEASURE_FINISH_MS(mining_duration_ms);
WLT_LOG_L0("PoS mining: " << ctx.iterations_processed << " iterations finished (" << std::fixed << std::setprecision(2) << (mining_duration_ms / 1000.0f) << "s), status: " << ctx.status << ", " << ctx.total_items_checked << " entries with total amount: " << print_money_brief(ctx.total_amount_checked));
return true;
return res;
}
//------------------------------------------------------------------
void wallet2::do_pos_mining_prepare_entry(mining_context& context, size_t transfer_index)

View file

@ -1274,6 +1274,9 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(zarcanum_block_with_txs);
GENERATE_AND_PLAY(asset_depoyment_and_few_zc_utxos);
GENERATE_AND_PLAY_HF(assets_and_pos_mining, "4-*");
GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*");
GENERATE_AND_PLAY_HF(attachment_isolation_test, "4-*");

View file

@ -45,3 +45,4 @@
#include "multiassets_test.h"
#include "ionic_swap_tests.h"
#include "attachment_isolation_encryption_test.h"
#include "pos_fuse_test.h"

View file

@ -0,0 +1,98 @@
// Copyright (c) 2014-2022 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chaingen.h"
#include "pos_fuse_test.h"
#include "wallet_test_core_proxy.h"
#include "random_helper.h"
#include "wallet/wallet_debug_events_definitions.h"
using namespace currency;
using namespace currency;
pos_fuse_test::pos_fuse_test()
{
REGISTER_CALLBACK_METHOD(pos_fuse_test, c1);
REGISTER_CALLBACK_METHOD(pos_fuse_test, configure_core);
}
bool pos_fuse_test::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
wallet_test::configure_core(c, ev_index, events);
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
pc.max_pos_difficulty = wide_difficulty_type(1);
//currency::core_runtime_config pc2;
//pc2 = pc;
c.get_blockchain_storage().set_core_runtime_config(pc);
currency::core_runtime_config pc2 = c.get_blockchain_storage().get_core_runtime_config();
LOG_PRINT_L1("Difficulty: " << pc2.max_pos_difficulty);
return true;
}
bool pos_fuse_test::generate(std::vector<test_event_entry>& events) const
{
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
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 10);
DO_CALLBACK(events, "c1");
return true;
}
bool pos_fuse_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
miner_wlt->refresh();
while (true)
{
miner_wlt->refresh();
wide_difficulty_type pos_diff = c.get_blockchain_storage().get_next_diff_conditional(true);
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
bool r = miner_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(r, false, "Failed ot mint pos block");
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
LOG_PRINT_MAGENTA("POS Difficulty: " << pos_diff << ", max allowed diff: " << pc.max_pos_difficulty, LOG_LEVEL_0);
if (pos_diff > pc.max_pos_difficulty)
{
break;
}
}
//check that PoW blocks not going
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(!r, false, "PoW block unexpectedly generated");
//check that PoS blocks not going
r = miner_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(!r, false, "PoS block unexpectedly mined");
try
{
miner_wlt->transfer(1000000, m_accounts[ALICE_ACC_IDX].get_public_address());
CHECK_AND_ASSERT_MES(false, false, "Transaction unexpectedly sent");
}
catch (...)
{
LOG_PRINT_L0("Expected exception catched");
}
return true;
}

View file

@ -0,0 +1,14 @@
// Copyright (c) 2014-2024 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "chaingen.h"
#include "wallet_tests_basic.h"
struct pos_fuse_test : public wallet_test
{
pos_fuse_test();
virtual bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
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);
};