Skip to main content

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