espresso_types/v0/v0_5/
header.rs

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