zingen/wasm/
host.rs

1//! Host functions
2
3use crate::{Error, Result};
4use anyhow::anyhow;
5use core::str::FromStr;
6use opcodes::{Cancun as OpCode, OpCode as _};
7
8/// EVM built-in function.
9#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
10pub enum HostFunc {
11    /// EVM assemble operations.
12    Evm(OpCode),
13    /// No operations, this only covers `push_$ty` at the moment.
14    NoOp,
15    // Zinkc helper functions
16    //
17    /// Emit ABI to the compiler.
18    EmitABI,
19    /// Push u256 max to stack
20    U256MAX,
21    /// Revert messages with length of slots
22    Revert(usize),
23    /// Compiler labels
24    Label(CompilerLabel),
25}
26
27impl HostFunc {
28    /// Stack input size.
29    pub fn stack_in(&self) -> u8 {
30        match self {
31            Self::Evm(op) => op.stack_in() as u8,
32            _ => 0,
33        }
34    }
35
36    /// Stack output size.
37    pub fn stack_out(&self) -> u8 {
38        match self {
39            Self::Evm(op) => op.stack_out() as u8,
40            _ => 0,
41        }
42    }
43}
44
45impl TryFrom<(&str, &str)> for HostFunc {
46    type Error = Error;
47
48    fn try_from(import: (&str, &str)) -> Result<Self> {
49        let (module, name) = import;
50        match import {
51            ("zinkc", name) => match name {
52                "emit_abi" => Ok(Self::EmitABI),
53                "label_reserve_mem_32" => Ok(Self::Label(CompilerLabel::ReserveMemory32)),
54                "label_reserve_mem_64" => Ok(Self::Label(CompilerLabel::ReserveMemory64)),
55                _ => Err(Error::HostFuncNotFound(module.into(), name.into())),
56            },
57            ("evm", name) => Ok(Self::Evm(OpCode::from_str(name).map_err(|_| {
58                tracing::error!("Failed to load host function: {:?}", import);
59                Error::HostFuncNotFound(module.into(), name.into())
60            })?)),
61            ("ext", name) => match name {
62                "u256_add" => Ok(Self::Evm(OpCode::ADD)),
63                "u256_sub" => Ok(Self::Evm(OpCode::SUB)),
64                "u256_lt" => Ok(Self::Evm(OpCode::LT)),
65                "u256_max" => Ok(Self::U256MAX),
66                "u256_addmod" => Ok(Self::Evm(OpCode::ADDMOD)),
67                "u256_mulmod" => Ok(Self::Evm(OpCode::MULMOD)),
68                n if n.starts_with("sload") => Ok(Self::Evm(OpCode::SLOAD)),
69                n if n.starts_with("tload") => Ok(Self::Evm(OpCode::TLOAD)),
70                n if n.starts_with("revert") => {
71                    let count = n.trim_start_matches("revert");
72                    Ok(Self::Revert(count.parse().map_err(|e| anyhow!("{e}"))?))
73                }
74                n if n.starts_with("mulmod") => Ok(Self::Evm(OpCode::MULMOD)),
75                n if n.starts_with("addmod") => Ok(Self::Evm(OpCode::ADDMOD)),
76                _ => Ok(Self::NoOp),
77            },
78            ("bytes", instr) => match instr {
79                push if push.starts_with("push_bytes") => Ok(Self::NoOp),
80                sload if sload.starts_with("sload_bytes") => Ok(Self::Evm(OpCode::SLOAD)),
81                eq if eq.ends_with("_eq") => Ok(Self::Evm(OpCode::EQ)),
82                _ => {
83                    tracing::warn!("Failed to load host function: {import:?} from module bytes");
84                    Err(Error::HostFuncNotFound(module.into(), name.into()))
85                }
86            },
87            _ => {
88                tracing::warn!("Failed to load host function: {:?}", import);
89                Err(Error::HostFuncNotFound(module.into(), name.into()))
90            }
91        }
92    }
93}
94
95/// Labels in host functions
96#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
97pub enum CompilerLabel {
98    ReserveMemory32,
99    ReserveMemory64,
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_addmod_mulmod_host_functions() -> anyhow::Result<()> {
108        let addmod_func = HostFunc::try_from(("ext", "u256_addmod"))?;
109        assert_eq!(addmod_func, HostFunc::Evm(OpCode::ADDMOD));
110
111        // Test MULMOD host function conversion
112        let mulmod_func = HostFunc::try_from(("ext", "u256_mulmod"));
113        assert!(mulmod_func.is_ok());
114        Ok(())
115    }
116}