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