diff --git a/common/config-parser/src/types/spec.rs b/common/config-parser/src/types/spec.rs index 17d74900b..ab59b5efd 100644 --- a/common/config-parser/src/types/spec.rs +++ b/common/config-parser/src/types/spec.rs @@ -14,8 +14,11 @@ use serde::Deserialize; use common_crypto::Secp256k1RecoverablePrivateKey; use protocol::{ - codec::{decode_256bits_key, deserialize_address}, - types::{Block, Bytes, Header, Key256Bits, Metadata, RichBlock, SignedTransaction, H160, U256}, + codec::{decode_256bits_key, deserialize_address, ProtocolCodec}, + types::{ + Block, HardforkInfoInner, Header, Key256Bits, Metadata, RichBlock, SignedTransaction, H160, + H256, U256, + }, }; use crate::parse_file; @@ -37,7 +40,7 @@ pub struct ChainSpec { #[derive(Clone, Debug, Deserialize)] pub struct Genesis { pub timestamp: u64, - pub extra_data: Bytes, + pub extra_data: HarforkInput, pub base_fee_per_gas: U256, pub chain_id: u64, @@ -256,10 +259,40 @@ impl Genesis { pub fn build_header(&self) -> Header { Header { timestamp: self.timestamp, - extra_data: self.extra_data.clone(), + // todo: if Hardforkinput is empty, it must change to latest hardfork info to init + // genesis + extra_data: Into::::into(self.extra_data.clone()) + .encode() + .unwrap(), base_fee_per_gas: self.base_fee_per_gas, chain_id: self.chain_id, ..Default::default() } } } + +#[derive(Clone, Debug, Deserialize)] +pub struct HarforkInput { + block_number: u64, + hardforks: Vec, +} + +impl From for HardforkInfoInner { + fn from(value: HarforkInput) -> Self { + let flags = { + let r = value.hardforks.into_iter().fold(0, |acc, s| acc | s as u64); + + H256::from_low_u64_be(r.to_be()) + }; + + HardforkInfoInner { + block_number: value.block_number, + flags, + } + } +} + +#[derive(Clone, Debug, Deserialize, Copy)] +enum HardforkType { + None = 0b0, +} diff --git a/core/consensus/src/adapter.rs b/core/consensus/src/adapter.rs index a7fd1a622..08972afdc 100644 --- a/core/consensus/src/adapter.rs +++ b/core/consensus/src/adapter.rs @@ -374,6 +374,20 @@ where .get_metadata_by_epoch(epoch) } + async fn get_metadata_root( + &self, + state_root: Hash, + proposal: &Proposal, + ) -> ProtocolResult { + let backend = AxonExecutorApplyAdapter::from_root( + state_root, + Arc::clone(&self.trie_db), + Arc::clone(&self.storage), + proposal.clone().into(), + )?; + Ok(backend.get_metadata_root()) + } + #[trace_span(kind = "consensus.adapter")] async fn broadcast_number(&self, ctx: Context, number: u64) -> ProtocolResult<()> { self.network diff --git a/core/consensus/src/engine.rs b/core/consensus/src/engine.rs index 8d432e552..77b602907 100644 --- a/core/consensus/src/engine.rs +++ b/core/consensus/src/engine.rs @@ -16,12 +16,16 @@ use common_apm_derive::trace_span; use common_crypto::BlsPublicKey; use common_logger::{json, log}; use common_merkle::TrieMerkle; +use core_executor::MetadataHandle; use protocol::traits::{ConsensusAdapter, Context, MessageTarget, NodeInfo}; use protocol::types::{ Block, BlockVersion, Bytes, ExecResp, Hash, Hex, Metadata, Proof, Proposal, SignedTransaction, ValidatorExtend, BASE_FEE_PER_GAS, MAX_BLOCK_GAS_LIMIT, RLP_NULL, }; -use protocol::{async_trait, tokio::sync::Mutex as AsyncMutex, ProtocolError, ProtocolResult}; +use protocol::{ + async_trait, codec::ProtocolCodec, tokio::sync::Mutex as AsyncMutex, types::HardforkInfoInner, + ProtocolError, ProtocolResult, +}; use crate::message::{ END_GOSSIP_AGGREGATED_VOTE, END_GOSSIP_SIGNED_CHOKE, END_GOSSIP_SIGNED_PROPOSAL, @@ -78,6 +82,22 @@ impl Engine for ConsensusEngine { + // remove invalid proposal + if v.block_number <= next_number { + hardfork.take(); + Default::default() + } else { + v.encode().unwrap() + } + } + None => Default::default(), + } + }; + let proposal = Proposal { version: BlockVersion::V0, prev_hash: status.prev_hash, @@ -88,7 +108,7 @@ impl Engine for ConsensusEngine Engine for ConsensusEngine ConsensusEngine { // Save the block self.adapter.save_block(ctx.clone(), block.clone()).await?; + let root = self + .adapter + .get_metadata_root( + block.header.state_root, + &Proposal::new_without_state_root(&block.header), + ) + .await?; + let handle = MetadataHandle::new(root); + let hardforks = handle.hardfork_infos()?; + + if let Some(data) = hardforks.inner.last() { + let mut self_proposal = self.node_info.hardfork_proposals.write().unwrap(); + + if self_proposal + .as_ref() + .map(|v| data.flags & v.flags == v.flags) + .unwrap_or_default() + { + // remove self hardfork proposal + self_proposal.take(); + } + } + if self .adapter .is_last_block_in_current_epoch(block_number) diff --git a/core/consensus/src/lib.rs b/core/consensus/src/lib.rs index 25615f53f..af7faf66a 100644 --- a/core/consensus/src/lib.rs +++ b/core/consensus/src/lib.rs @@ -183,6 +183,9 @@ pub enum ConsensusError { #[display(fmt = "Build trie merkle tree error {}", _0)] BuildMerkle(String), + + #[display(fmt = "Proposal hardfork info don't match")] + HardforkDontMatch, } #[derive(Debug, Display)] diff --git a/core/consensus/src/tests/mod.rs b/core/consensus/src/tests/mod.rs index fd3aea22a..c46003bf6 100644 --- a/core/consensus/src/tests/mod.rs +++ b/core/consensus/src/tests/mod.rs @@ -289,6 +289,14 @@ impl CommonConsensusAdapter for MockSyncAdapter { Ok(Metadata::default()) } + async fn get_metadata_root( + &self, + state_root: Hash, + proposal: &Proposal, + ) -> ProtocolResult { + Ok(H256::zero()) + } + async fn broadcast_number(&self, ctx: Context, height: u64) -> ProtocolResult<()> { Ok(()) } diff --git a/core/executor/src/system_contract/metadata/handle.rs b/core/executor/src/system_contract/metadata/handle.rs index 540801047..3765f704a 100644 --- a/core/executor/src/system_contract/metadata/handle.rs +++ b/core/executor/src/system_contract/metadata/handle.rs @@ -1,7 +1,9 @@ use protocol::types::{CkbRelatedInfo, HardforkInfo, Metadata, H160, H256}; use protocol::ProtocolResult; -use crate::system_contract::metadata::MetadataStore; +use std::sync::Arc; + +use crate::system_contract::metadata::{MetadataStore, HARDFORK_INFO}; /// The MetadataHandle is used to expose apis that can be accessed from outside /// of the system contract. @@ -51,4 +53,13 @@ impl MetadataHandle { pub fn hardfork_infos(&self) -> ProtocolResult { MetadataStore::new(self.root)?.hardfork_infos() } + + pub fn init_hardfork(&self, block_number: u64) -> ProtocolResult<()> { + let hardfork = MetadataStore::new(self.root)? + .hardfork_info(block_number) + .unwrap(); + + HARDFORK_INFO.swap(Arc::new(hardfork)); + Ok(()) + } } diff --git a/core/executor/src/system_contract/metadata/mod.rs b/core/executor/src/system_contract/metadata/mod.rs index 2603074fb..300010803 100644 --- a/core/executor/src/system_contract/metadata/mod.rs +++ b/core/executor/src/system_contract/metadata/mod.rs @@ -116,10 +116,6 @@ impl SystemContract fn after_block_hook(&self, adapter: &mut Adapter) { let block_number = adapter.block_number(); - if block_number.is_zero() { - return; - } - let root = CURRENT_METADATA_ROOT.with(|r| *r.borrow()); let mut store = MetadataStore::new(root).unwrap(); @@ -134,6 +130,10 @@ impl SystemContract HARDFORK_INFO.swap(Arc::new(hardfork)); + if block_number.is_zero() { + return; + } + if let Err(e) = store.update_propose_count(block_number.as_u64(), &adapter.origin()) { panic!("Update propose count at {:?} failed: {:?}", block_number, e) } diff --git a/core/run/src/lib.rs b/core/run/src/lib.rs index 8ec4a730c..bf8fb688d 100644 --- a/core/run/src/lib.rs +++ b/core/run/src/lib.rs @@ -197,8 +197,10 @@ async fn start( Proposal::new_without_state_root(¤t_block.header).into(), )? .get_metadata_root(); - let metadata = MetadataHandle::new(metadata_root) - .get_metadata_by_block_number(current_block.header.number)?; + + let metadata_handle = MetadataHandle::new(metadata_root); + metadata_handle.init_hardfork(current_block.header.number)?; + let metadata = metadata_handle.get_metadata_by_block_number(current_block.header.number)?; let validators: Vec = metadata .verifier_list .iter() diff --git a/core/run/src/tests.rs b/core/run/src/tests.rs index e71fca641..3d560d71d 100644 --- a/core/run/src/tests.rs +++ b/core/run/src/tests.rs @@ -39,7 +39,7 @@ const TESTCASES: &[TestCase] = &[ config_file: "config.toml", chain_spec_file: "specs/single_node/chain-spec.toml", key_file: "debug.key", - input_genesis_hash: "0x2043f690fc6e086c6940a083072a82dee16c18a4c4afaf6f4e1c7a585fae2543", + input_genesis_hash: "0x800456ba35af2363e015d780521421ba263767d9089be95816f6f077aa48547a", genesis_state_root: "0x601bd874d41eb9adb32021ee3ab934e0481065c58abfe7e757e33fb01be18dd5", genesis_receipts_root: "0x8544b530238201f1620b139861a6841040b37f78f8bdae8736ef5cec474e979b", }, @@ -48,7 +48,7 @@ const TESTCASES: &[TestCase] = &[ config_file: "nodes/node_1.toml", chain_spec_file: "specs/multi_nodes/chain-spec.toml", key_file: "debug.key", - input_genesis_hash: "0x5e5c47725bb1face59965a326b1d69e1ada1892da2e2f53c4520ed5da3d88d59", + input_genesis_hash: "0x9ef66e89ca4a36f98ea7ddc177dc37aae5b795c27c2e3e1fdf0a52ee1f439dc1", genesis_state_root: "0xc36f75519a047fec6a34c7be5dfca783a40eafa0d7418ad7b3ba99ad9c2dc655", genesis_receipts_root: "0x8544b530238201f1620b139861a6841040b37f78f8bdae8736ef5cec474e979b", }, @@ -57,7 +57,7 @@ const TESTCASES: &[TestCase] = &[ config_file: "nodes/node_1.toml", chain_spec_file: "specs/multi_nodes_short_epoch_len/chain-spec.toml", key_file: "debug.key", - input_genesis_hash: "0x2043f690fc6e086c6940a083072a82dee16c18a4c4afaf6f4e1c7a585fae2543", + input_genesis_hash: "0x800456ba35af2363e015d780521421ba263767d9089be95816f6f077aa48547a", genesis_state_root: "0x42886558baab8a3c310d5a8313398e5f353cc4f8192838b578c857a329e9bb65", genesis_receipts_root: "0x8544b530238201f1620b139861a6841040b37f78f8bdae8736ef5cec474e979b", }, diff --git a/devtools/chain/specs/multi_nodes/chain-spec.toml b/devtools/chain/specs/multi_nodes/chain-spec.toml index ef6d9dcda..3cc8e783f 100644 --- a/devtools/chain/specs/multi_nodes/chain-spec.toml +++ b/devtools/chain/specs/multi_nodes/chain-spec.toml @@ -4,12 +4,15 @@ [genesis] timestamp = 1680249207 -extra_data = [] base_fee_per_gas = "0x539" chain_id = 2022 # A JSON file which includes all transactions in the genesis block. transactions = "genesis_transactions.json" +[genesis.extra_data] +block_number = 0 +hardforks = [] + # # Accounts since the genesis block. # diff --git a/devtools/chain/specs/multi_nodes_short_epoch_len/chain-spec.toml b/devtools/chain/specs/multi_nodes_short_epoch_len/chain-spec.toml index c201fb319..c3fa769e3 100644 --- a/devtools/chain/specs/multi_nodes_short_epoch_len/chain-spec.toml +++ b/devtools/chain/specs/multi_nodes_short_epoch_len/chain-spec.toml @@ -4,12 +4,15 @@ [genesis] timestamp = 1679656015 -extra_data = [] base_fee_per_gas = "0x539" chain_id = 2022 # A JSON file which includes all transactions in the genesis block. transactions = "genesis_transactions.json" +[genesis.extra_data] +block_number = 0 +hardforks = [] + # # Accounts since the genesis block. # diff --git a/devtools/chain/specs/single_node/chain-spec.toml b/devtools/chain/specs/single_node/chain-spec.toml index ceb4aecb6..b97e74584 100644 --- a/devtools/chain/specs/single_node/chain-spec.toml +++ b/devtools/chain/specs/single_node/chain-spec.toml @@ -4,12 +4,15 @@ [genesis] timestamp = 1679656015 -extra_data = [] base_fee_per_gas = "0x539" chain_id = 2022 # A JSON file which includes all transactions in the genesis block. transactions = "genesis_transactions.json" +[genesis.extra_data] +block_number = 0 +hardforks = [] + # # Accounts since the genesis block. # diff --git a/protocol/src/traits/consensus.rs b/protocol/src/traits/consensus.rs index cdf37e9a8..4a385cc72 100644 --- a/protocol/src/traits/consensus.rs +++ b/protocol/src/traits/consensus.rs @@ -1,10 +1,13 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; use common_crypto::Secp256k1PublicKey; use crate::types::{ - Address, Block, BlockNumber, Bytes, ExecResp, Hash, Header, Hex, MerkleRoot, Metadata, - PackedTxHashes, Proof, Proposal, Receipt, SignedTransaction, Validator, U256, + Address, Block, BlockNumber, Bytes, ExecResp, HardforkInfoInner, Hash, Header, Hex, MerkleRoot, + Metadata, PackedTxHashes, Proof, Proposal, Receipt, SignedTransaction, Validator, U256, }; use crate::{async_trait, traits::Context, ProtocolResult}; @@ -16,9 +19,10 @@ pub enum MessageTarget { #[derive(Debug, Clone)] pub struct NodeInfo { - pub chain_id: u64, - pub self_pub_key: Secp256k1PublicKey, - pub self_address: Address, + pub chain_id: u64, + pub self_pub_key: Secp256k1PublicKey, + pub self_address: Address, + pub hardfork_proposals: Arc>>, } impl NodeInfo { @@ -28,6 +32,7 @@ impl NodeInfo { chain_id, self_pub_key: pubkey, self_address: address, + hardfork_proposals: Default::default(), } } } @@ -151,6 +156,12 @@ pub trait CommonConsensusAdapter: Send + Sync { async fn get_metadata_by_epoch(&self, epoch: u64) -> ProtocolResult; + async fn get_metadata_root( + &self, + state_root: Hash, + proposal: &Proposal, + ) -> ProtocolResult; + async fn broadcast_number(&self, ctx: Context, height: u64) -> ProtocolResult<()>; fn set_args(&self, context: Context, state_root: MerkleRoot, gas_limit: u64, max_tx_size: u64);