1use crate::{BootInfo, HintType, PreState};
4use alloc::{boxed::Box, string::ToString, sync::Arc, vec::Vec};
5use alloy_consensus::Header;
6use alloy_eips::eip2718::Decodable2718;
7use alloy_primitives::{Address, B256};
8use alloy_rlp::Decodable;
9use async_trait::async_trait;
10use kona_interop::InteropProvider;
11use kona_mpt::{OrderedListWalker, TrieHinter, TrieNode, TrieProvider};
12use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType, errors::PreimageOracleError};
13use kona_proof::{eip_2935_history_lookup, errors::OracleProviderError};
14use kona_registry::HashMap;
15use op_alloy_consensus::OpReceiptEnvelope;
16use spin::RwLock;
17
18#[derive(Debug, Clone)]
20pub struct OracleInteropProvider<T> {
21 oracle: Arc<T>,
23 boot: BootInfo,
25 safe_head_cache: Arc<RwLock<HashMap<u64, Header>>>,
27 chain_id: Arc<RwLock<Option<u64>>>,
29}
30
31impl<T> OracleInteropProvider<T>
32where
33 T: CommsClient + Send + Sync,
34{
35 pub fn new(oracle: Arc<T>, boot: BootInfo) -> Self {
37 Self {
38 oracle,
39 boot,
40 safe_head_cache: Arc::new(RwLock::new(HashMap::default())),
41 chain_id: Arc::new(RwLock::new(None)),
42 }
43 }
44
45 pub async fn header_by_hash(
47 &self,
48 chain_id: u64,
49 block_hash: B256,
50 ) -> Result<Header, <Self as InteropProvider>::Error> {
51 HintType::L2BlockHeader
52 .with_data(&[block_hash.as_slice(), chain_id.to_be_bytes().as_ref()])
53 .send(self.oracle.as_ref())
54 .await?;
55
56 let header_rlp = self
57 .oracle
58 .get(PreimageKey::new(*block_hash, PreimageKeyType::Keccak256))
59 .await
60 .map_err(OracleProviderError::Preimage)?;
61
62 Header::decode(&mut header_rlp.as_ref()).map_err(OracleProviderError::Rlp)
63 }
64
65 async fn derive_receipts(
67 &self,
68 chain_id: u64,
69 block_hash: B256,
70 header: &Header,
71 ) -> Result<Vec<OpReceiptEnvelope>, <Self as InteropProvider>::Error> {
72 HintType::L2Receipts
75 .with_data(&[block_hash.as_ref(), chain_id.to_be_bytes().as_slice()])
76 .send(self.oracle.as_ref())
77 .await?;
78 let trie_walker = OrderedListWalker::try_new_hydrated(header.receipts_root, self)
79 .map_err(OracleProviderError::TrieWalker)?;
80
81 let receipts = trie_walker
83 .into_iter()
84 .map(|(_, rlp)| {
85 let envelope = OpReceiptEnvelope::decode_2718(&mut rlp.as_ref())?;
86 Ok(envelope)
87 })
88 .collect::<Result<Vec<_>, _>>()
89 .map_err(OracleProviderError::Rlp)?;
90
91 Ok(receipts)
92 }
93}
94
95#[async_trait]
96impl<T> InteropProvider for OracleInteropProvider<T>
97where
98 T: CommsClient + Send + Sync,
99{
100 type Error = OracleProviderError;
101
102 async fn header_by_number(&self, chain_id: u64, number: u64) -> Result<Header, Self::Error> {
104 let mut header = if let Some(header) = self.safe_head_cache.read().get(&chain_id) {
108 header.clone()
109 } else {
110 let pre_state = match &self.boot.agreed_pre_state {
111 PreState::SuperRoot(super_root) => super_root,
112 PreState::TransitionState(transition_state) => &transition_state.pre_state,
113 };
114 let output = pre_state
115 .output_roots
116 .iter()
117 .find(|o| o.chain_id == chain_id)
118 .ok_or(OracleProviderError::UnknownChainId(chain_id))?;
119 HintType::L2OutputRoot
120 .with_data(&[
121 output.output_root.as_slice(),
122 output.chain_id.to_be_bytes().as_slice(),
123 ])
124 .send(self.oracle.as_ref())
125 .await?;
126 let output_preimage = self
127 .oracle
128 .get(PreimageKey::new(*output.output_root, PreimageKeyType::Keccak256))
129 .await
130 .map_err(OracleProviderError::Preimage)?;
131 let safe_head_hash = output_preimage[96..128]
132 .try_into()
133 .map_err(OracleProviderError::SliceConversion)?;
134
135 let header = self.header_by_hash(chain_id, safe_head_hash).await?;
137 self.safe_head_cache.write().insert(chain_id, header.clone());
138 header
139 };
140
141 if number > header.number {
143 return Err(OracleProviderError::BlockNumberPastHead(number, header.number));
144 }
145
146 let mut chain_id_lock = self.chain_id.write();
148 *chain_id_lock = Some(chain_id);
149 drop(chain_id_lock);
150
151 let rollup_config = self.boot.rollup_config(chain_id).ok_or_else(|| {
153 PreimageOracleError::Other("Missing rollup config for chain ID".to_string())
154 })?;
155 let mut linear_fallback = false;
156
157 while header.number > number {
158 if rollup_config.is_isthmus_active(header.timestamp) && !linear_fallback {
159 let block_hash = match eip_2935_history_lookup(&header, 0, self, self).await {
163 Ok(hash) => hash,
164 Err(_) => {
165 linear_fallback = true;
168 continue;
169 }
170 };
171
172 header = self.header_by_hash(chain_id, block_hash).await?;
173 } else {
174 header = self.header_by_hash(chain_id, header.parent_hash).await?;
176 }
177 }
178
179 Ok(header)
180 }
181
182 async fn receipts_by_number(
184 &self,
185 chain_id: u64,
186 number: u64,
187 ) -> Result<Vec<OpReceiptEnvelope>, Self::Error> {
188 let header = self.header_by_number(chain_id, number).await?;
189 self.derive_receipts(chain_id, header.hash_slow(), &header).await
190 }
191
192 async fn receipts_by_hash(
194 &self,
195 chain_id: u64,
196 block_hash: B256,
197 ) -> Result<Vec<OpReceiptEnvelope>, Self::Error> {
198 let header = self.header_by_hash(chain_id, block_hash).await?;
199 self.derive_receipts(chain_id, block_hash, &header).await
200 }
201}
202
203impl<T> TrieProvider for OracleInteropProvider<T>
204where
205 T: CommsClient + Send + Sync + Clone,
206{
207 type Error = OracleProviderError;
208
209 fn trie_node_by_hash(&self, key: B256) -> Result<TrieNode, Self::Error> {
210 kona_proof::block_on(async move {
211 let trie_node_rlp = self
212 .oracle
213 .get(PreimageKey::new(*key, PreimageKeyType::Keccak256))
214 .await
215 .map_err(OracleProviderError::Preimage)?;
216 TrieNode::decode(&mut trie_node_rlp.as_ref()).map_err(OracleProviderError::Rlp)
217 })
218 }
219}
220
221impl<T: CommsClient> TrieHinter for OracleInteropProvider<T> {
222 type Error = OracleProviderError;
223
224 fn hint_trie_node(&self, hash: B256) -> Result<(), Self::Error> {
225 kona_proof::block_on(async move {
226 HintType::L2StateNode
227 .with_data(&[hash.as_slice()])
228 .with_data(
229 self.chain_id.read().map_or_else(Vec::new, |id| id.to_be_bytes().to_vec()),
230 )
231 .send(self.oracle.as_ref())
232 .await
233 })
234 }
235
236 fn hint_account_proof(&self, address: Address, block_number: u64) -> Result<(), Self::Error> {
237 kona_proof::block_on(async move {
238 HintType::L2AccountProof
239 .with_data(&[block_number.to_be_bytes().as_ref(), address.as_slice()])
240 .with_data(
241 self.chain_id.read().map_or_else(Vec::new, |id| id.to_be_bytes().to_vec()),
242 )
243 .send(self.oracle.as_ref())
244 .await
245 })
246 }
247
248 fn hint_storage_proof(
249 &self,
250 address: alloy_primitives::Address,
251 slot: alloy_primitives::U256,
252 block_number: u64,
253 ) -> Result<(), Self::Error> {
254 kona_proof::block_on(async move {
255 HintType::L2AccountStorageProof
256 .with_data(&[
257 block_number.to_be_bytes().as_ref(),
258 address.as_slice(),
259 slot.to_be_bytes::<32>().as_ref(),
260 ])
261 .with_data(
262 self.chain_id.read().map_or_else(Vec::new, |id| id.to_be_bytes().to_vec()),
263 )
264 .send(self.oracle.as_ref())
265 .await
266 })
267 }
268
269 fn hint_execution_witness(
270 &self,
271 parent_hash: B256,
272 op_payload_attributes: &op_alloy_rpc_types_engine::OpPayloadAttributes,
273 ) -> Result<(), Self::Error> {
274 kona_proof::block_on(async move {
275 let encoded_attributes =
276 serde_json::to_vec(op_payload_attributes).map_err(OracleProviderError::Serde)?;
277
278 HintType::L2PayloadWitness
279 .with_data(&[parent_hash.as_slice(), &encoded_attributes])
280 .with_data(
281 self.chain_id.read().map_or_else(Vec::new, |id| id.to_be_bytes().to_vec()),
282 )
283 .send(self.oracle.as_ref())
284 .await
285 })
286 }
287}