difficulty adjectment
This commit is contained in:
parent
72b894d6cb
commit
88b043cf41
|
|
@ -1,7 +1,7 @@
|
|||
use crate::common::skein::{skein_256_hash_data, skein_512_hash_data};
|
||||
use crate::common::types::Transaction;
|
||||
use crate::records::block_height::get_block_height::get_height;
|
||||
use crate::records::memory::averages::{asert_anchor, update_block_data};
|
||||
use crate::records::memory::averages::asert_genesis_anchor;
|
||||
use crate::sled::Db;
|
||||
use crate::to_string;
|
||||
use crate::wallets::structures::Wallet;
|
||||
|
|
@ -11,7 +11,7 @@ use crate::{decode, encode};
|
|||
use crate::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
const TARGET_BLOCK_SECONDS: i128 = 15;
|
||||
const ASERT_HALF_LIFE_SECONDS: i128 = 1_800;
|
||||
const ASERT_HALF_LIFE_SECONDS: i128 = 300;
|
||||
const ASERT_RADIX_BITS: i128 = 16;
|
||||
const ASERT_FIXED_ONE: i128 = 1 << ASERT_RADIX_BITS;
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ impl UnminedBlock {
|
|||
raw_target.clamp(lower_bound, upper_bound)
|
||||
}
|
||||
|
||||
// Adjust difficulty based on ASERT drift from the oldest cached canonical block.
|
||||
// Adjust difficulty based on ASERT drift from the genesis anchor.
|
||||
pub async fn adjust_difficulty(
|
||||
current_timestamp: u32,
|
||||
db: &Db,
|
||||
|
|
@ -181,10 +181,8 @@ impl UnminedBlock {
|
|||
let block_number = get_height(db);
|
||||
let candidate_height = block_number + 1;
|
||||
|
||||
// Refresh cached canonical block data before reading the ASERT anchor.
|
||||
update_block_data(block_number).await;
|
||||
|
||||
let Some((anchor_height, anchor_timestamp, anchor_difficulty)) = asert_anchor().await
|
||||
let Some((anchor_height, anchor_timestamp, anchor_difficulty)) =
|
||||
asert_genesis_anchor().await
|
||||
else {
|
||||
return current_difficulty;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::records::block_height::decrease_block_height::decrease_height;
|
||||
use crate::records::memory::averages::{load_initial_blocks, DIFFICULTY_AVERAGE_WINDOW};
|
||||
use crate::remove_file;
|
||||
use crate::sled::Db;
|
||||
|
||||
|
|
@ -21,11 +20,6 @@ pub async fn undo_block(
|
|||
}
|
||||
|
||||
pub async fn finalize_undo_height(final_height: u32, db: &Db) {
|
||||
// once rollback is complete, lower the recorded chain
|
||||
// height and refresh the rolling averages cache
|
||||
// once rollback is complete, lower the recorded chain height
|
||||
decrease_height(final_height, db);
|
||||
// Difficulty averages are cached from recent blocks, so they must be
|
||||
// rebuilt after removing block files.
|
||||
let start_block = final_height.saturating_sub(DIFFICULTY_AVERAGE_WINDOW.saturating_sub(1));
|
||||
load_initial_blocks(start_block, final_height).await;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,126 +1,14 @@
|
|||
use crate::blocks::block::DIFFICULTY_OFFSET;
|
||||
use crate::common::network_paths_and_settings::block_extension_and_paths;
|
||||
use crate::lazy_static;
|
||||
use crate::HashMap;
|
||||
use crate::Mutex;
|
||||
use crate::PathBuf;
|
||||
use crate::records::unpack_block::unpack_header::load_block_header;
|
||||
|
||||
pub const DIFFICULTY_AVERAGE_WINDOW: u32 = 50;
|
||||
|
||||
lazy_static! {
|
||||
static ref AVERAGE_DATA: Mutex<HashMap<u32, (u32, u64)>> = Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
pub async fn load_initial_blocks(start: u32, stop: u32) {
|
||||
// Rebuild the rolling average cache from disk, keeping only the
|
||||
// most recent rolling difficulty window needed by the algorithm.
|
||||
let mut cache = AVERAGE_DATA.lock().await;
|
||||
*cache = HashMap::new(); // Clear and reset the cache
|
||||
let (
|
||||
_network_name,
|
||||
_padded_base_coin,
|
||||
file_ext,
|
||||
_torrent_path,
|
||||
_wallet_path,
|
||||
block_path,
|
||||
_db_path,
|
||||
_balance_path,
|
||||
_log_path,
|
||||
) = block_extension_and_paths();
|
||||
|
||||
for block_num in start..=stop {
|
||||
let file_path = PathBuf::from(&block_path).join(format!("{block_num}.{file_ext}"));
|
||||
|
||||
if let Ok(file_content) = tokio::fs::read(file_path).await {
|
||||
let timestamp = if file_content.len() >= 4 {
|
||||
u32::from_le_bytes(file_content[0..4].try_into().unwrap_or_default())
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let difficulty = if file_content.len() >= DIFFICULTY_OFFSET + 8 {
|
||||
u64::from_le_bytes(
|
||||
file_content[DIFFICULTY_OFFSET..DIFFICULTY_OFFSET + 8]
|
||||
.try_into()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
cache.insert(block_num, (timestamp, difficulty));
|
||||
|
||||
// Ensure only the configured rolling window is kept if starting from a larger range.
|
||||
if cache.len() > DIFFICULTY_AVERAGE_WINDOW as usize {
|
||||
let oldest_block = cache.keys().min().copied();
|
||||
if let Some(oldest) = oldest_block {
|
||||
cache.remove(&oldest);
|
||||
pub async fn asert_genesis_anchor() -> Option<(u32, u32, u64)> {
|
||||
// ASERT uses genesis as a fixed consensus anchor, so long-term drift
|
||||
// toward the 15-second schedule cannot be forgotten by a rolling cache.
|
||||
match load_block_header(0).await {
|
||||
Ok(header) => Some((
|
||||
0,
|
||||
header.unmined_block.timestamp,
|
||||
header.unmined_block.next_block_difficulty,
|
||||
)),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_block_data(block_num: u32) {
|
||||
let (
|
||||
_network_name,
|
||||
_padded_base_coin,
|
||||
file_ext,
|
||||
_torrent_path,
|
||||
_wallet_path,
|
||||
block_path,
|
||||
_db_path,
|
||||
_balance_path,
|
||||
_log_path,
|
||||
) = block_extension_and_paths();
|
||||
|
||||
// Avoid re-reading blocks that are already present in the rolling cache.
|
||||
let cache = AVERAGE_DATA.lock().await;
|
||||
if cache.contains_key(&block_num) {
|
||||
return;
|
||||
}
|
||||
|
||||
drop(cache);
|
||||
|
||||
let file_path = PathBuf::from(&block_path).join(format!("{block_num}.{file_ext}"));
|
||||
|
||||
if let Ok(file_content) = tokio::fs::read(file_path).await {
|
||||
let timestamp = if file_content.len() >= 4 {
|
||||
u32::from_le_bytes(file_content[0..4].try_into().unwrap_or_default())
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let difficulty = if file_content.len() >= DIFFICULTY_OFFSET + 8 {
|
||||
u64::from_le_bytes(
|
||||
file_content[DIFFICULTY_OFFSET..DIFFICULTY_OFFSET + 8]
|
||||
.try_into()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Reinsert under the cache lock and trim back to the rolling window.
|
||||
let mut cache = AVERAGE_DATA.lock().await;
|
||||
cache.insert(block_num, (timestamp, difficulty));
|
||||
|
||||
if cache.len() > DIFFICULTY_AVERAGE_WINDOW as usize {
|
||||
let oldest_block = cache.keys().min().copied();
|
||||
if let Some(oldest) = oldest_block {
|
||||
cache.remove(&oldest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn asert_anchor() -> Option<(u32, u32, u64)> {
|
||||
// ASERT uses the oldest cached canonical block as its reference point.
|
||||
// The cache is already rebuilt after startup and rollback, and it is
|
||||
// trimmed to the active difficulty window.
|
||||
let cache = AVERAGE_DATA.lock().await;
|
||||
cache
|
||||
.iter()
|
||||
.filter(|(_, (_, difficulty))| *difficulty > 0)
|
||||
.min_by_key(|(height, _)| *height)
|
||||
.map(|(height, (timestamp, difficulty))| (*height, *timestamp, *difficulty))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::miner::flag::{
|
|||
use crate::orphans::snapshot_check::{snapshot_height, update_snapshot};
|
||||
use crate::records::block_height::get_block_height::get_height;
|
||||
use crate::records::block_height::increase_block_height::increase_height;
|
||||
use crate::records::memory::averages::{asert_anchor, update_block_data};
|
||||
use crate::records::memory::averages::asert_genesis_anchor;
|
||||
use crate::records::memory::mempool::{
|
||||
apply_selected_transaction_math, mark_processed_by_signatures,
|
||||
mark_selected_transactions_processed, restore_processed_by_signatures,
|
||||
|
|
@ -211,14 +211,13 @@ async fn log_saved_block_difficulty(
|
|||
current_difficulty: u64,
|
||||
new_difficulty: u64,
|
||||
) {
|
||||
// Skip genesis because there is no prior rolling-average context to
|
||||
// compare against for a difficulty adjustment log line.
|
||||
// Skip genesis because it is the ASERT anchor itself.
|
||||
if block_number == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
update_block_data(block_number - 1).await;
|
||||
let Some((anchor_height, anchor_timestamp, anchor_difficulty)) = asert_anchor().await else {
|
||||
let Some((anchor_height, anchor_timestamp, anchor_difficulty)) = asert_genesis_anchor().await
|
||||
else {
|
||||
info!(
|
||||
"[difficulty] saved_block={block_number} timestamp={timestamp} current_difficulty={current_difficulty} new_difficulty={new_difficulty} asert_anchor=missing"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ use crate::log::error;
|
|||
use crate::miner::flag::{
|
||||
request_mining_stop, set_mining_state, set_node_mode, MiningState, NodeMode,
|
||||
};
|
||||
use crate::records::block_height::get_block_height::get_height;
|
||||
use crate::records::memory::averages::{load_initial_blocks, DIFFICULTY_AVERAGE_WINDOW};
|
||||
use crate::records::memory::connections::initialize_connection;
|
||||
use crate::wallets::structures::Wallet;
|
||||
use crate::Path;
|
||||
|
|
@ -82,8 +80,7 @@ pub async fn obtain_startup_wallet() -> Wallet {
|
|||
}
|
||||
|
||||
pub async fn open_chain_state() -> sled::Db {
|
||||
// Open the sled state database and warm the rolling averages cache
|
||||
// once the process is fully ready to continue startup.
|
||||
// Open the sled state database once the process is fully ready to continue startup.
|
||||
let (
|
||||
_network_name,
|
||||
_padded_base_coin,
|
||||
|
|
@ -100,10 +97,5 @@ pub async fn open_chain_state() -> sled::Db {
|
|||
.open()
|
||||
.expect("Failed to open the database");
|
||||
|
||||
let latest_block = get_height(&db);
|
||||
let start_block = latest_block.saturating_sub(DIFFICULTY_AVERAGE_WINDOW.saturating_sub(1));
|
||||
// Warm the rolling difficulty cache from the newest window before mining starts.
|
||||
load_initial_blocks(start_block, latest_block).await;
|
||||
|
||||
db
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ use crate::wallets::structures::Wallet;
|
|||
use crate::Arc;
|
||||
use crate::Utc;
|
||||
|
||||
const ALLOWED_FUTURE_BLOCK_SECONDS: u32 = 1;
|
||||
|
||||
impl Block {
|
||||
pub async fn verify(
|
||||
&self,
|
||||
|
|
@ -77,8 +79,9 @@ impl Block {
|
|||
// validate hash
|
||||
Self::validate_hash_difficulty(db, hash).await?;
|
||||
|
||||
// verify timestamp is not in the future
|
||||
if timestamp > current_timestamp {
|
||||
// Allow the same one-second clock skew tolerated during handshake,
|
||||
// while the parent timestamp check below still enforces block spacing.
|
||||
if timestamp > current_timestamp.saturating_add(ALLOWED_FUTURE_BLOCK_SECONDS) {
|
||||
return Err("Timestamp in the block is in the future.".to_string());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue