1#[cfg(not(feature = "celo"))]
4use crate::types::Withdrawal;
5use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, H256, U256, U64};
6use chrono::{DateTime, TimeZone, Utc};
7use serde::{
8 de::{MapAccess, Visitor},
9 ser::SerializeStruct,
10 Deserialize, Deserializer, Serialize, Serializer,
11};
12use std::{fmt, fmt::Formatter, str::FromStr};
13use thiserror::Error;
14
15#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
20pub struct Block<TX> {
21 pub hash: Option<H256>,
23 #[serde(default, rename = "parentHash")]
25 pub parent_hash: H256,
26 #[cfg(not(feature = "celo"))]
28 #[serde(default, rename = "sha3Uncles")]
29 pub uncles_hash: H256,
30 #[serde(default, rename = "miner")]
32 pub author: Option<Address>,
33 #[serde(default, rename = "stateRoot")]
35 pub state_root: H256,
36 #[serde(default, rename = "transactionsRoot")]
38 pub transactions_root: H256,
39 #[serde(default, rename = "receiptsRoot")]
41 pub receipts_root: H256,
42 pub number: Option<U64>,
44 #[serde(default, rename = "gasUsed")]
46 pub gas_used: U256,
47 #[cfg(not(feature = "celo"))]
49 #[serde(default, rename = "gasLimit")]
50 pub gas_limit: U256,
51 #[serde(default, rename = "extraData")]
53 pub extra_data: Bytes,
54 #[serde(rename = "logsBloom")]
56 pub logs_bloom: Option<Bloom>,
57 #[serde(default)]
59 pub timestamp: U256,
60 #[cfg(not(feature = "celo"))]
62 #[serde(default)]
63 pub difficulty: U256,
64 #[serde(rename = "totalDifficulty")]
66 pub total_difficulty: Option<U256>,
67 #[serde(default, rename = "sealFields", deserialize_with = "deserialize_null_default")]
69 pub seal_fields: Vec<Bytes>,
70 #[cfg(not(feature = "celo"))]
72 #[serde(default)]
73 pub uncles: Vec<H256>,
74 #[serde(bound = "TX: Serialize + serde::de::DeserializeOwned", default)]
76 pub transactions: Vec<TX>,
77 pub size: Option<U256>,
79 #[serde(rename = "mixHash")]
81 #[cfg(not(feature = "celo"))]
82 pub mix_hash: Option<H256>,
83 #[cfg(not(feature = "celo"))]
85 pub nonce: Option<crate::types::H64>,
86 #[serde(rename = "baseFeePerGas")]
88 pub base_fee_per_gas: Option<U256>,
89 #[serde(default, skip_serializing_if = "Option::is_none", rename = "blobGasUsed")]
91 #[cfg(not(feature = "celo"))]
92 pub blob_gas_used: Option<U256>,
93 #[serde(default, skip_serializing_if = "Option::is_none", rename = "excessBlobGas")]
95 #[cfg(not(feature = "celo"))]
96 pub excess_blob_gas: Option<U256>,
97 #[serde(default, skip_serializing_if = "Option::is_none", rename = "withdrawalsRoot")]
99 #[cfg(not(feature = "celo"))]
100 pub withdrawals_root: Option<H256>,
101 #[serde(default, skip_serializing_if = "Option::is_none")]
103 #[cfg(not(feature = "celo"))]
104 pub withdrawals: Option<Vec<Withdrawal>>,
105 #[serde(default, skip_serializing_if = "Option::is_none", rename = "parentBeaconBlockRoot")]
107 #[cfg(not(feature = "celo"))]
108 pub parent_beacon_block_root: Option<H256>,
109
110 #[cfg(feature = "celo")]
111 #[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
112 pub randomness: Randomness,
114
115 #[cfg(feature = "celo")]
117 #[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
118 #[serde(rename = "epochSnarkData", default)]
119 pub epoch_snark_data: Option<EpochSnarkData>,
120
121 #[cfg(not(feature = "celo"))]
123 #[serde(flatten)]
124 pub other: crate::types::OtherFields,
125}
126
127fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
128where
129 T: Default + Deserialize<'de>,
130 D: Deserializer<'de>,
131{
132 let opt = Option::deserialize(deserializer)?;
133 Ok(opt.unwrap_or_default())
134}
135
136#[derive(Clone, Copy, Debug, Error)]
138pub enum TimeError {
139 #[error("timestamp is zero")]
141 TimestampZero,
142
143 #[error("timestamp is too large")]
145 TimestampOverflow,
146}
147
148#[cfg(not(feature = "celo"))]
150pub const ELASTICITY_MULTIPLIER: U256 = U256([2u64, 0, 0, 0]);
151#[cfg(not(feature = "celo"))]
153pub const BASE_FEE_MAX_CHANGE_DENOMINATOR: U256 = U256([8u64, 0, 0, 0]);
154
155impl<TX> Block<TX> {
156 #[cfg(not(feature = "celo"))]
158 pub fn gas_target(&self) -> U256 {
159 self.gas_limit / ELASTICITY_MULTIPLIER
160 }
161
162 #[cfg(not(feature = "celo"))]
165 pub fn next_block_base_fee(&self) -> Option<U256> {
166 use core::cmp::Ordering;
167
168 let target_usage = self.gas_target();
169 let base_fee_per_gas = self.base_fee_per_gas?;
170
171 match self.gas_used.cmp(&target_usage) {
172 Ordering::Greater => {
173 let gas_used_delta = self.gas_used - self.gas_target();
174 let base_fee_per_gas_delta = U256::max(
175 base_fee_per_gas * gas_used_delta /
176 target_usage /
177 BASE_FEE_MAX_CHANGE_DENOMINATOR,
178 U256::from(1u32),
179 );
180 let expected_base_fee_per_gas = base_fee_per_gas + base_fee_per_gas_delta;
181 Some(expected_base_fee_per_gas)
182 }
183 Ordering::Less => {
184 let gas_used_delta = self.gas_target() - self.gas_used;
185 let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta /
186 target_usage /
187 BASE_FEE_MAX_CHANGE_DENOMINATOR;
188 let expected_base_fee_per_gas = base_fee_per_gas - base_fee_per_gas_delta;
189 Some(expected_base_fee_per_gas)
190 }
191 Ordering::Equal => self.base_fee_per_gas,
192 }
193 }
194
195 pub fn time(&self) -> Result<DateTime<Utc>, TimeError> {
203 if self.timestamp.is_zero() {
204 return Err(TimeError::TimestampZero)
205 }
206 if self.timestamp.bits() > 63 {
207 return Err(TimeError::TimestampOverflow)
208 }
209 let secs = self.timestamp.as_u64() as i64;
212 Ok(Utc.timestamp_opt(secs, 0).unwrap())
213 }
214}
215
216impl Block<TxHash> {
217 pub fn into_full_block(self, transactions: Vec<Transaction>) -> Block<Transaction> {
219 #[cfg(not(feature = "celo"))]
220 {
221 let Block {
222 hash,
223 parent_hash,
224 uncles_hash,
225 author,
226 state_root,
227 transactions_root,
228 receipts_root,
229 number,
230 gas_used,
231 gas_limit,
232 extra_data,
233 logs_bloom,
234 timestamp,
235 difficulty,
236 total_difficulty,
237 seal_fields,
238 uncles,
239 size,
240 mix_hash,
241 nonce,
242 base_fee_per_gas,
243 withdrawals_root,
244 withdrawals,
245 blob_gas_used,
246 excess_blob_gas,
247 parent_beacon_block_root,
248 other,
249 ..
250 } = self;
251 Block {
252 hash,
253 parent_hash,
254 uncles_hash,
255 author,
256 state_root,
257 transactions_root,
258 receipts_root,
259 number,
260 gas_used,
261 gas_limit,
262 extra_data,
263 logs_bloom,
264 timestamp,
265 difficulty,
266 total_difficulty,
267 seal_fields,
268 uncles,
269 size,
270 mix_hash,
271 nonce,
272 base_fee_per_gas,
273 withdrawals_root,
274 withdrawals,
275 transactions,
276 blob_gas_used,
277 excess_blob_gas,
278 parent_beacon_block_root,
279 other,
280 }
281 }
282
283 #[cfg(feature = "celo")]
284 {
285 let Block {
286 hash,
287 parent_hash,
288 author,
289 state_root,
290 transactions_root,
291 receipts_root,
292 number,
293 gas_used,
294 extra_data,
295 logs_bloom,
296 timestamp,
297 total_difficulty,
298 seal_fields,
299 size,
300 base_fee_per_gas,
301 randomness,
302 epoch_snark_data,
303 ..
304 } = self;
305
306 Block {
307 hash,
308 parent_hash,
309 author,
310 state_root,
311 transactions_root,
312 receipts_root,
313 number,
314 gas_used,
315 extra_data,
316 logs_bloom,
317 timestamp,
318 total_difficulty,
319 seal_fields,
320 size,
321 base_fee_per_gas,
322 randomness,
323 epoch_snark_data,
324 transactions,
325 }
326 }
327 }
328}
329
330impl From<Block<Transaction>> for Block<TxHash> {
331 fn from(full: Block<Transaction>) -> Self {
332 #[cfg(not(feature = "celo"))]
333 {
334 let Block {
335 hash,
336 parent_hash,
337 uncles_hash,
338 author,
339 state_root,
340 transactions_root,
341 receipts_root,
342 number,
343 gas_used,
344 gas_limit,
345 extra_data,
346 logs_bloom,
347 timestamp,
348 difficulty,
349 total_difficulty,
350 seal_fields,
351 uncles,
352 transactions,
353 size,
354 mix_hash,
355 nonce,
356 base_fee_per_gas,
357 withdrawals_root,
358 withdrawals,
359 excess_blob_gas,
360 blob_gas_used,
361 parent_beacon_block_root,
362 other,
363 } = full;
364 Block {
365 hash,
366 parent_hash,
367 uncles_hash,
368 author,
369 state_root,
370 transactions_root,
371 receipts_root,
372 number,
373 gas_used,
374 gas_limit,
375 extra_data,
376 logs_bloom,
377 timestamp,
378 difficulty,
379 total_difficulty,
380 seal_fields,
381 uncles,
382 size,
383 mix_hash,
384 nonce,
385 base_fee_per_gas,
386 withdrawals_root,
387 withdrawals,
388 excess_blob_gas,
389 blob_gas_used,
390 parent_beacon_block_root,
391 transactions: transactions.iter().map(|tx| tx.hash).collect(),
392 other,
393 }
394 }
395
396 #[cfg(feature = "celo")]
397 {
398 let Block {
399 hash,
400 parent_hash,
401 author,
402 state_root,
403 transactions_root,
404 receipts_root,
405 number,
406 gas_used,
407 extra_data,
408 logs_bloom,
409 timestamp,
410 total_difficulty,
411 seal_fields,
412 transactions,
413 size,
414 base_fee_per_gas,
415 randomness,
416 epoch_snark_data,
417 } = full;
418
419 Block {
420 hash,
421 parent_hash,
422 author,
423 state_root,
424 transactions_root,
425 receipts_root,
426 number,
427 gas_used,
428 extra_data,
429 logs_bloom,
430 timestamp,
431 total_difficulty,
432 seal_fields,
433 size,
434 base_fee_per_gas,
435 randomness,
436 epoch_snark_data,
437 transactions: transactions.iter().map(|tx| tx.hash).collect(),
438 }
439 }
440 }
441}
442
443#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
446#[cfg(feature = "celo")]
447pub struct Randomness {
448 pub committed: Bytes,
450 pub revealed: Bytes,
452}
453
454#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
456#[cfg(feature = "celo")]
457pub struct EpochSnarkData {
458 pub bitmap: Bytes,
460 pub signature: Bytes,
462}
463
464#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
466pub enum BlockId {
467 Hash(H256),
471 Number(BlockNumber),
473}
474
475impl From<u64> for BlockId {
476 fn from(num: u64) -> Self {
477 BlockNumber::Number(num.into()).into()
478 }
479}
480
481impl From<U64> for BlockId {
482 fn from(num: U64) -> Self {
483 BlockNumber::Number(num).into()
484 }
485}
486
487impl From<BlockNumber> for BlockId {
488 fn from(num: BlockNumber) -> Self {
489 BlockId::Number(num)
490 }
491}
492
493impl From<H256> for BlockId {
494 fn from(hash: H256) -> Self {
495 BlockId::Hash(hash)
496 }
497}
498
499impl Serialize for BlockId {
500 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
501 where
502 S: Serializer,
503 {
504 match *self {
505 BlockId::Hash(ref x) => {
506 let mut s = serializer.serialize_struct("BlockIdEip1898", 1)?;
507 s.serialize_field("blockHash", &format!("{x:?}"))?;
508 s.end()
509 }
510 BlockId::Number(ref num) => num.serialize(serializer),
511 }
512 }
513}
514
515impl<'de> Deserialize<'de> for BlockId {
516 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
517 where
518 D: Deserializer<'de>,
519 {
520 struct BlockIdVisitor;
521
522 impl<'de> Visitor<'de> for BlockIdVisitor {
523 type Value = BlockId;
524
525 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
526 formatter.write_str("Block identifier following EIP-1898")
527 }
528
529 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
530 where
531 E: serde::de::Error,
532 {
533 Ok(BlockId::Number(v.parse().map_err(serde::de::Error::custom)?))
534 }
535
536 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
537 where
538 A: MapAccess<'de>,
539 {
540 let mut number = None;
541 let mut hash = None;
542
543 while let Some(key) = map.next_key::<String>()? {
544 match key.as_str() {
545 "blockNumber" => {
546 if number.is_some() || hash.is_some() {
547 return Err(serde::de::Error::duplicate_field("blockNumber"))
548 }
549 number = Some(BlockId::Number(map.next_value::<BlockNumber>()?))
550 }
551 "blockHash" => {
552 if number.is_some() || hash.is_some() {
553 return Err(serde::de::Error::duplicate_field("blockHash"))
554 }
555 hash = Some(BlockId::Hash(map.next_value::<H256>()?))
556 }
557 key => {
558 return Err(serde::de::Error::unknown_field(
559 key,
560 &["blockNumber", "blockHash"],
561 ))
562 }
563 }
564 }
565
566 number.or(hash).ok_or_else(|| {
567 serde::de::Error::custom("Expected `blockNumber` or `blockHash`")
568 })
569 }
570 }
571
572 deserializer.deserialize_any(BlockIdVisitor)
573 }
574}
575
576impl FromStr for BlockId {
577 type Err = String;
578
579 fn from_str(s: &str) -> Result<Self, Self::Err> {
580 if s.starts_with("0x") && s.len() == 66 {
581 let hash = s.parse::<H256>().map_err(|e| e.to_string());
582 hash.map(Self::Hash)
583 } else {
584 s.parse().map(Self::Number)
585 }
586 }
587}
588
589#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
591pub enum BlockNumber {
592 #[default]
594 Latest,
595 Finalized,
597 Safe,
599 Earliest,
601 Pending,
603 Number(U64),
605}
606
607impl BlockNumber {
608 pub fn as_number(&self) -> Option<U64> {
610 match *self {
611 BlockNumber::Number(num) => Some(num),
612 _ => None,
613 }
614 }
615
616 pub fn is_number(&self) -> bool {
618 matches!(self, BlockNumber::Number(_))
619 }
620
621 pub fn is_latest(&self) -> bool {
623 matches!(self, BlockNumber::Latest)
624 }
625
626 pub fn is_finalized(&self) -> bool {
628 matches!(self, BlockNumber::Finalized)
629 }
630
631 pub fn is_safe(&self) -> bool {
633 matches!(self, BlockNumber::Safe)
634 }
635
636 pub fn is_pending(&self) -> bool {
638 matches!(self, BlockNumber::Pending)
639 }
640
641 pub fn is_earliest(&self) -> bool {
643 matches!(self, BlockNumber::Earliest)
644 }
645}
646
647impl<T: Into<U64>> From<T> for BlockNumber {
648 fn from(num: T) -> Self {
649 BlockNumber::Number(num.into())
650 }
651}
652
653impl Serialize for BlockNumber {
654 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
655 where
656 S: Serializer,
657 {
658 match *self {
659 BlockNumber::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")),
660 BlockNumber::Latest => serializer.serialize_str("latest"),
661 BlockNumber::Finalized => serializer.serialize_str("finalized"),
662 BlockNumber::Safe => serializer.serialize_str("safe"),
663 BlockNumber::Earliest => serializer.serialize_str("earliest"),
664 BlockNumber::Pending => serializer.serialize_str("pending"),
665 }
666 }
667}
668
669impl<'de> Deserialize<'de> for BlockNumber {
670 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
671 where
672 D: Deserializer<'de>,
673 {
674 let s = String::deserialize(deserializer)?.to_lowercase();
675 s.parse().map_err(serde::de::Error::custom)
676 }
677}
678
679impl FromStr for BlockNumber {
680 type Err = String;
681
682 fn from_str(s: &str) -> Result<Self, Self::Err> {
683 match s {
684 "latest" => Ok(Self::Latest),
685 "finalized" => Ok(Self::Finalized),
686 "safe" => Ok(Self::Safe),
687 "earliest" => Ok(Self::Earliest),
688 "pending" => Ok(Self::Pending),
689 n if n.starts_with("0x") => n.parse().map(Self::Number).map_err(|e| e.to_string()),
691 n => n.parse::<u64>().map(|n| Self::Number(n.into())).map_err(|e| e.to_string()),
693 }
694 }
695}
696
697impl fmt::Display for BlockNumber {
698 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699 match self {
700 BlockNumber::Number(ref x) => format!("0x{x:x}").fmt(f),
701 BlockNumber::Latest => f.write_str("latest"),
702 BlockNumber::Finalized => f.write_str("finalized"),
703 BlockNumber::Safe => f.write_str("safe"),
704 BlockNumber::Earliest => f.write_str("earliest"),
705 BlockNumber::Pending => f.write_str("pending"),
706 }
707 }
708}
709
710#[cfg(test)]
711#[cfg(not(feature = "celo"))]
712mod tests {
713 use super::*;
714 use crate::types::{Transaction, TxHash};
715
716 #[test]
717 fn can_parse_eip1898_block_ids() {
718 let num = serde_json::json!(
719 { "blockNumber": "0x0" }
720 );
721 let id = serde_json::from_value::<BlockId>(num).unwrap();
722 assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
723
724 let num = serde_json::json!(
725 { "blockNumber": "pending" }
726 );
727 let id = serde_json::from_value::<BlockId>(num).unwrap();
728 assert_eq!(id, BlockId::Number(BlockNumber::Pending));
729
730 let num = serde_json::json!(
731 { "blockNumber": "latest" }
732 );
733 let id = serde_json::from_value::<BlockId>(num).unwrap();
734 assert_eq!(id, BlockId::Number(BlockNumber::Latest));
735
736 let num = serde_json::json!(
737 { "blockNumber": "finalized" }
738 );
739 let id = serde_json::from_value::<BlockId>(num).unwrap();
740 assert_eq!(id, BlockId::Number(BlockNumber::Finalized));
741
742 let num = serde_json::json!(
743 { "blockNumber": "safe" }
744 );
745 let id = serde_json::from_value::<BlockId>(num).unwrap();
746 assert_eq!(id, BlockId::Number(BlockNumber::Safe));
747
748 let num = serde_json::json!(
749 { "blockNumber": "earliest" }
750 );
751 let id = serde_json::from_value::<BlockId>(num).unwrap();
752 assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
753
754 let num = serde_json::json!("0x0");
755 let id = serde_json::from_value::<BlockId>(num).unwrap();
756 assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
757
758 let num = serde_json::json!("pending");
759 let id = serde_json::from_value::<BlockId>(num).unwrap();
760 assert_eq!(id, BlockId::Number(BlockNumber::Pending));
761
762 let num = serde_json::json!("latest");
763 let id = serde_json::from_value::<BlockId>(num).unwrap();
764 assert_eq!(id, BlockId::Number(BlockNumber::Latest));
765
766 let num = serde_json::json!("finalized");
767 let id = serde_json::from_value::<BlockId>(num).unwrap();
768 assert_eq!(id, BlockId::Number(BlockNumber::Finalized));
769
770 let num = serde_json::json!("safe");
771 let id = serde_json::from_value::<BlockId>(num).unwrap();
772 assert_eq!(id, BlockId::Number(BlockNumber::Safe));
773
774 let num = serde_json::json!("earliest");
775 let id = serde_json::from_value::<BlockId>(num).unwrap();
776 assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
777
778 let num = serde_json::json!(
779 { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }
780 );
781 let id = serde_json::from_value::<BlockId>(num).unwrap();
782 assert_eq!(
783 id,
784 BlockId::Hash(
785 "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
786 .parse()
787 .unwrap()
788 )
789 );
790 }
791
792 #[test]
793 fn serde_block_number() {
794 for b in &[
795 BlockNumber::Latest,
796 BlockNumber::Finalized,
797 BlockNumber::Safe,
798 BlockNumber::Earliest,
799 BlockNumber::Pending,
800 ] {
801 let b_ser = serde_json::to_string(&b).unwrap();
802 let b_de: BlockNumber = serde_json::from_str(&b_ser).unwrap();
803 assert_eq!(b_de, *b);
804 }
805
806 let b = BlockNumber::Number(1042u64.into());
807 let b_ser = serde_json::to_string(&b).unwrap();
808 let b_de: BlockNumber = serde_json::from_str(&b_ser).unwrap();
809 assert_eq!(b_ser, "\"0x412\"");
810 assert_eq!(b_de, b);
811 }
812
813 #[test]
814 fn deserialize_blk_no_txs() {
815 let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":["0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067"],"uncles":[]}"#;
816 let _block: Block<TxHash> = serde_json::from_str(block).unwrap();
817 }
818
819 #[test]
820 fn deserialize_blk_with_txs() {
821 let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#;
822 let _block: Block<Transaction> = serde_json::from_str(block).unwrap();
823 }
824
825 #[test]
826 fn post_london_block() {
828 let json = serde_json::json!(
829 {
830 "baseFeePerGas": "0x7",
831 "miner": "0x0000000000000000000000000000000000000001",
832 "number": "0x1b4",
833 "hash": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
834 "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5",
835 "mixHash": "0x1010101010101010101010101010101010101010101010101010101010101010",
836 "nonce": "0x0000000000000000",
837 "sealFields": [
838 "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2",
839 "0x0000000000000042"
840 ],
841 "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
842 "logsBloom": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
843 "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
844 "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
845 "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff",
846 "difficulty": "0x27f07",
847 "totalDifficulty": "0x27f07",
848 "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
849 "size": "0x27f07",
850 "gasLimit": "0x9f759",
851 "minGasPrice": "0x9f759",
852 "gasUsed": "0x9f759",
853 "timestamp": "0x54e34e8e",
854 "transactions": [],
855 "uncles": []
856 }
857 );
858
859 let block: Block<()> = serde_json::from_value(json).unwrap();
860 assert_eq!(block.base_fee_per_gas, Some(U256::from(7)));
861 }
862
863 #[test]
864 fn test_next_block_base_fee() {
865 let block_14402566: Block<TxHash> = Block {
867 number: Some(U64::from(14402566u64)),
868 base_fee_per_gas: Some(U256::from(36_803_013_756u128)),
869 gas_limit: U256::from(30_087_887u128),
870 gas_used: U256::from(2_023_848u128),
871 ..Default::default()
872 };
873
874 assert_eq!(block_14402566.base_fee_per_gas, Some(U256::from(36_803_013_756u128)));
875 assert_eq!(block_14402566.gas_target(), U256::from(15_043_943u128));
876 assert_eq!(block_14402566.next_block_base_fee(), Some(U256::from(32_821_521_542u128)));
878
879 let block_14402712: Block<TxHash> = Block {
881 number: Some(U64::from(14402712u64)),
882 base_fee_per_gas: Some(U256::from(24_870_031_149u128)),
883 gas_limit: U256::from(30_000_000u128),
884 gas_used: U256::from(29_999_374u128),
885 ..Default::default()
886 };
887
888 assert_eq!(block_14402712.base_fee_per_gas, Some(U256::from(24_870_031_149u128)));
889 assert_eq!(block_14402712.gas_target(), U256::from(15_000_000u128));
890 assert_eq!(block_14402712.next_block_base_fee(), Some(U256::from(27_978_655_303u128)));
892 }
893
894 #[test]
895 fn pending_block() {
896 let json = serde_json::json!(
897 {
898 "baseFeePerGas": "0x3a460775a",
899 "difficulty": "0x329b1f81605a4a",
900 "extraData": "0xd983010a0d846765746889676f312e31362e3130856c696e7578",
901 "gasLimit": "0x1c950d9",
902 "gasUsed": "0x1386f81",
903 "hash": null,
904 "logsBloom": "0x5a3fc3425505bf83b1ebe6ffead1bbfdfcd6f9cfbd1e5fb7fbc9c96b1bbc2f2f6bfef959b511e4f0c7d3fbc60194fbff8bcff8e7b8f6ba9a9a956fe36473ed4deec3f1bc67f7dabe48f71afb377bdaa47f8f9bb1cd56930c7dfcbfddf283f9697fb1db7f3bedfa3e4dfd9fae4fb59df8ac5d9c369bff14efcee59997df8bb16d47d22f0bfbafb29fbfff6e1e41bca61e37e7bdfde1fe27b9fd3a7adfcb74fe98e6dbcc5f5bb3bd4d4bb6ccd29fd3bd446c7f38dcaf7ff78fb3f3aa668cbffe56291d7fbbebd2549fdfd9f223b3ba61dee9e92ebeb5dc967f711d039ff1cb3c3a8fb3b7cbdb29e6d1e79e6b95c596dfe2be36fd65a4f6fdeebe7efbe6e38037d7",
905 "miner": null,
906 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
907 "nonce": null,
908 "number": "0xe1a6ee",
909 "parentHash": "0xff1a940068dfe1f9e3f5514e6b7ff5092098d21d706396a9b19602f0f2b11d44",
910 "receiptsRoot": "0xe7df36675953a2d2dd22ec0e44ff59818891170875ebfba5a39f6c85084a6f10",
911 "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
912 "size": "0xd8d6",
913 "stateRoot": "0x2ed13b153d1467deb397a789aabccb287590579cf231bf4f1ff34ac3b4905ce2",
914 "timestamp": "0x6282b31e",
915 "totalDifficulty": null,
916 "transactions": [
917 "0xaad0d53ae350dd06481b42cd097e3f3a4a31e0ac980fac4663b7dd913af20d9b"
918 ],
919 "transactionsRoot": "0xfc5c28cb82d5878172cd1b97429d44980d1cee03c782950ee73e83f1fa8bfb49",
920 "uncles": []
921 }
922 );
923 let block: Block<H256> = serde_json::from_value(json).unwrap();
924 assert!(block.author.is_none());
925 }
926
927 #[test]
928 fn can_deserialize_with_sealed_fields() {
929 let json = serde_json::json!({
930 "number": "0x1b4",
931 "hash": "0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae",
932 "parentHash": "0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54",
933 "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
934 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
935 "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
936 "stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d",
937 "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
938 "miner": "0xbb7b8287f3f0a933474a79eae42cbca977791171",
939 "difficulty": "0x4ea3f27bc",
940 "totalDifficulty": "0x78ed983323d",
941 "sealFields": null,
942 "nonce": "0x689056015818adbe",
943 "mixHash": "0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843",
944 "extraData": "0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32",
945 "size": "0x0",
946 "gasLimit": "0x1388",
947 "gasUsed": "0x0",
948 "timestamp": "0x55ba467c",
949 "transactions": [],
950 "uncles": []
951 }
952 );
953 let _block: Block<TxHash> = serde_json::from_value(json).unwrap();
954 }
955}
956
957#[cfg(test)]
958#[cfg(feature = "celo")]
959mod celo_tests {
960 use super::*;
961
962 #[test]
963 fn block_without_snark_data() {
964 let block = r#"{"extraData":"0xd983010000846765746889676f312e31332e3130856c696e7578000000000000f8b2c0c080b841cfa11585812ec794c4baa46178690971b3c72e367211d68a9ea318ff500c5aeb7099cafc965240e3b57cf7355341cf76bdca74530334658370d2df7b2e030ab200f582027db017810fa05b4f35927f968f6be1a61e322d4ace3563feb8a489690a91c031fda640c55c216f6712a7bdde994338a5610080f58203ffb093cd643f5154979952791ff714eb885df0f18012f2211fb8c29a8947130dc3adf4ecb48a3c4a142a0faa51e5c60b048180","gasUsed":"0xbef6","hash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","logsBloom":"0x00000800000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000100000000000000000000000000000000000000000000000000000000000000080000000000001020000400000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000000000000000000100000040004000000000000800000000000000000084000000000000000000000000000000000020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000","miner":"0xcda518f6b5a797c3ec45d37c65b83e0b0748edca","number":"0x1b4","parentHash":"0xa6b4775f600c2981f9142cbc1361db02c7ba8c185a1110537b255356876301a2","randomness":{"committed":"0x049e84c89f1aa0e3a770b2545b05a30eb814dae322e7247fd2bf27e6cacb1f51","revealed":"0x5a8826bf59a7ed1ee86a9d6464fa9c1fcece78ffa7cf32b11a03ad251ddcefe6"},"receiptsRoot":"0x1724dc3e7c2bfa03974c1deedf5ea20ad30b72e25f3c62fbb5fd06fc107068d7","size":"0x3a0","stateRoot":"0xc45fa03e69dccb54b4981d23d77328ab8906ddd7a0d8238b9c54ae1a14df4d1c","timestamp":"0x5e90166d","totalDifficulty":"0x1b5","transactions":[{"blockHash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","blockNumber":"0x1b4","from":"0x456f41406b32c45d59e539e4bba3d7898c3584da","gas":"0x1312d00","gasPrice":"0x174876e800","feeCurrency":null,"gatewayFeeRecipient":null,"gatewayFee":"0x0","hash":"0xf7b1b588b1fc03305f556805812273d80fb61fc0ba7f812de27189e95c5ecfc5","input":"0xed385274000000000000000000000000b9ff7ab50a2f0fd3e2fb2814b016ac90c91df98f03386ba30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be951906eba2aa800000","nonce":"0x147","to":"0xa12a699c641cc875a7ca57495861c79c33d293b4","transactionIndex":"0x0","value":"0x0","v":"0x15e08","r":"0x5787d040d09a34cb2b9ffcd096be7fe66aa6a3ed0632f182d1f3045640a9ef8b","s":"0x7897f58740f2a1c645826579106a620c306fc56381520ae2f28880bb284c4abd"}],"transactionsRoot":"0xbc8cb40b809914b9cd735b12e9b1802cf5d85de5223a22bbdb249a7e8b45ec93"}"#;
965 let block: Block<Transaction> = serde_json::from_str(block).unwrap();
966 assert_eq!(block.epoch_snark_data, None);
967 }
968
969 #[test]
970 fn block_with_snark_data() {
971 let block = r#"{"extraData":"0xd983010000846765746889676f312e31332e3130856c696e7578000000000000f8b2c0c080b841cfa11585812ec794c4baa46178690971b3c72e367211d68a9ea318ff500c5aeb7099cafc965240e3b57cf7355341cf76bdca74530334658370d2df7b2e030ab200f582027db017810fa05b4f35927f968f6be1a61e322d4ace3563feb8a489690a91c031fda640c55c216f6712a7bdde994338a5610080f58203ffb093cd643f5154979952791ff714eb885df0f18012f2211fb8c29a8947130dc3adf4ecb48a3c4a142a0faa51e5c60b048180","gasUsed":"0xbef6","hash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","logsBloom":"0x00000800000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000100000000000000000000000000000000000000000000000000000000000000080000000000001020000400000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000000000000000000100000040004000000000000800000000000000000084000000000000000000000000000000000020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000","miner":"0xcda518f6b5a797c3ec45d37c65b83e0b0748edca","number":"0x1b4","parentHash":"0xa6b4775f600c2981f9142cbc1361db02c7ba8c185a1110537b255356876301a2","randomness":{"committed":"0x049e84c89f1aa0e3a770b2545b05a30eb814dae322e7247fd2bf27e6cacb1f51","revealed":"0x5a8826bf59a7ed1ee86a9d6464fa9c1fcece78ffa7cf32b11a03ad251ddcefe6"},"receiptsRoot":"0x1724dc3e7c2bfa03974c1deedf5ea20ad30b72e25f3c62fbb5fd06fc107068d7","size":"0x3a0","stateRoot":"0xc45fa03e69dccb54b4981d23d77328ab8906ddd7a0d8238b9c54ae1a14df4d1c","timestamp":"0x5e90166d","totalDifficulty":"0x1b5","transactions":[{"blockHash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","blockNumber":"0x1b4","from":"0x456f41406b32c45d59e539e4bba3d7898c3584da","gas":"0x1312d00","gasPrice":"0x174876e800","feeCurrency":null,"gatewayFeeRecipient":null,"gatewayFee":"0x0","hash":"0xf7b1b588b1fc03305f556805812273d80fb61fc0ba7f812de27189e95c5ecfc5","input":"0xed385274000000000000000000000000b9ff7ab50a2f0fd3e2fb2814b016ac90c91df98f03386ba30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be951906eba2aa800000","nonce":"0x147","to":"0xa12a699c641cc875a7ca57495861c79c33d293b4","transactionIndex":"0x0","value":"0x0","v":"0x15e08","r":"0x5787d040d09a34cb2b9ffcd096be7fe66aa6a3ed0632f182d1f3045640a9ef8b","s":"0x7897f58740f2a1c645826579106a620c306fc56381520ae2f28880bb284c4abd"}],"transactionsRoot":"0xbc8cb40b809914b9cd735b12e9b1802cf5d85de5223a22bbdb249a7e8b45ec93","epochSnarkData":{"bitmap": "0x01a72267ae3fe9fffb","signature": "0xcd803565d415c14b42d3aee51c5de1f6fd7d33cd036f03178c104c787a6ceafb8dd2b357d5fb5992fc2a23706625c800"}}"#;
972 let _block: Block<Transaction> = serde_json::from_str(block).unwrap();
973 }
974}