espresso_types/v0/impls/
header.rs

1use std::{collections::HashSet, fmt};
2
3use alloy::primitives::{B256, Keccak256};
4use anyhow::{Context, bail, ensure};
5use ark_serialize::CanonicalSerialize;
6use base64::{Engine, prelude::BASE64_STANDARD};
7use committable::{Commitment, Committable, RawCommitmentBuilder};
8use either::Either;
9use hotshot_query_service::{
10    availability::QueryableHeader, explorer::ExplorerHeader, types::HeightIndexed,
11};
12use hotshot_types::{
13    data::{EpochNumber, VidCommitment, ViewNumber, vid_commitment},
14    light_client::LightClientState,
15    traits::{
16        BlockPayload, EncodeBytes, ValidatedState as _,
17        block_contents::{BlockHeader, BuilderFee, GENESIS_VID_NUM_STORAGE_NODES},
18        election::Membership,
19        node_implementation::NodeType,
20        signature_key::BuilderSignatureKey,
21    },
22    utils::{BuilderCommitment, epoch_from_block_number, is_ge_epoch_root, is_last_block},
23};
24use jf_merkle_tree_compat::{AppendableMerkleTreeScheme, MerkleCommitment, MerkleTreeScheme};
25use serde::{
26    Deserialize, Deserializer, Serialize, Serializer,
27    de::{self, MapAccess, SeqAccess, Visitor},
28};
29use serde_json::{Map, Value};
30use thiserror::Error;
31use time::OffsetDateTime;
32use vbs::version::Version;
33use versions::{DRB_AND_HEADER_UPGRADE_VERSION, EPOCH_REWARD_VERSION, EPOCH_VERSION};
34
35use super::{
36    instance_state::NodeState, state::ValidatedState, v0_1::IterableFeeInfo, v0_3::ChainConfig,
37};
38use crate::{
39    BlockMerkleCommitment, FeeAccount, FeeAmount, FeeInfo, FeeMerkleCommitment, Header,
40    L1BlockInfo, L1Snapshot, Leaf2, NamespaceId, NsIndex, NsTable, PayloadByteLen, SeqTypes,
41    TimestampMillis, UpgradeType,
42    eth_signature_key::BuilderSignature,
43    v0::{
44        header::{EitherOrVersion, VersionedHeader},
45        impls::{StakeTableHash, distribute_block_reward},
46    },
47    v0_1::{self},
48    v0_2,
49    v0_3::{
50        self, REWARD_MERKLE_TREE_V1_HEIGHT, RewardAmount, RewardMerkleCommitmentV1,
51        RewardMerkleTreeV1,
52    },
53    v0_4::{self, RewardAccountV2, RewardMerkleCommitmentV2},
54    v0_5::{self, LeaderCounts, MAX_VALIDATORS},
55    v0_6::{self, REWARD_MERKLE_TREE_V2_HEIGHT, RewardMerkleTreeV2},
56};
57
58impl v0_1::Header {
59    pub(crate) fn commit(&self) -> Commitment<Header> {
60        let mut bmt_bytes = vec![];
61        self.block_merkle_tree_root
62            .serialize_with_mode(&mut bmt_bytes, ark_serialize::Compress::Yes)
63            .unwrap();
64        let mut fmt_bytes = vec![];
65        self.fee_merkle_tree_root
66            .serialize_with_mode(&mut fmt_bytes, ark_serialize::Compress::Yes)
67            .unwrap();
68
69        RawCommitmentBuilder::new(&Self::tag())
70            .field("chain_config", self.chain_config.commit())
71            .u64_field("height", self.height)
72            .u64_field("timestamp", self.timestamp)
73            .u64_field("l1_head", self.l1_head)
74            .optional("l1_finalized", &self.l1_finalized)
75            .constant_str("payload_commitment")
76            .fixed_size_bytes(self.payload_commitment.as_ref())
77            .constant_str("builder_commitment")
78            .fixed_size_bytes(self.builder_commitment.as_ref())
79            .field("ns_table", self.ns_table.commit())
80            .var_size_field("block_merkle_tree_root", &bmt_bytes)
81            .var_size_field("fee_merkle_tree_root", &fmt_bytes)
82            .field("fee_info", self.fee_info.commit())
83            .finalize()
84    }
85}
86
87impl Committable for Header {
88    fn commit(&self) -> Commitment<Self> {
89        match self {
90            Self::V1(header) => header.commit(),
91            Self::V2(fields) => RawCommitmentBuilder::new(&Self::tag())
92                .u64_field("version_major", 0)
93                .u64_field("version_minor", 2)
94                .field("fields", fields.commit())
95                .finalize(),
96            Self::V3(fields) => RawCommitmentBuilder::new(&Self::tag())
97                .u64_field("version_major", 0)
98                .u64_field("version_minor", 3)
99                .field("fields", fields.commit())
100                .finalize(),
101            Self::V4(fields) => RawCommitmentBuilder::new(&Self::tag())
102                .u64_field("version_major", 0)
103                .u64_field("version_minor", 4)
104                .field("fields", fields.commit())
105                .finalize(),
106            Self::V5(fields) => RawCommitmentBuilder::new(&Self::tag())
107                .u64_field("version_major", 0)
108                .u64_field("version_minor", 5)
109                .field("fields", fields.commit())
110                .finalize(),
111            Self::V6(fields) => RawCommitmentBuilder::new(&Self::tag())
112                .u64_field("version_major", 0)
113                .u64_field("version_minor", 6)
114                .field("fields", fields.commit())
115                .finalize(),
116        }
117    }
118
119    fn tag() -> String {
120        // We use the tag "BLOCK" since blocks are identified by the hash of their header. This will
121        // thus be more intuitive to users than "HEADER".
122        "BLOCK".into()
123    }
124}
125
126impl Serialize for Header {
127    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
128    where
129        S: Serializer,
130    {
131        match self {
132            Self::V1(header) => header.serialize(serializer),
133            Self::V2(fields) => VersionedHeader {
134                version: EitherOrVersion::Version(Version { major: 0, minor: 2 }),
135                fields: fields.clone(),
136            }
137            .serialize(serializer),
138            Self::V3(fields) => VersionedHeader {
139                version: EitherOrVersion::Version(Version { major: 0, minor: 3 }),
140                fields: fields.clone(),
141            }
142            .serialize(serializer),
143            Self::V4(fields) => VersionedHeader {
144                version: EitherOrVersion::Version(Version { major: 0, minor: 4 }),
145                fields: fields.clone(),
146            }
147            .serialize(serializer),
148            Self::V5(fields) => VersionedHeader {
149                version: EitherOrVersion::Version(Version { major: 0, minor: 5 }),
150                fields: fields.clone(),
151            }
152            .serialize(serializer),
153            Self::V6(fields) => VersionedHeader {
154                version: EitherOrVersion::Version(Version { major: 0, minor: 6 }),
155                fields: fields.clone(),
156            }
157            .serialize(serializer),
158        }
159    }
160}
161
162impl<'de> Deserialize<'de> for Header {
163    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
164    where
165        D: Deserializer<'de>,
166    {
167        struct HeaderVisitor;
168
169        impl<'de> Visitor<'de> for HeaderVisitor {
170            type Value = Header;
171
172            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
173                formatter.write_str("Header")
174            }
175
176            fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
177            where
178                V: SeqAccess<'de>,
179            {
180                let chain_config_or_version: EitherOrVersion = seq
181                    .next_element()?
182                    .ok_or_else(|| de::Error::missing_field("chain_config"))?;
183
184                match chain_config_or_version {
185                    // For v0.1, the first field in the sequence of fields is the first field of the struct, so we call a function to get the rest of
186                    // the fields from the sequence and pack them into the struct.
187                    EitherOrVersion::Left(cfg) => Ok(Header::V1(
188                        v0_1::Header::deserialize_with_chain_config(cfg.into(), seq)?,
189                    )),
190                    EitherOrVersion::Right(commit) => Ok(Header::V1(
191                        v0_1::Header::deserialize_with_chain_config(commit.into(), seq)?,
192                    )),
193                    // For all versions > 0.1, the first "field" is not actually part of the `Header` struct.
194                    // We just delegate directly to the derived deserialization impl for the appropriate version.
195                    EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
196                        seq.next_element()?
197                            .ok_or_else(|| de::Error::missing_field("fields"))?,
198                    )),
199                    EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
200                        seq.next_element()?
201                            .ok_or_else(|| de::Error::missing_field("fields"))?,
202                    )),
203                    EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
204                        seq.next_element()?
205                            .ok_or_else(|| de::Error::missing_field("fields"))?,
206                    )),
207                    EitherOrVersion::Version(Version { major: 0, minor: 5 }) => Ok(Header::V5(
208                        seq.next_element()?
209                            .ok_or_else(|| de::Error::missing_field("fields"))?,
210                    )),
211                    EitherOrVersion::Version(Version { major: 0, minor: 6 }) => Ok(Header::V6(
212                        seq.next_element()?
213                            .ok_or_else(|| de::Error::missing_field("fields"))?,
214                    )),
215                    EitherOrVersion::Version(v) => {
216                        Err(serde::de::Error::custom(format!("invalid version {v:?}")))
217                    },
218                }
219            }
220
221            fn visit_map<V>(self, mut map: V) -> Result<Header, V::Error>
222            where
223                V: MapAccess<'de>,
224            {
225                // insert all the fields in the serde_map as the map may have out of order fields.
226                let mut serde_map: Map<String, Value> = Map::new();
227
228                while let Some(key) = map.next_key::<String>()? {
229                    serde_map.insert(key.trim().to_owned(), map.next_value()?);
230                }
231
232                if let Some(v) = serde_map.get("version") {
233                    let fields = serde_map
234                        .get("fields")
235                        .ok_or_else(|| de::Error::missing_field("fields"))?;
236
237                    let version = serde_json::from_value::<EitherOrVersion>(v.clone())
238                        .map_err(de::Error::custom)?;
239                    let result = match version {
240                        EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
241                            serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
242                        )),
243                        EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
244                            serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
245                        )),
246                        EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
247                            serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
248                        )),
249                        EitherOrVersion::Version(Version { major: 0, minor: 5 }) => Ok(Header::V5(
250                            serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
251                        )),
252                        EitherOrVersion::Version(Version { major: 0, minor: 6 }) => Ok(Header::V6(
253                            serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
254                        )),
255                        EitherOrVersion::Version(v) => {
256                            Err(de::Error::custom(format!("invalid version {v:?}")))
257                        },
258                        chain_config => Err(de::Error::custom(format!(
259                            "expected version, found chain_config {chain_config:?}"
260                        ))),
261                    };
262                    return result;
263                }
264
265                Ok(Header::V1(
266                    serde_json::from_value(serde_map.into()).map_err(de::Error::custom)?,
267                ))
268            }
269        }
270
271        // List of all possible fields of all versions of the `Header`.
272        // serde's `deserialize_struct` works by deserializing to a struct with a specific list of fields.
273        // The length of the fields list we provide is always going to be greater than the length of the target struct.
274        // In our case, we are deserializing to either a V1 Header or a VersionedHeader for versions > 0.1.
275        // We use serde_json and bincode serialization in the sequencer.
276        // Fortunately, serde_json ignores fields parameter and only cares about our Visitor implementation.
277        // -  https://docs.rs/serde_json/1.0.120/serde_json/struct.Deserializer.html#method.deserialize_struct
278        // Bincode uses the length of the fields list, but the bincode deserialization only cares that the length of the fields
279        // is an upper bound of the target struct's fields length.
280        // -  https://docs.rs/bincode/1.3.3/src/bincode/de/mod.rs.html#313
281        // This works because the bincode deserializer only consumes the next field when `next_element` is called,
282        // and our visitor calls it the correct number of times.
283        // This would, however, break if the bincode deserializer implementation required an exact match of the field's length,
284        // consuming one element for each field.
285        let fields: &[&str] = &[
286            "fields",
287            "chain_config",
288            "version",
289            "height",
290            "timestamp",
291            "l1_head",
292            "l1_finalized",
293            "payload_commitment",
294            "builder_commitment",
295            "ns_table",
296            "block_merkle_tree_root",
297            "fee_merkle_tree_root",
298            "fee_info",
299            "builder_signature",
300        ];
301
302        deserializer.deserialize_struct("Header", fields, HeaderVisitor)
303    }
304}
305
306impl Header {
307    pub fn version(&self) -> Version {
308        match self {
309            Self::V1(_) => Version { major: 0, minor: 1 },
310            Self::V2(_) => Version { major: 0, minor: 2 },
311            Self::V3(_) => Version { major: 0, minor: 3 },
312            Self::V4(_) => Version { major: 0, minor: 4 },
313            Self::V5(_) => Version { major: 0, minor: 5 },
314            Self::V6(_) => Version { major: 0, minor: 6 },
315        }
316    }
317    #[allow(clippy::too_many_arguments)]
318    pub(crate) fn create(
319        chain_config: ChainConfig,
320        height: u64,
321        timestamp: u64,
322        timestamp_millis: u64,
323        l1_head: u64,
324        l1_finalized: Option<L1BlockInfo>,
325        payload_commitment: VidCommitment,
326        builder_commitment: BuilderCommitment,
327        ns_table: NsTable,
328        fee_merkle_tree_root: FeeMerkleCommitment,
329        block_merkle_tree_root: BlockMerkleCommitment,
330        reward_merkle_tree_root_v1: RewardMerkleCommitmentV1,
331        reward_merkle_tree_root_v2: RewardMerkleCommitmentV2,
332        fee_info: Vec<FeeInfo>,
333        builder_signature: Vec<BuilderSignature>,
334        total_reward_distributed: Option<RewardAmount>,
335        version: Version,
336        next_stake_table_hash: Option<StakeTableHash>,
337        leader_counts: Option<LeaderCounts>,
338    ) -> Self {
339        // Ensure FeeInfo contains at least 1 element
340        assert!(!fee_info.is_empty(), "Invalid fee_info length: 0");
341
342        match (version.major, version.minor) {
343            (0, 1) => Self::V1(v0_1::Header {
344                chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
345                    chain_config,
346                )),
347                height,
348                timestamp,
349                l1_head,
350                l1_finalized,
351                payload_commitment,
352                builder_commitment,
353                ns_table,
354                block_merkle_tree_root,
355                fee_merkle_tree_root,
356                fee_info: fee_info[0], // NOTE this is asserted to exist above
357                builder_signature: builder_signature.first().copied(),
358            }),
359            (0, 2) => Self::V2(v0_2::Header {
360                chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
361                    chain_config,
362                )),
363                height,
364                timestamp,
365                l1_head,
366                l1_finalized,
367                payload_commitment,
368                builder_commitment,
369                ns_table,
370                block_merkle_tree_root,
371                fee_merkle_tree_root,
372                fee_info: fee_info[0], // NOTE this is asserted to exist above
373                builder_signature: builder_signature.first().copied(),
374            }),
375            (0, 3) => Self::V3(v0_3::Header {
376                chain_config: chain_config.into(),
377                height,
378                timestamp,
379                l1_head,
380                l1_finalized,
381                payload_commitment,
382                builder_commitment,
383                ns_table,
384                block_merkle_tree_root,
385                fee_merkle_tree_root,
386                fee_info: fee_info[0], // NOTE this is asserted to exist above
387                builder_signature: builder_signature.first().copied(),
388                reward_merkle_tree_root: reward_merkle_tree_root_v1,
389            }),
390            (0, 4) => Self::V4(v0_4::Header {
391                chain_config: chain_config.into(),
392                height,
393                timestamp,
394                timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
395                l1_head,
396                l1_finalized,
397                payload_commitment,
398                builder_commitment,
399                ns_table,
400                block_merkle_tree_root,
401                fee_merkle_tree_root,
402                fee_info: fee_info[0], // NOTE this is asserted to exist above
403                builder_signature: builder_signature.first().copied(),
404                reward_merkle_tree_root: reward_merkle_tree_root_v2,
405                total_reward_distributed: total_reward_distributed.unwrap_or_default(),
406                next_stake_table_hash,
407            }),
408            (0, 5) => Self::V5(v0_5::Header {
409                chain_config: chain_config.into(),
410                height,
411                timestamp,
412                timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
413                l1_head,
414                l1_finalized,
415                payload_commitment,
416                builder_commitment,
417                ns_table,
418                block_merkle_tree_root,
419                fee_merkle_tree_root,
420                fee_info: fee_info[0], // NOTE this is asserted to exist above
421                builder_signature: builder_signature.first().copied(),
422                reward_merkle_tree_root: reward_merkle_tree_root_v2,
423                total_reward_distributed: total_reward_distributed.unwrap_or_default(),
424                next_stake_table_hash,
425                leader_counts: leader_counts.expect("leader_counts required for V5 header"),
426            }),
427            (0, 6) => Self::V6(v0_6::Header {
428                chain_config: chain_config.into(),
429                height,
430                timestamp,
431                timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
432                l1_head,
433                l1_finalized,
434                payload_commitment,
435                builder_commitment,
436                ns_table,
437                block_merkle_tree_root,
438                fee_merkle_tree_root,
439                fee_info: fee_info[0],
440                builder_signature: builder_signature.first().copied(),
441                reward_merkle_tree_root: reward_merkle_tree_root_v2,
442                total_reward_distributed: total_reward_distributed.unwrap_or_default(),
443                next_stake_table_hash,
444                leader_counts: leader_counts.expect("leader_counts required for V6 header"),
445            }),
446            // This case should never occur
447            // but if it does, we must panic
448            // because we don't have the versioned types for this version
449            _ => panic!("invalid version: {version}"),
450        }
451    }
452
453    pub fn next_stake_table_hash(&self) -> Option<StakeTableHash> {
454        match self {
455            Self::V4(fields) => fields.next_stake_table_hash,
456            Self::V5(fields) | Self::V6(fields) => fields.next_stake_table_hash,
457            _ => None,
458        }
459    }
460
461    /// Get the leader counts for V5+ headers.
462    /// Returns None for earlier versions.
463    pub fn leader_counts(&self) -> Option<&LeaderCounts> {
464        match self {
465            Self::V5(fields) | Self::V6(fields) => Some(&fields.leader_counts),
466            _ => None,
467        }
468    }
469
470    pub fn set_next_stake_table_hash(&mut self, hash: StakeTableHash) -> bool {
471        match self {
472            Self::V4(fields) => {
473                fields.next_stake_table_hash = Some(hash);
474                true
475            },
476            Self::V5(fields) | Self::V6(fields) => {
477                fields.next_stake_table_hash = Some(hash);
478                true
479            },
480            _ => false,
481        }
482    }
483}
484
485// Getter for a field which is the same across all versions.
486macro_rules! field {
487    ($obj:ident.$name:ident) => {
488        match $obj {
489            Self::V1(data) => &data.$name,
490            Self::V2(data) => &data.$name,
491            Self::V3(data) => &data.$name,
492            Self::V4(data) => &data.$name,
493            Self::V5(data) => &data.$name,
494            Self::V6(data) => &data.$name,
495        }
496    };
497}
498
499macro_rules! field_mut {
500    ($obj:ident.$name:ident) => {
501        match $obj {
502            Self::V1(data) => &mut data.$name,
503            Self::V2(data) => &mut data.$name,
504            Self::V3(data) => &mut data.$name,
505            Self::V4(data) => &mut data.$name,
506            Self::V5(data) => &mut data.$name,
507            Self::V6(data) => &mut data.$name,
508        }
509    };
510}
511
512impl Header {
513    #[allow(clippy::too_many_arguments)]
514    fn from_info(
515        payload_commitment: VidCommitment,
516        builder_commitment: BuilderCommitment,
517        ns_table: NsTable,
518        parent_leaf: &Leaf2,
519        mut l1: L1Snapshot,
520        l1_deposits: &[FeeInfo],
521        builder_fee: Vec<BuilderFee<SeqTypes>>,
522        mut timestamp: u64,
523        mut timestamp_millis: u64,
524        mut state: ValidatedState,
525        chain_config: ChainConfig,
526        version: Version,
527        total_reward_distributed: Option<RewardAmount>,
528        next_stake_table_hash: Option<StakeTableHash>,
529        leader_counts: Option<LeaderCounts>,
530    ) -> anyhow::Result<Self> {
531        ensure!(
532            version.major == 0,
533            "Invalid major version {}",
534            version.major
535        );
536
537        // Increment height.
538        let parent_header = parent_leaf.block_header();
539        let height = parent_header.height() + 1;
540
541        // Ensure the timestamp does not decrease. We can trust `parent.timestamp` because `parent`
542        // has already been voted on by consensus. If our timestamp is behind, either f + 1 nodes
543        // are lying about the current time, or our clock is just lagging.
544        if timestamp < parent_header.timestamp() {
545            tracing::warn!(
546                "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
547                parent_header.timestamp()
548            );
549            timestamp = parent_header.timestamp();
550        }
551
552        if timestamp_millis < parent_header.timestamp_millis() {
553            tracing::warn!(
554                "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
555                parent_header.timestamp_millis()
556            );
557            timestamp_millis = parent_header.timestamp_millis();
558        }
559
560        // Ensure the L1 block references don't decrease. Again, we can trust `parent.l1_*` are
561        // accurate.
562        if l1.head < parent_header.l1_head() {
563            tracing::warn!(
564                "L1 head {} behind parent {}, L1 client may be lagging",
565                l1.head,
566                parent_header.l1_head()
567            );
568            l1.head = parent_header.l1_head();
569        }
570        if l1.finalized < parent_header.l1_finalized() {
571            tracing::warn!(
572                "L1 finalized {:?} behind parent {:?}, L1 client may be lagging",
573                l1.finalized,
574                parent_header.l1_finalized()
575            );
576            l1.finalized = parent_header.l1_finalized();
577        }
578
579        // Enforce that the sequencer block timestamp is not behind the L1 block timestamp. This can
580        // only happen if our clock is badly out of sync with L1.
581        if let Some(l1_block) = &l1.finalized {
582            let l1_timestamp = l1_block.timestamp.to::<u64>();
583            if timestamp < l1_timestamp {
584                tracing::warn!(
585                    "Espresso timestamp {timestamp} behind L1 timestamp {l1_timestamp}, local \
586                     clock may be out of sync"
587                );
588                timestamp = l1_timestamp;
589            }
590
591            let l1_timestamp_millis = l1_timestamp * 1_000;
592
593            if timestamp_millis < l1_timestamp_millis {
594                tracing::warn!(
595                    "Espresso timestamp_millis {timestamp_millis} behind L1 timestamp \
596                     {l1_timestamp_millis}, local clock may be out of sync"
597                );
598                timestamp_millis = l1_timestamp_millis;
599            }
600        }
601
602        state
603            .block_merkle_tree
604            .push(parent_header.commit())
605            .context("missing blocks frontier")?;
606        let block_merkle_tree_root = state.block_merkle_tree.commitment();
607
608        // Insert the new L1 deposits
609        for fee_info in l1_deposits {
610            state
611                .insert_fee_deposit(*fee_info)
612                .context(format!("missing fee account {}", fee_info.account()))?;
613        }
614
615        // Validate and charge the builder fee.
616        for BuilderFee {
617            fee_account,
618            fee_signature,
619            fee_amount,
620        } in &builder_fee
621        {
622            ensure!(
623                fee_account.validate_fee_signature(fee_signature, *fee_amount, &ns_table)
624                    || fee_account.validate_fee_signature_with_vid_commitment(
625                        fee_signature,
626                        *fee_amount,
627                        &ns_table,
628                        &payload_commitment
629                    ),
630                "invalid builder signature"
631            );
632
633            let fee_info = FeeInfo::new(*fee_account, *fee_amount);
634            state
635                .charge_fee(fee_info, chain_config.fee_recipient)
636                .context(format!("invalid builder fee {fee_info:?}"))?;
637        }
638
639        let fee_info = FeeInfo::from_builder_fees(builder_fee.clone());
640
641        let builder_signature: Vec<BuilderSignature> =
642            builder_fee.iter().map(|e| e.fee_signature).collect();
643
644        let fee_merkle_tree_root = state.fee_merkle_tree.commitment();
645
646        let header = match (version.major, version.minor) {
647            (0, 1) => Self::V1(v0_1::Header {
648                chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
649                    chain_config,
650                )),
651                height,
652                timestamp,
653                l1_head: l1.head,
654                l1_finalized: l1.finalized,
655                payload_commitment,
656                builder_commitment,
657                ns_table,
658                block_merkle_tree_root,
659                fee_merkle_tree_root,
660                fee_info: fee_info[0],
661                builder_signature: builder_signature.first().copied(),
662            }),
663            (0, 2) => Self::V2(v0_2::Header {
664                chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
665                    chain_config,
666                )),
667                height,
668                timestamp,
669                l1_head: l1.head,
670                l1_finalized: l1.finalized,
671                payload_commitment,
672                builder_commitment,
673                ns_table,
674                block_merkle_tree_root,
675                fee_merkle_tree_root,
676                fee_info: fee_info[0],
677                builder_signature: builder_signature.first().copied(),
678            }),
679            (0, 3) => Self::V3(v0_3::Header {
680                chain_config: chain_config.into(),
681                height,
682                timestamp,
683                l1_head: l1.head,
684                l1_finalized: l1.finalized,
685                payload_commitment,
686                builder_commitment,
687                ns_table,
688                block_merkle_tree_root,
689                fee_merkle_tree_root,
690                reward_merkle_tree_root: state.reward_merkle_tree_v1.commitment(),
691                fee_info: fee_info[0],
692                builder_signature: builder_signature.first().copied(),
693            }),
694            (0, 4) => Self::V4(v0_4::Header {
695                chain_config: chain_config.into(),
696                height,
697                timestamp,
698                timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
699                l1_head: l1.head,
700                l1_finalized: l1.finalized,
701                payload_commitment,
702                builder_commitment,
703                ns_table,
704                block_merkle_tree_root,
705                fee_merkle_tree_root,
706                reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
707                fee_info: fee_info[0],
708                builder_signature: builder_signature.first().copied(),
709                total_reward_distributed: total_reward_distributed.unwrap_or_default(),
710                next_stake_table_hash,
711            }),
712            (0, 5) => Self::V5(v0_5::Header {
713                chain_config: chain_config.into(),
714                height,
715                timestamp,
716                timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
717                l1_head: l1.head,
718                l1_finalized: l1.finalized,
719                payload_commitment,
720                builder_commitment,
721                ns_table,
722                block_merkle_tree_root,
723                fee_merkle_tree_root,
724                reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
725                fee_info: fee_info[0],
726                builder_signature: builder_signature.first().copied(),
727                total_reward_distributed: total_reward_distributed.unwrap_or_default(),
728                next_stake_table_hash,
729                leader_counts: leader_counts.expect("leader_counts is required for V5 headers"),
730            }),
731            (0, 6) => Self::V6(v0_6::Header {
732                chain_config: chain_config.into(),
733                height,
734                timestamp,
735                timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
736                l1_head: l1.head,
737                l1_finalized: l1.finalized,
738                payload_commitment,
739                builder_commitment,
740                ns_table,
741                block_merkle_tree_root,
742                fee_merkle_tree_root,
743                reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
744                fee_info: fee_info[0],
745                builder_signature: builder_signature.first().copied(),
746                total_reward_distributed: total_reward_distributed.unwrap_or_default(),
747                next_stake_table_hash,
748                leader_counts: leader_counts.expect("leader_counts is required for V6 headers"),
749            }),
750            // This case should never occur
751            // but if it does, we must panic
752            // because we don't have the versioned types for this version
753            _ => panic!("invalid version: {version}"),
754        };
755        Ok(header)
756    }
757
758    /// Calculate the per-validator leader counts for the current block.
759    ///
760    /// The array is sized to [`MAX_VALIDATORS`] (100) because only the top 100
761    /// validators by stake are selected into the active set via
762    /// `select_active_validator_set()`. `leader_index` is a position within
763    /// that set, so it is always in the range 0..100.
764    pub fn calculate_leader_counts(
765        parent_header: &Header,
766        height: u64,
767        leader_index: usize,
768        epoch_height: u64,
769    ) -> LeaderCounts {
770        let mut leader_counts = [0u16; MAX_VALIDATORS];
771
772        // Get parent's leader counts
773        let parent_counts = parent_header.leader_counts();
774
775        // If parent was the last block of an epoch, current block is epoch start
776        let is_epoch_start = is_last_block(height.saturating_sub(1), epoch_height);
777
778        if is_epoch_start || parent_counts.is_none() {
779            leader_counts[leader_index] = 1;
780        } else if let Some(parent_counts) = parent_counts {
781            leader_counts = *parent_counts;
782            leader_counts[leader_index] += 1;
783        }
784
785        leader_counts
786    }
787
788    /// Look up the proposing leader's index in the active validator set for this view.
789    ///
790    /// Returns `None` for protocol versions before [`EPOCH_REWARD_VERSION`] (V5),
791    /// since per-epoch reward tracking was not yet active.
792    ///
793    /// The returned index is a position in the epoch's stake table (0..MAX_VALIDATORS)
794    /// and is used to increment that leader's count in [`LeaderCounts`].
795    pub async fn get_leader_index(
796        version: Version,
797        height: u64,
798        view_number: u64,
799        instance_state: &NodeState,
800    ) -> anyhow::Result<Option<usize>> {
801        // Leader counts are only tracked from V5 onward.
802        if version < EPOCH_REWARD_VERSION {
803            return Ok(None);
804        }
805
806        let epoch_height = instance_state
807            .epoch_height
808            .context("epoch height not in instance state for V6")?;
809        let epoch = EpochNumber::new(epoch_from_block_number(height, epoch_height));
810
811        let coordinator = instance_state.coordinator.clone();
812        let epoch_membership = coordinator
813            .membership_for_epoch(Some(epoch))
814            .await
815            .map_err(|e| anyhow::anyhow!("failed to get epoch membership: {e}"))?;
816        let membership = epoch_membership.coordinator.membership().read().await;
817
818        // Resolve the leader for this view and find their index in the stake table.
819        let leader = membership
820            .leader(ViewNumber::new(view_number), Some(epoch))
821            .context(format!("leader for epoch {epoch:?} not found"))?;
822
823        let index = membership
824            .get_validator_index(&epoch, &leader)
825            .context(format!(
826                "Leader {leader} not found in stake table for epoch {epoch}"
827            ))?;
828
829        Ok(Some(index))
830    }
831
832    /// Distribute per epoch rewards at epoch boundaries.
833    ///
834    /// Rewards are calculated in the background during an epoch and applied
835    /// atomically at the epoch boundary (the last block of each epoch).
836    ///
837    /// The flow for a given block at `height` in epoch E:
838    ///
839    /// - E ≤ first_epoch + 1 : no rewards exist yet, return zero.
840    /// - if the previous epoch's calculation hasn't started,
841    ///   kick it off in the background so it's ready by the boundary. Return zero rewards.
842    /// - Epoch boundary (last block of E): apply the previous epoch's
843    ///   (E-1) reward result to `validated_state.reward_merkle_tree_v2`, verify
844    ///   the resulting root against `header_root` if provided, then start the
845    ///   background calculation for the current epoch E. `header_root` is `None`
846    ///   during proposal (the leader is building the header) and `Some` during
847    ///   validation.
848    ///
849    /// If the previous epoch's result is missing at the boundary (e.g. after a
850    /// restart or catchup), the function fetches the epoch's leaf, recovers the
851    /// leader counts and stake table, computes rewards synchronously, and applies
852    /// them before proceeding.
853    ///
854    /// # Returns
855    /// `(total_rewards_applied, changed_accounts)` — the total reward amount
856    /// distributed and the set of accounts whose balances changed.
857    pub async fn handle_epoch_rewards(
858        height: u64,
859        leader_counts: &LeaderCounts,
860        instance_state: &NodeState,
861        validated_state: &mut ValidatedState,
862        header_root: Option<RewardMerkleCommitmentV2>,
863    ) -> anyhow::Result<(RewardAmount, HashSet<RewardAccountV2>)> {
864        let epoch_height = instance_state
865            .epoch_height
866            .context("epoch_height not configured")?;
867        let epoch = EpochNumber::new(epoch_from_block_number(height, epoch_height));
868        let prev_epoch = EpochNumber::new(*epoch - 1);
869        let coordinator = instance_state.coordinator.clone();
870        let first_epoch = coordinator
871            .membership()
872            .read()
873            .await
874            .first_epoch()
875            .context("first_epoch not available")?;
876
877        // No rewards data exists for the first two epochs.
878        if epoch <= first_epoch + 1 {
879            return Ok((RewardAmount::default(), HashSet::new()));
880        }
881
882        let mut reward_calculator = instance_state.epoch_rewards_calculator.lock().await;
883
884        // Eagerly start the previous epoch's reward calculation if it hasn't
885        // been kicked off yet, so the result is ready by the epoch boundary.
886        if epoch > first_epoch + 2 && !reward_calculator.is_calculating(prev_epoch) {
887            tracing::info!(%epoch, %prev_epoch, "triggering catchup reward calculation");
888            reward_calculator.spawn_background_task(
889                prev_epoch,
890                epoch_height,
891                validated_state.reward_merkle_tree_v2.clone(),
892                instance_state.clone(),
893                coordinator.clone(),
894                None,
895            );
896        }
897
898        if !is_last_block(height, epoch_height) {
899            return Ok((RewardAmount::default(), HashSet::new()));
900        }
901
902        tracing::info!(%height, %epoch, %prev_epoch, "epoch boundary: applying rewards");
903
904        let (epoch_rewards_applied, changed_accounts) = if let Some(result) =
905            reward_calculator.get_result(prev_epoch).await
906        {
907            tracing::info!(
908                %epoch,
909                prev_epoch = %result.epoch,
910                total = %result.total_distributed.0,
911                "applying epoch rewards"
912            );
913            validated_state.reward_merkle_tree_v2 = result.reward_tree.clone();
914            (result.total_distributed, result.changed_accounts)
915        } else if prev_epoch <= first_epoch + 1 {
916            // Previous epoch is too early to have rewards.
917            (RewardAmount::default(), HashSet::new())
918        } else {
919            // the background result is missing
920            // Fetch the previous epoch's leaf and compute rewards synchronously.
921            let prev_epoch_last_block = *prev_epoch * epoch_height;
922            if let Err(err) = coordinator.membership_for_epoch(Some(prev_epoch)).await {
923                tracing::info!(%prev_epoch, "stake table missing for prev_epoch, triggering catchup: {err:#}");
924                coordinator
925                    .wait_for_catchup(prev_epoch)
926                    .await
927                    .context(format!("failed to catch up for prev_epoch={prev_epoch}"))?;
928            }
929
930            let membership = coordinator.membership().read().await;
931            let stake_table = membership.stake_table(Some(prev_epoch));
932            let success_threshold = membership.success_threshold(Some(prev_epoch));
933            drop(membership);
934
935            let prev_epoch_leaf = instance_state
936                .state_catchup
937                .as_ref()
938                .fetch_leaf(prev_epoch_last_block, stake_table, success_threshold)
939                .await
940                .with_context(|| {
941                    format!(
942                        "failed to fetch leaf at height {prev_epoch_last_block} for prev_epoch \
943                         {prev_epoch}"
944                    )
945                })?;
946            let prev_epoch_header = prev_epoch_leaf.block_header();
947
948            if prev_epoch_header.version() >= EPOCH_REWARD_VERSION {
949                tracing::warn!(
950                    %epoch,
951                    %prev_epoch,
952                    "missing epoch rewards at boundary, spawning calculation now"
953                );
954
955                if !reward_calculator.is_calculating(prev_epoch) {
956                    // Pick the reward tree to build on
957                    // use the local tree if its
958                    // root matches what the previous epoch's header committed to,
959                    // otherwise start from an empty tree because catchup will fill it
960                    let expected_root = prev_epoch_header.reward_merkle_tree_root().right();
961                    let actual_root = validated_state.reward_merkle_tree_v2.commitment();
962                    let reward_tree = if expected_root == Some(actual_root) {
963                        validated_state.reward_merkle_tree_v2.clone()
964                    } else {
965                        tracing::warn!(
966                            %epoch,
967                            %prev_epoch,
968                            ?expected_root,
969                            ?actual_root,
970                            "reward merkle tree root mismatch, using empty tree for catchup"
971                        );
972                        RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT)
973                    };
974
975                    reward_calculator.spawn_background_task(
976                        prev_epoch,
977                        epoch_height,
978                        reward_tree,
979                        instance_state.clone(),
980                        coordinator.clone(),
981                        prev_epoch_header.leader_counts().copied(),
982                    );
983                }
984
985                let result = reward_calculator
986                    .get_result(prev_epoch)
987                    .await
988                    .context(format!(
989                        "failed to calculate missing rewards for epoch {prev_epoch}"
990                    ))?;
991
992                tracing::info!(
993                    %epoch,
994                    %prev_epoch,
995                    total = %result.total_distributed.0,
996                    "applied delayed epoch rewards"
997                );
998
999                validated_state.reward_merkle_tree_v2 = result.reward_tree.clone();
1000                (result.total_distributed, result.changed_accounts)
1001            } else {
1002                tracing::info!(%epoch, %prev_epoch, "no rewards for pre-V5 epoch");
1003                (RewardAmount::default(), HashSet::new())
1004            }
1005        };
1006
1007        // Verify the reward tree root matches the proposed header, if available.
1008        let calculated_root = validated_state.reward_merkle_tree_v2.commitment();
1009        if let Some(header_root) = header_root
1010            && calculated_root != header_root
1011        {
1012            bail!(
1013                "reward merkle tree root mismatch, using new merkle tree. Header root: \
1014                 {header_root}, Calculated root: {calculated_root}"
1015            );
1016        }
1017
1018        // Kick off the background calculation for the current epoch so it's
1019        // ready on the next epoch boundary.
1020        reward_calculator.spawn_background_task(
1021            epoch,
1022            epoch_height,
1023            validated_state.reward_merkle_tree_v2.clone(),
1024            instance_state.clone(),
1025            coordinator,
1026            Some(*leader_counts),
1027        );
1028
1029        Ok((epoch_rewards_applied, changed_accounts))
1030    }
1031
1032    async fn get_chain_config(
1033        validated_state: &ValidatedState,
1034        instance_state: &NodeState,
1035    ) -> anyhow::Result<ChainConfig> {
1036        let validated_cf = validated_state.chain_config;
1037        let instance_cf = instance_state.chain_config;
1038
1039        if validated_cf.commit() == instance_cf.commit() {
1040            return Ok(instance_cf);
1041        }
1042
1043        match validated_cf.resolve() {
1044            Some(cf) => Ok(cf),
1045            None => {
1046                tracing::info!("fetching chain config {} from peers", validated_cf.commit());
1047
1048                instance_state
1049                    .state_catchup
1050                    .as_ref()
1051                    .fetch_chain_config(validated_cf.commit())
1052                    .await
1053            },
1054        }
1055    }
1056}
1057
1058impl Header {
1059    /// A commitment to a ChainConfig or a full ChainConfig.
1060    pub fn chain_config(&self) -> v0_3::ResolvableChainConfig {
1061        match self {
1062            Self::V1(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
1063            Self::V2(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
1064            Self::V3(fields) => fields.chain_config,
1065            Self::V4(fields) => fields.chain_config,
1066            Self::V5(fields) => fields.chain_config,
1067            Self::V6(fields) => fields.chain_config,
1068        }
1069    }
1070
1071    pub fn height(&self) -> u64 {
1072        *field!(self.height)
1073    }
1074
1075    pub fn height_mut(&mut self) -> &mut u64 {
1076        &mut *field_mut!(self.height)
1077    }
1078
1079    pub fn timestamp_internal(&self) -> u64 {
1080        match self {
1081            Self::V1(fields) => fields.timestamp,
1082            Self::V2(fields) => fields.timestamp,
1083            Self::V3(fields) => fields.timestamp,
1084            Self::V4(fields) => fields.timestamp,
1085            Self::V5(fields) => fields.timestamp,
1086            Self::V6(fields) => fields.timestamp,
1087        }
1088    }
1089
1090    pub fn timestamp_millis_internal(&self) -> u64 {
1091        match self {
1092            Self::V1(fields) => fields.timestamp * 1_000,
1093            Self::V2(fields) => fields.timestamp * 1_000,
1094            Self::V3(fields) => fields.timestamp * 1_000,
1095            Self::V4(fields) => fields.timestamp_millis.u64(),
1096            Self::V5(fields) => fields.timestamp_millis.u64(),
1097            Self::V6(fields) => fields.timestamp_millis.u64(),
1098        }
1099    }
1100
1101    pub fn set_timestamp(&mut self, timestamp: u64, timestamp_millis: u64) {
1102        match self {
1103            Self::V1(fields) => {
1104                fields.timestamp = timestamp;
1105            },
1106            Self::V2(fields) => {
1107                fields.timestamp = timestamp;
1108            },
1109            Self::V3(fields) => {
1110                fields.timestamp = timestamp;
1111            },
1112            Self::V4(fields) => {
1113                fields.timestamp = timestamp;
1114                fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
1115            },
1116            Self::V5(fields) => {
1117                fields.timestamp = timestamp;
1118                fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
1119            },
1120            Self::V6(fields) => {
1121                fields.timestamp = timestamp;
1122                fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
1123            },
1124        };
1125    }
1126
1127    /// The Espresso block header includes a reference to the current head of the L1 chain.
1128    ///
1129    /// Rollups can use this to facilitate bridging between the L1 and L2 in a deterministic way.
1130    /// This field deterministically associates an L2 block with a recent L1 block the instant the
1131    /// L2 block is sequenced. Rollups can then define the L2 state after this block as the state
1132    /// obtained by executing all the transactions in this block _plus_ all the L1 deposits up to
1133    /// the given L1 block number. Since there is no need to wait for the L2 block to be reflected
1134    /// on the L1, this bridge design retains the low confirmation latency of HotShot.
1135    ///
1136    /// This block number indicates the unsafe head of the L1 chain, so it is subject to reorgs. For
1137    /// this reason, the Espresso header does not include any information that might change in a
1138    /// reorg, such as the L1 block timestamp or hash. It includes only the L1 block number, which
1139    /// will always refer to _some_ block after a reorg: if the L1 head at the time this block was
1140    /// sequenced gets reorged out, the L1 chain will eventually (and probably quickly) grow to the
1141    /// same height once again, and a different block will exist with the same height. In this way,
1142    /// Espresso does not have to handle L1 reorgs, and the Espresso blockchain will always be
1143    /// reflective of the current state of the L1 blockchain. Rollups that use this block number
1144    /// _do_ have to handle L1 reorgs, but each rollup and each rollup client can decide how many
1145    /// confirmations they want to wait for on top of this `l1_head` before they consider an L2
1146    /// block finalized. This offers a tradeoff between low-latency L1-L2 bridges and finality.
1147    ///
1148    /// Rollups that want a stronger guarantee of finality, or that want Espresso to attest to data
1149    /// from the L1 block that might change in reorgs, can instead use the latest L1 _finalized_
1150    /// block at the time this L2 block was sequenced: [`Self::l1_finalized`].
1151    pub fn l1_head(&self) -> u64 {
1152        *field!(self.l1_head)
1153    }
1154
1155    pub fn l1_head_mut(&mut self) -> &mut u64 {
1156        &mut *field_mut!(self.l1_head)
1157    }
1158
1159    /// The Espresso block header includes information about the latest finalized L1 block.
1160    ///
1161    /// Similar to [`l1_head`](Self::l1_head), rollups can use this information to implement a
1162    /// bridge between the L1 and L2 while retaining the finality of low-latency block confirmations
1163    /// from HotShot. Since this information describes the finalized L1 block, a bridge using this
1164    /// L1 block will have much higher latency than a bridge using [`l1_head`](Self::l1_head). In
1165    /// exchange, rollups that use the finalized block do not have to worry about L1 reorgs, and can
1166    /// inject verifiable attestations to the L1 block metadata (such as its timestamp or hash) into
1167    /// their execution layers, since Espresso replicas will sign this information for the finalized
1168    /// L1 block.
1169    ///
1170    /// This block may be `None` in the rare case where Espresso has started shortly after the
1171    /// genesis of the L1, and the L1 has yet to finalize a block. In all other cases it will be
1172    /// `Some`.
1173    pub fn l1_finalized(&self) -> Option<L1BlockInfo> {
1174        *field!(self.l1_finalized)
1175    }
1176
1177    pub fn l1_finalized_mut(&mut self) -> &mut Option<L1BlockInfo> {
1178        &mut *field_mut!(self.l1_finalized)
1179    }
1180
1181    pub fn payload_commitment(&self) -> VidCommitment {
1182        *field!(self.payload_commitment)
1183    }
1184
1185    pub fn payload_commitment_mut(&mut self) -> &mut VidCommitment {
1186        &mut *field_mut!(self.payload_commitment)
1187    }
1188
1189    pub fn builder_commitment(&self) -> &BuilderCommitment {
1190        field!(self.builder_commitment)
1191    }
1192
1193    pub fn builder_commitment_mut(&mut self) -> &mut BuilderCommitment {
1194        &mut *field_mut!(self.builder_commitment)
1195    }
1196
1197    pub fn ns_table(&self) -> &NsTable {
1198        field!(self.ns_table)
1199    }
1200
1201    pub fn ns_table_mut(&mut self) -> &mut NsTable {
1202        &mut *field_mut!(self.ns_table)
1203    }
1204
1205    /// Root Commitment of Block Merkle Tree
1206    pub fn block_merkle_tree_root(&self) -> BlockMerkleCommitment {
1207        *field!(self.block_merkle_tree_root)
1208    }
1209
1210    pub fn block_merkle_tree_root_mut(&mut self) -> &mut BlockMerkleCommitment {
1211        &mut *field_mut!(self.block_merkle_tree_root)
1212    }
1213
1214    /// Root Commitment of `FeeMerkleTree`
1215    pub fn fee_merkle_tree_root(&self) -> FeeMerkleCommitment {
1216        *field!(self.fee_merkle_tree_root)
1217    }
1218
1219    pub fn fee_merkle_tree_root_mut(&mut self) -> &mut FeeMerkleCommitment {
1220        &mut *field_mut!(self.fee_merkle_tree_root)
1221    }
1222
1223    /// Fee paid by the block builder
1224    pub fn fee_info(&self) -> Vec<FeeInfo> {
1225        match self {
1226            Self::V1(fields) => vec![fields.fee_info],
1227            Self::V2(fields) => vec![fields.fee_info],
1228            Self::V3(fields) => vec![fields.fee_info],
1229            Self::V4(fields) => vec![fields.fee_info],
1230            Self::V5(fields) => vec![fields.fee_info],
1231            Self::V6(fields) => vec![fields.fee_info],
1232        }
1233    }
1234
1235    pub fn reward_merkle_tree_root(
1236        &self,
1237    ) -> Either<RewardMerkleCommitmentV1, RewardMerkleCommitmentV2> {
1238        let empty_reward_merkle_tree = RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT);
1239        match self {
1240            Self::V1(_) => Either::Left(empty_reward_merkle_tree.commitment()),
1241            Self::V2(_) => Either::Left(empty_reward_merkle_tree.commitment()),
1242            Self::V3(fields) => Either::Left(fields.reward_merkle_tree_root),
1243            Self::V4(fields) => Either::Right(fields.reward_merkle_tree_root),
1244            Self::V5(fields) => Either::Right(fields.reward_merkle_tree_root),
1245            Self::V6(fields) => Either::Right(fields.reward_merkle_tree_root),
1246        }
1247    }
1248
1249    /// Account (etheruem address) of builder
1250    ///
1251    /// This signature is not considered formally part of the header; it is just evidence proving
1252    /// that other parts of the header ([`fee_info`](Self::fee_info)) are correct. It exists in the
1253    /// header so that it is available to all nodes to be used during validation. But since it is
1254    /// checked during consensus, any downstream client who has a proof of consensus finality of a
1255    /// header can trust that [`fee_info`](Self::fee_info) is correct without relying on the
1256    /// signature. Thus, this signature is not included in the header commitment.
1257    pub fn builder_signature(&self) -> Vec<BuilderSignature> {
1258        match self {
1259            // Previously we used `Option<BuilderSignature>` to
1260            // represent presence/absence of signature.  The simplest
1261            // way to represent the same now that we have a `Vec` is
1262            // empty/non-empty
1263            Self::V1(fields) => fields.builder_signature.as_slice().to_vec(),
1264            Self::V2(fields) => fields.builder_signature.as_slice().to_vec(),
1265            Self::V3(fields) => fields.builder_signature.as_slice().to_vec(),
1266            Self::V4(fields) => fields.builder_signature.as_slice().to_vec(),
1267            Self::V5(fields) => fields.builder_signature.as_slice().to_vec(),
1268            Self::V6(fields) => fields.builder_signature.as_slice().to_vec(),
1269        }
1270    }
1271
1272    pub fn total_reward_distributed(&self) -> Option<RewardAmount> {
1273        match self {
1274            Self::V1(_) | Self::V2(_) | Self::V3(_) => None,
1275            Self::V4(fields) => Some(fields.total_reward_distributed),
1276            Self::V5(fields) => Some(fields.total_reward_distributed),
1277            Self::V6(fields) => Some(fields.total_reward_distributed),
1278        }
1279    }
1280}
1281
1282#[derive(Debug, Error)]
1283#[error("Invalid Block Header {msg}")]
1284pub struct InvalidBlockHeader {
1285    msg: String,
1286}
1287impl InvalidBlockHeader {
1288    fn new(msg: String) -> Self {
1289        Self { msg }
1290    }
1291}
1292
1293impl From<anyhow::Error> for InvalidBlockHeader {
1294    fn from(err: anyhow::Error) -> Self {
1295        Self::new(format!("{err:#}"))
1296    }
1297}
1298
1299impl BlockHeader<SeqTypes> for Header {
1300    type Error = InvalidBlockHeader;
1301
1302    #[tracing::instrument(
1303        skip_all,
1304        fields(
1305            node_id = instance_state.node_id,
1306            view = ?parent_leaf.view_number(),
1307            height = parent_leaf.block_header().height(),
1308        ),
1309    )]
1310    #[tracing::instrument(
1311        skip_all,
1312        fields(
1313            height = parent_leaf.block_header().block_number() + 1,
1314            parent_view = ?parent_leaf.view_number(),
1315            payload_commitment,
1316            version,
1317        )
1318    )]
1319    async fn new(
1320        parent_state: &ValidatedState,
1321        instance_state: &NodeState,
1322        parent_leaf: &Leaf2,
1323        payload_commitment: VidCommitment,
1324        builder_commitment: BuilderCommitment,
1325        metadata: <<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
1326        builder_fee: BuilderFee<SeqTypes>,
1327        version: Version,
1328        view_number: u64,
1329    ) -> Result<Self, Self::Error> {
1330        tracing::info!("preparing to propose legacy header");
1331
1332        let height = parent_leaf.height();
1333        let view = parent_leaf.view_number();
1334
1335        let mut validated_state = parent_state.clone();
1336
1337        let chain_config = if version > instance_state.current_version {
1338            match instance_state.upgrades.get(&version) {
1339                Some(upgrade) => match upgrade.upgrade_type {
1340                    UpgradeType::Fee { chain_config } => chain_config,
1341                    UpgradeType::Epoch { chain_config } => chain_config,
1342                    UpgradeType::DrbAndHeader { chain_config } => chain_config,
1343                    UpgradeType::Da { chain_config } => chain_config,
1344                    UpgradeType::EpochReward { chain_config } => chain_config,
1345                },
1346                None => Header::get_chain_config(&validated_state, instance_state).await?,
1347            }
1348        } else {
1349            Header::get_chain_config(&validated_state, instance_state).await?
1350        };
1351
1352        validated_state.chain_config = chain_config.into();
1353
1354        // Fetch the latest L1 snapshot.
1355        let l1_snapshot = instance_state.l1_client.snapshot().await;
1356        // Fetch the new L1 deposits between parent and current finalized L1 block.
1357        let l1_deposits = if let (Some(addr), Some(block_info)) =
1358            (chain_config.fee_contract, l1_snapshot.finalized)
1359        {
1360            instance_state
1361                .l1_client
1362                .get_finalized_deposits(
1363                    addr,
1364                    parent_leaf
1365                        .block_header()
1366                        .l1_finalized()
1367                        .map(|block_info| block_info.number),
1368                    block_info.number,
1369                )
1370                .await
1371        } else {
1372            vec![]
1373        };
1374        // Find missing fee state entries. We will need to use the builder account which is paying a
1375        // fee and the recipient account which is receiving it, plus any counts receiving deposits
1376        // in this block.
1377        let missing_accounts = parent_state.forgotten_accounts(
1378            [builder_fee.fee_account, chain_config.fee_recipient]
1379                .into_iter()
1380                .chain(l1_deposits.iter().map(|info| info.account())),
1381        );
1382        if !missing_accounts.is_empty() {
1383            tracing::warn!(
1384                height,
1385                ?view,
1386                ?missing_accounts,
1387                "fetching missing accounts from peers"
1388            );
1389
1390            // Fetch missing fee state entries
1391            let missing_account_proofs = instance_state
1392                .state_catchup
1393                .as_ref()
1394                .fetch_accounts(
1395                    instance_state,
1396                    height,
1397                    view,
1398                    parent_state.fee_merkle_tree.commitment(),
1399                    missing_accounts,
1400                )
1401                .await?;
1402
1403            // Insert missing fee state entries
1404            for proof in missing_account_proofs.iter() {
1405                proof
1406                    .remember(&mut validated_state.fee_merkle_tree)
1407                    .context("remembering fee account")?;
1408            }
1409        }
1410
1411        // Ensure merkle tree has frontier
1412        if validated_state.need_to_fetch_blocks_mt_frontier() {
1413            tracing::warn!(height, ?view, "fetching block frontier from peers");
1414            instance_state
1415                .state_catchup
1416                .as_ref()
1417                .remember_blocks_merkle_tree(
1418                    instance_state,
1419                    height,
1420                    view,
1421                    &mut validated_state.block_merkle_tree,
1422                )
1423                .await
1424                .context("remembering block proof")?;
1425        }
1426
1427        // Handle rewards and calculate leader_counts based on version
1428        let (leader_counts, total_reward_distributed) = if version >= EPOCH_REWARD_VERSION {
1429            let epoch_height = instance_state
1430                .epoch_height
1431                .context("epoch_height not configured for V6")?;
1432            // Use the new block's height (parent + 1), not the parent's height
1433            let new_height = height + 1;
1434            let leader_index =
1435                Header::get_leader_index(version, new_height, view_number, instance_state)
1436                    .await?
1437                    .context("leader_index must be present for V6")?;
1438
1439            let leader_counts = Header::calculate_leader_counts(
1440                parent_leaf.block_header(),
1441                new_height,
1442                leader_index,
1443                epoch_height,
1444            );
1445
1446            let (epoch_rewards_applied, _changed_accounts) = Header::handle_epoch_rewards(
1447                new_height,
1448                &leader_counts,
1449                instance_state,
1450                &mut validated_state,
1451                None,
1452            )
1453            .await?;
1454
1455            // Note: changed_accounts are not used here during header creation.
1456            // Delta updates are handled in apply_header during validation.
1457
1458            let parent_total = parent_leaf
1459                .block_header()
1460                .total_reward_distributed()
1461                .unwrap_or_default();
1462
1463            (
1464                Some(leader_counts),
1465                Some(RewardAmount(parent_total.0 + epoch_rewards_applied.0)),
1466            )
1467        } else if version >= EPOCH_VERSION {
1468            // V3-V4: per-block distribution returns cumulative total
1469            let total = distribute_block_reward(
1470                instance_state,
1471                &mut validated_state,
1472                parent_leaf,
1473                ViewNumber::new(view_number),
1474                version,
1475            )
1476            .await?
1477            .map(|r| r.total_distributed());
1478
1479            (None, total)
1480        } else {
1481            (None, None)
1482        };
1483
1484        let mut next_stake_table_hash = None;
1485
1486        if version >= DRB_AND_HEADER_UPGRADE_VERSION {
1487            let epoch_height = instance_state
1488                .epoch_height
1489                .context("epoch height not in instance state")?;
1490            if is_ge_epoch_root(height + 1, epoch_height) {
1491                let coordinator = instance_state.coordinator.clone();
1492                let first_epoch = {
1493                    coordinator
1494                        .membership()
1495                        .read()
1496                        .await
1497                        .first_epoch()
1498                        .context("The first epoch was not set.")?
1499                };
1500
1501                let epoch = EpochNumber::new(epoch_from_block_number(height + 1, epoch_height));
1502
1503                // first 2 epochs don't have a stake table hash because they are configured.
1504                if epoch > first_epoch {
1505                    let epoch_membership = coordinator
1506                        .stake_table_for_epoch(Some(epoch + 1))
1507                        .await
1508                        .map_err(|e| anyhow::anyhow!("failed to get epoch membership: {e}"))?;
1509                    next_stake_table_hash = Some(
1510                        epoch_membership
1511                            .stake_table_hash()
1512                            .await
1513                            .context("failed to get next stake table hash")?,
1514                    );
1515                }
1516            }
1517        }
1518
1519        let now = OffsetDateTime::now_utc();
1520
1521        let timestamp = now.unix_timestamp() as u64;
1522        let timestamp_millis = TimestampMillis::from_time(&now).u64();
1523
1524        Ok(Self::from_info(
1525            payload_commitment,
1526            builder_commitment,
1527            metadata,
1528            parent_leaf,
1529            l1_snapshot,
1530            &l1_deposits,
1531            vec![builder_fee],
1532            timestamp,
1533            timestamp_millis,
1534            validated_state,
1535            chain_config,
1536            version,
1537            total_reward_distributed,
1538            next_stake_table_hash,
1539            leader_counts,
1540        )?)
1541    }
1542
1543    fn genesis(
1544        instance_state: &NodeState,
1545        payload: <SeqTypes as NodeType>::BlockPayload,
1546        metadata: &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
1547        _: Version,
1548    ) -> Self {
1549        let payload_bytes = payload.encode();
1550        let builder_commitment = payload.builder_commitment(metadata);
1551
1552        let vid_commitment_version = instance_state.genesis_version;
1553
1554        let payload_commitment = vid_commitment(
1555            &payload_bytes,
1556            &metadata.encode(),
1557            GENESIS_VID_NUM_STORAGE_NODES,
1558            vid_commitment_version,
1559        );
1560
1561        let ValidatedState {
1562            fee_merkle_tree,
1563            block_merkle_tree,
1564            reward_merkle_tree_v1,
1565            reward_merkle_tree_v2,
1566            ..
1567        } = ValidatedState::genesis(instance_state).0;
1568        let block_merkle_tree_root = block_merkle_tree.commitment();
1569        let fee_merkle_tree_root = fee_merkle_tree.commitment();
1570        let reward_merkle_tree_root = reward_merkle_tree_v2.commitment();
1571
1572        let time = instance_state.genesis_header.timestamp;
1573
1574        let timestamp = time.unix_timestamp();
1575        let timestamp_millis = time.unix_timestamp_millis();
1576
1577        //  The Header is versioned,
1578        //  so we create the genesis header for the current version of the sequencer.
1579        Self::create(
1580            instance_state.genesis_header.chain_config,
1581            0,
1582            timestamp,
1583            timestamp_millis,
1584            instance_state
1585                .l1_genesis
1586                .map(|block| block.number)
1587                .unwrap_or_default(),
1588            instance_state.l1_genesis,
1589            payload_commitment,
1590            builder_commitment.clone(),
1591            metadata.clone(),
1592            fee_merkle_tree_root,
1593            block_merkle_tree_root,
1594            reward_merkle_tree_v1.commitment(),
1595            reward_merkle_tree_root,
1596            vec![FeeInfo::genesis()],
1597            vec![],
1598            None,
1599            instance_state.genesis_version,
1600            None,
1601            Some([0; 100]),
1602        )
1603    }
1604
1605    fn timestamp(&self) -> u64 {
1606        self.timestamp_internal()
1607    }
1608
1609    fn timestamp_millis(&self) -> u64 {
1610        self.timestamp_millis_internal()
1611    }
1612
1613    fn block_number(&self) -> u64 {
1614        self.height()
1615    }
1616
1617    fn version(&self) -> Version {
1618        self.version()
1619    }
1620
1621    fn payload_commitment(&self) -> VidCommitment {
1622        self.payload_commitment()
1623    }
1624
1625    fn metadata(
1626        &self,
1627    ) -> &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata {
1628        self.ns_table()
1629    }
1630
1631    /// Commit over fee_amount, payload_commitment and metadata
1632    fn builder_commitment(&self) -> BuilderCommitment {
1633        self.builder_commitment().clone()
1634    }
1635
1636    fn get_light_client_state(&self, view: ViewNumber) -> anyhow::Result<LightClientState> {
1637        let mut block_comm_root_bytes = vec![];
1638        self.block_merkle_tree_root()
1639            .serialize_compressed(&mut block_comm_root_bytes)?;
1640
1641        Ok(LightClientState {
1642            view_number: view.u64(),
1643            block_height: self.height(),
1644            block_comm_root: hotshot_types::light_client::hash_bytes_to_field(
1645                &block_comm_root_bytes,
1646            )?,
1647        })
1648    }
1649
1650    fn auth_root(&self) -> anyhow::Result<B256> {
1651        match self {
1652            Header::V1(_) | Header::V2(_) | Header::V3(_) => Ok(B256::ZERO),
1653            Header::V4(header) => {
1654                // Temporary placeholder values for future fields
1655                let placeholder_1 = B256::ZERO;
1656                let placeholder_2 = B256::ZERO;
1657                let placeholder_3 = B256::ZERO;
1658                let placeholder_4 = B256::ZERO;
1659                let placeholder_5 = B256::ZERO;
1660                let placeholder_6 = B256::ZERO;
1661                let placeholder_7 = B256::ZERO;
1662
1663                let mut hasher = Keccak256::new();
1664
1665                let digest = header.reward_merkle_tree_root.digest();
1666                hasher.update(digest.0);
1667                hasher.update(placeholder_1);
1668                hasher.update(placeholder_2);
1669                hasher.update(placeholder_3);
1670                hasher.update(placeholder_4);
1671                hasher.update(placeholder_5);
1672                hasher.update(placeholder_6);
1673                hasher.update(placeholder_7);
1674
1675                Ok(hasher.finalize())
1676            },
1677            Header::V5(header) | Header::V6(header) => {
1678                // Temporary placeholder values for future fields
1679                let placeholder_1 = B256::ZERO;
1680                let placeholder_2 = B256::ZERO;
1681                let placeholder_3 = B256::ZERO;
1682                let placeholder_4 = B256::ZERO;
1683                let placeholder_5 = B256::ZERO;
1684                let placeholder_6 = B256::ZERO;
1685                let placeholder_7 = B256::ZERO;
1686
1687                let mut hasher = Keccak256::new();
1688
1689                // Start with the reward Merkle tree root digest as the base input
1690                let digest = header.reward_merkle_tree_root.digest();
1691                hasher.update(digest.0);
1692                hasher.update(placeholder_1);
1693                hasher.update(placeholder_2);
1694                hasher.update(placeholder_3);
1695                hasher.update(placeholder_4);
1696                hasher.update(placeholder_5);
1697                hasher.update(placeholder_6);
1698                hasher.update(placeholder_7);
1699
1700                Ok(hasher.finalize())
1701            },
1702        }
1703    }
1704}
1705
1706impl HeightIndexed for Header {
1707    fn height(&self) -> u64 {
1708        self.height()
1709    }
1710}
1711
1712impl QueryableHeader<SeqTypes> for Header {
1713    type NamespaceId = NamespaceId;
1714    type NamespaceIndex = NsIndex;
1715
1716    fn namespace_id(&self, i: &NsIndex) -> Option<NamespaceId> {
1717        self.ns_table().read_ns_id(i)
1718    }
1719
1720    fn namespace_size(&self, i: &NsIndex, payload_size: usize) -> u64 {
1721        self.ns_table()
1722            .ns_range(i, &PayloadByteLen(payload_size))
1723            .byte_len()
1724            .0 as u64
1725    }
1726
1727    fn ns_table(&self) -> String {
1728        BASE64_STANDARD.encode(&self.ns_table().bytes)
1729    }
1730}
1731
1732impl ExplorerHeader<SeqTypes> for Header {
1733    type BalanceAmount = FeeAmount;
1734    type WalletAddress = Vec<FeeAccount>;
1735    type ProposerId = Vec<FeeAccount>;
1736
1737    // TODO what are these expected values w/ multiple Fees
1738    fn proposer_id(&self) -> Self::ProposerId {
1739        self.fee_info().accounts()
1740    }
1741
1742    fn fee_info_account(&self) -> Self::WalletAddress {
1743        self.fee_info().accounts()
1744    }
1745
1746    fn fee_info_balance(&self) -> Self::BalanceAmount {
1747        // TODO this will panic if some amount or total does not fit in a u64
1748        self.fee_info().amount().unwrap()
1749    }
1750
1751    /// reward_balance at the moment is only implemented as a stub, as block
1752    /// rewards have not yet been implemented.
1753    ///
1754    /// TODO: update implementation when rewards have been created / supported.
1755    ///       Issue: https://github.com/EspressoSystems/espresso-network/issues/1453
1756    fn reward_balance(&self) -> Self::BalanceAmount {
1757        FeeAmount::from(0)
1758    }
1759
1760    fn namespace_ids(&self) -> Vec<NamespaceId> {
1761        self.ns_table()
1762            .iter()
1763            .map(|i| self.ns_table().read_ns_id_unchecked(&i))
1764            .collect()
1765    }
1766}
1767
1768#[cfg(test)]
1769mod test_headers {
1770    use std::sync::Arc;
1771
1772    use alloy::{
1773        node_bindings::Anvil,
1774        primitives::{Address, U256},
1775    };
1776    use hotshot_query_service::testing::mocks::MOCK_UPGRADE;
1777    use hotshot_types::traits::signature_key::BuilderSignatureKey;
1778    use v0_1::{BlockMerkleTree, FeeMerkleTree, L1Client};
1779    use vbs::{BinarySerializer, bincode_serializer::BincodeSerializer, version::StaticVersion};
1780    use versions::version;
1781
1782    use super::*;
1783    use crate::{
1784        Leaf,
1785        eth_signature_key::EthKeyPair,
1786        mock::MockStateCatchup,
1787        v0_3::{REWARD_MERKLE_TREE_V1_HEIGHT, RewardAccountV1, RewardAmount},
1788        v0_4::{REWARD_MERKLE_TREE_V2_HEIGHT, RewardAccountV2, RewardMerkleTreeV2},
1789    };
1790
1791    #[derive(Debug, Default)]
1792    #[must_use]
1793    struct TestCase {
1794        // Parent header info.
1795        parent_timestamp: u64,
1796        parent_timestamp_millis: u64,
1797        parent_l1_head: u64,
1798        parent_l1_finalized: Option<L1BlockInfo>,
1799
1800        // Environment at the time the new header is created.
1801        l1_head: u64,
1802        l1_finalized: Option<L1BlockInfo>,
1803        timestamp: u64,
1804        timestamp_millis: u64,
1805        l1_deposits: Vec<FeeInfo>,
1806
1807        // Expected new header info.
1808        expected_timestamp: u64,
1809        expected_timestamp_millis: u64,
1810        expected_l1_head: u64,
1811        expected_l1_finalized: Option<L1BlockInfo>,
1812    }
1813
1814    impl TestCase {
1815        async fn run(self) {
1816            // Check test case validity.
1817            assert!(self.expected_timestamp >= self.parent_timestamp);
1818            assert!(self.expected_timestamp_millis >= self.parent_timestamp_millis);
1819            assert!(self.expected_l1_head >= self.parent_l1_head);
1820            assert!(self.expected_l1_finalized >= self.parent_l1_finalized);
1821
1822            let genesis = GenesisForTest::default().await;
1823            let mut parent = genesis.header.clone();
1824            parent.set_timestamp(self.parent_timestamp, self.parent_timestamp_millis);
1825            *parent.l1_head_mut() = self.parent_l1_head;
1826            *parent.l1_finalized_mut() = self.parent_l1_finalized;
1827
1828            let mut parent_leaf = genesis.leaf.clone();
1829            *parent_leaf.block_header_mut() = parent.clone();
1830
1831            let block_merkle_tree =
1832                BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap();
1833
1834            let fee_info = FeeInfo::genesis();
1835            let fee_merkle_tree = FeeMerkleTree::from_kv_set(
1836                20,
1837                Vec::from([(fee_info.account(), fee_info.amount())]),
1838            )
1839            .unwrap();
1840
1841            let reward_account_v1 = RewardAccountV1::default();
1842            let reward_account = RewardAccountV2::default();
1843            let reward_amount = RewardAmount::default();
1844            let reward_merkle_tree_v2 =
1845                RewardMerkleTreeV2::from_kv_set(20, Vec::from([(reward_account, reward_amount)]))
1846                    .unwrap();
1847
1848            let reward_merkle_tree_v1 = RewardMerkleTreeV1::from_kv_set(
1849                20,
1850                Vec::from([(reward_account_v1, reward_amount)]),
1851            )
1852            .unwrap();
1853
1854            let mut validated_state = ValidatedState {
1855                block_merkle_tree: block_merkle_tree.clone(),
1856                fee_merkle_tree,
1857                reward_merkle_tree_v2,
1858                reward_merkle_tree_v1,
1859                chain_config: genesis.instance_state.chain_config.into(),
1860            };
1861
1862            let (fee_account, fee_key) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1863            let fee_amount = 0;
1864            let fee_signature =
1865                FeeAccount::sign_fee(&fee_key, fee_amount, &genesis.ns_table).unwrap();
1866
1867            let header = Header::from_info(
1868                genesis.header.payload_commitment(),
1869                genesis.header.builder_commitment().clone(),
1870                genesis.ns_table,
1871                &parent_leaf,
1872                L1Snapshot {
1873                    head: self.l1_head,
1874                    finalized: self.l1_finalized,
1875                },
1876                &self.l1_deposits,
1877                vec![BuilderFee {
1878                    fee_account,
1879                    fee_amount,
1880                    fee_signature,
1881                }],
1882                self.timestamp,
1883                self.timestamp_millis,
1884                validated_state.clone(),
1885                genesis.instance_state.chain_config,
1886                version(0, 1),
1887                None, // total_reward_distributed
1888                None, // next_stake_table_hash
1889                None, // leader_counts
1890            )
1891            .unwrap();
1892            assert_eq!(header.height(), parent.height() + 1);
1893            assert_eq!(header.timestamp(), self.expected_timestamp);
1894            assert_eq!(header.timestamp_millis(), self.expected_timestamp_millis);
1895            assert_eq!(header.l1_head(), self.expected_l1_head);
1896            assert_eq!(header.l1_finalized(), self.expected_l1_finalized);
1897
1898            // Check deposits were inserted before computing the fee merkle tree root.
1899            for fee_info in self.l1_deposits {
1900                validated_state.insert_fee_deposit(fee_info).unwrap();
1901            }
1902            assert_eq!(
1903                validated_state.fee_merkle_tree.commitment(),
1904                header.fee_merkle_tree_root(),
1905            );
1906
1907            assert_eq!(
1908                block_merkle_tree,
1909                BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap()
1910            );
1911        }
1912    }
1913
1914    fn l1_block(number: u64) -> L1BlockInfo {
1915        L1BlockInfo {
1916            number,
1917            ..Default::default()
1918        }
1919    }
1920
1921    #[test_log::test(tokio::test(flavor = "multi_thread"))]
1922    async fn test_new_header() {
1923        // Simplest case: building on genesis, L1 info and timestamp unchanged.
1924        TestCase::default().run().await
1925    }
1926
1927    #[test_log::test(tokio::test(flavor = "multi_thread"))]
1928    async fn test_new_header_advance_timestamp() {
1929        TestCase {
1930            timestamp: 1,
1931            timestamp_millis: 1_000,
1932            expected_timestamp: 1,
1933            expected_timestamp_millis: 1_000,
1934            ..Default::default()
1935        }
1936        .run()
1937        .await
1938    }
1939
1940    #[test_log::test(tokio::test(flavor = "multi_thread"))]
1941    async fn test_new_header_advance_l1_block() {
1942        TestCase {
1943            parent_l1_head: 0,
1944            parent_l1_finalized: Some(l1_block(0)),
1945
1946            l1_head: 1,
1947            l1_finalized: Some(l1_block(1)),
1948
1949            expected_l1_head: 1,
1950            expected_l1_finalized: Some(l1_block(1)),
1951
1952            ..Default::default()
1953        }
1954        .run()
1955        .await
1956    }
1957
1958    #[test_log::test(tokio::test(flavor = "multi_thread"))]
1959    async fn test_new_header_advance_l1_finalized_from_none() {
1960        TestCase {
1961            l1_finalized: Some(l1_block(1)),
1962            expected_l1_finalized: Some(l1_block(1)),
1963            ..Default::default()
1964        }
1965        .run()
1966        .await
1967    }
1968
1969    #[test_log::test(tokio::test(flavor = "multi_thread"))]
1970    async fn test_new_header_timestamp_behind_finalized_l1_block() {
1971        let l1_finalized = Some(L1BlockInfo {
1972            number: 1,
1973            timestamp: U256::from(1),
1974            ..Default::default()
1975        });
1976        TestCase {
1977            l1_head: 1,
1978            l1_finalized,
1979            timestamp: 0,
1980            timestamp_millis: 0,
1981
1982            expected_l1_head: 1,
1983            expected_l1_finalized: l1_finalized,
1984            expected_timestamp: 1,
1985            expected_timestamp_millis: 1_000,
1986
1987            ..Default::default()
1988        }
1989        .run()
1990        .await
1991    }
1992
1993    #[test_log::test(tokio::test(flavor = "multi_thread"))]
1994    async fn test_new_header_timestamp_behind() {
1995        TestCase {
1996            parent_timestamp: 1,
1997            parent_timestamp_millis: 1_000,
1998            timestamp: 0,
1999            timestamp_millis: 0,
2000            expected_timestamp: 1,
2001            expected_timestamp_millis: 1_000,
2002
2003            ..Default::default()
2004        }
2005        .run()
2006        .await
2007    }
2008
2009    #[test_log::test(tokio::test(flavor = "multi_thread"))]
2010    async fn test_new_header_l1_head_behind() {
2011        TestCase {
2012            parent_l1_head: 1,
2013            l1_head: 0,
2014            expected_l1_head: 1,
2015
2016            ..Default::default()
2017        }
2018        .run()
2019        .await
2020    }
2021
2022    #[test_log::test(tokio::test(flavor = "multi_thread"))]
2023    async fn test_new_header_l1_finalized_behind_some() {
2024        TestCase {
2025            parent_l1_finalized: Some(l1_block(1)),
2026            l1_finalized: Some(l1_block(0)),
2027            expected_l1_finalized: Some(l1_block(1)),
2028
2029            ..Default::default()
2030        }
2031        .run()
2032        .await
2033    }
2034
2035    #[test_log::test(tokio::test(flavor = "multi_thread"))]
2036    async fn test_new_header_l1_finalized_behind_none() {
2037        TestCase {
2038            parent_l1_finalized: Some(l1_block(0)),
2039            l1_finalized: None,
2040            expected_l1_finalized: Some(l1_block(0)),
2041
2042            ..Default::default()
2043        }
2044        .run()
2045        .await
2046    }
2047
2048    #[test_log::test(tokio::test(flavor = "multi_thread"))]
2049    async fn test_new_header_deposits_one() {
2050        TestCase {
2051            l1_deposits: vec![FeeInfo::new(Address::default(), 1)],
2052            ..Default::default()
2053        }
2054        .run()
2055        .await
2056    }
2057
2058    #[test_log::test(tokio::test(flavor = "multi_thread"))]
2059    async fn test_new_header_deposits_many() {
2060        TestCase {
2061            l1_deposits: [
2062                (Address::default(), 1),
2063                (Address::default(), 2),
2064                (Address::random(), 3),
2065            ]
2066            .iter()
2067            .map(|(address, amount)| FeeInfo::new(*address, *amount))
2068            .collect(),
2069            ..Default::default()
2070        }
2071        .run()
2072        .await
2073    }
2074
2075    struct GenesisForTest {
2076        pub instance_state: NodeState,
2077        pub validated_state: ValidatedState,
2078        pub leaf: Leaf2,
2079        pub header: Header,
2080        pub ns_table: NsTable,
2081    }
2082
2083    impl GenesisForTest {
2084        async fn default() -> Self {
2085            let instance_state = NodeState::mock();
2086            let validated_state = ValidatedState::genesis(&instance_state).0;
2087            let leaf: Leaf2 = Leaf::genesis(&validated_state, &instance_state, MOCK_UPGRADE.base)
2088                .await
2089                .into();
2090            let header = leaf.block_header().clone();
2091            let ns_table = leaf.block_payload().unwrap().ns_table().clone();
2092            Self {
2093                instance_state,
2094                validated_state,
2095                leaf,
2096                header,
2097                ns_table,
2098            }
2099        }
2100    }
2101
2102    #[test_log::test(tokio::test(flavor = "multi_thread"))]
2103    async fn test_proposal_validation_success() {
2104        let anvil = Anvil::new().block_time(1u64).spawn();
2105        let mut genesis_state = NodeState::mock()
2106            .with_l1(L1Client::new(vec![anvil.endpoint_url()]).expect("Failed to create L1 client"))
2107            .with_current_version(version(0, 1));
2108
2109        let genesis = GenesisForTest::default().await;
2110
2111        let mut parent_state = genesis.validated_state.clone();
2112
2113        let mut block_merkle_tree = parent_state.block_merkle_tree.clone();
2114        let fee_merkle_tree = parent_state.fee_merkle_tree.clone();
2115
2116        // Populate the tree with an initial `push`.
2117        block_merkle_tree.push(genesis.header.commit()).unwrap();
2118        let block_merkle_tree_root = block_merkle_tree.commitment();
2119        let fee_merkle_tree_root = fee_merkle_tree.commitment();
2120        parent_state.block_merkle_tree = block_merkle_tree.clone();
2121        parent_state.fee_merkle_tree = fee_merkle_tree.clone();
2122
2123        let mut parent_header = genesis.header.clone();
2124        *parent_header.block_merkle_tree_root_mut() = block_merkle_tree_root;
2125        *parent_header.fee_merkle_tree_root_mut() = fee_merkle_tree_root;
2126
2127        let mut parent_leaf = genesis.leaf.clone();
2128        *parent_leaf.block_header_mut() = parent_header.clone();
2129
2130        // Forget the state to trigger lookups in Header::new
2131        let forgotten_state = parent_state.forget();
2132        genesis_state.state_catchup = Arc::new(MockStateCatchup::from_iter([(
2133            parent_leaf.view_number(),
2134            Arc::new(parent_state.clone()),
2135        )]));
2136        // Get a proposal from a parent
2137
2138        // TODO this currently fails because after fetching the blocks frontier
2139        // the element (header commitment) does not match the one in the proof.
2140        let key_pair = EthKeyPair::for_test();
2141        let fee_amount = 0u64;
2142        let payload_commitment = parent_header.payload_commitment();
2143        let builder_commitment = parent_header.builder_commitment();
2144        let ns_table = genesis.ns_table;
2145        let fee_signature = FeeAccount::sign_fee(&key_pair, fee_amount, &ns_table).unwrap();
2146        let builder_fee = BuilderFee {
2147            fee_amount,
2148            fee_account: key_pair.fee_account(),
2149            fee_signature,
2150        };
2151        let proposal = Header::new(
2152            &forgotten_state,
2153            &genesis_state,
2154            &parent_leaf,
2155            payload_commitment,
2156            builder_commitment.clone(),
2157            ns_table,
2158            builder_fee,
2159            version(0, 1),
2160            *parent_leaf.view_number() + 1,
2161        )
2162        .await
2163        .unwrap();
2164
2165        let mut proposal_state = parent_state.clone();
2166        for fee_info in genesis_state
2167            .l1_client
2168            .get_finalized_deposits(Address::default(), None, 0)
2169            .await
2170        {
2171            proposal_state.insert_fee_deposit(fee_info).unwrap();
2172        }
2173
2174        let mut block_merkle_tree = proposal_state.block_merkle_tree.clone();
2175        block_merkle_tree.push(proposal.commit()).unwrap();
2176
2177        let _proposal_state = proposal_state
2178            .apply_header(
2179                &genesis_state,
2180                &genesis_state.state_catchup,
2181                &parent_leaf,
2182                &proposal,
2183                version(0, 1),
2184                parent_leaf.view_number() + 1,
2185            )
2186            .await
2187            .unwrap()
2188            .0;
2189
2190        // ValidatedTransition::new(
2191        //     proposal_state.clone(),
2192        //     &parent_leaf.block_header(),
2193        //     Proposal::new(&proposal, ADVZScheme::get_payload_byte_len(&vid_common)),
2194        // )
2195        // .validate()
2196        // .unwrap();
2197
2198        // assert_eq!(
2199        //     proposal_state.block_merkle_tree.commitment(),
2200        //     proposal.block_merkle_tree_root()
2201        // );
2202    }
2203
2204    #[test_log::test]
2205    fn verify_builder_signature() {
2206        // simulate a fixed size hash by padding our message
2207        let message = ";)";
2208        let mut commitment = [0u8; 32];
2209        commitment[..message.len()].copy_from_slice(message.as_bytes());
2210
2211        let key = FeeAccount::generated_from_seed_indexed([0; 32], 0).1;
2212        let signature = FeeAccount::sign_builder_message(&key, &commitment).unwrap();
2213        assert!(
2214            key.fee_account()
2215                .validate_builder_signature(&signature, &commitment)
2216        );
2217    }
2218
2219    #[test_log::test(tokio::test(flavor = "multi_thread"))]
2220    async fn test_versioned_header_serialization() {
2221        let genesis = GenesisForTest::default().await;
2222        let header = genesis.header.clone();
2223        let ns_table = genesis.ns_table;
2224
2225        let (fee_account, _) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
2226
2227        let v1_header = Header::create(
2228            genesis.instance_state.chain_config,
2229            1,
2230            2,
2231            2_000_000_000,
2232            3,
2233            Default::default(),
2234            header.payload_commitment(),
2235            header.builder_commitment().clone(),
2236            ns_table.clone(),
2237            header.fee_merkle_tree_root(),
2238            header.block_merkle_tree_root(),
2239            header.reward_merkle_tree_root().left().unwrap_or_else(|| {
2240                RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
2241            }),
2242            header.reward_merkle_tree_root().right().unwrap_or_else(|| {
2243                RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
2244            }),
2245            vec![FeeInfo {
2246                amount: 0.into(),
2247                account: fee_account,
2248            }],
2249            Default::default(),
2250            None,
2251            version(0, 1),
2252            None,
2253            None, // leader_counts
2254        );
2255
2256        let serialized = serde_json::to_string(&v1_header).unwrap();
2257        let deserialized: Header = serde_json::from_str(&serialized).unwrap();
2258        assert_eq!(v1_header, deserialized);
2259
2260        let v2_header = Header::create(
2261            genesis.instance_state.chain_config,
2262            1,
2263            2,
2264            2_000_000_000,
2265            3,
2266            Default::default(),
2267            header.payload_commitment(),
2268            header.builder_commitment().clone(),
2269            ns_table.clone(),
2270            header.fee_merkle_tree_root(),
2271            header.block_merkle_tree_root(),
2272            header.reward_merkle_tree_root().left().unwrap_or_else(|| {
2273                RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
2274            }),
2275            header.reward_merkle_tree_root().right().unwrap_or_else(|| {
2276                RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
2277            }),
2278            vec![FeeInfo {
2279                amount: 0.into(),
2280                account: fee_account,
2281            }],
2282            Default::default(),
2283            None,
2284            version(0, 2),
2285            None,
2286            None, // leader_counts
2287        );
2288
2289        let serialized = serde_json::to_string(&v2_header).unwrap();
2290        let deserialized: Header = serde_json::from_str(&serialized).unwrap();
2291        assert_eq!(v2_header, deserialized);
2292
2293        let v3_header = Header::create(
2294            genesis.instance_state.chain_config,
2295            1,
2296            2,
2297            2_000_000_000,
2298            3,
2299            Default::default(),
2300            header.payload_commitment(),
2301            header.builder_commitment().clone(),
2302            ns_table.clone(),
2303            header.fee_merkle_tree_root(),
2304            header.block_merkle_tree_root(),
2305            header.reward_merkle_tree_root().left().unwrap_or_else(|| {
2306                RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
2307            }),
2308            header.reward_merkle_tree_root().right().unwrap_or_else(|| {
2309                RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
2310            }),
2311            vec![FeeInfo {
2312                amount: 0.into(),
2313                account: fee_account,
2314            }],
2315            Default::default(),
2316            None,
2317            version(0, 3),
2318            None,
2319            None, // leader_counts
2320        );
2321
2322        let serialized = serde_json::to_string(&v3_header).unwrap();
2323        let deserialized: Header = serde_json::from_str(&serialized).unwrap();
2324        assert_eq!(v3_header, deserialized);
2325
2326        let v1_bytes = BincodeSerializer::<StaticVersion<0, 1>>::serialize(&v1_header).unwrap();
2327        let deserialized: Header =
2328            BincodeSerializer::<StaticVersion<0, 1>>::deserialize(&v1_bytes).unwrap();
2329        assert_eq!(v1_header, deserialized);
2330
2331        let v2_bytes = BincodeSerializer::<StaticVersion<0, 2>>::serialize(&v2_header).unwrap();
2332        let deserialized: Header =
2333            BincodeSerializer::<StaticVersion<0, 2>>::deserialize(&v2_bytes).unwrap();
2334        assert_eq!(v2_header, deserialized);
2335
2336        let v3_bytes = BincodeSerializer::<StaticVersion<0, 3>>::serialize(&v3_header).unwrap();
2337        let deserialized: Header =
2338            BincodeSerializer::<StaticVersion<0, 3>>::deserialize(&v3_bytes).unwrap();
2339        assert_eq!(v3_header, deserialized);
2340    }
2341}