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