anvil_zksync_core/
utils.rs

1use anvil_zksync_common::sh_err;
2use chrono::{DateTime, Utc};
3use std::fmt;
4use std::future::Future;
5use std::sync::Arc;
6use tokio::runtime::Builder;
7use tokio::sync::{RwLock, RwLockReadGuard};
8use zksync_multivm::interface::{Call, CallType, ExecutionResult, VmExecutionResultAndLogs};
9use zksync_types::{
10    api::{BlockNumber, DebugCall, DebugCallType},
11    web3::Bytes,
12    Transaction, CONTRACT_DEPLOYER_ADDRESS, U256, U64,
13};
14use zksync_web3_decl::error::Web3Error;
15
16/// Takes long integers and returns them in human friendly format with "_".
17/// For example: 12_334_093
18pub fn to_human_size(input: U256) -> String {
19    let input = format!("{:?}", input);
20    let tmp: Vec<_> = input
21        .chars()
22        .rev()
23        .enumerate()
24        .flat_map(|(index, val)| {
25            if index > 0 && index % 3 == 0 {
26                vec!['_', val]
27            } else {
28                vec![val]
29            }
30        })
31        .collect();
32    tmp.iter().rev().collect()
33}
34
35/// Returns the actual [U64] block number from [BlockNumber].
36///
37/// # Arguments
38///
39/// * `block_number` - [BlockNumber] for a block.
40/// * `latest_block_number` - A [U64] representing the latest block number.
41///
42/// # Returns
43///
44/// A [U64] representing the input block number.
45pub fn to_real_block_number(block_number: BlockNumber, latest_block_number: U64) -> U64 {
46    match block_number {
47        BlockNumber::FastFinalized
48        | BlockNumber::Finalized
49        | BlockNumber::Pending
50        | BlockNumber::Committed
51        | BlockNumber::L1Committed
52        | BlockNumber::Latest => latest_block_number,
53        BlockNumber::Earliest => U64::zero(),
54        BlockNumber::Number(n) => n,
55    }
56}
57
58/// Creates a [DebugCall] from a [L2Tx], [VmExecutionResultAndLogs] and a list of [Call]s.
59pub fn create_debug_output(
60    tx: &Transaction,
61    result: &VmExecutionResultAndLogs,
62    traces: Vec<Call>,
63) -> Result<DebugCall, Web3Error> {
64    let calltype = if tx
65        .recipient_account()
66        .map(|addr| addr == CONTRACT_DEPLOYER_ADDRESS)
67        .unwrap_or_default()
68    {
69        DebugCallType::Create
70    } else {
71        DebugCallType::Call
72    };
73    match &result.result {
74        ExecutionResult::Success { output } => Ok(DebugCall {
75            gas_used: result.statistics.gas_used.into(),
76            output: output.clone().into(),
77            r#type: calltype,
78            from: tx.initiator_account(),
79            to: tx.recipient_account().unwrap_or_default(),
80            gas: tx.gas_limit(),
81            value: tx.execute.value,
82            input: tx.execute.calldata().into(),
83            error: None,
84            revert_reason: None,
85            calls: traces.into_iter().map(call_to_debug_call).collect(),
86        }),
87        ExecutionResult::Revert { output } => Ok(DebugCall {
88            gas_used: result.statistics.gas_used.into(),
89            output: output.encoded_data().into(),
90            r#type: calltype,
91            from: tx.initiator_account(),
92            to: tx.recipient_account().unwrap_or_default(),
93            gas: tx.gas_limit(),
94            value: tx.execute.value,
95            input: tx.execute.calldata().into(),
96            error: None,
97            revert_reason: Some(output.to_string()),
98            calls: traces.into_iter().map(call_to_debug_call).collect(),
99        }),
100        ExecutionResult::Halt { reason } => Err(Web3Error::SubmitTransactionError(
101            reason.to_string(),
102            vec![],
103        )),
104    }
105}
106
107fn call_to_debug_call(value: Call) -> DebugCall {
108    let calls = value.calls.into_iter().map(call_to_debug_call).collect();
109    let debug_type = match value.r#type {
110        CallType::Call(_) => DebugCallType::Call,
111        CallType::Create => DebugCallType::Create,
112        CallType::NearCall => unreachable!("We have to filter our near calls before"),
113    };
114    DebugCall {
115        r#type: debug_type,
116        from: value.from,
117        to: value.to,
118        gas: U256::from(value.gas),
119        gas_used: U256::from(value.gas_used),
120        value: value.value,
121        output: Bytes::from(value.output.clone()),
122        input: Bytes::from(value.input.clone()),
123        error: value.error.clone(),
124        revert_reason: value.revert_reason,
125        calls,
126    }
127}
128
129/// Converts a timestamp in milliseconds since epoch to a [DateTime] in UTC.
130pub fn utc_datetime_from_epoch_ms(millis: u64) -> DateTime<Utc> {
131    let secs = millis / 1000;
132    let nanos = (millis % 1000) * 1_000_000;
133    // expect() is ok- nanos can't be >2M
134    DateTime::<Utc>::from_timestamp(secs as i64, nanos as u32).expect("valid timestamp")
135}
136
137/// Error that can be converted to a [`Web3Error`] and has transparent JSON-RPC error message (unlike `anyhow::Error` conversions).
138#[derive(Debug)]
139pub(crate) struct TransparentError(pub String);
140
141impl fmt::Display for TransparentError {
142    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
143        formatter.write_str(&self.0)
144    }
145}
146
147impl std::error::Error for TransparentError {}
148
149impl From<TransparentError> for Web3Error {
150    fn from(err: TransparentError) -> Self {
151        Self::InternalError(err.into())
152    }
153}
154
155pub fn internal_error(method_name: &'static str, error: impl fmt::Display) -> Web3Error {
156    sh_err!("Internal error in method {method_name}: {error}");
157    Web3Error::InternalError(anyhow::Error::msg(error.to_string()))
158}
159
160pub fn block_on<F: Future + Send + 'static>(future: F) -> F::Output
161where
162    F::Output: Send,
163{
164    std::thread::spawn(move || {
165        let runtime = Builder::new_current_thread()
166            .enable_all()
167            .build()
168            .expect("tokio runtime creation failed");
169        runtime.block_on(future)
170    })
171    .join()
172    .unwrap()
173}
174
175/// A special version of `Arc<RwLock<T>>` that can only be read from.
176#[derive(Debug)]
177pub struct ArcRLock<T>(Arc<RwLock<T>>);
178
179impl<T> Clone for ArcRLock<T> {
180    fn clone(&self) -> Self {
181        ArcRLock(self.0.clone())
182    }
183}
184
185impl<T> ArcRLock<T> {
186    /// Wrap writeable `Arc<RwLock<T>>` into a read-only `ArcRLock<T>`.
187    pub fn wrap(inner: Arc<RwLock<T>>) -> Self {
188        Self(inner)
189    }
190
191    /// Locks this `ArcRLock` with shared read access, causing the current task
192    /// to yield until the lock has been acquired.
193    pub async fn read(&self) -> RwLockReadGuard<T> {
194        self.0.read().await
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use zksync_types::U256;
201
202    use super::*;
203
204    #[test]
205    fn test_utc_datetime_from_epoch_ms() {
206        let actual = utc_datetime_from_epoch_ms(1623931200000);
207        assert_eq!(DateTime::from_timestamp(1623931200, 0).unwrap(), actual);
208    }
209
210    #[test]
211    fn test_human_sizes() {
212        assert_eq!("123", to_human_size(U256::from(123u64)));
213        assert_eq!("1_234", to_human_size(U256::from(1234u64)));
214        assert_eq!("12_345", to_human_size(U256::from(12345u64)));
215        assert_eq!("0", to_human_size(U256::from(0)));
216        assert_eq!("1", to_human_size(U256::from(1)));
217        assert_eq!("50_000_000", to_human_size(U256::from(50000000u64)));
218    }
219
220    #[test]
221    fn test_to_real_block_number_finalized() {
222        let actual = to_real_block_number(BlockNumber::Finalized, U64::from(10));
223        assert_eq!(U64::from(10), actual);
224    }
225
226    #[test]
227    fn test_to_real_block_number_pending() {
228        let actual = to_real_block_number(BlockNumber::Pending, U64::from(10));
229        assert_eq!(U64::from(10), actual);
230    }
231
232    #[test]
233    fn test_to_real_block_number_committed() {
234        let actual = to_real_block_number(BlockNumber::Committed, U64::from(10));
235        assert_eq!(U64::from(10), actual);
236    }
237
238    #[test]
239    fn test_to_real_block_number_latest() {
240        let actual = to_real_block_number(BlockNumber::Latest, U64::from(10));
241        assert_eq!(U64::from(10), actual);
242    }
243
244    #[test]
245    fn test_to_real_block_number_earliest() {
246        let actual = to_real_block_number(BlockNumber::Earliest, U64::from(10));
247        assert_eq!(U64::zero(), actual);
248    }
249
250    #[test]
251    fn test_to_real_block_number_number() {
252        let actual = to_real_block_number(BlockNumber::Number(U64::from(5)), U64::from(10));
253        assert_eq!(U64::from(5), actual);
254    }
255}