Skip to main content

yevm_core/
chain.rs

1use serde::{Deserialize, Serialize};
2use yevm_base::{Acc, Int};
3use yevm_misc::buf::Buf;
4
5use crate::{
6    Result,
7    call::{Block, Head},
8    evm::Fetch,
9    state::{Account, State},
10};
11
12#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
13#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
14pub trait Chain {
15    async fn get(&self, acc: &Acc, key: &Int) -> eyre::Result<Int>;
16    async fn acc(&self, acc: &Acc) -> eyre::Result<Account>;
17    async fn code(&self, acc: &Acc) -> eyre::Result<(Buf, Int)>;
18    async fn nonce(&self, acc: &Acc) -> eyre::Result<u64>;
19    async fn balance(&self, acc: &Acc) -> eyre::Result<Int>;
20    async fn head(&self, number: u64) -> eyre::Result<Head>;
21    async fn block(&self, number: u64) -> eyre::Result<Block>;
22    async fn chain_id(&self) -> eyre::Result<u64>;
23}
24
25#[allow(clippy::large_enum_variant)] // TODO: wrap Block with Box?
26#[derive(Clone, Debug, Deserialize, Serialize)]
27pub enum Fetched {
28    Account(Acc, Account),
29    State(Acc, Int, Int),
30    Hash(u64, Int),
31    Block(Block),
32    ChainId(u64),
33}
34
35pub async fn fetch(f: Fetch, state: &mut impl State, chain: &impl Chain) -> Result<()> {
36    match f {
37        Fetch::Account(acc) | Fetch::Balance(acc) | Fetch::Nonce(acc) | Fetch::Code(acc) => {
38            if state.is_offline() {
39                let next = state.next_fetched();
40                let Some(Fetched::Account(_, account)) = next else {
41                    return Err(
42                        eyre::eyre!("Offline fetch: expected account but got {next:?}").into(),
43                    );
44                };
45                state.merge(&acc, account.clone());
46            } else {
47                let account = chain.acc(&acc).await?;
48                state.merge(&acc, account.clone());
49                state.save_fetched(Fetched::Account(acc, account));
50            }
51            Ok(())
52        }
53        Fetch::BlockHash(number) => {
54            if state.is_offline() {
55                let next = state.next_fetched();
56                let Some(Fetched::Hash(number, hash)) = next else {
57                    return Err(
58                        eyre::eyre!("Offline fetch: expected block hash but got {next:?}").into(),
59                    );
60                };
61                state.hash(number, hash);
62            } else {
63                let hash = chain
64                    .head(number)
65                    .await
66                    .map(|head| head.hash)
67                    .unwrap_or(Int::ZERO);
68                state.hash(number, hash);
69                state.save_fetched(Fetched::Hash(number, hash));
70            }
71            Ok(())
72        }
73        Fetch::StateCell(acc, key) => {
74            if state.is_offline() {
75                let next = state.next_fetched();
76                let Some(Fetched::State(_, _, val)) = next else {
77                    return Err(
78                        eyre::eyre!("Offline fetch: expected state cell but got {next:?}").into(),
79                    );
80                };
81                state.init(&acc, &key, val);
82            } else {
83                let val = chain.get(&acc, &key).await?;
84                state.init(&acc, &key, val);
85                state.save_fetched(Fetched::State(acc, key, val));
86            }
87            Ok(())
88        }
89    }
90}