Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasm(host)): Add AddMod and MulMod to host functions #283

Merged
merged 20 commits into from
Nov 28, 2024
9 changes: 8 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ readme = "zink/README.md"
path = "zink/src/lib.rs"

[dependencies]
fmt = "0.1.0"
clearloop marked this conversation as resolved.
Show resolved Hide resolved
paste.workspace = true
zink-codegen.workspace = true

Expand Down
25 changes: 25 additions & 0 deletions codegen/src/wasm/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ impl TryFrom<(&str, &str)> for HostFunc {

// TODO: use anyhow instead of Error
Ok(Self::Revert(count.parse().map_err(|e| anyhow!("{e}"))?))
} else if name.starts_with("mulmod") {
Ok(Self::Evm(OpCode::MULMOD))
} else if name.starts_with("addmod") {
Ok(Self::Evm(OpCode::ADDMOD))
} else {
Ok(Self::NoOp)
}
Expand All @@ -72,6 +76,8 @@ impl TryFrom<(&str, &str)> for HostFunc {
("zinkc", "u256_sub") => Ok(Self::Evm(OpCode::SUB)),
("zinkc", "u256_lt") => Ok(Self::Evm(OpCode::LT)),
("zinkc", "u256_max") => Ok(Self::U256MAX),
("zinkc", "u256_addmod") => Ok(Self::Evm(OpCode::ADDMOD)),
("zinkc", "u256_mulmod") => Ok(Self::Evm(OpCode::MULMOD)),
("zinkc", "label_reserve_mem_32") => Ok(Self::Label(CompilerLabel::ReserveMemory32)),
("zinkc", "label_reserve_mem_64") => Ok(Self::Label(CompilerLabel::ReserveMemory64)),
_ => {
Expand All @@ -88,3 +94,22 @@ pub enum CompilerLabel {
ReserveMemory32,
ReserveMemory64,
}

#[cfg(test)]
mod tests {
use anyhow::Ok;

use super::*;

#[test]
fn test_addmod_mulmod_host_functions() -> anyhow::Result<()> {
let addmod_func = HostFunc::try_from(("zinkc", "u256_addmod"))?;

assert_eq!(addmod_func, HostFunc::Evm(OpCode::ADDMOD));

// Test MULMOD host function conversion
let mulmod_func = HostFunc::try_from(("zinkc", "u256_mulmod"));
assert!(mulmod_func.is_ok());
Ok(())
}
}
86 changes: 86 additions & 0 deletions examples/addmod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! Addmod example for i64, i32, u64, u32.
#![cfg_attr(target_arch = "wasm32", no_std)]
#![cfg_attr(target_arch = "wasm32", no_main)]

extern crate zink;
use zink::{primitives::{numeric::Numeric, U256}, Asm};
clearloop marked this conversation as resolved.
Show resolved Hide resolved

#[zink::external]
pub fn addmod_i32(a: i32, b: i32, n: i32) -> i32 {
a.addmod(b, n)
}

#[zink::external]
pub fn addmod_i64(a: i64, b: i64, n: i64) -> i64 {
a.addmod(b, n)
}

#[zink::external]
pub fn addmod_u32(a: u32, b: u32, n: u32) -> u32 {
a.addmod(b, n)
}

#[zink::external]
pub fn addmod_u64(a: u64, b: u64, n: u64) -> u64 {
a.addmod(b, n)
}

#[zink::external]
pub fn addmod_U256(a: U256, b: U256, n: U256) -> U256 {
a.addmod(b, n)
}

#[cfg(not(target_arch = "wasm32"))]
fn main() {}

#[test]
fn test() -> anyhow::Result<()> {
use zint::{Bytes32 as _, Contract};
clearloop marked this conversation as resolved.
Show resolved Hide resolved
// Test for i32
let mut contract = Contract::search("addmod")?.compile()?;

let info_i32 = contract.execute([
"addmod_i32(int32,int32,int32)".as_bytes(),
&3i32.to_bytes32(),
&5i32.to_bytes32(),
&7i32.to_bytes32(),
])?;
assert_eq!(info_i32.ret, 1i32.to_bytes32());

// Test for i64
let info_i64 = contract.execute([
"addmod_i64(int64,int64,int64)".as_bytes(),
&3i64.to_bytes32(),
&5i64.to_bytes32(),
&7i64.to_bytes32(),
])?;
assert_eq!(info_i64.ret, 1i64.to_bytes32());

let info_u32 = contract.execute([
"addmod_u32(uint32,uint32,uint32)".as_bytes(),
&3u32.to_bytes32(),
&5u32.to_bytes32(),
&7u32.to_bytes32(),
])?;
assert_eq!(info_u32.ret, 1u32.to_bytes32());

// Test for u64
let info_u64 = contract.execute([
"addmod_u64(uint64,uint64,uint64)".as_bytes(),
&3u64.to_bytes32(),
&5u64.to_bytes32(),
&7u64.to_bytes32(),
])?;
assert_eq!(info_u64.ret, 1u64.to_bytes32());

malik672 marked this conversation as resolved.
Show resolved Hide resolved
//Test for U256
let info_u256 = contract.execute([
"addmod_U256(uint256,uint256,uint256)".as_bytes(),
&3i32.bytes32(),
&5i32.bytes32(),
&7i32.bytes32(),
])?;
assert_eq!(info_u256.ret, 1i32.bytes32());

Ok(())
}
40 changes: 40 additions & 0 deletions zink/src/ffi/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,46 @@ extern "C" {
/// Push u256 to stack
pub fn push_u256(u256: U256);

/// Emit opcode ADDMOD
pub fn addmod_i8(a: i8, b: i8, n: i8) -> i8;
/// Emit opcode ADDMOD
pub fn mulmod_i8(a: i8, b: i8, n: i8) -> i8;

/// Emit opcode ADDMOD
pub fn addmod_i16(a: i16, b: i16, n: i16) -> i16;
/// Emit opcode ADDMOD
pub fn mulmod_i16(a: i16, b: i16, n: i16) -> i16;

/// Emit opcode ADDMOD
pub fn addmod_i32(a: i32, b: i32, n: i32) -> i32;
/// Emit opcode ADDMOD
pub fn mulmod_i32(a: i32, b: i32, n: i32) -> i32;

/// Emit opcode ADDMOD
pub fn addmod_i64(a: i64, b: i64, n: i64) -> i64;
/// Emit opcode ADDMOD
pub fn mulmod_i64(a: i64, b: i64, n: i64) -> i64;

/// Emit opcode ADDMOD
pub fn addmod_u8(a: u8, b: u8, n: u8) -> u8;
/// Emit opcode ADDMOD
pub fn mulmod_u8(a: u8, b: u8, n: u8) -> u8;

/// Emit opcode ADDMOD
pub fn addmod_u16(a: u16, b: u16, n: u16) -> u16;
/// Emit opcode ADDMOD
pub fn mulmod_u16(a: u16, b: u16, n: u16) -> u16;

/// Emit opcode ADDMOD
pub fn addmod_u32(a: u32, b: u32, n: u32) -> u32;
/// Emit opcode ADDMOD
pub fn mulmod_u32(a: u32, b: u32, n: u32) -> u32;

/// Emit opcode ADDMOD
pub fn addmod_u64(a: u64, b: u64, n: u64) -> u64;
/// Emit opcode ADDMOD
pub fn mulmod_u64(a: u64, b: u64, n: u64) -> u64;

/// Revert with message in 32 bytes
pub fn revert1(message: &'static str);

Expand Down
6 changes: 6 additions & 0 deletions zink/src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ extern "C" {
/// Equal operation for addresses
pub fn u256_max() -> U256;

/// Addmod operation for addresses
pub fn u256_addmod(this: U256, other: U256, modulus: U256) -> U256;

/// Equal operation for addresses
pub fn u256_mulmod(this: U256, other: U256, modulus: U256) -> U256;

/// Set up a label for reserving 32 bytes in memory
pub fn label_reserve_mem_32();

Expand Down
1 change: 1 addition & 0 deletions zink/src/primitives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Zink primitive types

mod address;
pub mod numeric;
mod u256;

pub use address::Address;
Expand Down
35 changes: 35 additions & 0 deletions zink/src/primitives/numeric.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::ffi;

/// A trait for modular arithmetic operations on numeric types.
pub trait Numeric: Copy {
fn addmod(self, other: Self, n: Self) -> Self;
fn mulmod(self, other: Self, n: Self) -> Self;
}

macro_rules! impl_numeric {
($($t:ty, $addmod_fn:ident, $mulmod_fn:ident);* $(;)?) => {
$(
impl Numeric for $t {
#[inline(always)]
fn addmod(self, other: Self, n: Self) -> Self {
unsafe { ffi::asm::$addmod_fn(n, other, self) }
}
#[inline(always)]
fn mulmod(self, other: Self, n: Self) -> Self {
unsafe { ffi::asm::$mulmod_fn(n, other, self) }
}
}
)*
};
}

impl_numeric! {
i8, addmod_i8, mulmod_i8;
u8, addmod_u8, mulmod_u8;
i16, addmod_i16, mulmod_i16;
u16, addmod_u16, mulmod_u16;
i32, addmod_i32, mulmod_i32;
u32, addmod_u32, mulmod_u32;
i64, addmod_i64, mulmod_i64;
u64, addmod_u64, mulmod_u64;
}
12 changes: 12 additions & 0 deletions zink/src/primitives/u256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ impl U256 {
pub fn max() -> Self {
unsafe { ffi::u256_max() }
}

/// Addmod for U256
#[inline(always)]
pub fn addmod(self, other: Self, modulus: Self) -> Self {
unsafe { ffi::u256_addmod(modulus, other, self) }
}

/// Mulmod for U256
#[inline(always)]
pub fn mulmod(self, other: Self, modulus: Self) -> Self {
unsafe { ffi::u256_mulmod(modulus, other, self) }
}
}

impl Asm for U256 {
Expand Down
Loading