alloy_network_primitives/
block.rs1use alloy_primitives::B256;
2use serde::{Deserialize, Serialize};
3
4use crate::TransactionResponse;
5use alloc::{vec, vec::Vec};
6use alloy_consensus::error::ValueError;
7use alloy_eips::Encodable2718;
8use core::slice;
9
10#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(untagged)]
14pub enum BlockTransactions<T> {
15 Full(Vec<T>),
17 Hashes(Vec<B256>),
19 Uncle,
21}
22
23impl<T> Default for BlockTransactions<T> {
24 fn default() -> Self {
25 Self::Hashes(Vec::default())
26 }
27}
28
29impl<T> BlockTransactions<T> {
30 #[inline]
32 pub const fn is_hashes(&self) -> bool {
33 matches!(self, Self::Hashes(_))
34 }
35
36 pub fn as_hashes(&self) -> Option<&[B256]> {
38 match self {
39 Self::Hashes(hashes) => Some(hashes),
40 _ => None,
41 }
42 }
43
44 #[inline]
46 pub const fn is_full(&self) -> bool {
47 matches!(self, Self::Full(_))
48 }
49
50 pub fn map<U>(self, f: impl FnMut(T) -> U) -> BlockTransactions<U> {
54 match self {
55 Self::Full(txs) => BlockTransactions::Full(txs.into_iter().map(f).collect()),
56 Self::Hashes(hashes) => BlockTransactions::Hashes(hashes),
57 Self::Uncle => BlockTransactions::Uncle,
58 }
59 }
60
61 pub fn try_map<U, E>(
65 self,
66 f: impl FnMut(T) -> Result<U, E>,
67 ) -> Result<BlockTransactions<U>, E> {
68 match self {
69 Self::Full(txs) => {
70 Ok(BlockTransactions::Full(txs.into_iter().map(f).collect::<Result<_, _>>()?))
71 }
72 Self::Hashes(hashes) => Ok(BlockTransactions::Hashes(hashes)),
73 Self::Uncle => Ok(BlockTransactions::Uncle),
74 }
75 }
76
77 pub fn as_transactions(&self) -> Option<&[T]> {
81 match self {
82 Self::Full(txs) => Some(txs),
83 _ => None,
84 }
85 }
86
87 pub fn calculate_transactions_root(&self) -> Option<B256>
91 where
92 T: Encodable2718,
93 {
94 self.as_transactions().map(alloy_consensus::proofs::calculate_transaction_root)
95 }
96
97 #[inline]
99 pub const fn is_uncle(&self) -> bool {
100 matches!(self, Self::Uncle)
101 }
102
103 #[doc(alias = "transactions")]
107 pub fn txns(&self) -> impl Iterator<Item = &T> {
108 self.as_transactions().map(|txs| txs.iter()).unwrap_or_else(|| [].iter())
109 }
110
111 pub fn into_transactions(self) -> vec::IntoIter<T> {
114 match self {
115 Self::Full(txs) => txs.into_iter(),
116 _ => vec::IntoIter::default(),
117 }
118 }
119
120 pub fn into_transactions_vec(self) -> Vec<T> {
124 match self {
125 Self::Full(txs) => txs,
126 _ => vec![],
127 }
128 }
129
130 pub fn try_into_transactions(self) -> Result<Vec<T>, ValueError<Self>> {
134 match self {
135 Self::Full(txs) => Ok(txs),
136 txs @ Self::Hashes(_) => Err(ValueError::new_static(txs, "Unexpected hashes variant")),
137 txs @ Self::Uncle => Err(ValueError::new_static(txs, "Unexpected uncle variant")),
138 }
139 }
140
141 #[inline]
143 pub const fn uncle() -> Self {
144 Self::Uncle
145 }
146
147 #[inline]
149 pub fn len(&self) -> usize {
150 match self {
151 Self::Hashes(h) => h.len(),
152 Self::Full(f) => f.len(),
153 Self::Uncle => 0,
154 }
155 }
156
157 #[inline]
159 pub fn is_empty(&self) -> bool {
160 self.len() == 0
161 }
162}
163
164impl<T: TransactionResponse> BlockTransactions<T> {
165 pub fn new_hashes(txs: impl IntoIterator<Item = impl AsRef<T>>) -> Self {
167 Self::Hashes(txs.into_iter().map(|tx| tx.as_ref().tx_hash()).collect())
168 }
169
170 #[inline]
172 pub fn convert_to_hashes(&mut self) {
173 if !self.is_hashes() {
174 *self = Self::Hashes(self.hashes().collect());
175 }
176 }
177
178 #[inline]
180 pub fn convert_to_hashes_if(&mut self, condition: bool) {
181 if !condition {
182 return;
183 }
184 self.convert_to_hashes();
185 }
186
187 #[inline]
189 pub fn into_hashes(mut self) -> Self {
190 self.convert_to_hashes();
191 self
192 }
193
194 #[inline]
196 pub fn into_hashes_if(self, condition: bool) -> Self {
197 if !condition {
198 return self;
199 }
200 self.into_hashes()
201 }
202
203 #[deprecated = "use `hashes` instead"]
205 #[inline]
206 pub fn iter(&self) -> BlockTransactionHashes<'_, T> {
207 self.hashes()
208 }
209
210 #[inline]
212 pub fn hashes(&self) -> BlockTransactionHashes<'_, T> {
213 BlockTransactionHashes::new(self)
214 }
215}
216
217impl<T> From<Vec<B256>> for BlockTransactions<T> {
218 fn from(hashes: Vec<B256>) -> Self {
219 Self::Hashes(hashes)
220 }
221}
222
223impl<T: TransactionResponse> From<Vec<T>> for BlockTransactions<T> {
224 fn from(transactions: Vec<T>) -> Self {
225 Self::Full(transactions)
226 }
227}
228
229#[derive(Clone, Debug)]
233pub struct BlockTransactionHashes<'a, T>(BlockTransactionHashesInner<'a, T>);
234
235#[derive(Clone, Debug)]
236enum BlockTransactionHashesInner<'a, T> {
237 Hashes(slice::Iter<'a, B256>),
238 Full(slice::Iter<'a, T>),
239 Uncle,
240}
241
242impl<'a, T> BlockTransactionHashes<'a, T> {
243 #[inline]
244 fn new(txs: &'a BlockTransactions<T>) -> Self {
245 Self(match txs {
246 BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()),
247 BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()),
248 BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle,
249 })
250 }
251}
252
253impl<T: TransactionResponse> Iterator for BlockTransactionHashes<'_, T> {
254 type Item = B256;
255
256 #[inline]
257 fn next(&mut self) -> Option<Self::Item> {
258 match &mut self.0 {
259 BlockTransactionHashesInner::Hashes(txs) => txs.next().copied(),
260 BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| tx.tx_hash()),
261 BlockTransactionHashesInner::Uncle => None,
262 }
263 }
264
265 #[inline]
266 fn size_hint(&self) -> (usize, Option<usize>) {
267 match &self.0 {
268 BlockTransactionHashesInner::Full(txs) => txs.size_hint(),
269 BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(),
270 BlockTransactionHashesInner::Uncle => (0, Some(0)),
271 }
272 }
273}
274
275impl<T: TransactionResponse> ExactSizeIterator for BlockTransactionHashes<'_, T> {
276 #[inline]
277 fn len(&self) -> usize {
278 match &self.0 {
279 BlockTransactionHashesInner::Full(txs) => txs.len(),
280 BlockTransactionHashesInner::Hashes(txs) => txs.len(),
281 BlockTransactionHashesInner::Uncle => 0,
282 }
283 }
284}
285
286impl<T: TransactionResponse> DoubleEndedIterator for BlockTransactionHashes<'_, T> {
287 #[inline]
288 fn next_back(&mut self) -> Option<Self::Item> {
289 match &mut self.0 {
290 BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| tx.tx_hash()),
291 BlockTransactionHashesInner::Hashes(txs) => txs.next_back().copied(),
292 BlockTransactionHashesInner::Uncle => None,
293 }
294 }
295}
296
297#[cfg(feature = "std")]
298impl<T: TransactionResponse> std::iter::FusedIterator for BlockTransactionHashes<'_, T> {}
299
300#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
305pub enum BlockTransactionsKind {
306 #[default]
308 Hashes,
309 Full,
311}
312
313impl BlockTransactionsKind {
314 pub const fn is_hashes(&self) -> bool {
316 matches!(self, Self::Hashes)
317 }
318
319 pub const fn is_full(&self) -> bool {
321 matches!(self, Self::Full)
322 }
323}
324
325impl From<bool> for BlockTransactionsKind {
326 fn from(is_full: bool) -> Self {
327 if is_full {
328 Self::Full
329 } else {
330 Self::Hashes
331 }
332 }
333}
334
335impl From<BlockTransactionsKind> for bool {
336 fn from(kind: BlockTransactionsKind) -> Self {
337 match kind {
338 BlockTransactionsKind::Full => true,
339 BlockTransactionsKind::Hashes => false,
340 }
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 #[test]
349 fn test_full_conversion() {
350 let full = true;
351 assert_eq!(BlockTransactionsKind::Full, full.into());
352
353 let full = false;
354 assert_eq!(BlockTransactionsKind::Hashes, full.into());
355 }
356
357 #[test]
358 fn test_block_transactions_default() {
359 let default: BlockTransactions<()> = BlockTransactions::default();
360 assert!(default.is_hashes());
361 assert_eq!(default.len(), 0);
362 }
363
364 #[test]
365 fn test_block_transactions_is_methods() {
366 let hashes: BlockTransactions<()> = BlockTransactions::Hashes(vec![B256::ZERO]);
367 let full: BlockTransactions<u32> = BlockTransactions::Full(vec![42]);
368 let uncle: BlockTransactions<()> = BlockTransactions::Uncle;
369
370 assert!(hashes.is_hashes());
371 assert!(!hashes.is_full());
372 assert!(!hashes.is_uncle());
373
374 assert!(full.is_full());
375 assert!(!full.is_hashes());
376 assert!(!full.is_uncle());
377
378 assert!(uncle.is_uncle());
379 assert!(!uncle.is_full());
380 assert!(!uncle.is_hashes());
381 }
382
383 #[test]
384 fn test_as_hashes() {
385 let hashes = vec![B256::ZERO, B256::repeat_byte(1)];
386 let tx_hashes: BlockTransactions<()> = BlockTransactions::Hashes(hashes.clone());
387
388 assert_eq!(tx_hashes.as_hashes(), Some(hashes.as_slice()));
389 }
390
391 #[test]
392 fn test_as_transactions() {
393 let transactions = vec![42, 43];
394 let txs = BlockTransactions::Full(transactions.clone());
395
396 assert_eq!(txs.as_transactions(), Some(transactions.as_slice()));
397 }
398
399 #[test]
400 fn test_block_transactions_len_and_is_empty() {
401 let hashes: BlockTransactions<()> = BlockTransactions::Hashes(vec![B256::ZERO]);
402 let full = BlockTransactions::Full(vec![42]);
403 let uncle: BlockTransactions<()> = BlockTransactions::Uncle;
404
405 assert_eq!(hashes.len(), 1);
406 assert_eq!(full.len(), 1);
407 assert_eq!(uncle.len(), 0);
408
409 assert!(!hashes.is_empty());
410 assert!(!full.is_empty());
411 assert!(uncle.is_empty());
412 }
413
414 #[test]
415 fn test_block_transactions_txns_iterator() {
416 let transactions = vec![42, 43];
417 let txs = BlockTransactions::Full(transactions);
418 let mut iter = txs.txns();
419
420 assert_eq!(iter.next(), Some(&42));
421 assert_eq!(iter.next(), Some(&43));
422 assert_eq!(iter.next(), None);
423 }
424
425 #[test]
426 fn test_block_transactions_into_transactions() {
427 let transactions = vec![42, 43];
428 let txs = BlockTransactions::Full(transactions.clone());
429 let collected: Vec<_> = txs.into_transactions().collect();
430
431 assert_eq!(collected, transactions);
432 }
433
434 #[test]
435 fn test_block_transactions_kind_conversion() {
436 let full: BlockTransactionsKind = true.into();
437 assert_eq!(full, BlockTransactionsKind::Full);
438
439 let hashes: BlockTransactionsKind = false.into();
440 assert_eq!(hashes, BlockTransactionsKind::Hashes);
441
442 let bool_full: bool = BlockTransactionsKind::Full.into();
443 assert!(bool_full);
444
445 let bool_hashes: bool = BlockTransactionsKind::Hashes.into();
446 assert!(!bool_hashes);
447 }
448}