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                "u256_add" => Ok(Self::Evm(OpCode::ADD)),
54                "u256_sub" => Ok(Self::Evm(OpCode::SUB)),
55                "u256_lt" => Ok(Self::Evm(OpCode::LT)),
56                "u256_max" => Ok(Self::U256MAX),
57                "u256_addmod" => Ok(Self::Evm(OpCode::ADDMOD)),
58                "u256_mulmod" => Ok(Self::Evm(OpCode::MULMOD)),
59                "label_reserve_mem_32" => Ok(Self::Label(CompilerLabel::ReserveMemory32)),
60                "label_reserve_mem_64" => Ok(Self::Label(CompilerLabel::ReserveMemory64)),
61                _ => Err(Error::HostFuncNotFound(module.into(), name.into())),
62            },
63            ("evm", name) => Ok(Self::Evm(OpCode::from_str(name).map_err(|_| {
64                tracing::error!("Failed to load host function: {:?}", import);
65                Error::HostFuncNotFound(module.into(), name.into())
66            })?)),
67            ("asm", name) => match name {
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 anyhow::Ok;
105
106    use super::*;
107
108    #[test]
109    fn test_addmod_mulmod_host_functions() -> anyhow::Result<()> {
110        let addmod_func = HostFunc::try_from(("zinkc", "u256_addmod"))?;
111
112        assert_eq!(addmod_func, HostFunc::Evm(OpCode::ADDMOD));
113
114        // Test MULMOD host function conversion
115        let mulmod_func = HostFunc::try_from(("zinkc", "u256_mulmod"));
116        assert!(mulmod_func.is_ok());
117        Ok(())
118    }
119}