diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index c5257d95..95a5d88e 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -289,7 +289,14 @@ POP_GCC_WARNINGS buff.append(reinterpret_cast(&pod), sizeof(pod)); } - + template + bool get_pod_from_strbuff(const std::string& buff, pod_t& output) + { + if (buff.size() != sizeof(pod_t)) + return false; + output = *reinterpret_cast(buff.data()); + return true; + } template bool parse_commandline(std::map& res, int argc, char** argv) diff --git a/src/currency_core/bc_block_datetime_service.h b/src/currency_core/bc_block_datetime_service.h new file mode 100644 index 00000000..4db3d743 --- /dev/null +++ b/src/currency_core/bc_block_datetime_service.h @@ -0,0 +1,7 @@ +// Copyright (c) 2014-2021 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 + +#define BC_BLOCK_DATETIME_SERVICE_ID "d" +#define BC_BLOCK_DATETIME_INSTRUCTION_DEFAULT "" diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index b2bae2b9..54813472 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -2114,7 +2114,7 @@ bool blockchain_storage::get_tx_rpc_details(const crypto::hash& h, tx_rpc_extend if (tx_ptr && !timestamp) { - timestamp = get_actual_timestamp(m_db_blocks[tx_ptr->m_keeper_block_height]->bl); + timestamp = get_block_datetime(m_db_blocks[tx_ptr->m_keeper_block_height]->bl); } tei.keeper_block = static_cast(tx_ptr->m_keeper_block_height); fill_tx_rpc_details(tei, tx_ptr->tx, &(*tx_ptr), h, timestamp, is_short); @@ -2203,11 +2203,11 @@ bool blockchain_storage::get_main_block_rpc_details(uint64_t i, block_rpc_extend crypto::hash coinbase_id = get_transaction_hash(core_bei_ptr->bl.miner_tx); //load transactions details bei.transactions_details.push_back(tx_rpc_extended_info()); - get_tx_rpc_details(coinbase_id, bei.transactions_details.back(), get_actual_timestamp(core_bei_ptr->bl), true); + get_tx_rpc_details(coinbase_id, bei.transactions_details.back(), get_block_datetime(core_bei_ptr->bl), true); for (auto& h : core_bei_ptr->bl.tx_hashes) { bei.transactions_details.push_back(tx_rpc_extended_info()); - get_tx_rpc_details(h, bei.transactions_details.back(), get_actual_timestamp(core_bei_ptr->bl), true); + get_tx_rpc_details(h, bei.transactions_details.back(), get_block_datetime(core_bei_ptr->bl), true); bei.total_fee += bei.transactions_details.back().fee; bei.total_txs_size += bei.transactions_details.back().blob_size; } @@ -2288,13 +2288,13 @@ bool blockchain_storage::get_alt_block_rpc_details(const block_extended_info& be crypto::hash coinbase_id = get_transaction_hash(bei_core.bl.miner_tx); //load transactions details bei.transactions_details.push_back(tx_rpc_extended_info()); - fill_tx_rpc_details(bei.transactions_details.back(), bei_core.bl.miner_tx, nullptr, coinbase_id, get_actual_timestamp(bei_core.bl)); + fill_tx_rpc_details(bei.transactions_details.back(), bei_core.bl.miner_tx, nullptr, coinbase_id, get_block_datetime(bei_core.bl)); bei.total_fee = 0; for (auto& h : bei_core.bl.tx_hashes) { bei.transactions_details.push_back(tx_rpc_extended_info()); - if (!get_tx_rpc_details(h, bei.transactions_details.back(), get_actual_timestamp(bei_core.bl), true)) + if (!get_tx_rpc_details(h, bei.transactions_details.back(), get_block_datetime(bei_core.bl), true)) { //tx not in blockchain, supposed to be in tx pool m_tx_pool.get_transaction_details(h, bei.transactions_details.back()); @@ -2407,8 +2407,8 @@ uint64_t blockchain_storage::get_seconds_between_last_n_block(size_t n) const if (m_db_blocks.size() <= n) return 0; - uint64_t top_block_ts = get_actual_timestamp(m_db_blocks[m_db_blocks.size() - 1]->bl); - uint64_t n_block_ts = get_actual_timestamp(m_db_blocks[m_db_blocks.size() - 1 - n]->bl); + uint64_t top_block_ts = get_block_datetime(m_db_blocks[m_db_blocks.size() - 1]->bl); + uint64_t n_block_ts = get_block_datetime(m_db_blocks[m_db_blocks.size() - 1 - n]->bl); return top_block_ts > n_block_ts ? top_block_ts - n_block_ts : 0; } @@ -4905,7 +4905,7 @@ void blockchain_storage::get_pos_mining_estimate(uint64_t amount_coins, auto bei = m_db_blocks[h]; if (!is_pos_block(bei->bl)) continue; - uint64_t ts = get_actual_timestamp(bei->bl); + uint64_t ts = get_block_datetime(bei->bl); pos_ts_min = min(pos_ts_min, ts); pos_ts_max = max(pos_ts_max, ts); pos_total_minted_money += get_reward_from_miner_tx(bei->bl.miner_tx); @@ -5060,7 +5060,8 @@ bool blockchain_storage::validate_pos_block(const block& b, } - //check actual time if it there + // the following check is de-facto not applicable since 2021-10, but left intact to avoid consensus issues + // PoS blocks don't use etc_tx_time anymore to store actual timestamp; instead, they use tx_service_attachment in mining tx extra uint64_t actual_ts = get_actual_timestamp(b); if ((actual_ts > b.timestamp && actual_ts - b.timestamp > POS_MAX_ACTUAL_TIMESTAMP_TO_MINED) || (actual_ts < b.timestamp && b.timestamp - actual_ts > POS_MAX_ACTUAL_TIMESTAMP_TO_MINED) @@ -5379,7 +5380,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt block_fees.reserve(bl.tx_hashes.size()); //process transactions TIME_MEASURE_START_PD(all_txs_insert_time_5); - if (!add_transaction_from_block(bl.miner_tx, get_transaction_hash(bl.miner_tx), id, get_current_blockchain_size(), get_actual_timestamp(bl))) + if (!add_transaction_from_block(bl.miner_tx, get_transaction_hash(bl.miner_tx), id, get_current_blockchain_size(), get_block_datetime(bl))) { LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); bvc.m_verification_failed = true; @@ -5454,7 +5455,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt TIME_MEASURE_START_PD(tx_prapare_append); uint64_t current_bc_size = get_current_blockchain_size(); - uint64_t actual_timestamp = get_actual_timestamp(bl); + uint64_t actual_timestamp = get_block_datetime(bl); TIME_MEASURE_FINISH_PD(tx_prapare_append); TIME_MEASURE_START_PD(tx_append_time); if(!add_transaction_from_block(tx, tx_id, id, current_bc_size, actual_timestamp)) @@ -5622,7 +5623,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt stringstream powpos_str_entry, timestamp_str_entry; if (is_pos_bl) { // PoS - int64_t actual_ts = get_actual_timestamp(bei.bl); // signed int is intentionally used here + int64_t actual_ts = get_block_datetime(bei.bl); // signed int is intentionally used here int64_t ts_diff = actual_ts - m_core_runtime_config.get_core_time(); powpos_str_entry << "PoS:\t" << proof_hash << ", stake amount: " << print_money_brief(pos_coinstake_amount) << ", final_difficulty: " << this_coin_diff; timestamp_str_entry << ", actual ts: " << actual_ts << " (diff: " << std::showpos << ts_diff << "s) block ts: " << std::noshowpos << bei.bl.timestamp << " (shift: " << std::showpos << static_cast(bei.bl.timestamp) - actual_ts << ")"; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index e6a162de..7019ce41 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -26,6 +26,7 @@ using namespace epee; #include "bc_payments_id_service.h" #include "bc_escrow_service.h" #include "bc_attachments_helpers.h" +#include "bc_block_datetime_service.h" #include "genesis.h" #include "genesis_acc.h" #include "common/mnemonic-encoding.h" @@ -2108,6 +2109,8 @@ namespace currency return median_fee * 10; } //--------------------------------------------------------------- + // NOTE: this function is obsolete and depricated + // PoS block real timestamp is set using a service attachment in mining tx extra since 2021-10 uint64_t get_actual_timestamp(const block& b) { uint64_t tes_ts = b.timestamp; @@ -2119,6 +2122,41 @@ namespace currency } return tes_ts; } + //--------------------------------------------------------------- + // returns timestamp from BC_BLOCK_DATETIME_SERVICE_ID via tx_service_attachment in extra + // fallbacks to old-style actual timestamp via etc_tx_time, then to block timestamp + uint64_t get_block_datetime(const block& b) + { + // first try BC_BLOCK_DATETIME_SERVICE_ID + tx_service_attachment sa = AUTO_VAL_INIT(sa); + if (get_type_in_variant_container(b.miner_tx.extra, sa)) + { + if (sa.service_id == BC_BLOCK_DATETIME_SERVICE_ID && sa.instruction == BC_BLOCK_DATETIME_INSTRUCTION_DEFAULT) + { + uint64_t ts; + if (epee::string_tools::get_pod_from_strbuff(sa.body, ts)) + return ts; + } + } + + // next try etc_tx_time + etc_tx_time t = AUTO_VAL_INIT(t); + if (get_type_in_variant_container(b.miner_tx.extra, t)) + return t.v; + + // otherwise return default: block.ts + return b.timestamp; + } + //--------------------------------------------------------------- + void set_block_datetime(uint64_t datetime, block& b) + { + tx_service_attachment sa = AUTO_VAL_INIT(sa); + sa.service_id = BC_BLOCK_DATETIME_SERVICE_ID; + sa.instruction = BC_BLOCK_DATETIME_INSTRUCTION_DEFAULT; + sa.flags = 0; + epee::string_tools::append_pod_to_strbuff(sa.body, datetime); + b.miner_tx.extra.push_back(sa); + } //------------------------------------------------------------------ bool validate_alias_name(const std::string& al) { @@ -2777,7 +2815,7 @@ namespace currency pei_rpc.timestamp = bei_chain.bl.timestamp; pei_rpc.id = epee::string_tools::pod_to_hex(h); pei_rpc.prev_id = epee::string_tools::pod_to_hex(bei_chain.bl.prev_id); - pei_rpc.actual_timestamp = get_actual_timestamp(bei_chain.bl); + pei_rpc.actual_timestamp = get_block_datetime(bei_chain.bl); pei_rpc.type = is_pos_block(bei_chain.bl) ? 0 : 1; pei_rpc.already_generated_coins = boost::lexical_cast(bei_chain.already_generated_coins); pei_rpc.this_block_fee_median = bei_chain.this_block_tx_fee_median; diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 4b9e9030..725b39e1 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -329,8 +329,10 @@ namespace currency // prints amount in format "3.14", "0.0" std::string print_money_brief(uint64_t amount); - uint64_t get_actual_timestamp(const block& b); - + uint64_t get_actual_timestamp(const block& b); // obsolete and depricated, use get_block_datetime + uint64_t get_block_datetime(const block& b); + void set_block_datetime(uint64_t datetime, block& b); + bool addendum_to_hexstr(const std::vector& add, std::string& hex_buff); bool hexstr_to_addendum(const std::string& hex_buff, std::vector& add); bool set_payment_id_to_tx(std::vector& att, const std::string& payment_id); diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index dee2a49d..4573b806 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -330,7 +330,7 @@ private: currency::block_extended_info bei = AUTO_VAL_INIT(bei); CHECK_AND_ASSERT_MES(bcs.get_block_extended_info_by_height(tx_chain_entry->m_keeper_block_height, bei), false, "cannot find block by height " << tx_chain_entry->m_keeper_block_height); - LOG_PRINT_L0("Key image found in tx: " << tx_id << " height " << tx_chain_entry->m_keeper_block_height << " (ts: " << epee::misc_utils::get_time_str_v2(currency::get_actual_timestamp(bei.bl)) << ")" << ENDL + LOG_PRINT_L0("Key image found in tx: " << tx_id << " height " << tx_chain_entry->m_keeper_block_height << " (ts: " << epee::misc_utils::get_time_str_v2(currency::get_block_datetime(bei.bl)) << ")" << ENDL << obj_to_json_str(tx_chain_entry->tx)); } else diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 2c287ed7..b6634494 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -180,7 +180,7 @@ namespace currency res.pos_block_ts_shift_vs_actual = 0; auto last_pos_block_ptr = m_core.get_blockchain_storage().get_last_block_of_type(true); if (last_pos_block_ptr) - res.pos_block_ts_shift_vs_actual = last_pos_block_ptr->bl.timestamp - get_actual_timestamp(last_pos_block_ptr->bl); + res.pos_block_ts_shift_vs_actual = last_pos_block_ptr->bl.timestamp - get_block_datetime(last_pos_block_ptr->bl); } if (req.flags&COMMAND_RPC_GET_INFO_FLAG_OUTS_STAT) m_core.get_blockchain_storage().get_outs_index_stat(res.outs_stat); @@ -918,10 +918,10 @@ namespace currency } //@#@ //temporary double check timestamp - if (time(NULL) - static_cast(get_actual_timestamp(b)) > 5) + if (time(NULL) - static_cast(get_block_datetime(b)) > 5) { - LOG_PRINT_RED_L0("Found block (" << get_block_hash(b) << ") timestamp (" << get_actual_timestamp(b) - << ") is suspiciously less (" << time(NULL) - static_cast(get_actual_timestamp(b)) << ") than current time ( " << time(NULL) << ")"); + LOG_PRINT_RED_L0("Found block (" << get_block_hash(b) << ") timestamp (" << get_block_datetime(b) + << ") is suspiciously less (" << time(NULL) - static_cast(get_block_datetime(b)) << ") than current time ( " << time(NULL) << ")"); //mark node to make it easier to find it via scanner m_core.get_blockchain_storage().get_performnce_data().epic_failure_happend = true; } @@ -975,10 +975,10 @@ namespace currency } //@#@ //temporary double check timestamp - if (time(NULL) - static_cast(get_actual_timestamp(b)) > 5) + if (time(NULL) - static_cast(get_block_datetime(b)) > 5) { - LOG_PRINT_RED_L0("Found block (" << get_block_hash(b) << ") timestamp (" << get_actual_timestamp(b) - << ") is suspiciously less (" << time(NULL) - static_cast(get_actual_timestamp(b)) << ") than current time ( " << time(NULL) << ")"); + LOG_PRINT_RED_L0("Found block (" << get_block_hash(b) << ") timestamp (" << get_block_datetime(b) + << ") is suspiciously less (" << time(NULL) - static_cast(get_block_datetime(b)) << ") than current time ( " << time(NULL) << ")"); //mark node to make it easier to find it via scanner m_core.get_blockchain_storage().get_performnce_data().epic_failure_happend = true; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 52f07402..08cae0e2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1281,7 +1281,7 @@ void wallet2::handle_money_received2(const currency::block& b, const currency::t m_transfer_history.push_back(AUTO_VAL_INIT(wallet_public::wallet_transfer_info())); wallet_public::wallet_transfer_info& wti = m_transfer_history.back(); wti.is_income = true; - prepare_wti(wti, get_block_height(b), get_actual_timestamp(b), tx, amount, td); + prepare_wti(wti, get_block_height(b), get_block_datetime(b), tx, amount, td); WLT_LOG_L1("[MONEY RECEIVED]: " << epee::serialization::store_t_to_json(wti)); rise_on_transfer2(wti); } @@ -1311,7 +1311,7 @@ void wallet2::handle_money_spent2(const currency::block& b, wti.remote_addresses = recipients; wti.recipients_aliases = recipients_aliases; - prepare_wti(wti, get_block_height(b), get_actual_timestamp(b), in_tx, amount, td); + prepare_wti(wti, get_block_height(b), get_block_datetime(b), in_tx, amount, td); WLT_LOG_L1("[MONEY SPENT]: " << epee::serialization::store_t_to_json(wti)); rise_on_transfer2(wti); } @@ -3454,7 +3454,7 @@ bool wallet2::try_mint_pos(const currency::account_public_address& miner_address build_minted_block(ctx.sp, ctx.rsp, miner_address); } - WLT_LOG_L0("PoS mining iteration finished, status: " << ctx.rsp.status << ", used " << ctx.sp.pos_entries.size() << " entries with total amount: " << print_money_brief(pos_entries_amount)); + WLT_LOG_L0("PoS mining: " << ctx.rsp.iterations_processed << " iterations finished, status: " << ctx.rsp.status << ", used " << ctx.sp.pos_entries.size() << " entries with total amount: " << print_money_brief(pos_entries_amount)); return true; } @@ -3530,12 +3530,10 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request& const currency::txout_to_key& txtokey = boost::get(target); keys_ptrs.push_back(&txtokey.key); - //put actual time for tx block - b.timestamp = rsp.block_timestamp; - currency::etc_tx_time tt = AUTO_VAL_INIT(tt); - tt.v = m_core_runtime_config.get_core_time(); - b.miner_tx.extra.push_back(tt); - WLT_LOG_MAGENTA("Applying actual timestamp: " << epee::misc_utils::get_time_str(tt.v), LOG_LEVEL_0); + // set a real timestamp + uint64_t current_timestamp = m_core_runtime_config.get_core_time(); + set_block_datetime(current_timestamp, b); + WLT_LOG_MAGENTA("Applying actual timestamp: " << current_timestamp, LOG_LEVEL_0); //sign block res = prepare_and_sign_pos_block(b, @@ -3563,10 +3561,10 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request& m_wcallback->on_pos_block_found(b); //@#@ //double check timestamp - if (time(NULL) - static_cast(get_actual_timestamp(b)) > 5) + if (time(NULL) - static_cast(get_block_datetime(b)) > 5) { - WLT_LOG_RED("Found block (" << get_block_hash(b) << ") timestamp (" << get_actual_timestamp(b) - << ") is suspiciously less (" << time(NULL) - static_cast(get_actual_timestamp(b)) << ") than current time ( " << time(NULL) << ")", LOG_LEVEL_0); + WLT_LOG_RED("Found block (" << get_block_hash(b) << ") timestamp (" << get_block_datetime(b) + << ") is suspiciously less (" << time(NULL) - static_cast(get_block_datetime(b)) << ") than current time ( " << time(NULL) << ")", LOG_LEVEL_0); } // return true; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a67d6314..70a674a1 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1203,16 +1203,20 @@ namespace tools const currency::core_runtime_config &runtime_config) { cxt.rsp.status = API_RETURN_CODE_NOT_FOUND; - uint64_t timstamp_start = runtime_config.get_core_time(); uint64_t timstamp_last_idle_call = runtime_config.get_core_time(); cxt.rsp.iterations_processed = 0; + uint64_t ts_from = cxt.rsp.starter_timestamp; // median ts of last BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW blocks + ts_from = ts_from - (ts_from % POS_SCAN_STEP) + POS_SCAN_STEP; + uint64_t ts_to = runtime_config.get_core_time() + CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT - 5; + ts_to = ts_to - (ts_to % POS_SCAN_STEP); + CHECK_AND_ASSERT_MES(ts_to > ts_from, false, "scan_pos: ts_to <= ts_from: " << ts_to << ", " << ts_from); + uint64_t ts_middle = (ts_to + ts_from) / 2; + ts_middle -= ts_middle % POS_SCAN_STEP; + uint64_t ts_window = std::min(ts_middle - ts_from, ts_to - ts_middle); + for (size_t i = 0; i != cxt.sp.pos_entries.size(); i++) { - //set timestamp starting from timestamp%POS_SCAN_STEP = 0 - uint64_t adjusted_starter_timestamp = timstamp_start - POS_SCAN_STEP; - adjusted_starter_timestamp = POS_SCAN_STEP * 2 - (adjusted_starter_timestamp%POS_SCAN_STEP) + adjusted_starter_timestamp; - bool go_past = true; uint64_t step = 0; @@ -1232,7 +1236,7 @@ namespace tools } }; - while(step <= POS_SCAN_WINDOW) + while(step <= ts_window) { //check every WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL seconds if top block changes, in case - break loop @@ -1248,8 +1252,8 @@ namespace tools } - uint64_t ts = go_past ? adjusted_starter_timestamp - step : adjusted_starter_timestamp + step; - if (ts < cxt.rsp.starter_timestamp) + uint64_t ts = go_past ? ts_middle - step : ts_middle + step; + if (ts < ts_from || ts > ts_to) { next_turn(); continue; diff --git a/tests/core_tests/emission_test.cpp b/tests/core_tests/emission_test.cpp index dcb26c84..a033f7a3 100644 --- a/tests/core_tests/emission_test.cpp +++ b/tests/core_tests/emission_test.cpp @@ -112,7 +112,7 @@ bool emission_test::c1(currency::core& c, size_t ev_index, const std::vector()); pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, difficulty, prev_id, null_hash, timestamp); pb.step4_generate_coinbase_tx(0, already_generated_coins, m_miner_acc.get_public_address()); - pb.m_block.miner_tx.extra.push_back(currency::etc_tx_time({ timestamp })); // actual timestamp + set_block_datetime(timestamp, pb.m_block); pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, m_miner_acc); c.handle_incoming_block(t_serializable_object_to_blob(pb.m_block), bvc); diff --git a/tests/core_tests/hard_fork_1.cpp b/tests/core_tests/hard_fork_1.cpp index c4083254..dafe48b8 100644 --- a/tests/core_tests/hard_fork_1.cpp +++ b/tests/core_tests/hard_fork_1.cpp @@ -634,7 +634,7 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector& e { MAKE_NEXT_POS_BLOCK(events, b, prev, miner_acc, std::list{miner_acc}); prev = b; - events.push_back(event_core_time(get_actual_timestamp(b) + 100)); + events.push_back(event_core_time(get_block_datetime(b) + 100)); } diff --git a/tests/core_tests/wallet_tests.cpp b/tests/core_tests/wallet_tests.cpp index 18b03b9e..ab62563f 100644 --- a/tests/core_tests/wallet_tests.cpp +++ b/tests/core_tests/wallet_tests.cpp @@ -1365,7 +1365,7 @@ bool gen_wallet_transfers_and_chain_switch::generate(std::vector