From de534ff5210e583843619065a9aed2a0d3d8ef10 Mon Sep 17 00:00:00 2001 From: nk_ysg Date: Thu, 16 Feb 2023 11:00:08 +0800 Subject: [PATCH] Barnard hard fork test (#3834) * 1.add BARNARD_HARD_FORK logic 2.add version_string ban logic * add release version compare ban logic * fix test * fix fmt --- chain/api/src/errors.rs | 6 ++ network/src/service.rs | 77 +++++++++++++++++++ node/src/node.rs | 1 + storage/src/block/mod.rs | 2 +- storage/src/chain_info/mod.rs | 18 ++++- storage/src/lib.rs | 2 + storage/src/storage.rs | 10 +++ storage/src/upgrade.rs | 52 +++++++++++++ sync/src/block_connector/write_block_chain.rs | 10 +++ sync/src/tasks/block_sync_task.rs | 8 ++ types/src/startup_info.rs | 37 +++++++++ vm/move-package-manager/src/release.rs | 2 +- 12 files changed, 222 insertions(+), 3 deletions(-) diff --git a/chain/api/src/errors.rs b/chain/api/src/errors.rs index d6fe09cb06..777cb19e7c 100644 --- a/chain/api/src/errors.rs +++ b/chain/api/src/errors.rs @@ -61,6 +61,8 @@ pub enum ConnectBlockError { ParentNotExist(Box), #[error("Verify block {0} failed: {1:?}")] VerifyBlockFailed(VerifyBlockField, Error), + #[error("Barnard hard fork block: {:?} ", .0.header())] + BarnardHardFork(Box), } impl ConnectBlockError { @@ -70,12 +72,15 @@ impl ConnectBlockError { ReputationChange::new_fatal("ParentNotExist"); pub const REP_VERIFY_BLOCK_FAILED: ReputationChange = ReputationChange::new_fatal("VerifyBlockFailed"); + pub const REP_BARNARD_HARD_FORK: ReputationChange = + ReputationChange::new_fatal("BarnardHardFork"); pub fn reason(&self) -> &str { match self { ConnectBlockError::FutureBlock(_) => "FutureBlock", ConnectBlockError::ParentNotExist(_) => "ParentNotExist", ConnectBlockError::VerifyBlockFailed(_, _) => "VerifyBlockFailed", + ConnectBlockError::BarnardHardFork(_) => "BarnardHardFork", } } @@ -86,6 +91,7 @@ impl ConnectBlockError { ConnectBlockError::VerifyBlockFailed(_, _) => { ConnectBlockError::REP_VERIFY_BLOCK_FAILED } + ConnectBlockError::BarnardHardFork(_) => ConnectBlockError::REP_BARNARD_HARD_FORK, } } } diff --git a/network/src/service.rs b/network/src/service.rs index 0d61c35549..8512357014 100644 --- a/network/src/service.rs +++ b/network/src/service.rs @@ -32,10 +32,14 @@ use starcoin_types::startup_info::{ChainInfo, ChainStatus}; use starcoin_types::sync_status::SyncStatus; use starcoin_types::system_events::SyncStatusChangeEvent; use std::borrow::Cow; +use std::cmp::Ordering; use std::collections::HashMap; use std::ops::RangeInclusive; use std::sync::Arc; +const BARNARD_HARD_FORK_PEER_VERSION_STRING_PREFIX: &str = "barnard_rollback_block_fix"; +const BARNARD_HARD_FORK_VERSION: [i32; 3] = [1, 12, 9]; + pub struct NetworkActorService { worker: Option, inner: Inner, @@ -114,6 +118,49 @@ impl EventHandler for NetworkActorService { self.inner.update_chain_status(msg.0); } } +// ver_str like starcoin/1.12.6 (build:v1.12.6) (kele01) +fn greater_barnard_fork_version(ver_str: &str) -> bool { + let start = ver_str.find("build:v"); + if start.is_none() { + return false; + } + let end = ver_str.find(") "); + if end.is_none() { + return false; + } + let i = start.unwrap().saturating_add(7); + let j = end.unwrap(); + if i >= j { + return false; + } + let str = &ver_str[i..j]; + + let mut ver = String::from(""); + for c in str.chars() { + if !c.is_numeric() && c != '.' { + break; + } else { + ver.push(c); + } + } + let str_list: Vec<&str> = ver.split('.').collect(); + if str_list.len() != 3 { + return false; + } + let nums: Result, _> = str_list.iter().map(|x| x.parse()).collect(); + if nums.is_err() { + return false; + } + let nums = nums.unwrap(); + for (a, b) in nums.iter().zip(BARNARD_HARD_FORK_VERSION.iter()) { + match a.cmp(b) { + Ordering::Greater => return true, + Ordering::Less => return false, + Ordering::Equal => continue, + } + } + false +} impl EventHandler for NetworkActorService { fn handle_event(&mut self, event: Event, ctx: &mut ServiceContext) { @@ -135,6 +182,21 @@ impl EventHandler for NetworkActorService { "Connected peer {:?}, protocol: {}, notif_protocols: {:?}, rpc_protocols: {:?}", remote, protocol, notif_protocols, rpc_protocols ); + if info.chain_id().is_barnard() { + info!("Connected peer ver_string {:?}", version_string); + if let Some(ref ver_str) = version_string { + if !ver_str.contains(BARNARD_HARD_FORK_PEER_VERSION_STRING_PREFIX) + && !greater_barnard_fork_version(ver_str) + { + info!( + "ban {} peer {:?} ver_str {}", + BARNARD_HARD_FORK_PEER_VERSION_STRING_PREFIX, remote, ver_str + ); + self.inner.network_service.ban_peer(remote, true); + return; + } + } + } let peer_event = PeerEvent::Open(remote.into(), info.clone()); self.inner.on_peer_connected( remote.into(), @@ -802,6 +864,7 @@ where #[cfg(test)] mod test { + use crate::service::greater_barnard_fork_version; use crate::service::select_random_peers; use network_api::PeerId; @@ -835,4 +898,18 @@ mod test { ); assert_eq!(select_random_peers(3..=3, create_peers(3).iter()).len(), 3); } + + #[test] + fn greater_version_test() { + let v1 = String::from("starcoin/1.12.6 (build:v1.12.6) (kele01)"); + assert!(!greater_barnard_fork_version(&v1)); + let v2 = String::from("starcoin 1.13.0-alpha (build:halley-v1.13.1-alpha-dirty)"); + assert!(!greater_barnard_fork_version(&v2)); + let v3 = String::from("starcoin/1.13.0-alpha (build:v1.13.0-alpha) (kele01)"); + assert!(greater_barnard_fork_version(&v3)); + let v4 = String::from("starcoin/1.12.9 (build:v1.12.9) (kele01)"); + assert!(!greater_barnard_fork_version(&v4)); + let v5 = String::from("starcoin/1.13.1 (build:v1.13.1) (kele01)"); + assert!(greater_barnard_fork_version(&v5)); + } } diff --git a/node/src/node.rs b/node/src/node.rs index 4bffdc3f5e..6c3d8f0e90 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -305,6 +305,7 @@ impl NodeService { let start_time = SystemTime::now(); storage_instance.check_upgrade()?; + storage_instance.barnard_hard_fork(config.clone())?; let upgrade_time = SystemTime::now().duration_since(start_time)?; let storage = Arc::new(Storage::new(storage_instance)?); registry.put_shared(storage.clone()).await?; diff --git a/storage/src/block/mod.rs b/storage/src/block/mod.rs index 2d27e0089d..d4c1fa1cee 100644 --- a/storage/src/block/mod.rs +++ b/storage/src/block/mod.rs @@ -110,7 +110,7 @@ define_storage!( #[derive(Clone)] pub struct BlockStorage { block_store: BlockInnerStorage, - header_store: BlockHeaderStorage, + pub(crate) header_store: BlockHeaderStorage, body_store: BlockBodyStorage, block_txns_store: BlockTransactionsStorage, block_txn_infos_store: BlockTransactionInfosStorage, diff --git a/storage/src/chain_info/mod.rs b/storage/src/chain_info/mod.rs index 611572b3ae..3f193be3f0 100644 --- a/storage/src/chain_info/mod.rs +++ b/storage/src/chain_info/mod.rs @@ -5,7 +5,7 @@ use crate::storage::{ColumnFamily, InnerStorage, KVStore}; use crate::{StorageVersion, CHAIN_INFO_PREFIX_NAME}; use anyhow::Result; use starcoin_crypto::HashValue; -use starcoin_types::startup_info::{SnapshotRange, StartupInfo}; +use starcoin_types::startup_info::{BarnardHardFork, SnapshotRange, StartupInfo}; use std::convert::{TryFrom, TryInto}; #[derive(Clone)] @@ -27,6 +27,7 @@ impl ChainInfoStorage { const GENESIS_KEY: &'static str = "genesis"; const STORAGE_VERSION_KEY: &'static str = "storage_version"; const SNAPSHOT_RANGE_KEY: &'static str = "snapshot_height"; + const BARNARD_HARD_FORK: &'static str = "barnard_hard_fork"; pub fn get_startup_info(&self) -> Result> { self.get(Self::STARTUP_INFO_KEY.as_bytes()) @@ -95,4 +96,19 @@ impl ChainInfoStorage { snapshot_range.try_into()?, ) } + + pub fn get_barnard_hard_fork(&self) -> Result> { + self.get(Self::BARNARD_HARD_FORK.as_bytes()) + .and_then(|bytes| match bytes { + Some(bytes) => Ok(Some(bytes.try_into()?)), + None => Ok(None), + }) + } + + pub fn save_barnard_hard_fork(&self, barnard_hard_fork: BarnardHardFork) -> Result<()> { + self.put_sync( + Self::BARNARD_HARD_FORK.as_bytes().to_vec(), + barnard_hard_fork.try_into()?, + ) + } } diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 92f4bc23e8..e61d882e6d 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -32,6 +32,8 @@ use starcoin_types::{ use std::collections::BTreeMap; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; +pub use upgrade::BARNARD_HARD_FORK_HASH; +pub use upgrade::BARNARD_HARD_FORK_HEIGHT; pub mod accumulator; pub mod batch; diff --git a/storage/src/storage.rs b/storage/src/storage.rs index 9f65f0b474..cddd7269b1 100644 --- a/storage/src/storage.rs +++ b/storage/src/storage.rs @@ -7,7 +7,9 @@ use crate::db_storage::{DBStorage, SchemaIterator}; use crate::upgrade::DBUpgrade; use anyhow::{bail, format_err, Result}; use byteorder::{BigEndian, ReadBytesExt}; +use starcoin_config::NodeConfig; use starcoin_crypto::HashValue; +use starcoin_logger::prelude::info; use starcoin_vm_types::state_store::table::TableHandle; use std::convert::TryInto; use std::fmt::Debug; @@ -108,6 +110,14 @@ impl StorageInstance { pub fn check_upgrade(&mut self) -> Result<()> { DBUpgrade::check_upgrade(self) } + + pub fn barnard_hard_fork(&mut self, config: Arc) -> Result<()> { + if config.net().id().chain_id().is_barnard() { + info!("barnard_hard_fork in"); + return DBUpgrade::barnard_hard_fork(self); + } + Ok(()) + } } impl InnerStore for StorageInstance { diff --git a/storage/src/upgrade.rs b/storage/src/upgrade.rs index 4e4db06c4a..87b259026c 100644 --- a/storage/src/upgrade.rs +++ b/storage/src/upgrade.rs @@ -12,12 +12,24 @@ use crate::{ BLOCK_BODY_PREFIX_NAME, TRANSACTION_INFO_PREFIX_NAME, }; use anyhow::{bail, ensure, format_err, Result}; +use once_cell::sync::Lazy; +use starcoin_crypto::HashValue; use starcoin_logger::prelude::{debug, info, warn}; +use starcoin_types::block::BlockNumber; +use starcoin_types::startup_info::{BarnardHardFork, StartupInfo}; use starcoin_types::transaction::Transaction; use std::cmp::Ordering; pub struct DBUpgrade; +pub static BARNARD_HARD_FORK_HEIGHT: BlockNumber = 9716880; +pub static BARNARD_HARD_FORK_HASH: Lazy = Lazy::new(|| { + HashValue::from_hex_literal( + "0x98f32397569e26540985f0d487c5e7cc229a8c9be9afe10f973b3d95204d06d7", + ) + .expect("") +}); + impl DBUpgrade { pub fn check_upgrade(instance: &mut StorageInstance) -> Result<()> { let version_in_db = { @@ -181,4 +193,44 @@ impl DBUpgrade { } Ok(()) } + + pub fn barnard_hard_fork(instance: &mut StorageInstance) -> Result<()> { + let block_storage = BlockStorage::new(instance.clone()); + let chain_info_storage = ChainInfoStorage::new(instance.clone()); + let barnard_hard_fork = chain_info_storage.get_barnard_hard_fork()?; + + let barnard_info = BarnardHardFork::new(BARNARD_HARD_FORK_HEIGHT, *BARNARD_HARD_FORK_HASH); + if barnard_hard_fork == Some(barnard_info.clone()) { + info!("barnard had forked"); + return Ok(()); + } + + let block = block_storage.get_block_by_hash(*BARNARD_HARD_FORK_HASH)?; + if let Some(block) = block { + if block.header().number() == BARNARD_HARD_FORK_HEIGHT { + info!("barnard hard fork rollback height"); + let mut processed_count = 0; + let block_info_storage = BlockInfoStorage::new(instance.clone()); + let mut iter = block_storage.header_store.iter()?; + iter.seek_to_first(); + for item in iter { + let (id, block_header) = item?; + if block_header.number() >= BARNARD_HARD_FORK_HEIGHT { + block_info_storage.remove(id)?; + processed_count += 1; + if processed_count % 10000 == 0 { + info!( + "barnard hard fork rollback height processed items: {}", + processed_count + ); + } + } + } + let main_hash = block.header().parent_hash(); + chain_info_storage.save_barnard_hard_fork(barnard_info)?; + chain_info_storage.save_startup_info(StartupInfo::new(main_hash))?; + } + } + Ok(()) + } } diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index 13af902588..c22ff42408 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -400,6 +400,12 @@ where fn connect_inner(&mut self, block: Block) -> Result { let block_id = block.id(); + if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH + && block.header().number() == starcoin_storage::BARNARD_HARD_FORK_HEIGHT + { + debug!("barnard hard fork {}", block_id); + return Err(ConnectBlockError::BarnardHardFork(Box::new(block)).into()); + } if self.main.current_header().id() == block_id { debug!("Repeat connect, current header is {} already.", block_id); return Ok(ConnectOk::Duplicate); @@ -430,6 +436,10 @@ where block: block.clone(), block_info, })?; + info!( + "Block {} main has been processed, trigger head selection", + block_id + ); self.do_new_head(executed_block, 1, vec![block], 0, vec![])?; Ok(ConnectOk::Connect) } diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 933f302740..57f6703a9d 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -13,6 +13,7 @@ use starcoin_chain::{verifier::BasicVerifier, BlockChain}; use starcoin_chain_api::{ChainReader, ChainWriter, ConnectBlockError, ExecutedBlock}; use starcoin_config::G_CRATE_VERSION; use starcoin_logger::prelude::*; +use starcoin_storage::BARNARD_HARD_FORK_HASH; use starcoin_sync_api::SyncTarget; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; use std::collections::HashMap; @@ -236,6 +237,13 @@ where return Err(format_err!("collect previous failed block:{}", block.id())); } } + if block.id() == *BARNARD_HARD_FORK_HASH { + if let Some(peer) = peer_id { + warn!("[barnard hard fork] ban peer {}", peer); + self.peer_provider.ban_peer(peer, true); + } + return Err(format_err!("reject barnard hard fork block:{}", block.id())); + } let apply_result = if self.skip_pow_verify { self.chain .apply_with_verifier::(block.clone()) diff --git a/types/src/startup_info.rs b/types/src/startup_info.rs index 9068b14454..73a7bdb190 100644 --- a/types/src/startup_info.rs +++ b/types/src/startup_info.rs @@ -234,3 +234,40 @@ impl TryInto> for SnapshotRange { self.encode() } } + +#[derive(Eq, PartialEq, Hash, Deserialize, Serialize, Clone, Debug)] +pub struct BarnardHardFork { + // [number, ...) block will remove + number: BlockNumber, + hash: HashValue, +} + +impl BarnardHardFork { + pub fn new(number: BlockNumber, hash: HashValue) -> Self { + Self { number, hash } + } + + pub fn get_number(&self) -> BlockNumber { + self.number + } + + pub fn get_hash(&self) -> HashValue { + self.hash + } +} + +impl TryFrom> for BarnardHardFork { + type Error = anyhow::Error; + + fn try_from(value: Vec) -> Result { + BarnardHardFork::decode(value.as_slice()) + } +} + +impl TryInto> for BarnardHardFork { + type Error = anyhow::Error; + + fn try_into(self) -> Result> { + self.encode() + } +} diff --git a/vm/move-package-manager/src/release.rs b/vm/move-package-manager/src/release.rs index a43f89df04..b29f9fea40 100644 --- a/vm/move-package-manager/src/release.rs +++ b/vm/move-package-manager/src/release.rs @@ -24,7 +24,7 @@ pub const DEFAULT_RELEASE_DIR: &str = "release"; pub struct Release { #[clap(name = "move-version", long = "move-version", default_value="4", possible_values=&["4", "6"])] /// specify the move lang version for the release. - /// currently, only v3, v4 are supported. + /// currently, only v4, v6 are supported. language_version: u8, #[clap(name="release-dir", long, parse(from_os_str), default_value=DEFAULT_RELEASE_DIR)]