Skip to content

Commit

Permalink
feat: hardfork proposal feature (#1404)
Browse files Browse the repository at this point in the history
* feat: hardfork proposal feature

* feat: allow users to configure hardfork information in chain spec, the default is the latest version
  • Loading branch information
driftluo authored Sep 11, 2023
1 parent 10b8b15 commit 0dc70a9
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 25 deletions.
41 changes: 37 additions & 4 deletions common/config-parser/src/types/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,

Expand Down Expand Up @@ -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::<HardforkInfoInner>::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<HardforkType>,
}

impl From<HarforkInput> 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,
}
14 changes: 14 additions & 0 deletions core/consensus/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,20 @@ where
.get_metadata_by_epoch(epoch)
}

async fn get_metadata_root(
&self,
state_root: Hash,
proposal: &Proposal,
) -> ProtocolResult<Hash> {
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
Expand Down
61 changes: 59 additions & 2 deletions core/consensus/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -78,6 +82,22 @@ impl<Adapter: ConsensusAdapter + 'static> Engine<Proposal> for ConsensusEngine<A
RLP_NULL
};

let extra_data_hardfork = {
let mut hardfork = self.node_info.hardfork_proposals.write().unwrap();
match &*hardfork {
Some(v) => {
// 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,
Expand All @@ -88,7 +108,7 @@ impl<Adapter: ConsensusAdapter + 'static> Engine<Proposal> for ConsensusEngine<A
timestamp: time_now(),
number: next_number,
gas_limit: MAX_BLOCK_GAS_LIMIT.into(),
extra_data: Default::default(),
extra_data: extra_data_hardfork,
base_fee_per_gas: BASE_FEE_PER_GAS.into(),
proof: status.proof,
chain_id: self.node_info.chain_id,
Expand Down Expand Up @@ -135,6 +155,20 @@ impl<Adapter: ConsensusAdapter + 'static> Engine<Proposal> for ConsensusEngine<A
.into());
}

if let Ok(data) = HardforkInfoInner::decode(&proposal.extra_data) {
if !self
.node_info
.hardfork_proposals
.read()
.unwrap()
.as_ref()
.map(|v| &data == v)
.unwrap_or_default()
{
return Err(ProtocolError::from(ConsensusError::HardforkDontMatch).into());
}
}

let tx_hashes = proposal.tx_hashes.clone();
let tx_hashes_len = tx_hashes.len();
let exemption = {
Expand Down Expand Up @@ -608,6 +642,29 @@ impl<Adapter: ConsensusAdapter + 'static> ConsensusEngine<Adapter> {
// 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)
Expand Down
3 changes: 3 additions & 0 deletions core/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
8 changes: 8 additions & 0 deletions core/consensus/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ impl CommonConsensusAdapter for MockSyncAdapter {
Ok(Metadata::default())
}

async fn get_metadata_root(
&self,
state_root: Hash,
proposal: &Proposal,
) -> ProtocolResult<Hash> {
Ok(H256::zero())
}

async fn broadcast_number(&self, ctx: Context, height: u64) -> ProtocolResult<()> {
Ok(())
}
Expand Down
13 changes: 12 additions & 1 deletion core/executor/src/system_contract/metadata/handle.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -51,4 +53,13 @@ impl MetadataHandle {
pub fn hardfork_infos(&self) -> ProtocolResult<HardforkInfo> {
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(())
}
}
8 changes: 4 additions & 4 deletions core/executor/src/system_contract/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,6 @@ impl<Adapter: ExecutorAdapter + ApplyBackend> SystemContract<Adapter>

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();
Expand All @@ -134,6 +130,10 @@ impl<Adapter: ExecutorAdapter + ApplyBackend> SystemContract<Adapter>

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)
}
Expand Down
6 changes: 4 additions & 2 deletions core/run/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@ async fn start<K: KeyProvider>(
Proposal::new_without_state_root(&current_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<Validator> = metadata
.verifier_list
.iter()
Expand Down
6 changes: 3 additions & 3 deletions core/run/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
Expand All @@ -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",
},
Expand All @@ -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",
},
Expand Down
5 changes: 4 additions & 1 deletion devtools/chain/specs/multi_nodes/chain-spec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand Down
5 changes: 4 additions & 1 deletion devtools/chain/specs/single_node/chain-spec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand Down
Loading

0 comments on commit 0dc70a9

Please sign in to comment.