Skip to main content

foundry_evm_core/fork/
resolved.rs

1use alloy_eips::{BlockId, BlockNumHash};
2use alloy_primitives::{B256, BlockNumber};
3use std::fmt;
4
5/// A fork selector and block identity resolved from a configured RPC source.
6///
7/// This context binds exact preflight reads and EVM environment reconstruction to the source,
8/// selector, and block that were resolved together. The fork database itself remains
9/// number-pinned.
10#[derive(Clone, PartialEq, Eq, Hash)]
11pub struct ResolvedFork {
12    source: ForkSource,
13    selector: Option<BlockNumber>,
14    block: BlockNumHash,
15}
16
17#[derive(Clone, PartialEq, Eq, Hash)]
18struct ForkSource {
19    url: String,
20    headers: Vec<String>,
21}
22
23impl ResolvedFork {
24    pub(crate) fn new(
25        url: &str,
26        headers: Option<&[String]>,
27        selector: Option<BlockNumber>,
28        block: BlockNumHash,
29    ) -> Self {
30        Self {
31            source: ForkSource {
32                url: url.to_string(),
33                headers: headers.unwrap_or_default().to_vec(),
34            },
35            selector,
36            block,
37        }
38    }
39
40    pub(crate) fn matches(
41        &self,
42        url: &str,
43        headers: Option<&[String]>,
44        selector: Option<BlockNumber>,
45    ) -> bool {
46        self.source.url == url
47            && self.source.headers.as_slice() == headers.unwrap_or_default()
48            && self.selector == selector
49    }
50
51    /// Returns the resolved block number.
52    pub const fn number(&self) -> BlockNumber {
53        self.block.number
54    }
55
56    /// Returns the resolved block hash.
57    pub const fn hash(&self) -> B256 {
58        self.block.hash
59    }
60
61    /// Returns an EIP-1898 selector for the exact resolved block.
62    ///
63    /// The block is not required to remain canonical so callers can still query the resolved
64    /// state after a reorganization.
65    pub fn exact_block_id(&self) -> BlockId {
66        BlockId::from((self.hash(), Some(false)))
67    }
68
69    /// Returns the resolved block number and hash.
70    pub(crate) const fn block(&self) -> BlockNumHash {
71        self.block
72    }
73}
74
75impl fmt::Debug for ResolvedFork {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        let mut debug = f.debug_struct("ResolvedFork");
78        debug.field("source", &"<redacted>");
79        if let Some(number) = self.selector {
80            debug.field("selector", &number);
81        } else {
82            debug.field("selector", &"latest");
83        }
84        debug.field("number", &self.number()).field("hash", &self.hash()).finish()
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use serde_json::json;
92
93    #[test]
94    fn exact_block_id_serializes_as_eip_1898_object() {
95        let hash = B256::with_last_byte(1);
96        let fork =
97            ResolvedFork::new("http://localhost:8545", None, None, BlockNumHash::new(1, hash));
98
99        assert_eq!(
100            serde_json::to_value(fork.exact_block_id()).unwrap(),
101            json!({
102                "blockHash": hash,
103                "requireCanonical": false,
104            })
105        );
106    }
107}