Skip to content

Commit

Permalink
feat(zink): introduce u256
Browse files Browse the repository at this point in the history
  • Loading branch information
clearloop committed Nov 3, 2024
1 parent 11a36f1 commit 52aa316
Show file tree
Hide file tree
Showing 10 changed files with 212 additions and 20 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,7 @@ opcodes = { workspace = true, features = ["data"] }
tracing.workspace = true
zint.workspace = true
hex.workspace = true

# [features]
# default = [ "evm" ]
# evm = [ ]
42 changes: 28 additions & 14 deletions examples/erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,26 @@

extern crate zink;

use zink::{Mapping, Storage};
use zink::{
primitives::{Address, String32, U256},
Mapping, Storage,
};

#[zink::storage(u32)]
#[zink::storage(String32)]
pub struct Name;

#[zink::storage(u32)]
#[zink::storage(String32)]
pub struct Symbol;

#[zink::storage(u32)]
#[zink::storage(U256)]
pub struct TotalSupply;

#[zink::storage(i32, i32)]
#[zink::storage(Address, U256)]
pub struct Balances;

/// Get value from the storage.
#[zink::external]
pub fn init(name: u32, symbol: u32) {
pub fn init(name: String32, symbol: String32) {
Name::set(name);
Symbol::set(symbol);
}
Expand All @@ -31,6 +34,12 @@ pub fn decimals() -> u32 {
8
}

fn _transfer(_from: Address, _to: Address) {
// TODO: check and reverts
}

fn _update(_from: Address) {}

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

Expand All @@ -41,18 +50,23 @@ fn deploy() -> anyhow::Result<()> {
let mut contract = Contract::search("erc20")?.compile()?;

let mut evm = EVM::default();
let mut calldata: Vec<u8> = Default::default();
calldata.extend_from_slice(&42.to_bytes32());
calldata.extend_from_slice(&42.to_bytes32());
let name = "The Zink Language";
let symbol = "zink";

// 1. deploy
let info = evm.deploy(
&contract
.construct(
[
(vec![0].try_into()?, vec![42].try_into()?),
(vec![1].try_into()?, vec![42].try_into()?),
(vec![2].try_into()?, vec![42].try_into()?),
(Name::STORAGE_KEY.to_bytes32().into(), name.to_vec().into()),
(
Symbol::STORAGE_KEY.to_bytes32().into(),
symbol.to_vec().into(),
),
(
TotalSupply::STORAGE_KEY.to_bytes32().into(),
vec![42].try_into()?,
),
]
.into_iter()
.collect(),
Expand All @@ -65,13 +79,13 @@ fn deploy() -> anyhow::Result<()> {
let info = evm
.calldata(&contract.encode(&[b"name()".to_vec()])?)
.call(address)?;
assert_eq!(info.ret, 42u64.to_bytes32());
assert_eq!(info.ret, name.to_bytes32());

// 3. get symbol
let info = evm
.calldata(&contract.encode(&[b"symbol()".to_vec()])?)
.call(address)?;
assert_eq!(info.ret, 42u64.to_bytes32());
assert_eq!(info.ret, symbol.to_bytes32());

// 3. get total supply
let info = evm
Expand Down
1 change: 1 addition & 0 deletions zink/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use syn::{parse_macro_input, Attribute, DeriveInput, ItemFn, ItemStruct};
mod event;
mod selector;
mod storage;
mod utils;

/// Event logging interface
///
Expand Down
8 changes: 4 additions & 4 deletions zink/codegen/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern crate proc_macro;

use crate::utils::Bytes32;
use heck::AsSnakeCase;
use proc_macro::TokenStream;
use proc_macro2::{Literal, Span, TokenTree};
Expand Down Expand Up @@ -47,8 +48,7 @@ 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 key = slot.to_bytes32();

let keyl = Literal::byte_string(&key);
let mut expanded = quote! {
Expand Down Expand Up @@ -98,7 +98,7 @@ impl Storage {

let mut seed = [0; 64];
seed[..32].copy_from_slice(&key.bytes32());
seed[60..].copy_from_slice(&Self::STORAGE_SLOT.to_le_bytes());
seed[60..].copy_from_slice(&Self::STORAGE_SLOT.bytes32());
zink::keccak256(&seed)
}
}
Expand Down Expand Up @@ -140,7 +140,7 @@ impl Storage {

let mut seed = [0; 64];
seed[..32].copy_from_slice(&key1.bytes32());
seed[60..].copy_from_slice(&Self::STORAGE_SLOT.to_le_bytes());
seed[60..].copy_from_slice(&Self::STORAGE_SLOT.bytes32());
let skey1 = zink::keccak256(&seed);
seed[..32].copy_from_slice(&skey1);
seed[32..].copy_from_slice(&key2.bytes32());
Expand Down
121 changes: 121 additions & 0 deletions zink/codegen/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! Utils for bytes conversion.
//!
//! TODO: move this util to other library
/// Trait for converting type to bytes32.
pub trait Bytes32: Sized {
/// Convert type to the lowest significant bytes 32.
fn to_bytes32(&self) -> [u8; 32];

/// Convert type to vec of bytes.
fn to_vec(&self) -> Vec<u8> {
self.to_bytes32().to_vec()
}
}

/// Implement Bytes32 for types.
macro_rules! impl_bytes32 {
($($ty:ident),+) => {
$(
impl Bytes32 for $ty {
fn to_bytes32(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
let ls_bytes = {
self.to_le_bytes()
.into_iter()
.rev()
.skip_while(|b| *b == 0)
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect::<Vec<_>>()
};

bytes[(32 - ls_bytes.len())..].copy_from_slice(&ls_bytes);
bytes
}

fn to_vec(&self) -> Vec<u8> {
self.to_le_bytes().to_vec()
}
}
)+
};
}

impl Bytes32 for Vec<u8> {
fn to_bytes32(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
bytes[(32 - self.len())..].copy_from_slice(self);
bytes
}

fn to_vec(&self) -> Vec<u8> {
self.clone()
}
}

impl Bytes32 for [u8; 20] {
fn to_bytes32(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
bytes[12..].copy_from_slice(self);
bytes
}
}

impl Bytes32 for [u8; 32] {
fn to_bytes32(&self) -> [u8; 32] {
*self
}

fn to_vec(&self) -> Vec<u8> {
self.as_ref().into()
}
}

impl Bytes32 for &[u8] {
fn to_bytes32(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
bytes[(32 - self.len())..].copy_from_slice(self);
bytes
}

fn to_vec(&self) -> Vec<u8> {
(*self).into()
}
}

impl Bytes32 for () {
fn to_bytes32(&self) -> [u8; 32] {
[0; 32]
}

fn to_vec(&self) -> Vec<u8> {
Default::default()
}
}

impl Bytes32 for &str {
fn to_bytes32(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
bytes[(32 - self.len())..].copy_from_slice(self.as_bytes());
bytes
}

fn to_vec(&self) -> Vec<u8> {
self.as_bytes().into()
}
}

impl Bytes32 for bool {
fn to_bytes32(&self) -> [u8; 32] {
let mut output = [0; 32];
if *self {
output[31] = 1;
}

output
}
}

impl_bytes32!(i8, u8, i16, u16, i32, u32, usize, i64, u64, i128, u128);
8 changes: 7 additions & 1 deletion zink/src/ffi/asm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Assembly FFI.
use crate::primitives::Address;
use crate::primitives::{Address, U256};

#[link(wasm_import_module = "asm")]
#[allow(improper_ctypes)]
Expand Down Expand Up @@ -32,6 +32,9 @@ extern "C" {
/// Push address to stack
pub fn push_address(address: Address);

/// Push u256 to stack
pub fn push_u256(u256: U256);

/// Load a 8-bit signed integer from the storage.
pub fn sload_i8() -> i8;

Expand All @@ -58,4 +61,7 @@ extern "C" {

/// Load address from storage
pub fn sload_address() -> Address;

/// Load address from storage
pub fn sload_u256() -> U256;
}
2 changes: 1 addition & 1 deletion zink/src/ffi/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extern "C" {
pub fn push31(val: i32);

/// Push 32 bytes to the stack.
pub fn push32(val: i32);
pub fn push32();

/// Store a value in the storage
pub fn sstore();
Expand Down
6 changes: 6 additions & 0 deletions zink/src/primitives/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ pub struct Address(
);

impl Address {
/// Returns empty address
#[cfg(not(target_family = "wasm"))]
pub const fn empty() -> Self {
Address([0; 20])
}

/// if self equal to another
///
/// NOTE: not using core::cmp because it uses registers in wasm
Expand Down
6 changes: 6 additions & 0 deletions zink/src/primitives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
//! Zink primitive types
mod address;
mod u256;

pub use address::Address;
pub use u256::U256;

pub type Bytes20 = Address;
pub type Bytes32 = U256;
pub type String32 = U256;
34 changes: 34 additions & 0 deletions zink/src/primitives/u256.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::{ffi, storage::StorageValue, Asm};

/// Account address
#[repr(C)]
#[derive(Clone, Copy)]
pub struct U256(
#[cfg(target_family = "wasm")] i32,
#[cfg(not(target_family = "wasm"))] [u8; 32],
);

impl U256 {
/// Returns empty address
#[cfg(not(target_family = "wasm"))]
pub const fn empty() -> Self {
U256([0; 32])
}
}

impl Asm for U256 {
fn push(self) {
unsafe { ffi::asm::push_u256(self) }
}

#[cfg(not(target_family = "wasm"))]
fn bytes32(&self) -> [u8; 32] {
self.0
}
}

impl StorageValue for U256 {
fn sload() -> Self {
unsafe { ffi::asm::sload_u256() }
}
}

0 comments on commit 52aa316

Please sign in to comment.