diff --git a/Cargo.lock b/Cargo.lock index 57cd80fb9..6a9b7cbc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2628,6 +2628,7 @@ dependencies = [ "evm-opcodes", "hex", "paste", + "tiny-keccak", "tracing", "zink-codegen", "zinkc-filetests", @@ -2639,6 +2640,7 @@ name = "zink-codegen" version = "0.1.11" dependencies = [ "heck 0.5.0", + "hex", "proc-macro2", "quote", "syn 2.0.77", diff --git a/Cargo.toml b/Cargo.toml index ec64dfae3..88bef034e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,9 @@ path = "zink/src/lib.rs" paste.workspace = true zink-codegen.workspace = true +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tiny-keccak.workspace = true + [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] anyhow.workspace = true filetests.workspace = true diff --git a/abi/src/abi.rs b/abi/src/abi.rs new file mode 100644 index 000000000..36986047f --- /dev/null +++ b/abi/src/abi.rs @@ -0,0 +1,77 @@ +//! Zink ABI implementation +//! +//! Currently just a wrapper of solidity ABI. + +use core::ops::{Deref, DerefMut}; + +/// Function ABI. +#[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Abi(sol_abi::Abi); + +impl Deref for Abi { + type Target = sol_abi::Abi; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Abi { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(feature = "bytes")] +impl Abi { + /// Convert [`Abi`] to bytes. + pub fn to_bytes(&self) -> postcard::Result> { + postcard::to_stdvec(self).map_err(Into::into) + } + + /// Convert bytes to [`Abi`]. + pub fn from_bytes(bytes: impl AsRef<[u8]>) -> postcard::Result { + postcard::from_bytes(bytes.as_ref()).map_err(Into::into) + } +} + +#[cfg(feature = "hex")] +mod hex_impl { + use crate::{result::Result, Abi}; + use core::fmt; + + impl Abi { + /// Convert [`Abi`] to hex string. + pub fn to_hex(&self) -> Result { + Ok("0x".to_string() + &hex::encode(self.to_bytes()?)) + } + + /// Convert hex string to [`Abi`]. + pub fn from_hex(hex: impl AsRef) -> Result { + Self::from_bytes(hex::decode(hex.as_ref().trim_start_matches("0x"))?) + .map_err(Into::into) + } + } + + impl fmt::Display for Abi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_hex().unwrap_or_default()) + } + } + + impl core::str::FromStr for Abi { + type Err = crate::result::Error; + + fn from_str(hex: &str) -> Result { + Self::from_hex(hex) + } + } +} + +#[cfg(feature = "syn")] +impl From<&syn::Signature> for Abi { + fn from(sig: &syn::Signature) -> Self { + Self(sol_abi::Abi::from(sig)) + } +} diff --git a/abi/src/lib.rs b/abi/src/lib.rs index 0dcfd32c4..32871f5e4 100644 --- a/abi/src/lib.rs +++ b/abi/src/lib.rs @@ -2,79 +2,11 @@ //! //! Currently just a wrapper of solidity ABI. +mod abi; pub mod result; pub mod selector; -use core::ops::{Deref, DerefMut}; +pub use abi::Abi; -/// Function ABI. -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Abi(sol_abi::Abi); - -impl Deref for Abi { - type Target = sol_abi::Abi; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Abi { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -#[cfg(feature = "bytes")] -impl Abi { - /// Convert [`Abi`] to bytes. - pub fn to_bytes(&self) -> postcard::Result> { - postcard::to_stdvec(self).map_err(Into::into) - } - - /// Convert bytes to [`Abi`]. - pub fn from_bytes(bytes: impl AsRef<[u8]>) -> postcard::Result { - postcard::from_bytes(bytes.as_ref()).map_err(Into::into) - } -} - -#[cfg(feature = "hex")] -mod hex_impl { - use crate::{result::Result, Abi}; - use core::fmt; - - impl Abi { - /// Convert [`Abi`] to hex string. - pub fn to_hex(&self) -> Result { - Ok("0x".to_string() + &hex::encode(self.to_bytes()?)) - } - - /// Convert hex string to [`Abi`]. - pub fn from_hex(hex: impl AsRef) -> Result { - Self::from_bytes(hex::decode(hex.as_ref().trim_start_matches("0x"))?) - .map_err(Into::into) - } - } - - impl fmt::Display for Abi { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_hex().unwrap_or_default()) - } - } - - impl core::str::FromStr for Abi { - type Err = crate::result::Error; - - fn from_str(hex: &str) -> Result { - Self::from_hex(hex) - } - } -} - -#[cfg(feature = "syn")] -impl From<&syn::Signature> for Abi { - fn from(sig: &syn::Signature) -> Self { - Self(sol_abi::Abi::from(sig)) - } -} +#[cfg(feature = "selector")] +pub use selector::keccak256; diff --git a/examples/dkmapping.rs b/examples/dkmapping.rs index bf0f3ec8b..f48745a06 100644 --- a/examples/dkmapping.rs +++ b/examples/dkmapping.rs @@ -41,7 +41,8 @@ fn storage_double_key_mapping() -> anyhow::Result<()> { assert!(info.ret.is_empty()); // verify result with database - let storage_key = zint::keccak256(&[[0; 32], [0; 32], 1.to_bytes32()].concat()); + let _sk = zint::keccak256(&[[0; 32], [0; 32], 1.to_bytes32()].concat()); + let storage_key = DoubleKeyMapping::storage_key(key1, key2); assert_eq!( evm.storage(contract.address, storage_key)?, value.to_bytes32(), diff --git a/examples/mapping.rs b/examples/mapping.rs index d34bc184b..ad3748ebd 100644 --- a/examples/mapping.rs +++ b/examples/mapping.rs @@ -39,7 +39,7 @@ fn storage_mapping() -> anyhow::Result<()> { assert!(info.ret.is_empty()); // verify result with database - let storage_key = zint::keccak256(&[0; 0x40]); + let storage_key = Mapping::storage_key(key); assert_eq!( evm.storage(contract.address, storage_key)?, value.to_bytes32(), diff --git a/examples/storage.rs b/examples/storage.rs index 40d79b1ac..eb5974266 100644 --- a/examples/storage.rs +++ b/examples/storage.rs @@ -26,11 +26,13 @@ fn value() -> anyhow::Result<()> { let mut contract = Contract::search("storage")?.compile()?; { - let key = 0; let value: i32 = 42; let info = contract.execute(&[b"set(int32)".to_vec(), value.to_bytes32().to_vec()])?; assert!(info.ret.is_empty()); - assert_eq!(info.storage.get(&U256::from(key)), Some(&U256::from(value))); + assert_eq!( + info.storage.get(&U256::from_le_bytes(Counter::STORAGE_KEY)), + Some(&U256::from(value)) + ); } { diff --git a/zink/codegen/Cargo.toml b/zink/codegen/Cargo.toml index 85a95d57b..f925f27bb 100644 --- a/zink/codegen/Cargo.toml +++ b/zink/codegen/Cargo.toml @@ -14,6 +14,7 @@ proc-macro = true [dependencies] heck.workspace = true +hex.workspace = true proc-macro2.workspace = true quote.workspace = true syn.workspace = true diff --git a/zink/codegen/src/storage.rs b/zink/codegen/src/storage.rs index 56b943b4b..33a7cc35f 100644 --- a/zink/codegen/src/storage.rs +++ b/zink/codegen/src/storage.rs @@ -2,7 +2,7 @@ extern crate proc_macro; use heck::AsSnakeCase; use proc_macro::TokenStream; -use proc_macro2::{Span, TokenTree}; +use proc_macro2::{Literal, Span, TokenTree}; use quote::quote; use std::{cell::RefCell, collections::HashSet}; use syn::{ @@ -47,12 +47,19 @@ impl Storage { let is = &self.target; let name = self.target.ident.clone(); let slot = storage_slot(name.to_string()); + let mut key = [0; 32]; + key[28..].copy_from_slice(&slot.to_le_bytes()); + + let keyl = Literal::byte_string(&key); let mut expanded = quote! { #is impl zink::storage::Storage for #name { - type Value = #value; + #[cfg(not(target_family = "wasm"))] + const STORAGE_KEY: [u8; 32] = *#keyl; const STORAGE_SLOT: i32 = #slot; + + type Value = #value; } }; @@ -75,6 +82,10 @@ impl Storage { let is = &self.target; let name = self.target.ident.clone(); let slot = storage_slot(name.to_string()); + let mut seed = [0; 64]; + seed[29..=32].copy_from_slice(&slot.to_le_bytes()); + + let seedl = Literal::byte_string(&seed); let mut expanded = quote! { #is @@ -83,6 +94,15 @@ impl Storage { type Key = #key; type Value = #value; + + #[cfg(not(target_family = "wasm"))] + fn storage_key(key: Self::Key) -> [u8; 32] { + use zink::Asm; + + let mut seed = *#seedl; + seed[32..].copy_from_slice(&key.bytes32()); + zink::keccak256(&seed) + } } }; @@ -105,6 +125,10 @@ impl Storage { let is = &self.target; let name = self.target.ident.clone(); let slot = storage_slot(name.to_string()); + let mut seed = [0; 96]; + seed[29..=32].copy_from_slice(&slot.to_le_bytes()); + + let seedl = Literal::byte_string(&seed); let mut expanded = quote! { #is @@ -114,6 +138,16 @@ impl Storage { type Key1 = #key1; type Key2 = #key2; type Value = #value; + + #[cfg(not(target_family = "wasm"))] + fn storage_key(key1: Self::Key1, key2: Self::Key2) -> [u8; 32] { + use zink::Asm; + + let mut seed = *#seedl; + seed[33..=64].copy_from_slice(&key1.bytes32()); + seed[64..].copy_from_slice(&key2.bytes32()); + zink::keccak256(&seed) + } } }; diff --git a/zink/src/asm.rs b/zink/src/asm.rs index 75f77525d..1fdd26858 100644 --- a/zink/src/asm.rs +++ b/zink/src/asm.rs @@ -7,6 +7,9 @@ use paste::paste; pub trait Asm: Copy { /// Push self on the stack. fn push(self); + + #[cfg(not(target_family = "wasm"))] + fn bytes32(&self) -> [u8; 32]; } macro_rules! impl_asm { @@ -17,6 +20,11 @@ macro_rules! impl_asm { paste! { ffi::asm::[](self); } } } + + #[cfg(not(target_family = "wasm"))] + fn bytes32(&self) -> [u8; 32] { + crate::to_bytes32(&self.to_le_bytes()) + } } }; ($($ty:tt),+) => { diff --git a/zink/src/lib.rs b/zink/src/lib.rs index b2aa84dd2..8fa280035 100644 --- a/zink/src/lib.rs +++ b/zink/src/lib.rs @@ -2,6 +2,9 @@ #![no_std] +#[cfg(not(target_family = "wasm"))] +extern crate alloc; + mod asm; mod event; pub mod ffi; @@ -12,6 +15,37 @@ pub use self::{asm::Asm, event::Event}; pub use storage::{DoubleKeyMapping, Mapping, Storage}; pub use zink_codegen::{external, storage, Event}; +/// Generate a keccak hash of the input (sha3) +#[cfg(not(target_family = "wasm"))] +pub fn keccak256(input: &[u8]) -> [u8; 32] { + use tiny_keccak::{Hasher, Keccak}; + let mut hasher = Keccak::v256(); + let mut output = [0; 32]; + hasher.update(input); + hasher.finalize(&mut output); + output +} + +/// Convert bytes to ls bytes +#[cfg(not(target_family = "wasm"))] +pub fn to_bytes32(src: &[u8]) -> [u8; 32] { + use alloc::vec::Vec; + let mut bytes = [0u8; 32]; + let ls_bytes = { + src.iter() + .cloned() + .rev() + .skip_while(|b| *b == 0) + .collect::>() + .into_iter() + .rev() + .collect::>() + }; + + bytes[(32 - ls_bytes.len())..].copy_from_slice(&ls_bytes); + bytes +} + // Panic hook implementation #[cfg(target_arch = "wasm32")] #[panic_handler] diff --git a/zink/src/primitives/address.rs b/zink/src/primitives/address.rs index e922ac397..be979b915 100644 --- a/zink/src/primitives/address.rs +++ b/zink/src/primitives/address.rs @@ -3,7 +3,10 @@ use crate::{ffi, storage::StorageValue, Asm}; /// Account address #[repr(C)] #[derive(Clone, Copy)] -pub struct Address(i32); +pub struct Address( + #[cfg(target_family = "wasm")] i32, + #[cfg(not(target_family = "wasm"))] [u8; 20], +); impl Address { /// if self equal to another @@ -19,6 +22,13 @@ impl Asm for Address { fn push(self) { unsafe { ffi::asm::push_address(self) } } + + #[cfg(not(target_family = "wasm"))] + fn bytes32(&self) -> [u8; 32] { + let mut output = [0; 32]; + output[12..].copy_from_slice(&self.0); + output + } } impl StorageValue for Address { diff --git a/zink/src/storage/dkmapping.rs b/zink/src/storage/dkmapping.rs index 37973b6d3..6191a5d77 100644 --- a/zink/src/storage/dkmapping.rs +++ b/zink/src/storage/dkmapping.rs @@ -10,6 +10,9 @@ pub trait DoubleKeyMapping { type Key2: Asm; type Value: StorageValue; + #[cfg(not(target_family = "wasm"))] + fn storage_key(key1: Self::Key1, key2: Self::Key2) -> [u8; 32]; + /// Get value from storage key. fn get(key1: Self::Key1, key2: Self::Key2) -> Self::Value { load_double_key(key1, key2, Self::STORAGE_SLOT); diff --git a/zink/src/storage/mapping.rs b/zink/src/storage/mapping.rs index 8c79d9c6e..09ff634be 100644 --- a/zink/src/storage/mapping.rs +++ b/zink/src/storage/mapping.rs @@ -9,6 +9,9 @@ pub trait Mapping { type Key: Asm; type Value: StorageValue; + #[cfg(not(target_family = "wasm"))] + fn storage_key(key: Self::Key) -> [u8; 32]; + /// Get value from storage key. fn get(key: Self::Key) -> Self::Value { load_key(key, Self::STORAGE_SLOT); diff --git a/zink/src/storage/value.rs b/zink/src/storage/value.rs index 8338084b0..d3c5caa3d 100644 --- a/zink/src/storage/value.rs +++ b/zink/src/storage/value.rs @@ -3,7 +3,10 @@ use crate::{ffi, storage::StorageValue, Asm}; /// Storage trait. Currently not for public use pub trait Storage { + #[cfg(not(target_family = "wasm"))] + const STORAGE_KEY: [u8; 32]; const STORAGE_SLOT: i32; + type Value: StorageValue + Asm; /// Get value from storage.