1use borsh::{BorshDeserialize, BorshSerialize};
2use hyli_net_traits::TcpMessageLabel;
3use serde::{Deserialize, Serialize};
4use sha3::{Digest, Sha3_256};
5use std::{fmt::Display, sync::RwLock};
6
7use crate::*;
8
9#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, TcpMessageLabel)]
10pub enum MempoolStatusEvent {
11 WaitingDissemination {
12 parent_data_proposal_hash: DataProposalHash,
13 txs: Vec<Transaction>,
14 },
15 DataProposalCreated {
16 parent_data_proposal_hash: DataProposalHash,
17 data_proposal_hash: DataProposalHash,
18 txs_metadatas: Vec<TransactionMetadata>,
19 },
20}
21
22#[derive(Debug, Clone, Deserialize, Serialize)]
23pub enum MempoolBlockEvent {
24 BuiltSignedBlock(SignedBlock),
25 StartedBuildingBlocks(BlockHeight),
26}
27
28#[derive(
29 Debug,
30 Clone,
31 Serialize,
32 Deserialize,
33 BorshSerialize,
34 BorshDeserialize,
35 PartialEq,
36 Eq,
37 Hash,
38 Ord,
39 PartialOrd,
40)]
41#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
42pub enum DataProposalParent {
43 LaneRoot(LaneId),
44 DP(DataProposalHash),
45}
46
47impl DataProposalParent {
48 pub fn dp_hash(&self) -> Option<&DataProposalHash> {
49 match self {
50 DataProposalParent::DP(hash) => Some(hash),
51 DataProposalParent::LaneRoot(_) => None,
52 }
53 }
54
55 pub fn lane_id(&self) -> Option<&LaneId> {
56 match self {
57 DataProposalParent::LaneRoot(lane_id) => Some(lane_id),
58 DataProposalParent::DP(_) => None,
59 }
60 }
61
62 pub fn is_lane_root(&self) -> bool {
63 matches!(self, DataProposalParent::LaneRoot(_))
64 }
65
66 pub fn as_tx_parent_hash(&self) -> DataProposalHash {
67 match self {
68 DataProposalParent::LaneRoot(lane_id) => DataProposalHash(lane_id.to_bytes()),
69 DataProposalParent::DP(hash) => hash.clone(),
70 }
71 }
72}
73
74#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize, TcpMessageLabel)]
75#[readonly::make]
76pub struct DataProposal {
77 pub parent_data_proposal_hash: DataProposalParent,
78 pub txs: Vec<Transaction>,
79 #[borsh(skip)]
81 hash_cache: RwLock<Option<DataProposalHash>>,
82}
83
84impl DataProposal {
85 pub fn new(parent_data_proposal_hash: DataProposalHash, txs: Vec<Transaction>) -> Self {
86 Self {
87 parent_data_proposal_hash: DataProposalParent::DP(parent_data_proposal_hash),
88 txs,
89 hash_cache: RwLock::new(None),
90 }
91 }
92
93 pub fn new_root(lane_id: LaneId, txs: Vec<Transaction>) -> Self {
94 Self {
95 parent_data_proposal_hash: DataProposalParent::LaneRoot(lane_id),
96 txs,
97 hash_cache: RwLock::new(None),
98 }
99 }
100
101 pub fn remove_proofs(&mut self) {
102 self.txs.iter_mut().for_each(|tx| {
103 match &mut tx.transaction_data {
104 TransactionData::VerifiedProof(proof_tx) => {
105 proof_tx.proof = None;
106 }
107 TransactionData::Proof(_) => {
108 unreachable!();
111 }
112 TransactionData::Blob(_) => {}
113 }
114 });
115 }
116
117 pub fn take_proofs(&mut self) -> Vec<(u64, ProofData)> {
118 self.txs
119 .iter_mut()
120 .enumerate()
121 .filter_map(|(tx_idx, tx)| {
122 match &mut tx.transaction_data {
123 TransactionData::VerifiedProof(proof_tx) => {
124 proof_tx.proof.take().map(|proof| (tx_idx as u64, proof))
125 }
126 TransactionData::Proof(_) => {
127 unreachable!();
130 }
131 TransactionData::Blob(_) => None,
132 }
133 })
134 .collect()
135 }
136
137 pub fn hydrate_proofs(&mut self, proofs: Vec<(u64, ProofData)>) {
139 for (tx_idx, proof) in proofs {
140 let Ok(tx_idx) = usize::try_from(tx_idx) else {
141 continue;
142 };
143 let Some(tx) = self.txs.get_mut(tx_idx) else {
144 continue;
145 };
146 if let TransactionData::VerifiedProof(ref mut vpt) = tx.transaction_data {
147 if vpt.proof.is_none() {
148 vpt.proof = Some(proof);
149 }
150 }
151 }
152 }
153
154 pub unsafe fn unsafe_set_hash(&mut self, hash: &DataProposalHash) {
159 self.hash_cache.write().unwrap().replace(hash.clone());
160 }
161}
162
163impl Clone for DataProposal {
164 fn clone(&self) -> Self {
165 DataProposal {
166 parent_data_proposal_hash: self.parent_data_proposal_hash.clone(),
167 txs: self.txs.clone(),
168 hash_cache: RwLock::new(self.hash_cache.read().unwrap().clone()),
169 }
170 }
171}
172
173impl PartialEq for DataProposal {
174 fn eq(&self, other: &Self) -> bool {
175 self.hashed() == other.hashed()
176 }
177}
178
179impl Eq for DataProposal {}
180
181impl DataSized for DataProposal {
182 fn estimate_size(&self) -> usize {
183 self.txs.iter().map(|tx| tx.estimate_size()).sum()
184 }
185}
186
187#[derive(
188 Default,
189 Serialize,
190 Deserialize,
191 Debug,
192 Clone,
193 PartialEq,
194 Eq,
195 Hash,
196 Ord,
197 PartialOrd,
198 BorshDeserialize,
199 BorshSerialize,
200)]
201#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
202pub struct TxId(pub DataProposalHash, pub TxHash);
203
204#[derive(
205 Clone,
206 Default,
207 Serialize,
208 Deserialize,
209 BorshSerialize,
210 BorshDeserialize,
211 PartialEq,
212 Eq,
213 Hash,
214 Ord,
215 PartialOrd,
216)]
217#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
218pub struct DataProposalHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
219
220impl From<Vec<u8>> for DataProposalHash {
221 fn from(v: Vec<u8>) -> Self {
222 DataProposalHash(v)
223 }
224}
225impl From<&[u8]> for DataProposalHash {
226 fn from(v: &[u8]) -> Self {
227 DataProposalHash(v.to_vec())
228 }
229}
230impl<const N: usize> From<&[u8; N]> for DataProposalHash {
231 fn from(v: &[u8; N]) -> Self {
232 DataProposalHash(v.to_vec())
233 }
234}
235impl DataProposalHash {
236 pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
237 crate::utils::decode_hex_string_checked(s).map(DataProposalHash)
238 }
239}
240
241impl std::fmt::Debug for DataProposalHash {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 write!(f, "DataProposalHash({})", hex::encode(&self.0))
244 }
245}
246
247impl Hashed<DataProposalHash> for DataProposal {
248 fn hashed(&self) -> DataProposalHash {
249 if let Some(hash) = self.hash_cache.read().unwrap().as_ref() {
250 return hash.clone();
251 }
252 let mut hasher = Sha3_256::new();
253 match &self.parent_data_proposal_hash {
254 DataProposalParent::LaneRoot(lane_id) => {
255 hasher.update(lane_id.to_string().as_bytes());
256 }
257 DataProposalParent::DP(parent_data_proposal_hash) => {
258 hasher.update(&parent_data_proposal_hash.0);
259 }
260 }
261 for tx in self.txs.iter() {
262 hasher.update(&tx.hashed().0);
263 }
264 let hash = DataProposalHash(hasher.finalize().to_vec());
265 *self.hash_cache.write().unwrap() = Some(hash.clone());
266 hash
267 }
268}
269
270impl std::hash::Hash for DataProposal {
272 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
273 self.hashed().hash(state);
274 }
275}
276
277impl Display for DataProposalHash {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 write!(f, "{}", hex::encode(&self.0))
280 }
281}
282impl Display for DataProposal {
283 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284 write!(f, "{}", self.hashed())
285 }
286}
287
288pub type PoDA = AggregateSignature;
289pub type Cut = Vec<(LaneId, DataProposalHash, LaneBytesSize, PoDA)>;
290
291impl Display for TxId {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 write!(f, "{}/{}", self.0, self.1)
294 }
295}
296
297pub struct CutDisplay<'a>(pub &'a Cut);
299impl Display for CutDisplay<'_> {
300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 let mut cut_str = String::new();
302 for (lane_id, hash, size, _) in self.0.iter() {
303 cut_str.push_str(&format!("{lane_id}:{hash}({size}), "));
304 }
305 write!(f, "{}", cut_str.trim_end())
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn data_proposal_hash_from_hex_str_roundtrip() {
315 let hex_str = "746573745f6470";
316 let hash = DataProposalHash::from_hex(hex_str).expect("data proposal hash hex");
317 assert_eq!(hash.0, b"test_dp".to_vec());
318 assert_eq!(format!("{hash}"), hex_str);
319 let json = serde_json::to_string(&hash).expect("serialize data proposal hash");
320 assert_eq!(json, "\"746573745f6470\"");
321 let decoded: DataProposalHash =
322 serde_json::from_str(&json).expect("deserialize data proposal hash");
323 assert_eq!(decoded, hash);
324 }
325}