2023-12-16 23:03:02 +00:00
|
|
|
use std::ops::Range;
|
|
|
|
|
2023-09-03 22:50:38 +00:00
|
|
|
use tower::ServiceExt;
|
|
|
|
use tracing::instrument;
|
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
use cuprate_consensus_rules::{HFVotes, HFsInfo, HardFork};
|
2024-07-29 00:13:08 +00:00
|
|
|
use cuprate_types::{
|
|
|
|
blockchain::{BCReadRequest, BCResponse},
|
|
|
|
Chain,
|
|
|
|
};
|
2023-12-16 23:03:02 +00:00
|
|
|
|
2024-06-04 17:19:35 +00:00
|
|
|
use crate::{Database, ExtendedConsensusError};
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// The default amount of hard-fork votes to track to decide on activation of a hard-fork.
|
|
|
|
///
|
|
|
|
/// ref: <https://cuprate.github.io/monero-docs/consensus_rules/hardforks.html#accepting-a-fork>
|
2023-09-03 22:50:38 +00:00
|
|
|
const DEFAULT_WINDOW_SIZE: u64 = 10080; // supermajority window check length - a week
|
2023-10-15 19:35:33 +00:00
|
|
|
|
|
|
|
/// Configuration for hard-forks.
|
|
|
|
///
|
2024-07-29 00:13:08 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
2023-10-15 19:35:33 +00:00
|
|
|
pub struct HardForkConfig {
|
|
|
|
/// The network we are on.
|
2024-01-19 00:34:30 +00:00
|
|
|
pub(crate) info: HFsInfo,
|
2023-10-15 19:35:33 +00:00
|
|
|
/// The amount of votes we are taking into account to decide on a fork activation.
|
2024-01-19 00:34:30 +00:00
|
|
|
pub(crate) window: u64,
|
2023-10-15 19:35:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl HardForkConfig {
|
2024-05-31 00:52:12 +00:00
|
|
|
/// Config for main-net.
|
2023-10-28 23:39:22 +00:00
|
|
|
pub const fn main_net() -> HardForkConfig {
|
2023-10-15 19:35:33 +00:00
|
|
|
Self {
|
2023-12-16 23:03:02 +00:00
|
|
|
info: HFsInfo::main_net(),
|
2023-10-15 19:35:33 +00:00
|
|
|
window: DEFAULT_WINDOW_SIZE,
|
|
|
|
}
|
|
|
|
}
|
2024-01-05 22:36:47 +00:00
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// Config for stage-net.
|
2024-01-05 22:36:47 +00:00
|
|
|
pub const fn stage_net() -> HardForkConfig {
|
|
|
|
Self {
|
|
|
|
info: HFsInfo::stage_net(),
|
|
|
|
window: DEFAULT_WINDOW_SIZE,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// Config for test-net.
|
2024-01-05 22:36:47 +00:00
|
|
|
pub const fn test_net() -> HardForkConfig {
|
|
|
|
Self {
|
|
|
|
info: HFsInfo::test_net(),
|
|
|
|
window: DEFAULT_WINDOW_SIZE,
|
|
|
|
}
|
|
|
|
}
|
2023-10-15 19:35:33 +00:00
|
|
|
}
|
|
|
|
|
2023-09-03 22:50:38 +00:00
|
|
|
/// A struct that keeps track of the current hard-fork and current votes.
|
2024-07-29 00:13:08 +00:00
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
2023-10-03 21:10:31 +00:00
|
|
|
pub struct HardForkState {
|
2024-05-31 00:52:12 +00:00
|
|
|
/// The current active hard-fork.
|
2024-01-19 00:34:30 +00:00
|
|
|
pub(crate) current_hardfork: HardFork,
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// The hard-fork config.
|
2024-01-19 00:34:30 +00:00
|
|
|
pub(crate) config: HardForkConfig,
|
2024-05-31 00:52:12 +00:00
|
|
|
/// The votes in the current window.
|
2024-01-19 00:34:30 +00:00
|
|
|
pub(crate) votes: HFVotes,
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// The last block height accounted for.
|
2024-01-19 00:34:30 +00:00
|
|
|
pub(crate) last_height: u64,
|
2023-09-03 22:50:38 +00:00
|
|
|
}
|
|
|
|
|
2023-10-03 21:10:31 +00:00
|
|
|
impl HardForkState {
|
2024-05-31 00:52:12 +00:00
|
|
|
/// Initialize the [`HardForkState`] from the specified chain height.
|
2023-10-03 21:10:31 +00:00
|
|
|
#[instrument(name = "init_hardfork_state", skip(config, database), level = "info")]
|
|
|
|
pub async fn init_from_chain_height<D: Database + Clone>(
|
2023-09-05 18:49:01 +00:00
|
|
|
chain_height: u64,
|
2023-10-22 16:27:37 +00:00
|
|
|
config: HardForkConfig,
|
2023-09-05 18:49:01 +00:00
|
|
|
mut database: D,
|
2023-12-16 23:03:02 +00:00
|
|
|
) -> Result<Self, ExtendedConsensusError> {
|
2023-10-03 21:10:31 +00:00
|
|
|
tracing::info!("Initializing hard-fork state this may take a while.");
|
|
|
|
|
2023-09-06 14:54:49 +00:00
|
|
|
let block_start = chain_height.saturating_sub(config.window);
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2023-10-23 21:24:02 +00:00
|
|
|
let votes = get_votes_in_range(
|
|
|
|
database.clone(),
|
|
|
|
block_start..chain_height,
|
|
|
|
usize::try_from(config.window).unwrap(),
|
|
|
|
)
|
|
|
|
.await?;
|
2023-09-03 22:50:38 +00:00
|
|
|
|
|
|
|
if chain_height > config.window {
|
2023-09-05 18:13:46 +00:00
|
|
|
debug_assert_eq!(votes.total_votes(), config.window)
|
2023-09-03 22:50:38 +00:00
|
|
|
}
|
|
|
|
|
2024-06-04 17:19:35 +00:00
|
|
|
let BCResponse::BlockExtendedHeader(ext_header) = database
|
2023-10-02 20:07:11 +00:00
|
|
|
.ready()
|
|
|
|
.await?
|
2024-06-04 17:19:35 +00:00
|
|
|
.call(BCReadRequest::BlockExtendedHeader(chain_height - 1))
|
2023-10-02 20:07:11 +00:00
|
|
|
.await?
|
|
|
|
else {
|
|
|
|
panic!("Database sent incorrect response!");
|
|
|
|
};
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2024-06-04 17:19:35 +00:00
|
|
|
let current_hardfork =
|
|
|
|
HardFork::from_version(ext_header.version).expect("Stored block has invalid hardfork");
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2023-10-03 21:10:31 +00:00
|
|
|
let mut hfs = HardForkState {
|
2023-09-03 22:50:38 +00:00
|
|
|
config,
|
|
|
|
current_hardfork,
|
|
|
|
votes,
|
|
|
|
last_height: chain_height - 1,
|
|
|
|
};
|
|
|
|
|
2023-09-05 18:13:46 +00:00
|
|
|
hfs.check_set_new_hf();
|
|
|
|
|
2023-10-03 21:10:31 +00:00
|
|
|
tracing::info!(
|
|
|
|
"Initialized Hfs, current fork: {:?}, {}",
|
|
|
|
hfs.current_hardfork,
|
|
|
|
hfs.votes
|
|
|
|
);
|
2023-09-03 22:50:38 +00:00
|
|
|
|
|
|
|
Ok(hfs)
|
|
|
|
}
|
|
|
|
|
2024-07-29 00:13:08 +00:00
|
|
|
/// Pop some blocks from the top of the cache.
|
|
|
|
///
|
|
|
|
/// The cache will be returned to the state it would have been in `numb_blocks` ago.
|
|
|
|
///
|
|
|
|
/// # Invariant
|
|
|
|
///
|
|
|
|
/// This _must_ only be used on a main-chain cache.
|
|
|
|
pub async fn pop_blocks_main_chain<D: Database + Clone>(
|
|
|
|
&mut self,
|
|
|
|
numb_blocks: u64,
|
|
|
|
database: D,
|
|
|
|
) -> Result<(), ExtendedConsensusError> {
|
|
|
|
let Some(retained_blocks) = self.votes.total_votes().checked_sub(self.config.window) else {
|
|
|
|
*self = Self::init_from_chain_height(
|
|
|
|
self.last_height + 1 - numb_blocks,
|
|
|
|
self.config,
|
|
|
|
database,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
return Ok(());
|
|
|
|
};
|
|
|
|
|
|
|
|
let current_chain_height = self.last_height + 1;
|
|
|
|
|
|
|
|
let oldest_votes = get_votes_in_range(
|
|
|
|
database,
|
|
|
|
current_chain_height
|
|
|
|
.saturating_sub(self.config.window)
|
|
|
|
.saturating_sub(numb_blocks)
|
|
|
|
..current_chain_height
|
|
|
|
.saturating_sub(numb_blocks)
|
|
|
|
.saturating_sub(retained_blocks),
|
|
|
|
usize::try_from(numb_blocks).unwrap(),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
self.votes
|
|
|
|
.reverse_blocks(usize::try_from(numb_blocks).unwrap(), oldest_votes);
|
|
|
|
self.last_height -= numb_blocks;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// Add a new block to the cache.
|
2023-10-31 02:59:31 +00:00
|
|
|
pub fn new_block(&mut self, vote: HardFork, height: u64) {
|
2024-05-31 00:52:12 +00:00
|
|
|
// We don't _need_ to take in `height` but it's for safety, so we don't silently loose track
|
|
|
|
// of blocks.
|
2023-10-02 20:07:11 +00:00
|
|
|
assert_eq!(self.last_height + 1, height);
|
2023-09-03 22:50:38 +00:00
|
|
|
self.last_height += 1;
|
|
|
|
|
2023-09-05 18:13:46 +00:00
|
|
|
tracing::debug!(
|
|
|
|
"Accounting for new blocks vote, height: {}, vote: {:?}",
|
|
|
|
self.last_height,
|
|
|
|
vote
|
|
|
|
);
|
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
// This function remove votes outside the window as well.
|
2023-09-03 22:50:38 +00:00
|
|
|
self.votes.add_vote_for_hf(&vote);
|
|
|
|
|
|
|
|
if height > self.config.window {
|
2023-09-05 18:13:46 +00:00
|
|
|
debug_assert_eq!(self.votes.total_votes(), self.config.window);
|
2023-09-03 22:50:38 +00:00
|
|
|
}
|
|
|
|
|
2023-10-02 20:07:11 +00:00
|
|
|
self.check_set_new_hf();
|
2023-09-03 22:50:38 +00:00
|
|
|
}
|
|
|
|
|
2023-10-02 20:07:11 +00:00
|
|
|
/// Checks if the next hard-fork should be activated and activates it if it should.
|
2023-09-06 18:50:49 +00:00
|
|
|
///
|
|
|
|
/// https://cuprate.github.io/monero-docs/consensus_rules/hardforks.html#accepting-a-fork
|
2023-09-05 18:13:46 +00:00
|
|
|
fn check_set_new_hf(&mut self) {
|
2023-12-17 03:13:30 +00:00
|
|
|
self.current_hardfork = self.votes.current_fork(
|
2023-12-16 23:03:02 +00:00
|
|
|
&self.current_hardfork,
|
|
|
|
self.last_height + 1,
|
|
|
|
self.config.window,
|
|
|
|
&self.config.info,
|
2023-12-17 03:13:30 +00:00
|
|
|
);
|
2023-09-03 22:50:38 +00:00
|
|
|
}
|
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// Returns the current hard-fork.
|
2023-10-22 16:27:37 +00:00
|
|
|
pub fn current_hardfork(&self) -> HardFork {
|
|
|
|
self.current_hardfork
|
|
|
|
}
|
2023-09-03 22:50:38 +00:00
|
|
|
}
|
|
|
|
|
2024-05-31 00:52:12 +00:00
|
|
|
/// Returns the block votes for blocks in the specified range.
|
2023-10-02 20:07:11 +00:00
|
|
|
#[instrument(name = "get_votes", skip(database))]
|
2023-10-03 21:10:31 +00:00
|
|
|
async fn get_votes_in_range<D: Database>(
|
2023-09-05 18:13:46 +00:00
|
|
|
database: D,
|
2023-09-03 22:50:38 +00:00
|
|
|
block_heights: Range<u64>,
|
2023-10-23 21:24:02 +00:00
|
|
|
window_size: usize,
|
2023-12-16 23:03:02 +00:00
|
|
|
) -> Result<HFVotes, ExtendedConsensusError> {
|
2023-10-23 21:24:02 +00:00
|
|
|
let mut votes = HFVotes::new(window_size);
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2024-06-04 17:19:35 +00:00
|
|
|
let BCResponse::BlockExtendedHeaderInRange(vote_list) = database
|
2024-07-29 00:13:08 +00:00
|
|
|
.oneshot(BCReadRequest::BlockExtendedHeaderInRange(
|
|
|
|
block_heights,
|
|
|
|
Chain::Main,
|
|
|
|
))
|
2023-10-02 20:07:11 +00:00
|
|
|
.await?
|
|
|
|
else {
|
|
|
|
panic!("Database sent incorrect response!");
|
|
|
|
};
|
2023-09-03 22:50:38 +00:00
|
|
|
|
2023-10-02 20:07:11 +00:00
|
|
|
for hf_info in vote_list.into_iter() {
|
2024-06-04 17:19:35 +00:00
|
|
|
votes.add_vote_for_hf(&HardFork::from_vote(hf_info.vote));
|
2023-09-03 22:50:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(votes)
|
|
|
|
}
|