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