1use std::fmt;
2
3use anyhow::{ensure, Context};
4use ark_serialize::CanonicalSerialize;
5use committable::{Commitment, Committable, RawCommitmentBuilder};
6use hotshot_query_service::{availability::QueryableHeader, explorer::ExplorerHeader};
7use hotshot_types::{
8 data::{vid_commitment, VidCommitment, ViewNumber},
9 light_client::LightClientState,
10 traits::{
11 block_contents::{BlockHeader, BuilderFee, GENESIS_VID_NUM_STORAGE_NODES},
12 node_implementation::{ConsensusTime, NodeType, Versions},
13 signature_key::BuilderSignatureKey,
14 BlockPayload, EncodeBytes, ValidatedState as _,
15 },
16 utils::BuilderCommitment,
17};
18use jf_merkle_tree::{AppendableMerkleTreeScheme, MerkleTreeScheme};
19use serde::{
20 de::{self, MapAccess, SeqAccess, Visitor},
21 Deserialize, Deserializer, Serialize, Serializer,
22};
23use serde_json::{Map, Value};
24use thiserror::Error;
25use time::OffsetDateTime;
26use vbs::version::{StaticVersionType, Version};
27
28use super::{
29 instance_state::NodeState,
30 state::ValidatedState,
31 v0_1::{IterableFeeInfo, RewardMerkleCommitment, RewardMerkleTree, REWARD_MERKLE_TREE_HEIGHT},
32 v0_3::ChainConfig,
33};
34use crate::{
35 eth_signature_key::BuilderSignature,
36 v0::{
37 header::{EitherOrVersion, VersionedHeader},
38 impls::reward::{find_validator_info, first_two_epochs, RewardDistributor},
39 },
40 v0_1, v0_2, v0_3, v0_4, BlockMerkleCommitment, EpochVersion, FeeAccount, FeeAmount, FeeInfo,
41 FeeMerkleCommitment, Header, L1BlockInfo, L1Snapshot, Leaf2, NamespaceId, NsIndex, NsTable,
42 PayloadByteLen, SeqTypes, TimestampMillis, UpgradeType,
43};
44
45impl v0_1::Header {
46 pub(crate) fn commit(&self) -> Commitment<Header> {
47 let mut bmt_bytes = vec![];
48 self.block_merkle_tree_root
49 .serialize_with_mode(&mut bmt_bytes, ark_serialize::Compress::Yes)
50 .unwrap();
51 let mut fmt_bytes = vec![];
52 self.fee_merkle_tree_root
53 .serialize_with_mode(&mut fmt_bytes, ark_serialize::Compress::Yes)
54 .unwrap();
55
56 RawCommitmentBuilder::new(&Self::tag())
57 .field("chain_config", self.chain_config.commit())
58 .u64_field("height", self.height)
59 .u64_field("timestamp", self.timestamp)
60 .u64_field("l1_head", self.l1_head)
61 .optional("l1_finalized", &self.l1_finalized)
62 .constant_str("payload_commitment")
63 .fixed_size_bytes(self.payload_commitment.as_ref())
64 .constant_str("builder_commitment")
65 .fixed_size_bytes(self.builder_commitment.as_ref())
66 .field("ns_table", self.ns_table.commit())
67 .var_size_field("block_merkle_tree_root", &bmt_bytes)
68 .var_size_field("fee_merkle_tree_root", &fmt_bytes)
69 .field("fee_info", self.fee_info.commit())
70 .finalize()
71 }
72}
73
74impl Committable for Header {
75 fn commit(&self) -> Commitment<Self> {
76 match self {
77 Self::V1(header) => header.commit(),
78 Self::V2(fields) => RawCommitmentBuilder::new(&Self::tag())
79 .u64_field("version_major", 0)
80 .u64_field("version_minor", 2)
81 .field("fields", fields.commit())
82 .finalize(),
83 Self::V3(fields) => RawCommitmentBuilder::new(&Self::tag())
84 .u64_field("version_major", 0)
85 .u64_field("version_minor", 3)
86 .field("fields", fields.commit())
87 .finalize(),
88 Self::V4(fields) => RawCommitmentBuilder::new(&Self::tag())
89 .u64_field("version_major", 0)
90 .u64_field("version_minor", 4)
91 .field("fields", fields.commit())
92 .finalize(),
93 }
94 }
95
96 fn tag() -> String {
97 "BLOCK".into()
100 }
101}
102
103impl Serialize for Header {
104 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
105 where
106 S: Serializer,
107 {
108 match self {
109 Self::V1(header) => header.serialize(serializer),
110 Self::V2(fields) => VersionedHeader {
111 version: EitherOrVersion::Version(Version { major: 0, minor: 2 }),
112 fields: fields.clone(),
113 }
114 .serialize(serializer),
115 Self::V3(fields) => VersionedHeader {
116 version: EitherOrVersion::Version(Version { major: 0, minor: 3 }),
117 fields: fields.clone(),
118 }
119 .serialize(serializer),
120 Self::V4(fields) => VersionedHeader {
121 version: EitherOrVersion::Version(Version { major: 0, minor: 4 }),
122 fields: fields.clone(),
123 }
124 .serialize(serializer),
125 }
126 }
127}
128
129impl<'de> Deserialize<'de> for Header {
130 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
131 where
132 D: Deserializer<'de>,
133 {
134 struct HeaderVisitor;
135
136 impl<'de> Visitor<'de> for HeaderVisitor {
137 type Value = Header;
138
139 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
140 formatter.write_str("Header")
141 }
142
143 fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
144 where
145 V: SeqAccess<'de>,
146 {
147 let chain_config_or_version: EitherOrVersion = seq
148 .next_element()?
149 .ok_or_else(|| de::Error::missing_field("chain_config"))?;
150
151 match chain_config_or_version {
152 EitherOrVersion::Left(cfg) => Ok(Header::V1(
155 v0_1::Header::deserialize_with_chain_config(cfg.into(), seq)?,
156 )),
157 EitherOrVersion::Right(commit) => Ok(Header::V1(
158 v0_1::Header::deserialize_with_chain_config(commit.into(), seq)?,
159 )),
160 EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
163 seq.next_element()?
164 .ok_or_else(|| de::Error::missing_field("fields"))?,
165 )),
166 EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
167 seq.next_element()?
168 .ok_or_else(|| de::Error::missing_field("fields"))?,
169 )),
170 EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
171 seq.next_element()?
172 .ok_or_else(|| de::Error::missing_field("fields"))?,
173 )),
174 EitherOrVersion::Version(v) => {
175 Err(serde::de::Error::custom(format!("invalid version {v:?}")))
176 },
177 }
178 }
179
180 fn visit_map<V>(self, mut map: V) -> Result<Header, V::Error>
181 where
182 V: MapAccess<'de>,
183 {
184 let mut serde_map: Map<String, Value> = Map::new();
186
187 while let Some(key) = map.next_key::<String>()? {
188 serde_map.insert(key.trim().to_owned(), map.next_value()?);
189 }
190
191 if let Some(v) = serde_map.get("version") {
192 let fields = serde_map
193 .get("fields")
194 .ok_or_else(|| de::Error::missing_field("fields"))?;
195
196 let version = serde_json::from_value::<EitherOrVersion>(v.clone())
197 .map_err(de::Error::custom)?;
198 let result = match version {
199 EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
200 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
201 )),
202 EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
203 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
204 )),
205 EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
206 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
207 )),
208 EitherOrVersion::Version(v) => {
209 Err(de::Error::custom(format!("invalid version {v:?}")))
210 },
211 chain_config => Err(de::Error::custom(format!(
212 "expected version, found chain_config {chain_config:?}"
213 ))),
214 };
215 return result;
216 }
217
218 Ok(Header::V1(
219 serde_json::from_value(serde_map.into()).map_err(de::Error::custom)?,
220 ))
221 }
222 }
223
224 let fields: &[&str] = &[
239 "fields",
240 "chain_config",
241 "version",
242 "height",
243 "timestamp",
244 "l1_head",
245 "l1_finalized",
246 "payload_commitment",
247 "builder_commitment",
248 "ns_table",
249 "block_merkle_tree_root",
250 "fee_merkle_tree_root",
251 "fee_info",
252 "builder_signature",
253 ];
254
255 deserializer.deserialize_struct("Header", fields, HeaderVisitor)
256 }
257}
258
259impl Header {
260 pub fn version(&self) -> Version {
261 match self {
262 Self::V1(_) => Version { major: 0, minor: 1 },
263 Self::V2(_) => Version { major: 0, minor: 2 },
264 Self::V3(_) => Version { major: 0, minor: 3 },
265 Self::V4(_) => Version { major: 0, minor: 4 },
266 }
267 }
268 #[allow(clippy::too_many_arguments)]
269 pub(crate) fn create(
270 chain_config: ChainConfig,
271 height: u64,
272 timestamp: u64,
273 timestamp_millis: u64,
274 l1_head: u64,
275 l1_finalized: Option<L1BlockInfo>,
276 payload_commitment: VidCommitment,
277 builder_commitment: BuilderCommitment,
278 ns_table: NsTable,
279 fee_merkle_tree_root: FeeMerkleCommitment,
280 block_merkle_tree_root: BlockMerkleCommitment,
281 reward_merkle_tree_root: RewardMerkleCommitment,
282 fee_info: Vec<FeeInfo>,
283 builder_signature: Vec<BuilderSignature>,
284 version: Version,
285 ) -> Self {
286 assert!(!fee_info.is_empty(), "Invalid fee_info length: 0");
288
289 match (version.major, version.minor) {
290 (0, 1) => Self::V1(v0_1::Header {
291 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
292 chain_config,
293 )),
294 height,
295 timestamp,
296 l1_head,
297 l1_finalized,
298 payload_commitment,
299 builder_commitment,
300 ns_table,
301 block_merkle_tree_root,
302 fee_merkle_tree_root,
303 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
305 }),
306 (0, 2) => Self::V2(v0_2::Header {
307 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
308 chain_config,
309 )),
310 height,
311 timestamp,
312 l1_head,
313 l1_finalized,
314 payload_commitment,
315 builder_commitment,
316 ns_table,
317 block_merkle_tree_root,
318 fee_merkle_tree_root,
319 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
321 }),
322 (0, 3) => Self::V3(v0_3::Header {
323 chain_config: chain_config.into(),
324 height,
325 timestamp,
326 l1_head,
327 l1_finalized,
328 payload_commitment,
329 builder_commitment,
330 ns_table,
331 block_merkle_tree_root,
332 fee_merkle_tree_root,
333 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
335 reward_merkle_tree_root,
336 }),
337 (0, 4) => Self::V4(v0_4::Header {
338 chain_config: chain_config.into(),
339 height,
340 timestamp,
341 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
342 l1_head,
343 l1_finalized,
344 payload_commitment,
345 builder_commitment,
346 ns_table,
347 block_merkle_tree_root,
348 fee_merkle_tree_root,
349 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
351 reward_merkle_tree_root,
352 }),
353 _ => panic!("invalid version: {version}"),
357 }
358 }
359}
360
361macro_rules! field {
363 ($obj:ident.$name:ident) => {
364 match $obj {
365 Self::V1(data) => &data.$name,
366 Self::V2(data) => &data.$name,
367 Self::V3(data) => &data.$name,
368 Self::V4(data) => &data.$name,
369 }
370 };
371}
372
373macro_rules! field_mut {
374 ($obj:ident.$name:ident) => {
375 match $obj {
376 Self::V1(data) => &mut data.$name,
377 Self::V2(data) => &mut data.$name,
378 Self::V3(data) => &mut data.$name,
379 Self::V4(data) => &mut data.$name,
380 }
381 };
382}
383
384impl Header {
385 #[allow(clippy::too_many_arguments)]
386 fn from_info(
387 payload_commitment: VidCommitment,
388 builder_commitment: BuilderCommitment,
389 ns_table: NsTable,
390 parent_leaf: &Leaf2,
391 mut l1: L1Snapshot,
392 l1_deposits: &[FeeInfo],
393 builder_fee: Vec<BuilderFee<SeqTypes>>,
394 mut timestamp: u64,
395 mut timestamp_millis: u64,
396 mut state: ValidatedState,
397 chain_config: ChainConfig,
398 version: Version,
399 reward_distributor: Option<RewardDistributor>,
400 ) -> anyhow::Result<Self> {
401 ensure!(
402 version.major == 0,
403 "Invalid major version {}",
404 version.major
405 );
406
407 let parent_header = parent_leaf.block_header();
409 let height = parent_header.height() + 1;
410
411 if timestamp < parent_header.timestamp() {
415 tracing::warn!(
416 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
417 parent_header.timestamp()
418 );
419 timestamp = parent_header.timestamp();
420 }
421
422 if timestamp_millis < parent_header.timestamp_millis() {
423 tracing::warn!(
424 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
425 parent_header.timestamp_millis()
426 );
427 timestamp_millis = parent_header.timestamp_millis();
428 }
429
430 if l1.head < parent_header.l1_head() {
433 tracing::warn!(
434 "L1 head {} behind parent {}, L1 client may be lagging",
435 l1.head,
436 parent_header.l1_head()
437 );
438 l1.head = parent_header.l1_head();
439 }
440 if l1.finalized < parent_header.l1_finalized() {
441 tracing::warn!(
442 "L1 finalized {:?} behind parent {:?}, L1 client may be lagging",
443 l1.finalized,
444 parent_header.l1_finalized()
445 );
446 l1.finalized = parent_header.l1_finalized();
447 }
448
449 if let Some(l1_block) = &l1.finalized {
452 let l1_timestamp = l1_block.timestamp.to::<u64>();
453 if timestamp < l1_timestamp {
454 tracing::warn!("Espresso timestamp {timestamp} behind L1 timestamp {l1_timestamp}, local clock may be out of sync");
455 timestamp = l1_timestamp;
456 }
457
458 let l1_timestamp_millis = l1_timestamp * 1_000;
459
460 if timestamp_millis < l1_timestamp_millis {
461 tracing::warn!("Espresso timestamp_millis {timestamp_millis} behind L1 timestamp {l1_timestamp_millis}, local clock may be out of sync");
462 timestamp_millis = l1_timestamp_millis;
463 }
464 }
465
466 state
467 .block_merkle_tree
468 .push(parent_header.commit())
469 .context("missing blocks frontier")?;
470 let block_merkle_tree_root = state.block_merkle_tree.commitment();
471
472 for fee_info in l1_deposits {
474 state
475 .insert_fee_deposit(*fee_info)
476 .context(format!("missing fee account {}", fee_info.account()))?;
477 }
478
479 for BuilderFee {
481 fee_account,
482 fee_signature,
483 fee_amount,
484 } in &builder_fee
485 {
486 ensure!(
487 fee_account.validate_fee_signature(fee_signature, *fee_amount, &ns_table)
488 || fee_account.validate_fee_signature_with_vid_commitment(
489 fee_signature,
490 *fee_amount,
491 &ns_table,
492 &payload_commitment
493 ),
494 "invalid builder signature"
495 );
496
497 let fee_info = FeeInfo::new(*fee_account, *fee_amount);
498 state
499 .charge_fee(fee_info, chain_config.fee_recipient)
500 .context(format!("invalid builder fee {fee_info:?}"))?;
501 }
502
503 let fee_info = FeeInfo::from_builder_fees(builder_fee.clone());
504
505 let builder_signature: Vec<BuilderSignature> =
506 builder_fee.iter().map(|e| e.fee_signature).collect();
507
508 let fee_merkle_tree_root = state.fee_merkle_tree.commitment();
509
510 if let Some(reward_distributor) = reward_distributor {
511 let reward_state =
512 reward_distributor.apply_rewards(state.reward_merkle_tree.clone())?;
513 state.reward_merkle_tree = reward_state;
514 }
515
516 let header = match (version.major, version.minor) {
517 (0, 1) => Self::V1(v0_1::Header {
518 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
519 chain_config,
520 )),
521 height,
522 timestamp,
523 l1_head: l1.head,
524 l1_finalized: l1.finalized,
525 payload_commitment,
526 builder_commitment,
527 ns_table,
528 block_merkle_tree_root,
529 fee_merkle_tree_root,
530 fee_info: fee_info[0],
531 builder_signature: builder_signature.first().copied(),
532 }),
533 (0, 2) => Self::V2(v0_2::Header {
534 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
535 chain_config,
536 )),
537 height,
538 timestamp,
539 l1_head: l1.head,
540 l1_finalized: l1.finalized,
541 payload_commitment,
542 builder_commitment,
543 ns_table,
544 block_merkle_tree_root,
545 fee_merkle_tree_root,
546 fee_info: fee_info[0],
547 builder_signature: builder_signature.first().copied(),
548 }),
549 (0, 3) => Self::V3(v0_3::Header {
550 chain_config: chain_config.into(),
551 height,
552 timestamp,
553 l1_head: l1.head,
554 l1_finalized: l1.finalized,
555 payload_commitment,
556 builder_commitment,
557 ns_table,
558 block_merkle_tree_root,
559 fee_merkle_tree_root,
560 reward_merkle_tree_root: state.reward_merkle_tree.commitment(),
561 fee_info: fee_info[0],
562 builder_signature: builder_signature.first().copied(),
563 }),
564 (0, 4) => Self::V4(v0_4::Header {
565 chain_config: chain_config.into(),
566 height,
567 timestamp,
568 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
569 l1_head: l1.head,
570 l1_finalized: l1.finalized,
571 payload_commitment,
572 builder_commitment,
573 ns_table,
574 block_merkle_tree_root,
575 fee_merkle_tree_root,
576 reward_merkle_tree_root: state.reward_merkle_tree.commitment(),
577 fee_info: fee_info[0],
578 builder_signature: builder_signature.first().copied(),
579 }),
580 _ => panic!("invalid version: {version}"),
584 };
585 Ok(header)
586 }
587
588 async fn get_chain_config(
589 validated_state: &ValidatedState,
590 instance_state: &NodeState,
591 ) -> anyhow::Result<ChainConfig> {
592 let validated_cf = validated_state.chain_config;
593 let instance_cf = instance_state.chain_config;
594
595 if validated_cf.commit() == instance_cf.commit() {
596 return Ok(instance_cf);
597 }
598
599 match validated_cf.resolve() {
600 Some(cf) => Ok(cf),
601 None => {
602 tracing::info!("fetching chain config {} from peers", validated_cf.commit());
603
604 instance_state
605 .state_catchup
606 .as_ref()
607 .fetch_chain_config(validated_cf.commit())
608 .await
609 },
610 }
611 }
612}
613
614impl Header {
615 pub fn chain_config(&self) -> v0_3::ResolvableChainConfig {
617 match self {
618 Self::V1(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
619 Self::V2(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
620 Self::V3(fields) => fields.chain_config,
621 Self::V4(fields) => fields.chain_config,
622 }
623 }
624
625 pub fn height(&self) -> u64 {
626 *field!(self.height)
627 }
628
629 pub fn height_mut(&mut self) -> &mut u64 {
630 &mut *field_mut!(self.height)
631 }
632
633 pub fn timestamp_internal(&self) -> u64 {
634 match self {
635 Self::V1(fields) => fields.timestamp,
636 Self::V2(fields) => fields.timestamp,
637 Self::V3(fields) => fields.timestamp,
638 Self::V4(fields) => fields.timestamp,
639 }
640 }
641
642 pub fn timestamp_millis_internal(&self) -> u64 {
643 match self {
644 Self::V1(fields) => fields.timestamp * 1_000,
645 Self::V2(fields) => fields.timestamp * 1_000,
646 Self::V3(fields) => fields.timestamp * 1_000,
647 Self::V4(fields) => fields.timestamp_millis.u64(),
648 }
649 }
650
651 pub fn set_timestamp(&mut self, timestamp: u64, timestamp_millis: u64) {
652 match self {
653 Self::V1(fields) => {
654 fields.timestamp = timestamp;
655 },
656 Self::V2(fields) => {
657 fields.timestamp = timestamp;
658 },
659 Self::V3(fields) => {
660 fields.timestamp = timestamp;
661 },
662 Self::V4(fields) => {
663 fields.timestamp = timestamp;
664 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
665 },
666 };
667 }
668
669 pub fn l1_head(&self) -> u64 {
694 *field!(self.l1_head)
695 }
696
697 pub fn l1_head_mut(&mut self) -> &mut u64 {
698 &mut *field_mut!(self.l1_head)
699 }
700
701 pub fn l1_finalized(&self) -> Option<L1BlockInfo> {
716 *field!(self.l1_finalized)
717 }
718
719 pub fn l1_finalized_mut(&mut self) -> &mut Option<L1BlockInfo> {
720 &mut *field_mut!(self.l1_finalized)
721 }
722
723 pub fn payload_commitment(&self) -> VidCommitment {
724 *field!(self.payload_commitment)
725 }
726
727 pub fn payload_commitment_mut(&mut self) -> &mut VidCommitment {
728 &mut *field_mut!(self.payload_commitment)
729 }
730
731 pub fn builder_commitment(&self) -> &BuilderCommitment {
732 field!(self.builder_commitment)
733 }
734
735 pub fn builder_commitment_mut(&mut self) -> &mut BuilderCommitment {
736 &mut *field_mut!(self.builder_commitment)
737 }
738
739 pub fn ns_table(&self) -> &NsTable {
740 field!(self.ns_table)
741 }
742
743 pub fn block_merkle_tree_root(&self) -> BlockMerkleCommitment {
745 *field!(self.block_merkle_tree_root)
746 }
747
748 pub fn block_merkle_tree_root_mut(&mut self) -> &mut BlockMerkleCommitment {
749 &mut *field_mut!(self.block_merkle_tree_root)
750 }
751
752 pub fn fee_merkle_tree_root(&self) -> FeeMerkleCommitment {
754 *field!(self.fee_merkle_tree_root)
755 }
756
757 pub fn fee_merkle_tree_root_mut(&mut self) -> &mut FeeMerkleCommitment {
758 &mut *field_mut!(self.fee_merkle_tree_root)
759 }
760
761 pub fn fee_info(&self) -> Vec<FeeInfo> {
763 match self {
764 Self::V1(fields) => vec![fields.fee_info],
765 Self::V2(fields) => vec![fields.fee_info],
766 Self::V3(fields) => vec![fields.fee_info],
767 Self::V4(fields) => vec![fields.fee_info],
768 }
769 }
770
771 pub fn reward_merkle_tree_root(&self) -> RewardMerkleCommitment {
773 let empty_reward_merkle_tree = RewardMerkleTree::new(REWARD_MERKLE_TREE_HEIGHT);
774 match self {
775 Self::V1(_) => empty_reward_merkle_tree.commitment(),
776 Self::V2(_) => empty_reward_merkle_tree.commitment(),
777 Self::V3(fields) => fields.reward_merkle_tree_root,
778 Self::V4(fields) => fields.reward_merkle_tree_root,
779 }
780 }
781
782 pub fn builder_signature(&self) -> Vec<BuilderSignature> {
791 match self {
792 Self::V1(fields) => fields.builder_signature.as_slice().to_vec(),
797 Self::V2(fields) => fields.builder_signature.as_slice().to_vec(),
798 Self::V3(fields) => fields.builder_signature.as_slice().to_vec(),
799 Self::V4(fields) => fields.builder_signature.as_slice().to_vec(),
800 }
801 }
802}
803
804#[derive(Debug, Error)]
805#[error("Invalid Block Header {msg}")]
806pub struct InvalidBlockHeader {
807 msg: String,
808}
809impl InvalidBlockHeader {
810 fn new(msg: String) -> Self {
811 Self { msg }
812 }
813}
814
815impl From<anyhow::Error> for InvalidBlockHeader {
816 fn from(err: anyhow::Error) -> Self {
817 Self::new(format!("{err:#}"))
818 }
819}
820
821impl BlockHeader<SeqTypes> for Header {
822 type Error = InvalidBlockHeader;
823
824 #[tracing::instrument(
825 skip_all,
826 fields(
827 node_id = instance_state.node_id,
828 view = ?parent_leaf.view_number(),
829 height = parent_leaf.block_header().height(),
830 ),
831 )]
832 #[tracing::instrument(
833 skip_all,
834 fields(
835 height = parent_leaf.block_header().block_number() + 1,
836 parent_view = ?parent_leaf.view_number(),
837 payload_commitment,
838 version,
839 )
840 )]
841 async fn new(
842 parent_state: &ValidatedState,
843 instance_state: &NodeState,
844 parent_leaf: &Leaf2,
845 payload_commitment: VidCommitment,
846 builder_commitment: BuilderCommitment,
847 metadata: <<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
848 builder_fee: BuilderFee<SeqTypes>,
849 version: Version,
850 view_number: u64,
851 ) -> Result<Self, Self::Error> {
852 tracing::info!("preparing to propose legacy header");
853
854 let height = parent_leaf.height();
855 let view = parent_leaf.view_number();
856
857 let mut validated_state = parent_state.clone();
858
859 let chain_config = if version > instance_state.current_version {
860 match instance_state.upgrades.get(&version) {
861 Some(upgrade) => match upgrade.upgrade_type {
862 UpgradeType::Fee { chain_config } => chain_config,
863 UpgradeType::Epoch { chain_config } => chain_config,
864 UpgradeType::DrbAndHeader { chain_config } => chain_config,
865 },
866 None => Header::get_chain_config(&validated_state, instance_state).await?,
867 }
868 } else {
869 Header::get_chain_config(&validated_state, instance_state).await?
870 };
871
872 validated_state.chain_config = chain_config.into();
873
874 let l1_snapshot = instance_state.l1_client.snapshot().await;
876 let l1_deposits = if let (Some(addr), Some(block_info)) =
878 (chain_config.fee_contract, l1_snapshot.finalized)
879 {
880 instance_state
881 .l1_client
882 .get_finalized_deposits(
883 addr,
884 parent_leaf
885 .block_header()
886 .l1_finalized()
887 .map(|block_info| block_info.number),
888 block_info.number,
889 )
890 .await
891 } else {
892 vec![]
893 };
894 let missing_accounts = parent_state.forgotten_accounts(
898 [builder_fee.fee_account, chain_config.fee_recipient]
899 .into_iter()
900 .chain(l1_deposits.iter().map(|info| info.account())),
901 );
902 if !missing_accounts.is_empty() {
903 tracing::warn!(
904 height,
905 ?view,
906 ?missing_accounts,
907 "fetching missing accounts from peers"
908 );
909
910 let missing_account_proofs = instance_state
912 .state_catchup
913 .as_ref()
914 .fetch_accounts(
915 instance_state,
916 height,
917 view,
918 parent_state.fee_merkle_tree.commitment(),
919 missing_accounts,
920 )
921 .await?;
922
923 for proof in missing_account_proofs.iter() {
925 proof
926 .remember(&mut validated_state.fee_merkle_tree)
927 .context("remembering fee account")?;
928 }
929 }
930
931 if validated_state.need_to_fetch_blocks_mt_frontier() {
933 tracing::warn!(height, ?view, "fetching block frontier from peers");
934 instance_state
935 .state_catchup
936 .as_ref()
937 .remember_blocks_merkle_tree(
938 instance_state,
939 height,
940 view,
941 &mut validated_state.block_merkle_tree,
942 )
943 .await
944 .context("remembering block proof")?;
945 }
946
947 let mut compute_reward = None;
948 let proposed_header_height = parent_leaf.height() + 1;
951 if version >= EpochVersion::version()
952 && !first_two_epochs(proposed_header_height, instance_state).await?
953 {
954 let leader = find_validator_info(
955 instance_state,
956 &mut validated_state,
957 parent_leaf,
958 ViewNumber::new(view_number),
959 )
960 .await?;
961 let block_reward = instance_state.block_reward().await;
962 compute_reward = Some(RewardDistributor::new(leader, block_reward));
963 };
964
965 let now = OffsetDateTime::now_utc();
966
967 let timestamp = now.unix_timestamp() as u64;
968 let timestamp_millis = TimestampMillis::from_time(&now).u64();
969
970 Ok(Self::from_info(
971 payload_commitment,
972 builder_commitment,
973 metadata,
974 parent_leaf,
975 l1_snapshot,
976 &l1_deposits,
977 vec![builder_fee],
978 timestamp,
979 timestamp_millis,
980 validated_state,
981 chain_config,
982 version,
983 compute_reward,
984 )?)
985 }
986
987 fn genesis<V: Versions>(
988 instance_state: &NodeState,
989 payload: <SeqTypes as NodeType>::BlockPayload,
990 metadata: &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
991 ) -> Self {
992 let payload_bytes = payload.encode();
993 let builder_commitment = payload.builder_commitment(metadata);
994
995 let vid_commitment_version = instance_state.genesis_version;
996
997 let payload_commitment = vid_commitment::<V>(
998 &payload_bytes,
999 &metadata.encode(),
1000 GENESIS_VID_NUM_STORAGE_NODES,
1001 vid_commitment_version,
1002 );
1003
1004 let ValidatedState {
1005 fee_merkle_tree,
1006 block_merkle_tree,
1007 reward_merkle_tree,
1008 ..
1009 } = ValidatedState::genesis(instance_state).0;
1010 let block_merkle_tree_root = block_merkle_tree.commitment();
1011 let fee_merkle_tree_root = fee_merkle_tree.commitment();
1012 let reward_merkle_tree_root = reward_merkle_tree.commitment();
1013
1014 let time = instance_state.genesis_header.timestamp;
1015
1016 let timestamp = time.unix_timestamp();
1017 let timestamp_millis = time.unix_timestamp_millis();
1018
1019 Self::create(
1022 instance_state.chain_config,
1023 0,
1024 timestamp,
1025 timestamp_millis,
1026 instance_state
1027 .l1_genesis
1028 .map(|block| block.number)
1029 .unwrap_or_default(),
1030 instance_state.l1_genesis,
1031 payload_commitment,
1032 builder_commitment.clone(),
1033 metadata.clone(),
1034 fee_merkle_tree_root,
1035 block_merkle_tree_root,
1036 reward_merkle_tree_root,
1037 vec![FeeInfo::genesis()],
1038 vec![],
1039 instance_state.current_version,
1040 )
1041 }
1042
1043 fn timestamp(&self) -> u64 {
1044 self.timestamp_internal()
1045 }
1046
1047 fn timestamp_millis(&self) -> u64 {
1048 self.timestamp_millis_internal()
1049 }
1050
1051 fn block_number(&self) -> u64 {
1052 self.height()
1053 }
1054
1055 fn payload_commitment(&self) -> VidCommitment {
1056 self.payload_commitment()
1057 }
1058
1059 fn metadata(
1060 &self,
1061 ) -> &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata {
1062 self.ns_table()
1063 }
1064
1065 fn builder_commitment(&self) -> BuilderCommitment {
1067 self.builder_commitment().clone()
1068 }
1069
1070 fn get_light_client_state(
1071 &self,
1072 view: <SeqTypes as NodeType>::View,
1073 ) -> anyhow::Result<LightClientState> {
1074 let mut block_comm_root_bytes = vec![];
1075 self.block_merkle_tree_root()
1076 .serialize_compressed(&mut block_comm_root_bytes)?;
1077
1078 Ok(LightClientState {
1079 view_number: view.u64(),
1080 block_height: self.height(),
1081 block_comm_root: hotshot_types::light_client::hash_bytes_to_field(
1082 &block_comm_root_bytes,
1083 )?,
1084 })
1085 }
1086}
1087
1088impl QueryableHeader<SeqTypes> for Header {
1089 type NamespaceId = NamespaceId;
1090 type NamespaceIndex = NsIndex;
1091
1092 fn namespace_id(&self, i: &NsIndex) -> Option<NamespaceId> {
1093 self.ns_table().read_ns_id(i)
1094 }
1095
1096 fn namespace_size(&self, i: &NsIndex, payload_size: usize) -> u64 {
1097 self.ns_table()
1098 .ns_range(i, &PayloadByteLen(payload_size))
1099 .byte_len()
1100 .0 as u64
1101 }
1102}
1103
1104impl ExplorerHeader<SeqTypes> for Header {
1105 type BalanceAmount = FeeAmount;
1106 type WalletAddress = Vec<FeeAccount>;
1107 type ProposerId = Vec<FeeAccount>;
1108
1109 fn proposer_id(&self) -> Self::ProposerId {
1111 self.fee_info().accounts()
1112 }
1113
1114 fn fee_info_account(&self) -> Self::WalletAddress {
1115 self.fee_info().accounts()
1116 }
1117
1118 fn fee_info_balance(&self) -> Self::BalanceAmount {
1119 self.fee_info().amount().unwrap()
1121 }
1122
1123 fn reward_balance(&self) -> Self::BalanceAmount {
1129 FeeAmount::from(0)
1130 }
1131
1132 fn namespace_ids(&self) -> Vec<NamespaceId> {
1133 self.ns_table()
1134 .iter()
1135 .map(|i| self.ns_table().read_ns_id_unchecked(&i))
1136 .collect()
1137 }
1138}
1139
1140#[cfg(test)]
1141mod test_headers {
1142 use std::sync::Arc;
1143
1144 use alloy::{
1145 node_bindings::Anvil,
1146 primitives::{Address, U256},
1147 };
1148 use hotshot_query_service::testing::mocks::MockVersions;
1149 use hotshot_types::traits::signature_key::BuilderSignatureKey;
1150 use sequencer_utils::test_utils::setup_test;
1151 use v0_1::{BlockMerkleTree, FeeMerkleTree, L1Client};
1152 use vbs::{bincode_serializer::BincodeSerializer, version::StaticVersion, BinarySerializer};
1153
1154 use super::*;
1155 use crate::{
1156 eth_signature_key::EthKeyPair,
1157 mock::MockStateCatchup,
1158 v0_1::{RewardInfo, RewardMerkleTree},
1159 Leaf,
1160 };
1161
1162 #[derive(Debug, Default)]
1163 #[must_use]
1164 struct TestCase {
1165 parent_timestamp: u64,
1167 parent_timestamp_millis: u64,
1168 parent_l1_head: u64,
1169 parent_l1_finalized: Option<L1BlockInfo>,
1170
1171 l1_head: u64,
1173 l1_finalized: Option<L1BlockInfo>,
1174 timestamp: u64,
1175 timestamp_millis: u64,
1176 l1_deposits: Vec<FeeInfo>,
1177
1178 expected_timestamp: u64,
1180 expected_timestamp_millis: u64,
1181 expected_l1_head: u64,
1182 expected_l1_finalized: Option<L1BlockInfo>,
1183 }
1184
1185 impl TestCase {
1186 async fn run(self) {
1187 setup_test();
1188
1189 assert!(self.expected_timestamp >= self.parent_timestamp);
1191 assert!(self.expected_timestamp_millis >= self.parent_timestamp_millis);
1192 assert!(self.expected_l1_head >= self.parent_l1_head);
1193 assert!(self.expected_l1_finalized >= self.parent_l1_finalized);
1194
1195 let genesis = GenesisForTest::default().await;
1196 let mut parent = genesis.header.clone();
1197 parent.set_timestamp(self.parent_timestamp, self.parent_timestamp_millis);
1198 *parent.l1_head_mut() = self.parent_l1_head;
1199 *parent.l1_finalized_mut() = self.parent_l1_finalized;
1200
1201 let mut parent_leaf = genesis.leaf.clone();
1202 *parent_leaf.block_header_mut() = parent.clone();
1203
1204 let block_merkle_tree =
1205 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap();
1206
1207 let fee_info = FeeInfo::genesis();
1208 let fee_merkle_tree = FeeMerkleTree::from_kv_set(
1209 20,
1210 Vec::from([(fee_info.account(), fee_info.amount())]),
1211 )
1212 .unwrap();
1213
1214 let reward_info = RewardInfo {
1215 account: Default::default(),
1216 amount: Default::default(),
1217 };
1218 let reward_merkle_tree = RewardMerkleTree::from_kv_set(
1219 20,
1220 Vec::from([(reward_info.account, reward_info.amount)]),
1221 )
1222 .unwrap();
1223 let mut validated_state = ValidatedState {
1224 block_merkle_tree: block_merkle_tree.clone(),
1225 fee_merkle_tree,
1226 reward_merkle_tree,
1227 chain_config: genesis.instance_state.chain_config.into(),
1228 };
1229
1230 let (fee_account, fee_key) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1231 let fee_amount = 0;
1232 let fee_signature =
1233 FeeAccount::sign_fee(&fee_key, fee_amount, &genesis.ns_table).unwrap();
1234
1235 let header = Header::from_info(
1236 genesis.header.payload_commitment(),
1237 genesis.header.builder_commitment().clone(),
1238 genesis.ns_table,
1239 &parent_leaf,
1240 L1Snapshot {
1241 head: self.l1_head,
1242 finalized: self.l1_finalized,
1243 },
1244 &self.l1_deposits,
1245 vec![BuilderFee {
1246 fee_account,
1247 fee_amount,
1248 fee_signature,
1249 }],
1250 self.timestamp,
1251 self.timestamp_millis,
1252 validated_state.clone(),
1253 genesis.instance_state.chain_config,
1254 Version { major: 0, minor: 1 },
1255 None,
1256 )
1257 .unwrap();
1258 assert_eq!(header.height(), parent.height() + 1);
1259 assert_eq!(header.timestamp(), self.expected_timestamp);
1260 assert_eq!(header.timestamp_millis(), self.expected_timestamp_millis);
1261 assert_eq!(header.l1_head(), self.expected_l1_head);
1262 assert_eq!(header.l1_finalized(), self.expected_l1_finalized);
1263
1264 for fee_info in self.l1_deposits {
1266 validated_state.insert_fee_deposit(fee_info).unwrap();
1267 }
1268 assert_eq!(
1269 validated_state.fee_merkle_tree.commitment(),
1270 header.fee_merkle_tree_root(),
1271 );
1272
1273 assert_eq!(
1274 block_merkle_tree,
1275 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap()
1276 );
1277 }
1278 }
1279
1280 fn l1_block(number: u64) -> L1BlockInfo {
1281 L1BlockInfo {
1282 number,
1283 ..Default::default()
1284 }
1285 }
1286
1287 #[tokio::test(flavor = "multi_thread")]
1288 async fn test_new_header() {
1289 TestCase::default().run().await
1291 }
1292
1293 #[tokio::test(flavor = "multi_thread")]
1294 async fn test_new_header_advance_timestamp() {
1295 TestCase {
1296 timestamp: 1,
1297 timestamp_millis: 1_000,
1298 expected_timestamp: 1,
1299 expected_timestamp_millis: 1_000,
1300 ..Default::default()
1301 }
1302 .run()
1303 .await
1304 }
1305
1306 #[tokio::test(flavor = "multi_thread")]
1307 async fn test_new_header_advance_l1_block() {
1308 TestCase {
1309 parent_l1_head: 0,
1310 parent_l1_finalized: Some(l1_block(0)),
1311
1312 l1_head: 1,
1313 l1_finalized: Some(l1_block(1)),
1314
1315 expected_l1_head: 1,
1316 expected_l1_finalized: Some(l1_block(1)),
1317
1318 ..Default::default()
1319 }
1320 .run()
1321 .await
1322 }
1323
1324 #[tokio::test(flavor = "multi_thread")]
1325 async fn test_new_header_advance_l1_finalized_from_none() {
1326 TestCase {
1327 l1_finalized: Some(l1_block(1)),
1328 expected_l1_finalized: Some(l1_block(1)),
1329 ..Default::default()
1330 }
1331 .run()
1332 .await
1333 }
1334
1335 #[tokio::test(flavor = "multi_thread")]
1336 async fn test_new_header_timestamp_behind_finalized_l1_block() {
1337 let l1_finalized = Some(L1BlockInfo {
1338 number: 1,
1339 timestamp: U256::from(1),
1340 ..Default::default()
1341 });
1342 TestCase {
1343 l1_head: 1,
1344 l1_finalized,
1345 timestamp: 0,
1346 timestamp_millis: 0,
1347
1348 expected_l1_head: 1,
1349 expected_l1_finalized: l1_finalized,
1350 expected_timestamp: 1,
1351 expected_timestamp_millis: 1_000,
1352
1353 ..Default::default()
1354 }
1355 .run()
1356 .await
1357 }
1358
1359 #[tokio::test(flavor = "multi_thread")]
1360 async fn test_new_header_timestamp_behind() {
1361 TestCase {
1362 parent_timestamp: 1,
1363 parent_timestamp_millis: 1_000,
1364 timestamp: 0,
1365 timestamp_millis: 0,
1366 expected_timestamp: 1,
1367 expected_timestamp_millis: 1_000,
1368
1369 ..Default::default()
1370 }
1371 .run()
1372 .await
1373 }
1374
1375 #[tokio::test(flavor = "multi_thread")]
1376 async fn test_new_header_l1_head_behind() {
1377 TestCase {
1378 parent_l1_head: 1,
1379 l1_head: 0,
1380 expected_l1_head: 1,
1381
1382 ..Default::default()
1383 }
1384 .run()
1385 .await
1386 }
1387
1388 #[tokio::test(flavor = "multi_thread")]
1389 async fn test_new_header_l1_finalized_behind_some() {
1390 TestCase {
1391 parent_l1_finalized: Some(l1_block(1)),
1392 l1_finalized: Some(l1_block(0)),
1393 expected_l1_finalized: Some(l1_block(1)),
1394
1395 ..Default::default()
1396 }
1397 .run()
1398 .await
1399 }
1400
1401 #[tokio::test(flavor = "multi_thread")]
1402 async fn test_new_header_l1_finalized_behind_none() {
1403 TestCase {
1404 parent_l1_finalized: Some(l1_block(0)),
1405 l1_finalized: None,
1406 expected_l1_finalized: Some(l1_block(0)),
1407
1408 ..Default::default()
1409 }
1410 .run()
1411 .await
1412 }
1413
1414 #[tokio::test(flavor = "multi_thread")]
1415 async fn test_new_header_deposits_one() {
1416 TestCase {
1417 l1_deposits: vec![FeeInfo::new(Address::default(), 1)],
1418 ..Default::default()
1419 }
1420 .run()
1421 .await
1422 }
1423
1424 #[tokio::test(flavor = "multi_thread")]
1425 async fn test_new_header_deposits_many() {
1426 TestCase {
1427 l1_deposits: [
1428 (Address::default(), 1),
1429 (Address::default(), 2),
1430 (Address::random(), 3),
1431 ]
1432 .iter()
1433 .map(|(address, amount)| FeeInfo::new(*address, *amount))
1434 .collect(),
1435 ..Default::default()
1436 }
1437 .run()
1438 .await
1439 }
1440
1441 struct GenesisForTest {
1442 pub instance_state: NodeState,
1443 pub validated_state: ValidatedState,
1444 pub leaf: Leaf2,
1445 pub header: Header,
1446 pub ns_table: NsTable,
1447 }
1448
1449 impl GenesisForTest {
1450 async fn default() -> Self {
1451 let instance_state = NodeState::mock();
1452 let validated_state = ValidatedState::genesis(&instance_state).0;
1453 let leaf: Leaf2 = Leaf::genesis::<MockVersions>(&validated_state, &instance_state)
1454 .await
1455 .into();
1456 let header = leaf.block_header().clone();
1457 let ns_table = leaf.block_payload().unwrap().ns_table().clone();
1458 Self {
1459 instance_state,
1460 validated_state,
1461 leaf,
1462 header,
1463 ns_table,
1464 }
1465 }
1466 }
1467
1468 #[tokio::test(flavor = "multi_thread")]
1469 async fn test_proposal_validation_success() {
1470 setup_test();
1471
1472 let anvil = Anvil::new().block_time(1u64).spawn();
1473 let mut genesis_state = NodeState::mock()
1474 .with_l1(L1Client::new(vec![anvil.endpoint_url()]).expect("Failed to create L1 client"))
1475 .with_current_version(StaticVersion::<0, 1>::version());
1476
1477 let genesis = GenesisForTest::default().await;
1478
1479 let mut parent_state = genesis.validated_state.clone();
1480
1481 let mut block_merkle_tree = parent_state.block_merkle_tree.clone();
1482 let fee_merkle_tree = parent_state.fee_merkle_tree.clone();
1483
1484 block_merkle_tree.push(genesis.header.commit()).unwrap();
1486 let block_merkle_tree_root = block_merkle_tree.commitment();
1487 let fee_merkle_tree_root = fee_merkle_tree.commitment();
1488 parent_state.block_merkle_tree = block_merkle_tree.clone();
1489 parent_state.fee_merkle_tree = fee_merkle_tree.clone();
1490
1491 let mut parent_header = genesis.header.clone();
1492 *parent_header.block_merkle_tree_root_mut() = block_merkle_tree_root;
1493 *parent_header.fee_merkle_tree_root_mut() = fee_merkle_tree_root;
1494
1495 let mut parent_leaf = genesis.leaf.clone();
1496 *parent_leaf.block_header_mut() = parent_header.clone();
1497
1498 let forgotten_state = parent_state.forget();
1500 genesis_state.state_catchup = Arc::new(MockStateCatchup::from_iter([(
1501 parent_leaf.view_number(),
1502 Arc::new(parent_state.clone()),
1503 )]));
1504 let key_pair = EthKeyPair::for_test();
1509 let fee_amount = 0u64;
1510 let payload_commitment = parent_header.payload_commitment();
1511 let builder_commitment = parent_header.builder_commitment();
1512 let ns_table = genesis.ns_table;
1513 let fee_signature = FeeAccount::sign_fee(&key_pair, fee_amount, &ns_table).unwrap();
1514 let builder_fee = BuilderFee {
1515 fee_amount,
1516 fee_account: key_pair.fee_account(),
1517 fee_signature,
1518 };
1519 let proposal = Header::new(
1520 &forgotten_state,
1521 &genesis_state,
1522 &parent_leaf,
1523 payload_commitment,
1524 builder_commitment.clone(),
1525 ns_table,
1526 builder_fee,
1527 StaticVersion::<0, 1>::version(),
1528 *parent_leaf.view_number() + 1,
1529 )
1530 .await
1531 .unwrap();
1532
1533 let mut proposal_state = parent_state.clone();
1534 for fee_info in genesis_state
1535 .l1_client
1536 .get_finalized_deposits(Address::default(), None, 0)
1537 .await
1538 {
1539 proposal_state.insert_fee_deposit(fee_info).unwrap();
1540 }
1541
1542 let mut block_merkle_tree = proposal_state.block_merkle_tree.clone();
1543 block_merkle_tree.push(proposal.commit()).unwrap();
1544
1545 let _proposal_state = proposal_state
1546 .apply_header(
1547 &genesis_state,
1548 &genesis_state.state_catchup,
1549 &parent_leaf,
1550 &proposal,
1551 StaticVersion::<0, 1>::version(),
1552 parent_leaf.view_number() + 1,
1553 )
1554 .await
1555 .unwrap()
1556 .0;
1557
1558 }
1571
1572 #[test]
1573 fn verify_builder_signature() {
1574 let message = ";)";
1576 let mut commitment = [0u8; 32];
1577 commitment[..message.len()].copy_from_slice(message.as_bytes());
1578
1579 let key = FeeAccount::generated_from_seed_indexed([0; 32], 0).1;
1580 let signature = FeeAccount::sign_builder_message(&key, &commitment).unwrap();
1581 assert!(key
1582 .fee_account()
1583 .validate_builder_signature(&signature, &commitment));
1584 }
1585
1586 #[tokio::test(flavor = "multi_thread")]
1587 async fn test_versioned_header_serialization() {
1588 setup_test();
1589
1590 let genesis = GenesisForTest::default().await;
1591 let header = genesis.header.clone();
1592 let ns_table = genesis.ns_table;
1593
1594 let (fee_account, _) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1595
1596 let v1_header = Header::create(
1597 genesis.instance_state.chain_config,
1598 1,
1599 2,
1600 2_000_000_000,
1601 3,
1602 Default::default(),
1603 header.payload_commitment(),
1604 header.builder_commitment().clone(),
1605 ns_table.clone(),
1606 header.fee_merkle_tree_root(),
1607 header.block_merkle_tree_root(),
1608 header.reward_merkle_tree_root(),
1609 vec![FeeInfo {
1610 amount: 0.into(),
1611 account: fee_account,
1612 }],
1613 Default::default(),
1614 Version { major: 0, minor: 1 },
1615 );
1616
1617 let serialized = serde_json::to_string(&v1_header).unwrap();
1618 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1619 assert_eq!(v1_header, deserialized);
1620
1621 let v2_header = Header::create(
1622 genesis.instance_state.chain_config,
1623 1,
1624 2,
1625 2_000_000_000,
1626 3,
1627 Default::default(),
1628 header.payload_commitment(),
1629 header.builder_commitment().clone(),
1630 ns_table.clone(),
1631 header.fee_merkle_tree_root(),
1632 header.block_merkle_tree_root(),
1633 header.reward_merkle_tree_root(),
1634 vec![FeeInfo {
1635 amount: 0.into(),
1636 account: fee_account,
1637 }],
1638 Default::default(),
1639 Version { major: 0, minor: 2 },
1640 );
1641
1642 let serialized = serde_json::to_string(&v2_header).unwrap();
1643 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1644 assert_eq!(v2_header, deserialized);
1645
1646 let v3_header = Header::create(
1647 genesis.instance_state.chain_config,
1648 1,
1649 2,
1650 2_000_000_000,
1651 3,
1652 Default::default(),
1653 header.payload_commitment(),
1654 header.builder_commitment().clone(),
1655 ns_table.clone(),
1656 header.fee_merkle_tree_root(),
1657 header.block_merkle_tree_root(),
1658 header.reward_merkle_tree_root(),
1659 vec![FeeInfo {
1660 amount: 0.into(),
1661 account: fee_account,
1662 }],
1663 Default::default(),
1664 Version { major: 0, minor: 3 },
1665 );
1666
1667 let serialized = serde_json::to_string(&v3_header).unwrap();
1668 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1669 assert_eq!(v3_header, deserialized);
1670
1671 let v1_bytes = BincodeSerializer::<StaticVersion<0, 1>>::serialize(&v1_header).unwrap();
1672 let deserialized: Header =
1673 BincodeSerializer::<StaticVersion<0, 1>>::deserialize(&v1_bytes).unwrap();
1674 assert_eq!(v1_header, deserialized);
1675
1676 let v2_bytes = BincodeSerializer::<StaticVersion<0, 2>>::serialize(&v2_header).unwrap();
1677 let deserialized: Header =
1678 BincodeSerializer::<StaticVersion<0, 2>>::deserialize(&v2_bytes).unwrap();
1679 assert_eq!(v2_header, deserialized);
1680
1681 let v3_bytes = BincodeSerializer::<StaticVersion<0, 3>>::serialize(&v3_header).unwrap();
1682 let deserialized: Header =
1683 BincodeSerializer::<StaticVersion<0, 3>>::deserialize(&v3_bytes).unwrap();
1684 assert_eq!(v3_header, deserialized);
1685 }
1686}