hotshot_builder_api/v0_1/
block_info.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7use std::{hash::Hash, marker::PhantomData};
8
9use hotshot_types::{
10    traits::{node_implementation::NodeType, signature_key::BuilderSignatureKey, BlockPayload},
11    utils::BuilderCommitment,
12    vid::advz::ADVZCommitment,
13};
14use serde::{Deserialize, Serialize};
15
16#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
17#[serde(bound = "")]
18pub struct AvailableBlockInfo<TYPES: NodeType> {
19    pub block_hash: BuilderCommitment,
20    pub block_size: u64,
21    pub offered_fee: u64,
22    pub signature:
23        <<TYPES as NodeType>::BuilderSignatureKey as BuilderSignatureKey>::BuilderSignature,
24    pub sender: <TYPES as NodeType>::BuilderSignatureKey,
25    pub _phantom: PhantomData<TYPES>,
26}
27
28#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
29#[serde(bound = "")]
30pub struct AvailableBlockData<TYPES: NodeType> {
31    pub block_payload: TYPES::BlockPayload,
32    pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
33    pub signature:
34        <<TYPES as NodeType>::BuilderSignatureKey as BuilderSignatureKey>::BuilderSignature,
35    pub sender: <TYPES as NodeType>::BuilderSignatureKey,
36}
37
38impl<TYPES: NodeType> AvailableBlockData<TYPES> {
39    pub fn validate_signature(&self) -> bool {
40        // verify the signature over the message, construct the builder commitment
41        let builder_commitment = self.block_payload.builder_commitment(&self.metadata);
42        self.sender
43            .validate_builder_signature(&self.signature, builder_commitment.as_ref())
44    }
45}
46
47#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
48#[serde(bound = "")]
49pub struct AvailableBlockHeaderInputV1<TYPES: NodeType> {
50    // TODO Add precompute back.
51    // signature over vid_commitment, BlockPayload::Metadata, and offered_fee
52    pub fee_signature:
53        <<TYPES as NodeType>::BuilderSignatureKey as BuilderSignatureKey>::BuilderSignature,
54    pub sender: <TYPES as NodeType>::BuilderSignatureKey,
55}
56
57impl<TYPES: NodeType> AvailableBlockHeaderInputV1<TYPES> {
58    pub fn validate_signature(
59        &self,
60        offered_fee: u64,
61        metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
62    ) -> bool {
63        self.sender
64            .validate_fee_signature(&self.fee_signature, offered_fee, metadata)
65    }
66}
67
68#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
69#[serde(bound = "")]
70pub struct AvailableBlockHeaderInputV2<TYPES: NodeType> {
71    // signature over vid_commitment, BlockPayload::Metadata, and offered_fee
72    pub fee_signature:
73        <<TYPES as NodeType>::BuilderSignatureKey as BuilderSignatureKey>::BuilderSignature,
74    pub sender: <TYPES as NodeType>::BuilderSignatureKey,
75}
76
77/// legacy version of the AvailableBlockHeaderInputV2 type, used on git tag `20250228-patch3`
78///
79/// this was inadvertently changed to remove some deprecated fields,
80/// which resulted in a builder incompatibility.
81///
82/// This type can be removed after the builder is upgraded in deployment.
83#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
84#[serde(bound = "")]
85pub struct AvailableBlockHeaderInputV2Legacy<TYPES: NodeType> {
86    pub vid_commitment: ADVZCommitment,
87    // signature over vid_commitment, BlockPayload::Metadata, and offered_fee
88    pub fee_signature:
89        <<TYPES as NodeType>::BuilderSignatureKey as BuilderSignatureKey>::BuilderSignature,
90    // signature over the current response
91    pub message_signature:
92        <<TYPES as NodeType>::BuilderSignatureKey as BuilderSignatureKey>::BuilderSignature,
93    pub sender: <TYPES as NodeType>::BuilderSignatureKey,
94}
95
96/// either version of the AvailableBlockHeaderInputV2 type. Note that we try to deserialize legacy first,
97/// as that has extra fields that are not present in the current version. When presented with a legacy
98/// input, we'll first try to validate its signature as the current version.
99#[derive(Clone, Debug, PartialEq, Eq, Hash)]
100pub enum AvailableBlockHeaderInputV2Either<TYPES: NodeType> {
101    Current(AvailableBlockHeaderInputV2<TYPES>),
102    Legacy(AvailableBlockHeaderInputV2Legacy<TYPES>),
103}
104
105impl<TYPES: NodeType> AvailableBlockHeaderInputV2Legacy<TYPES> {
106    pub fn validate_signature(
107        &self,
108        offered_fee: u64,
109        metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
110    ) -> bool {
111        self.sender
112            .validate_builder_signature(&self.message_signature, self.vid_commitment.as_ref())
113            && self.sender.validate_fee_signature_with_vid_commitment(
114                &self.fee_signature,
115                offered_fee,
116                metadata,
117                &hotshot_types::data::VidCommitment::V0(self.vid_commitment),
118            )
119    }
120}
121
122impl<TYPES: NodeType> AvailableBlockHeaderInputV2<TYPES> {
123    pub fn validate_signature(
124        &self,
125        offered_fee: u64,
126        metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
127    ) -> bool {
128        self.sender
129            .validate_fee_signature(&self.fee_signature, offered_fee, metadata)
130    }
131}
132
133impl<TYPES: NodeType> AvailableBlockHeaderInputV2Either<TYPES> {
134    pub fn validate_signature_and_get_input(
135        &self,
136        offered_fee: u64,
137        metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
138    ) -> Option<AvailableBlockHeaderInputV2<TYPES>> {
139        match self {
140            AvailableBlockHeaderInputV2Either::Legacy(legacy) => {
141                // Try to validate this as a current signature first, then fall back to legacy validation
142                // Note that "legacy" as a variable name might be misleading here, as in the first case
143                // we're treating the 'legacy' struct as 'current' with extra fields. This mirrors the previous
144                // behavior of the code.
145                if legacy.sender.validate_fee_signature(
146                    &legacy.fee_signature,
147                    offered_fee,
148                    metadata,
149                ) || legacy.validate_signature(offered_fee, metadata)
150                {
151                    Some(AvailableBlockHeaderInputV2 {
152                        fee_signature: legacy.fee_signature.clone(),
153                        sender: legacy.sender.clone(),
154                    })
155                } else {
156                    None
157                }
158            },
159            AvailableBlockHeaderInputV2Either::Current(current) => {
160                if current.validate_signature(offered_fee, metadata) {
161                    Some(current.clone())
162                } else {
163                    None
164                }
165            },
166        }
167    }
168}