Skip to main content

espresso_types/v0/v0_5/
header.rs

1use ark_serialize::CanonicalSerialize;
2use committable::{Commitment, Committable, RawCommitmentBuilder};
3use hotshot_types::{data::VidCommitment, utils::BuilderCommitment};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6use super::{
7    BlockMerkleCommitment, BuilderSignature, FeeInfo, FeeMerkleCommitment, L1BlockInfo,
8    ResolvableChainConfig,
9};
10use crate::{
11    NsTable, TimestampMillis,
12    v0::impls::StakeTableHash,
13    v0_3::RewardAmount,
14    v0_4::RewardMerkleCommitmentV2,
15    v0_5::{LeaderCounts, MAX_VALIDATORS},
16};
17
18mod leader_counts_serde {
19    use super::*;
20
21    pub fn serialize<S>(counts: &LeaderCounts, serializer: S) -> Result<S::Ok, S::Error>
22    where
23        S: Serializer,
24    {
25        counts.as_slice().serialize(serializer)
26    }
27
28    pub fn deserialize<'de, D>(deserializer: D) -> Result<LeaderCounts, D::Error>
29    where
30        D: Deserializer<'de>,
31    {
32        let vec: Vec<u16> = Vec::deserialize(deserializer)?;
33        if vec.len() != MAX_VALIDATORS {
34            return Err(serde::de::Error::custom(format!(
35                "expected {} elements, got {}",
36                MAX_VALIDATORS,
37                vec.len()
38            )));
39        }
40        let mut arr = [0u16; MAX_VALIDATORS];
41        arr.copy_from_slice(&vec);
42        Ok(arr)
43    }
44}
45
46/// Header V5 with leader_counts for per-epoch reward distribution.
47///
48/// This version introduces epoch-based reward distribution where:
49/// - `leader_counts` tracks how many blocks each validator has led during the current epoch
50/// - Rewards are computed at epoch end but applied in the next epoch
51/// - The reward merkle tree only changes when pending rewards are applied, not at epoch boundary
52#[derive(Clone, Debug, Deserialize, Serialize, Hash, PartialEq, Eq)]
53pub struct Header {
54    /// A commitment to a ChainConfig or a full ChainConfig.
55    pub(crate) chain_config: ResolvableChainConfig,
56    pub(crate) height: u64,
57    pub(crate) timestamp: u64,
58    pub(crate) timestamp_millis: TimestampMillis,
59    pub(crate) l1_head: u64,
60    pub(crate) l1_finalized: Option<L1BlockInfo>,
61    pub(crate) payload_commitment: VidCommitment,
62    pub(crate) builder_commitment: BuilderCommitment,
63    pub(crate) ns_table: NsTable,
64    pub(crate) block_merkle_tree_root: BlockMerkleCommitment,
65    pub(crate) fee_merkle_tree_root: FeeMerkleCommitment,
66    pub(crate) fee_info: FeeInfo,
67    pub(crate) builder_signature: Option<BuilderSignature>,
68    pub(crate) reward_merkle_tree_root: RewardMerkleCommitmentV2,
69    pub(crate) total_reward_distributed: RewardAmount,
70    pub(crate) next_stake_table_hash: Option<StakeTableHash>,
71    /// leader counts for the current epoch, indexed by validator position
72    /// in the stake table. Fixed to [`MAX_VALIDATORS`] as the active validator
73    /// set is capped at 100 validators.
74    #[serde(with = "leader_counts_serde")]
75    pub(crate) leader_counts: LeaderCounts,
76}
77
78impl Committable for Header {
79    fn commit(&self) -> Commitment<Self> {
80        let mut bmt_bytes = vec![];
81        self.block_merkle_tree_root
82            .serialize_with_mode(&mut bmt_bytes, ark_serialize::Compress::Yes)
83            .unwrap();
84        let mut fmt_bytes = vec![];
85        self.fee_merkle_tree_root
86            .serialize_with_mode(&mut fmt_bytes, ark_serialize::Compress::Yes)
87            .unwrap();
88
89        let mut rwd_bytes = vec![];
90        self.reward_merkle_tree_root
91            .serialize_with_mode(&mut rwd_bytes, ark_serialize::Compress::Yes)
92            .unwrap();
93
94        let leader_counts_bytes: Vec<u8> = self
95            .leader_counts
96            .iter()
97            .flat_map(|&count| count.to_le_bytes())
98            .collect();
99
100        let mut cb = RawCommitmentBuilder::new(&Self::tag())
101            .field("chain_config", self.chain_config.commit())
102            .u64_field("height", self.height)
103            .u64_field("timestamp", self.timestamp)
104            .u64_field("timestamp_millis", self.timestamp_millis.u64())
105            .u64_field("l1_head", self.l1_head)
106            .optional("l1_finalized", &self.l1_finalized)
107            .constant_str("payload_commitment")
108            .fixed_size_bytes(self.payload_commitment.as_ref())
109            .constant_str("builder_commitment")
110            .fixed_size_bytes(self.builder_commitment.as_ref())
111            .field("ns_table", self.ns_table.commit())
112            .var_size_field("block_merkle_tree_root", &bmt_bytes)
113            .var_size_field("fee_merkle_tree_root", &fmt_bytes)
114            .field("fee_info", self.fee_info.commit())
115            .var_size_field("reward_merkle_tree_root", &rwd_bytes)
116            .var_size_field(
117                "total_reward_distributed",
118                &self.total_reward_distributed.to_fixed_bytes(),
119            )
120            .var_size_field("leader_counts", &leader_counts_bytes);
121
122        if let Some(next_stake_table_hash) = self.next_stake_table_hash {
123            cb = cb.field("next_stake_table_hash", next_stake_table_hash);
124        }
125
126        cb.finalize()
127    }
128
129    fn tag() -> String {
130        crate::v0_1::Header::tag()
131    }
132}