alloy_network_primitives/
block.rs1use alloy_primitives::B256;
2
3use crate::TransactionResponse;
4use alloc::{vec, vec::Vec};
5use alloy_consensus::error::ValueError;
6use alloy_eips::Encodable2718;
7use core::slice;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "serde", serde(untagged))]
20pub enum BlockTransactions<T> {
21 Full(Vec<T>),
23 Hashes(Vec<B256>),
25 Uncle,
27}
28
29impl<T> Default for BlockTransactions<T> {
30 fn default() -> Self {
31 Self::Hashes(Vec::default())
32 }
33}
34
35impl<T> BlockTransactions<T> {
36 #[inline]
38 pub const fn is_hashes(&self) -> bool {
39 matches!(self, Self::Hashes(_))
40 }
41
42 pub fn as_hashes(&self) -> Option<&[B256]> {
44 match self {
45 Self::Hashes(hashes) => Some(hashes),
46 _ => None,
47 }
48 }
49
50 pub fn first_transaction(&self) -> Option<&T> {
52 self.as_transactions().and_then(|txs| txs.first())
53 }
54
55 #[inline]
57 pub const fn is_full(&self) -> bool {
58 matches!(self, Self::Full(_))
59 }
60
61 pub fn map<U>(self, f: impl FnMut(T) -> U) -> BlockTransactions<U> {
66 match self {
67 Self::Full(txs) => BlockTransactions::Full(txs.into_iter().map(f).collect()),
68 Self::Hashes(hashes) => BlockTransactions::Hashes(hashes),
69 Self::Uncle => BlockTransactions::Uncle,
70 }
71 }
72
73 pub fn try_map<U, E>(
78 self,
79 f: impl FnMut(T) -> Result<U, E>,
80 ) -> Result<BlockTransactions<U>, E> {
81 match self {
82 Self::Full(txs) => {
83 Ok(BlockTransactions::Full(txs.into_iter().map(f).collect::<Result<_, _>>()?))
84 }
85 Self::Hashes(hashes) => Ok(BlockTransactions::Hashes(hashes)),
86 Self::Uncle => Ok(BlockTransactions::Uncle),
87 }
88 }
89
90 pub fn as_transactions(&self) -> Option<&[T]> {
94 match self {
95 Self::Full(txs) => Some(txs),
96 _ => None,
97 }
98 }
99
100 pub fn calculate_transactions_root(&self) -> Option<B256>
105 where
106 T: Encodable2718,
107 {
108 self.as_transactions().map(alloy_consensus::proofs::calculate_transaction_root)
109 }
110
111 #[inline]
113 pub const fn is_uncle(&self) -> bool {
114 matches!(self, Self::Uncle)
115 }
116
117 #[doc(alias = "transactions")]
124 pub fn txns(&self) -> impl Iterator<Item = &T> {
125 self.as_transactions().map(|txs| txs.iter()).unwrap_or_else(|| [].iter())
126 }
127
128 pub fn into_transactions(self) -> vec::IntoIter<T> {
134 match self {
135 Self::Full(txs) => txs.into_iter(),
136 _ => vec::IntoIter::default(),
137 }
138 }
139
140 pub fn into_transactions_vec(self) -> Vec<T> {
145 match self {
146 Self::Full(txs) => txs,
147 _ => vec![],
148 }
149 }
150
151 pub fn try_into_transactions(self) -> Result<Vec<T>, ValueError<Self>> {
155 match self {
156 Self::Full(txs) => Ok(txs),
157 txs @ Self::Hashes(_) => Err(ValueError::new_static(txs, "Unexpected hashes variant")),
158 txs @ Self::Uncle => Err(ValueError::new_static(txs, "Unexpected uncle variant")),
159 }
160 }
161
162 #[inline]
164 pub const fn uncle() -> Self {
165 Self::Uncle
166 }
167
168 #[inline]
170 pub const fn len(&self) -> usize {
171 match self {
172 Self::Hashes(h) => h.len(),
173 Self::Full(f) => f.len(),
174 Self::Uncle => 0,
175 }
176 }
177
178 #[inline]
180 pub const fn is_empty(&self) -> bool {
181 self.len() == 0
182 }
183}
184
185impl<T: TransactionResponse> BlockTransactions<T> {
186 pub fn new_hashes(txs: impl IntoIterator<Item = impl AsRef<T>>) -> Self {
188 Self::Hashes(txs.into_iter().map(|tx| tx.as_ref().tx_hash()).collect())
189 }
190
191 #[inline]
196 pub fn convert_to_hashes(&mut self) {
197 if !self.is_hashes() {
198 *self = Self::Hashes(self.hashes().collect());
199 }
200 }
201
202 #[inline]
204 pub fn convert_to_hashes_if(&mut self, condition: bool) {
205 if !condition {
206 return;
207 }
208 self.convert_to_hashes();
209 }
210
211 #[inline]
216 pub fn into_hashes(mut self) -> Self {
217 self.convert_to_hashes();
218 self
219 }
220
221 #[inline]
223 pub fn into_hashes_if(self, condition: bool) -> Self {
224 if !condition {
225 return self;
226 }
227 self.into_hashes()
228 }
229
230 #[inline]
235 pub fn hashes(&self) -> BlockTransactionHashes<'_, T> {
236 BlockTransactionHashes::new(self)
237 }
238
239 pub fn into_hashes_vec(self) -> Vec<B256> {
243 match self {
244 Self::Hashes(hashes) => hashes,
245 this => this.hashes().collect(),
246 }
247 }
248}
249
250impl<T> From<Vec<B256>> for BlockTransactions<T> {
251 fn from(hashes: Vec<B256>) -> Self {
252 Self::Hashes(hashes)
253 }
254}
255
256impl<T: TransactionResponse> From<Vec<T>> for BlockTransactions<T> {
257 fn from(transactions: Vec<T>) -> Self {
258 Self::Full(transactions)
259 }
260}
261
262#[derive(Clone, Debug)]
266pub struct BlockTransactionHashes<'a, T>(BlockTransactionHashesInner<'a, T>);
267
268#[derive(Clone, Debug)]
269enum BlockTransactionHashesInner<'a, T> {
270 Hashes(slice::Iter<'a, B256>),
271 Full(slice::Iter<'a, T>),
272 Uncle,
273}
274
275impl<'a, T> BlockTransactionHashes<'a, T> {
276 #[inline]
277 fn new(txs: &'a BlockTransactions<T>) -> Self {
278 Self(match txs {
279 BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()),
280 BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()),
281 BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle,
282 })
283 }
284}
285
286impl<T: TransactionResponse> Iterator for BlockTransactionHashes<'_, T> {
287 type Item = B256;
288
289 #[inline]
290 fn next(&mut self) -> Option<Self::Item> {
291 match &mut self.0 {
292 BlockTransactionHashesInner::Hashes(txs) => txs.next().copied(),
293 BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| tx.tx_hash()),
294 BlockTransactionHashesInner::Uncle => None,
295 }
296 }
297
298 #[inline]
299 fn size_hint(&self) -> (usize, Option<usize>) {
300 match &self.0 {
301 BlockTransactionHashesInner::Full(txs) => txs.size_hint(),
302 BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(),
303 BlockTransactionHashesInner::Uncle => (0, Some(0)),
304 }
305 }
306}
307
308impl<T: TransactionResponse> ExactSizeIterator for BlockTransactionHashes<'_, T> {
309 #[inline]
310 fn len(&self) -> usize {
311 match &self.0 {
312 BlockTransactionHashesInner::Full(txs) => txs.len(),
313 BlockTransactionHashesInner::Hashes(txs) => txs.len(),
314 BlockTransactionHashesInner::Uncle => 0,
315 }
316 }
317}
318
319impl<T: TransactionResponse> DoubleEndedIterator for BlockTransactionHashes<'_, T> {
320 #[inline]
321 fn next_back(&mut self) -> Option<Self::Item> {
322 match &mut self.0 {
323 BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| tx.tx_hash()),
324 BlockTransactionHashesInner::Hashes(txs) => txs.next_back().copied(),
325 BlockTransactionHashesInner::Uncle => None,
326 }
327 }
328}
329
330#[cfg(feature = "std")]
331impl<T: TransactionResponse> std::iter::FusedIterator for BlockTransactionHashes<'_, T> {}
332
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
338#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
339pub enum BlockTransactionsKind {
340 #[default]
342 Hashes,
343 Full,
345}
346
347impl BlockTransactionsKind {
348 pub const fn is_hashes(&self) -> bool {
350 matches!(self, Self::Hashes)
351 }
352
353 pub const fn is_full(&self) -> bool {
355 matches!(self, Self::Full)
356 }
357}
358
359impl From<bool> for BlockTransactionsKind {
360 fn from(is_full: bool) -> Self {
361 if is_full {
362 Self::Full
363 } else {
364 Self::Hashes
365 }
366 }
367}
368
369impl From<BlockTransactionsKind> for bool {
370 fn from(kind: BlockTransactionsKind) -> Self {
371 match kind {
372 BlockTransactionsKind::Full => true,
373 BlockTransactionsKind::Hashes => false,
374 }
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 #[test]
383 fn test_full_conversion() {
384 let full = true;
385 assert_eq!(BlockTransactionsKind::Full, full.into());
386
387 let full = false;
388 assert_eq!(BlockTransactionsKind::Hashes, full.into());
389 }
390
391 #[test]
392 fn test_block_transactions_default() {
393 let default: BlockTransactions<()> = BlockTransactions::default();
394 assert!(default.is_hashes());
395 assert_eq!(default.len(), 0);
396 }
397
398 #[test]
399 fn test_block_transactions_is_methods() {
400 let hashes: BlockTransactions<()> = BlockTransactions::Hashes(vec![B256::ZERO]);
401 let full: BlockTransactions<u32> = BlockTransactions::Full(vec![42]);
402 let uncle: BlockTransactions<()> = BlockTransactions::Uncle;
403
404 assert!(hashes.is_hashes());
405 assert!(!hashes.is_full());
406 assert!(!hashes.is_uncle());
407
408 assert!(full.is_full());
409 assert!(!full.is_hashes());
410 assert!(!full.is_uncle());
411
412 assert!(uncle.is_uncle());
413 assert!(!uncle.is_full());
414 assert!(!uncle.is_hashes());
415 }
416
417 #[test]
418 fn test_as_hashes() {
419 let hashes = vec![B256::ZERO, B256::repeat_byte(1)];
420 let tx_hashes: BlockTransactions<()> = BlockTransactions::Hashes(hashes.clone());
421
422 assert_eq!(tx_hashes.as_hashes(), Some(hashes.as_slice()));
423 }
424
425 #[test]
426 fn test_as_transactions() {
427 let transactions = vec![42, 43];
428 let txs = BlockTransactions::Full(transactions.clone());
429
430 assert_eq!(txs.as_transactions(), Some(transactions.as_slice()));
431 }
432
433 #[test]
434 fn test_block_transactions_len_and_is_empty() {
435 let hashes: BlockTransactions<()> = BlockTransactions::Hashes(vec![B256::ZERO]);
436 let full = BlockTransactions::Full(vec![42]);
437 let uncle: BlockTransactions<()> = BlockTransactions::Uncle;
438
439 assert_eq!(hashes.len(), 1);
440 assert_eq!(full.len(), 1);
441 assert_eq!(uncle.len(), 0);
442
443 assert!(!hashes.is_empty());
444 assert!(!full.is_empty());
445 assert!(uncle.is_empty());
446 }
447
448 #[test]
449 fn test_block_transactions_txns_iterator() {
450 let transactions = vec![42, 43];
451 let txs = BlockTransactions::Full(transactions);
452 let mut iter = txs.txns();
453
454 assert_eq!(iter.next(), Some(&42));
455 assert_eq!(iter.next(), Some(&43));
456 assert_eq!(iter.next(), None);
457 }
458
459 #[test]
460 fn test_block_transactions_into_transactions() {
461 let transactions = vec![42, 43];
462 let txs = BlockTransactions::Full(transactions.clone());
463 let collected: Vec<_> = txs.into_transactions().collect();
464
465 assert_eq!(collected, transactions);
466 }
467
468 #[test]
469 fn test_block_transactions_kind_conversion() {
470 let full: BlockTransactionsKind = true.into();
471 assert_eq!(full, BlockTransactionsKind::Full);
472
473 let hashes: BlockTransactionsKind = false.into();
474 assert_eq!(hashes, BlockTransactionsKind::Hashes);
475
476 let bool_full: bool = BlockTransactionsKind::Full.into();
477 assert!(bool_full);
478
479 let bool_hashes: bool = BlockTransactionsKind::Hashes.into();
480 assert!(!bool_hashes);
481 }
482}