From cdc0cfc45a34e1755df38dd2bebd32c71af6bdb1 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:48:23 +0800 Subject: [PATCH] feat(zinkc): make ABI an output option (#193) * feat(zinkc): embed ABI * chore(cli): re-assemble command line tools * feat(zinkc): embed cli in zinkc * feat(zinkc): write abi to disk if enabled * chore(abi): lowercase enum * refactor(examples): use generated ABI for the test of example constructor * chore(filetests): use filetests instead of zinkc-filetests * chore(ccli): to crates.io * feat(abi): introduce no-std solidity abi * docs(RELEASES): append release notes * chore(workspace): bumps version * ci(clippy): make clippy happy --- Cargo.lock | 107 +++++++++++++---------------- Cargo.toml | 42 +++++------ Conta.toml | 5 +- RELEASES.md | 8 +++ abi/src/lib.rs | 2 +- cli/Cargo.toml | 35 ---------- cli/README.md | 25 ------- cli/cli/Cargo.toml | 16 +++++ cli/{src/lib.rs => cli/README.md} | 29 ++++---- cli/cli/src/lib.rs | 33 +++++++++ cli/conta/Cargo.toml | 4 -- cli/conta/{ => src}/bin/conta.rs | 0 cli/conta/src/cmd/publish.rs | 16 ++++- cli/elko/Cargo.toml | 3 +- cli/{ => elko}/src/bin/elko.rs | 13 ++-- cli/elko/src/lib.rs | 1 + cli/src/compile.rs | 39 ----------- codegen/src/code/mod.rs | 2 +- codegen/src/dispatcher.rs | 13 +++- codegen/src/jump/table.rs | 2 +- compiler/Cargo.toml | 10 +++ {cli => compiler}/src/bin/zinkc.rs | 19 +++-- compiler/src/cli.rs | 58 ++++++++++++++++ compiler/src/compiler.rs | 8 +++ compiler/src/lib.rs | 1 + evm/abi/Cargo.toml | 8 ++- evm/abi/README.md | 4 ++ evm/abi/src/abi.rs | 78 ++++++++++++++++----- evm/abi/src/{input.rs => arg.rs} | 32 +++++++-- evm/abi/src/lib.rs | 15 +++- examples/constructor.rs | 31 ++------- tests/add.rs | 2 +- tests/br_if.rs | 2 +- tests/call.rs | 2 +- tests/if.rs | 2 +- tests/log.rs | 2 +- tests/loop.rs | 2 +- tests/recursion.rs | 2 +- tests/select.rs | 2 +- tests/storage.rs | 2 +- tests/sub.rs | 2 +- zint/Cargo.toml | 1 + zint/src/contract.rs | 18 +++-- 43 files changed, 406 insertions(+), 292 deletions(-) delete mode 100644 cli/Cargo.toml delete mode 100644 cli/README.md create mode 100644 cli/cli/Cargo.toml rename cli/{src/lib.rs => cli/README.md} (69%) create mode 100644 cli/cli/src/lib.rs rename cli/conta/{ => src}/bin/conta.rs (100%) rename cli/{ => elko}/src/bin/elko.rs (73%) delete mode 100644 cli/src/compile.rs rename {cli => compiler}/src/bin/zinkc.rs (59%) create mode 100644 compiler/src/cli.rs rename evm/abi/src/{input.rs => arg.rs} (68%) diff --git a/Cargo.lock b/Cargo.lock index 0f2458440..280009bbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,6 +457,16 @@ dependencies = [ "libc", ] +[[package]] +name = "ccli" +version = "0.0.1" +dependencies = [ + "anyhow", + "clap", + "color-eyre", + "tracing-subscriber", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -725,15 +735,15 @@ dependencies = [ [[package]] name = "crates-io" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876aa69b4afca5f2eb5e23daa3445930faf829bcb67075a20ffa884f11f8c57c" +checksum = "1aadfd000bd635ce58527e2dffe008339867991ab12a786b859d9cfe967c0f72" dependencies = [ - "anyhow", "curl", "percent-encoding", "serde", "serde_json", + "thiserror", "url", ] @@ -1015,10 +1025,11 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elko" -version = "0.1.7" +version = "0.1.8" dependencies = [ "anyhow", "cargo_metadata", + "ccli", "clap", "colored", "etc", @@ -1722,19 +1733,13 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.3" @@ -1922,16 +1927,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.1.0" @@ -1939,7 +1934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -2579,7 +2574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.1.0", + "indexmap", ] [[package]] @@ -3046,7 +3041,7 @@ dependencies = [ "bitvec", "c-kzg", "enumn", - "hashbrown 0.14.3", + "hashbrown", "hex", "once_cell", ] @@ -3768,18 +3763,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", @@ -3952,7 +3947,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap", "toml_datetime", "winnow", ] @@ -3963,7 +3958,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.1.0", + "indexmap", "toml_datetime", "winnow", ] @@ -3974,7 +3969,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap 2.1.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -4316,9 +4311,9 @@ dependencies = [ [[package]] name = "wasm-opt" -version = "0.113.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65a2799e08026234b07b44da6363703974e75be21430cef00756bbc438c8ff8a" +checksum = "fc942673e7684671f0c5708fc18993569d184265fd5223bb51fc8e5b9b6cfd52" dependencies = [ "anyhow", "libc", @@ -4332,9 +4327,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.113.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d26f86d1132245e8bcea8fac7f02b10fb885b6696799969c94d7d3c14db5e1" +checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e" dependencies = [ "anyhow", "cxx", @@ -4344,9 +4339,9 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.113.0" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497d069cd3420cdd52154a320b901114a20946878e2de62c670f9d906e472370" +checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe" dependencies = [ "anyhow", "cc", @@ -4356,11 +4351,11 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.107.0" +version = "0.118.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e3ac9b780c7dda0cac7a52a5d6d2d6707cc6e3451c9db209b6c758f40d7acb" +checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" dependencies = [ - "indexmap 1.9.3", + "indexmap", "semver", ] @@ -4631,7 +4626,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zabi" -version = "0.1.7" +version = "0.1.8" dependencies = [ "hex", "postcard", @@ -4684,11 +4679,11 @@ dependencies = [ [[package]] name = "zingen" -version = "0.1.7" +version = "0.1.8" dependencies = [ "anyhow", "evm-opcodes", - "indexmap 2.1.0", + "indexmap", "paste", "smallvec", "thiserror", @@ -4699,7 +4694,7 @@ dependencies = [ [[package]] name = "zink" -version = "0.1.7" +version = "0.1.8" dependencies = [ "anyhow", "paste", @@ -4711,7 +4706,7 @@ dependencies = [ [[package]] name = "zink-codegen" -version = "0.1.7" +version = "0.1.8" dependencies = [ "proc-macro2", "quote", @@ -4721,12 +4716,14 @@ dependencies = [ [[package]] name = "zinkc" -version = "0.1.7" +version = "0.1.8" dependencies = [ "anyhow", + "ccli", "etc", "hex", "paste", + "serde_json", "thiserror", "tracing", "tracing-subscriber", @@ -4738,7 +4735,7 @@ dependencies = [ [[package]] name = "zinkc-filetests" -version = "0.1.7" +version = "0.1.8" dependencies = [ "anyhow", "cargo_metadata", @@ -4752,22 +4749,9 @@ dependencies = [ "zinkc", ] -[[package]] -name = "zinkup" -version = "0.1.7" -dependencies = [ - "anyhow", - "clap", - "color-eyre", - "elko", - "tracing-subscriber", - "zink", - "zinkc", -] - [[package]] name = "zint" -version = "0.1.7" +version = "0.1.8" dependencies = [ "anyhow", "cargo_metadata", @@ -4776,6 +4760,7 @@ dependencies = [ "hex", "revm", "serde", + "serde_json", "thiserror", "toml", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 90cda50b9..8d57290fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,7 @@ [workspace] members = [ "abi", - "cli", - "cli/conta", - "cli/elko", + "cli/*", "codegen", "compiler", "compiler/filetests", @@ -15,7 +13,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.1.7" +version = "0.1.8" authors = ["clearloop"] edition = "2021" license = "GPL-3.0-only" @@ -29,7 +27,7 @@ clap = "4.4.11" curl = "0.4.44" color-eyre = "0.6.2" colored = "2.1.0" -crates-io = "0.37.0" +crates-io = "0.39.0" etc = "0.1.16" ethers = "2.0.11" hex = "0.4.3" @@ -42,32 +40,36 @@ proc-macro2 = "1.0.70" quote = "1.0.33" revm = "3.5.0" semver = "1.0.20" -serde = "1.0.193" +serde = { version = "1.0.193", default-features = false } +serde_json = "1.0.108" sha3 = "0.10.8" smallvec = "1.11.2" syn = { version = "2.0.41", features = [ "full" ] } target-lexicon = "0.12.12" -thiserror = "1.0.50" +thiserror = "1.0.51" tokio = "1.35.0" toml = "0.8.8" tracing = "0.1.40" tracing-subscriber = "0.3.18" url = "2.5.0" -wasm-opt = "0.113.0" -wasmparser = "0.107.0" +wasm-opt = "0.116.0" +wasmparser = "0.118.1" wat = "1.0.82" -elko = { path = "cli/elko", version = "=0.1.7" } -opcodes = { package = "evm-opcodes", path = "evm/opcodes", version = "=0.0.3", features = ["data"] } +## Local packages +ccli = { path = "cli/cli", version = "=0.0.1" } sol-abi = { path = "evm/abi", version = "=0.0.1" } -zabi = { path = "abi", version = "=0.1.7" } -zinkup = { path = "cli", version = "=0.1.7" } -zingen = { path = "codegen", version = "=0.1.7" } -zinkc = { path = "compiler", version = "=0.1.7" } -zinkc-filetests = { path = "compiler/filetests", version = "=0.1.7" } -zink = { path = ".", version = "=0.1.7" } -zink-codegen = { path = "zink/codegen", version = "=0.1.7" } -zint = { path = "zint", version = "=0.1.7" } +opcodes = { package = "evm-opcodes", path = "evm/opcodes", version = "=0.0.3", features = [ "data" ] } + +## Zink packages +elko = { path = "cli/elko", version = "=0.1.8" } +zabi = { path = "abi", version = "=0.1.8" } +zingen = { path = "codegen", version = "=0.1.8" } +zinkc = { path = "compiler", version = "=0.1.8" } +filetests = { package = "zinkc-filetests", path = "compiler/filetests", version = "=0.1.8" } +zink = { path = ".", version = "=0.1.8" } +zink-codegen = { path = "zink/codegen", version = "=0.1.8" } +zint = { path = "zint", version = "=0.1.8" } [profile] dev = { panic = "abort"} @@ -97,6 +99,6 @@ zink-codegen.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] anyhow.workspace = true paste.workspace = true -zinkc-filetests.workspace = true +filetests.workspace = true zint.workspace = true tokio = { workspace = true, features = [ "macros" ] } diff --git a/Conta.toml b/Conta.toml index a6aad142c..088017e24 100644 --- a/Conta.toml +++ b/Conta.toml @@ -2,10 +2,9 @@ packages = [ "zabi", "zingen", "zinkc", - "zinkc-filetests", + "filetests", "zint", "zink-codegen", "zink", - "elko", - "zinkup" + "elko" ] diff --git a/RELEASES.md b/RELEASES.md index 23d068f0b..34b6170f8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,10 +1,18 @@ ## v0.1.8 +### Added + +- ABI output in zink compiler +- no_std solidity ABI + ### Changes - Solidity compatible ABI - Refactor `zabi` a wrapper a `sol-abi` - Conditional compilation for abi related crates +- Compile `zinkc` binary in crate `zinkc` +- Use generated ABI for the constructor tests +- Rename zinkc-filetests to filetests ## v0.1.7 diff --git a/abi/src/lib.rs b/abi/src/lib.rs index c814aa687..cf14b2dcf 100644 --- a/abi/src/lib.rs +++ b/abi/src/lib.rs @@ -8,7 +8,7 @@ pub mod selector; use core::ops::{Deref, DerefMut}; /// Function ABI. -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Abi(sol_abi::Abi); diff --git a/cli/Cargo.toml b/cli/Cargo.toml deleted file mode 100644 index ceac537bc..000000000 --- a/cli/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "zinkup" -description = "Zink toolchain" -documentation = "https://docs.rs/zinkup" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true - -[[bin]] -name = "zinkc" -required-features = [ "zinkc" ] - -[[bin]] -name = "elko" -required-features = [ "elko" ] - -[dependencies] -anyhow.workspace = true -clap = { workspace = true, features = [ "derive" ] } -color-eyre.workspace = true -tracing-subscriber = { workspace = true, features = [ "env-filter" ] } - -elko = { workspace = true, optional = true } -zinkc = { workspace = true, optional = true } - -[dev-dependencies] -zink.workspace = true - -[features] -default = [ "elko", "zinkc" ] -elko = [ "dep:elko" ] -zinkc = [ "dep:zinkc" ] diff --git a/cli/README.md b/cli/README.md deleted file mode 100644 index 48717daf6..000000000 --- a/cli/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# `zinkup` - -The zink components are gathered here, you can install all of the -components directly with: - -```bash -cargo install zinkup -``` - -For installing only specified binaries: - -```bash -cargo install zinkup --features elko,zinkc -``` - -Available binaries: - -| Name | Description | -| ------- | ----------------------- | -| `elko` | Zink\'s package manager | -| `zinkc` | The zink compiler | - -## LICENSE - -GPL-3.0 diff --git a/cli/cli/Cargo.toml b/cli/cli/Cargo.toml new file mode 100644 index 000000000..17caab0d7 --- /dev/null +++ b/cli/cli/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "ccli" +description = "Common command line interface" +documentation = "https://docs.rs/ccli" +version = "0.0.1" +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +anyhow.workspace = true +clap = { workspace = true, features = [ "derive" ] } +color-eyre.workspace = true +tracing-subscriber = { workspace = true, features = [ "env-filter" ] } diff --git a/cli/src/lib.rs b/cli/cli/README.md similarity index 69% rename from cli/src/lib.rs rename to cli/cli/README.md index ecdc45fef..6128efd89 100644 --- a/cli/src/lib.rs +++ b/cli/cli/README.md @@ -1,26 +1,22 @@ -//! Zink toolchain. +# `ccli` + +Common command line interface. + +```rust +//! Common command line interface. use anyhow::Error; -use clap::Parser; -use color_eyre::{eyre::eyre, Result}; -pub use commands::*; +pub use clap::{self, Parser}; +pub use color_eyre::{eyre::eyre, Result}; use tracing_subscriber::filter::EnvFilter; -mod compile; -mod commands { - #[cfg(feature = "zinkc")] - pub use crate::compile::Compile; - #[cfg(feature = "elko")] - pub use elko::{Build, New}; -} - /// Shared application interface. pub trait App: Parser { /// Verbose logging level. fn verbose(&self) -> u8; /// Run application. - fn run(&self) -> std::result::Result<(), Error>; + fn run(&self) -> anyhow::Result<()>; /// Start application. fn start() -> Result<()> { @@ -32,7 +28,7 @@ pub trait App: Parser { EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new(match app.verbose() { 0 => format!("{name}=info"), 1 => format!("{name}=debug"), - 2 => format!("{name}=trace"), + 2 => "debug".into(), _ => "trace".into(), })); @@ -41,3 +37,8 @@ pub trait App: Parser { Ok(()) } } +``` + +## LICENSE + +GPL-3.0 diff --git a/cli/cli/src/lib.rs b/cli/cli/src/lib.rs new file mode 100644 index 000000000..8224cff91 --- /dev/null +++ b/cli/cli/src/lib.rs @@ -0,0 +1,33 @@ +//! Common command line interface. + +pub use clap::{self, Parser}; +pub use color_eyre::{eyre::eyre, Result}; +use tracing_subscriber::filter::EnvFilter; + +/// Shared application interface. +pub trait App: Parser { + /// Verbose logging level. + fn verbose(&self) -> u8; + + /// Run application. + fn run(&self) -> anyhow::Result<()>; + + /// Start application. + fn start() -> Result<()> { + color_eyre::install()?; + + let app = Self::parse(); + let name = Self::command().get_name().to_string(); + let env = + EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new(match app.verbose() { + 0 => format!("{name}=info"), + 1 => format!("{name}=debug"), + 2 => "debug".into(), + _ => "trace".into(), + })); + + tracing_subscriber::fmt().with_env_filter(env).init(); + app.run().map_err(|e| eyre!("Failed to run app, {e}"))?; + Ok(()) + } +} diff --git a/cli/conta/Cargo.toml b/cli/conta/Cargo.toml index 0cb031f6c..7889087e0 100644 --- a/cli/conta/Cargo.toml +++ b/cli/conta/Cargo.toml @@ -9,10 +9,6 @@ license.workspace = true homepage.workspace = true repository.workspace = true -[[bin]] -name = "conta" -path = "bin/conta.rs" - [dependencies] anyhow.workspace = true cargo_metadata.workspace = true diff --git a/cli/conta/bin/conta.rs b/cli/conta/src/bin/conta.rs similarity index 100% rename from cli/conta/bin/conta.rs rename to cli/conta/src/bin/conta.rs diff --git a/cli/conta/src/cmd/publish.rs b/cli/conta/src/cmd/publish.rs index 2f3778e67..b0fefbc28 100644 --- a/cli/conta/src/cmd/publish.rs +++ b/cli/conta/src/cmd/publish.rs @@ -33,7 +33,11 @@ impl Publish { } /// Publish cargo package - fn publish(&self, package: &str) -> Result { + fn publish(&self, mut package: &str) -> Result { + if package == "zinkc-filetests" { + package = "filetests"; + } + let mut cargo = Command::new("cargo"); cargo.arg("publish").arg("-p").arg(package); @@ -69,11 +73,17 @@ impl Publish { packages .iter() .filter_map(|pkg| -> Option> { - let Some((name, version)) = pkgs.get_key_value(pkg) else { + let pkg = if pkg.as_str() == "filetests" { + "zinkc-filetests".into() + } else { + pkg.clone() + }; + + let Some((name, version)) = pkgs.get_key_value(&pkg) else { return Some(Err(anyhow!("Package {} not found in metadata", pkg))); }; - if let Ok((crates, _total)) = registry.search(pkg, 1) { + if let Ok((crates, _total)) = registry.search(&pkg, 1) { if crates.len() == 1 && crates[0].max_version == *version { println!("Package {}@{} has already been published.", name, version); return None; diff --git a/cli/elko/Cargo.toml b/cli/elko/Cargo.toml index 8931e7b45..4105dfe4e 100644 --- a/cli/elko/Cargo.toml +++ b/cli/elko/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true [dependencies] anyhow.workspace = true +ccli.workspace = true clap = { workspace = true, features = [ "derive" ] } cargo_metadata.workspace = true colored.workspace = true @@ -20,4 +21,4 @@ thiserror.workspace = true toml.workspace = true tracing.workspace = true wasm-opt.workspace = true -zinkc.workspace = true +zinkc = { workspace = true, features = [ "cli" ] } diff --git a/cli/src/bin/elko.rs b/cli/elko/src/bin/elko.rs similarity index 73% rename from cli/src/bin/elko.rs rename to cli/elko/src/bin/elko.rs index 7a9bb66ab..51465bd81 100644 --- a/cli/src/bin/elko.rs +++ b/cli/elko/src/bin/elko.rs @@ -1,18 +1,18 @@ -//! Zink's package manager +//! The package manager of zink. #![deny(missing_docs)] -use clap::{Parser, Subcommand}; -use color_eyre::Result; -use zinkup::{App, Build, New}; +use ccli::{clap::Subcommand, App, Parser, Result}; +use elko::{Build, Compile, New}; -/// elko commands +/// Elko commands #[derive(Debug, Subcommand)] enum Command { New(New), Build(Build), + Compile(Compile), } -/// Zink's package manager +/// The package manager of zink. #[derive(Debug, Parser)] #[command(name = "elko", version)] pub struct Elko { @@ -32,6 +32,7 @@ impl App for Elko { match &self.command { Command::Build(build) => build.run(), Command::New(new) => new.run(), + Command::Compile(compile) => compile.run(), } } } diff --git a/cli/elko/src/lib.rs b/cli/elko/src/lib.rs index ae8dbeb14..aac989e20 100644 --- a/cli/elko/src/lib.rs +++ b/cli/elko/src/lib.rs @@ -5,3 +5,4 @@ mod new; pub mod utils; pub use self::{build::Build, new::New}; +pub use zinkc::cli::Compile; diff --git a/cli/src/compile.rs b/cli/src/compile.rs deleted file mode 100644 index 6bd0ae5f9..000000000 --- a/cli/src/compile.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Command `Compile`. -#![cfg(feature = "zinkc")] - -use anyhow::Result; -use clap::Parser; -use std::{env, fs, path::PathBuf}; -use zinkc::Compiler; - -/// Compile WASM to EVM bytecode. -#[derive(Debug, Parser)] -#[command(name = "build", version)] -pub struct Compile { - /// The path of the wasm file. - #[clap(value_name = "INPUT")] - input: PathBuf, - /// Write output to \ - #[clap(short, long)] - output: Option, - /// If enable dispatcher. - #[clap(short, long)] - dispatcher: bool, -} - -impl Compile { - /// Run compile. - pub fn run(&self) -> Result<()> { - let output = if let Some(output) = self.output.as_ref() { - output.into() - } else { - env::current_dir()?.join(self.input.with_extension("")) - }; - - let bin = Compiler::default() - .dispatcher(self.dispatcher) - .compile(&fs::read(&self.input)?)?; - fs::write(output, bin)?; - Ok(()) - } -} diff --git a/codegen/src/code/mod.rs b/codegen/src/code/mod.rs index 6ece1163b..188d72d00 100644 --- a/codegen/src/code/mod.rs +++ b/codegen/src/code/mod.rs @@ -6,7 +6,7 @@ use indexmap::IndexMap; mod func; /// Code section for EVM. -#[derive(Default, Debug)] +#[derive(Clone, Default, Debug)] pub struct Code { offset: usize, /// Function table. diff --git a/codegen/src/dispatcher.rs b/codegen/src/dispatcher.rs index 7ff62fdb5..0e2882c13 100644 --- a/codegen/src/dispatcher.rs +++ b/codegen/src/dispatcher.rs @@ -21,6 +21,10 @@ pub struct Dispatcher<'d> { pub data: DataSet, /// Jump table pub table: JumpTable, + /// ABI for the current function + /// + /// TODO: refactor this. (#192) + pub abi: Vec, } impl<'d> Dispatcher<'d> { @@ -33,6 +37,7 @@ impl<'d> Dispatcher<'d> { imports: Default::default(), data: Default::default(), table: Default::default(), + abi: Default::default(), } } @@ -179,6 +184,10 @@ impl<'d> Dispatcher<'d> { /// Emit selector to buffer. fn emit_selector(&mut self, selector: &Function<'_>, last: bool) -> Result<()> { let abi = self.load_abi(selector)?; + + // TODO: refactor this. (#192) + self.abi.push(abi.clone()); + let selector_bytes = abi.selector(); tracing::trace!( @@ -236,7 +245,7 @@ impl<'d> Dispatcher<'d> { } /// Emit compiled code to the given buffer. - pub fn finish(mut self, selectors: Functions<'_>, table: &mut JumpTable) -> Result> { + pub fn finish(&mut self, selectors: Functions<'_>, table: &mut JumpTable) -> Result> { if selectors.is_empty() { return Err(Error::SelectorNotFound); } @@ -252,7 +261,7 @@ impl<'d> Dispatcher<'d> { len -= 1; } - table.merge(self.table, 0)?; + table.merge(self.table.clone(), 0)?; Ok(self.asm.buffer().into()) } } diff --git a/codegen/src/jump/table.rs b/codegen/src/jump/table.rs index 769bfab0b..8113a3023 100644 --- a/codegen/src/jump/table.rs +++ b/codegen/src/jump/table.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; /// Jump table implementation. /// -#[derive(Default, Debug)] +#[derive(Clone, Default, Debug)] pub struct JumpTable { /// Jump table. pub(crate) jump: BTreeMap, diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index b8bf08b8f..dc963bbe9 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -9,12 +9,19 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[[bin]] +name = "zinkc" +required-features = [ "cli" ] + [dependencies] anyhow.workspace = true thiserror.workspace = true tracing.workspace = true wasmparser.workspace = true +zabi.workspace = true zingen.workspace = true +ccli = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true } [dev-dependencies] hex.workspace = true @@ -23,3 +30,6 @@ tracing-subscriber = { workspace = true, features = ["env-filter"]} paste.workspace = true zabi.workspace = true etc.workspace = true + +[features] +cli = [ "ccli", "serde_json" ] diff --git a/cli/src/bin/zinkc.rs b/compiler/src/bin/zinkc.rs similarity index 59% rename from cli/src/bin/zinkc.rs rename to compiler/src/bin/zinkc.rs index c9045fbd7..add26196b 100644 --- a/cli/src/bin/zinkc.rs +++ b/compiler/src/bin/zinkc.rs @@ -1,17 +1,16 @@ -//! Zink Compiler +//! Zink compiler. #![deny(missing_docs)] +#![cfg(feature = "cli")] -use clap::Parser; -use color_eyre::Result; -use zinkup::{App, Compile}; +use ccli::{clap, App, Parser, Result}; +use zinkc::cli::Compile; -/// Zink Compiler +/// The Zink Compiler. #[derive(Debug, Parser)] -#[command(name = "zinkc", version)] +#[command(name = "zinkc", version, arg_required_else_help(true))] pub struct Zinkc { - /// The entry of the zinkc compiler. - #[command(flatten)] - pub compile: Compile, + #[clap(flatten)] + command: Compile, /// Verbose mode (-v, -vv, -vvv, etc.) #[clap(short, long, action = clap::ArgAction::Count)] verbose: u8, @@ -23,7 +22,7 @@ impl App for Zinkc { } fn run(&self) -> anyhow::Result<()> { - self.compile.run() + self.command.run() } } diff --git a/compiler/src/cli.rs b/compiler/src/cli.rs new file mode 100644 index 000000000..1551f9642 --- /dev/null +++ b/compiler/src/cli.rs @@ -0,0 +1,58 @@ +//! Zink compiler command line interface. +#![cfg(feature = "cli")] + +use crate::Compiler; +use ccli::{clap, Parser}; +use std::{env, fs, path::PathBuf}; + +/// Compile WASM to EVM bytecode. +#[derive(Debug, Parser)] +#[command(name = "zinkc")] +pub struct Compile { + /// Write ABI to disk. + #[clap(short, long)] + abi: bool, + /// The path of the wasm file. + #[clap(value_name = "INPUT")] + input: PathBuf, + /// Write output to + #[clap(short, long)] + output: Option, + /// If enable dispatcher. + #[clap(short, long)] + dispatcher: bool, +} + +impl Compile { + /// Run compile. + pub fn run(&self) -> anyhow::Result<()> { + let output = if let Some(output) = self.output.as_ref() { + output.into() + } else { + env::current_dir()?.join(self.input.with_extension("")) + }; + + let mut compiler = Compiler::default().dispatcher(self.dispatcher); + let bin = compiler.compile(&fs::read(&self.input)?)?; + + output.parent().map(fs::create_dir_all); + fs::write(&output, bin)?; + + if !self.abi { + return Ok(()); + } + + let abi = output + .parent() + .ok_or_else(|| anyhow::anyhow!("invalid output path: {output:?}"))? + .join( + output + .file_name() + .ok_or_else(|| anyhow::anyhow!("invalid file name: {output:?}"))?, + ) + .with_extension("abi.json"); + + fs::write(abi, serde_json::to_string_pretty(&compiler.abi())?)?; + Ok(()) + } +} diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index d8a067da7..d68fb22c7 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -1,6 +1,7 @@ //! Zink compiler use crate::{parser::Parser, Config, Error, Result}; +use zabi::Abi; use zingen::{ Buffer, CodeGen, Constructor, DataSet, Dispatcher, Function, Imports, JumpTable, BUFFER_LIMIT, }; @@ -8,6 +9,7 @@ use zingen::{ /// Zink Compiler #[derive(Default)] pub struct Compiler { + abi: Vec, buffer: Buffer, table: JumpTable, config: Config, @@ -71,6 +73,7 @@ impl Compiler { return Err(Error::BufferOverflow(self.buffer.len())); } + self.abi = dispatcher.abi; Ok(()) } @@ -116,6 +119,11 @@ impl Compiler { Ok(()) } + /// Get the abi of the compiled contract. + pub fn abi(&self) -> Vec { + self.abi.clone() + } + /// Returns bytecode. fn bytecode(&self, constructor: Option>) -> Result { Constructor::new(constructor, self.buffer.clone())? diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index aa3ad1baf..6004240cb 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -7,6 +7,7 @@ pub use crate::{ result::{Error, Result}, }; +pub mod cli; mod compiler; mod config; mod parser; diff --git a/evm/abi/Cargo.toml b/evm/abi/Cargo.toml index 18d5a11d2..728b3dd26 100644 --- a/evm/abi/Cargo.toml +++ b/evm/abi/Cargo.toml @@ -1,6 +1,9 @@ [package] name = "sol-abi" +description = "Solidity ABI implementation" version = "0.0.1" +keywords = [ "no-std", "solidity", "ethereum" ] +documentation = "https://docs.rs/sol-abi" authors.workspace = true edition.workspace = true license.workspace = true @@ -8,10 +11,11 @@ homepage.workspace = true repository.workspace = true [dependencies] -serde = { workspace = true, optional = true } +serde = { workspace = true, features = [ "derive" ], optional = true } syn = { workspace = true, optional = true } quote = { workspace = true, optional = true } [features] default = [ "serde", "syn" ] -syn = [ "dep:syn", "quote" ] +syn = [ "dep:syn", "quote", "std" ] +std = [ "serde/std" ] diff --git a/evm/abi/README.md b/evm/abi/README.md index ecd4eae49..63246d06f 100644 --- a/evm/abi/README.md +++ b/evm/abi/README.md @@ -4,3 +4,7 @@ An implementation of solidity ABI in rust. Currently only used by the zink language, so the provided features is syncing with the development of zink. + +## LICENSE + +GPL-3.0 diff --git a/evm/abi/src/abi.rs b/evm/abi/src/abi.rs index d3befa47b..92c22ff47 100644 --- a/evm/abi/src/abi.rs +++ b/evm/abi/src/abi.rs @@ -1,50 +1,92 @@ //! Solidity ABI abstraction. -use crate::Input; + +use crate::Arg; +use core::{convert::Infallible, str::FromStr}; + +#[cfg(not(feature = "std"))] +use crate::std::{String, ToString, Vec}; /// Solidity ABI abstraction. -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Abi { /// ABI name. pub name: String, /// ABI type. + #[cfg_attr(feature = "serde", serde(rename = "type"))] pub ty: Type, - /// ABI inputs. - pub inputs: Vec, + /// An array of arguments. + pub inputs: Vec, + /// An array of arguments, similar to inputs. + pub outputs: Vec, } #[cfg(feature = "syn")] impl From<&syn::Signature> for Abi { fn from(sig: &syn::Signature) -> Self { - let args = sig.inputs.iter().filter_map(|arg| { - if let syn::FnArg::Typed(syn::PatType { ty, .. }) = arg { - Some(Input { - name: sig.ident.to_string(), - ty: crate::Param::from(ty), - }) - } else { - None - } - }); + let inputs = sig + .inputs + .iter() + .filter_map(|arg| { + if let syn::FnArg::Typed(syn::PatType { ty, .. }) = arg { + Some(Arg { + name: sig.ident.to_string(), + ty: crate::Param::from(ty), + }) + } else { + None + } + }) + .collect(); + + let outputs = if let syn::ReturnType::Type(_, ty) = &sig.output { + vec![Arg { + name: sig.ident.to_string(), + ty: crate::Param::from(ty), + }] + } else { + vec![] + }; + let name = sig.ident.to_string(); Abi { - name: sig.ident.to_string(), - inputs: args.collect(), - ty: Type::Function, + ty: Type::from(name.as_str()), + name, + inputs, + outputs, } } } /// Solidity ABI type. -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))] pub enum Type { /// Constructor ABI. Constructor, /// Function ABI. + #[default] Function, } +impl From<&str> for Type { + fn from(s: &str) -> Self { + match s { + "constructor" => Type::Constructor, + _ => Type::Function, + } + } +} + +impl FromStr for Type { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self::from(s)) + } +} + impl AsRef for Type { fn as_ref(&self) -> &str { match self { diff --git a/evm/abi/src/input.rs b/evm/abi/src/arg.rs similarity index 68% rename from evm/abi/src/input.rs rename to evm/abi/src/arg.rs index e16636424..7d3969a9c 100644 --- a/evm/abi/src/input.rs +++ b/evm/abi/src/arg.rs @@ -1,18 +1,25 @@ -//! Input of solidity ABI. +//! Arg of solidity ABI. -/// Input of solidity ABI. -#[derive(Debug, Clone)] +use core::{convert::Infallible, str::FromStr}; + +#[cfg(not(feature = "std"))] +use crate::std::{String, ToString}; + +/// Arg of solidity ABI. +#[derive(Clone, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Input { +pub struct Arg { /// Name of the input. pub name: String, /// Type of the input. + #[cfg_attr(feature = "serde", serde(rename = "type"))] pub ty: Param, } /// The canonical type of the parameter. -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))] pub enum Param { /// A 32-bit integer. Int32, @@ -23,6 +30,7 @@ pub enum Param { /// A 64-bit unsigned integer. UInt64, /// An unknown type. + #[default] Unknown, } @@ -38,6 +46,14 @@ impl From<&str> for Param { } } +impl FromStr for Param { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self::from(s)) + } +} + impl AsRef for Param { fn as_ref(&self) -> &str { match self { @@ -50,6 +66,12 @@ impl AsRef for Param { } } +impl ToString for Param { + fn to_string(&self) -> String { + self.as_ref().to_string() + } +} + #[cfg(feature = "syn")] impl From<&Box> for Param { fn from(ty: &Box) -> Self { diff --git a/evm/abi/src/lib.rs b/evm/abi/src/lib.rs index 1b314d950..fd346e3aa 100644 --- a/evm/abi/src/lib.rs +++ b/evm/abi/src/lib.rs @@ -2,11 +2,22 @@ //! //! https://docs.soliditylang.org/en/latest/abi-spec.html#json #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] mod abi; -mod input; +mod arg; + +#[cfg(not(feature = "std"))] +pub(crate) mod std { + extern crate alloc; + + pub use alloc::{ + string::{String, ToString}, + vec::Vec, + }; +} pub use self::{ abi::Abi, - input::{Input, Param}, + arg::{Arg, Param}, }; diff --git a/examples/constructor.rs b/examples/constructor.rs index 56b427e91..811dbdc9e 100644 --- a/examples/constructor.rs +++ b/examples/constructor.rs @@ -33,38 +33,19 @@ fn main() {} #[cfg(test)] mod tests { - use zint::{ - ethers::abi::{Abi, Function, Param, ParamType, StateMutability}, - Contract, Ethers, - }; + use zint::{ethers::abi::Abi, Contract, Ethers}; #[tokio::test] #[allow(deprecated)] async fn constructor() -> zint::Result<()> { let api = Ethers::anvil()?; - let bytecode = Contract::search("constructor")? + let contract = Contract::search("constructor")? .constructor(true) - .compile()? - .bytecode; + .compile()?; - // TODO: Generate ABI issue #47 - let mut abi: Abi = Default::default(); - abi.functions.insert( - "get".into(), - vec![Function { - name: "get".into(), - inputs: Default::default(), - outputs: vec![Param { - name: Default::default(), - kind: ParamType::Int(32usize), - internal_type: None, - }], - constant: None, - state_mutability: StateMutability::View, - }], - ); - - let factory = api.factory(abi, bytecode)?; + let abi: Abi = Abi::load(&*contract.json_abi()?.as_bytes()) + .map_err(|e| anyhow::anyhow!("Failed to load abi {e}"))?; + let factory = api.factory(abi, contract.bytecode)?; let contract = factory.deploy(())?.legacy().send().await?; let r = contract.method::<(), i32>("get", ())?.call().await?; diff --git a/tests/add.rs b/tests/add.rs index d8bfabcef..5ded6631a 100644 --- a/tests/add.rs +++ b/tests/add.rs @@ -2,7 +2,7 @@ #![cfg(test)] use anyhow::Result; -use zinkc_filetests::{impl_tests, Test}; +use filetests::{impl_tests, Test}; use zint::{Bytes32, Contract}; fn params(module: &str) -> Result<()> { diff --git a/tests/br_if.rs b/tests/br_if.rs index 258233f0b..8469542e4 100644 --- a/tests/br_if.rs +++ b/tests/br_if.rs @@ -1,6 +1,6 @@ //! br_if tests for the zink compiler. use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Contract, InstructionResult}; #[test] diff --git a/tests/call.rs b/tests/call.rs index c5b0c04fa..447c381c2 100644 --- a/tests/call.rs +++ b/tests/call.rs @@ -2,7 +2,7 @@ #![cfg(test)] use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Bytes32, Contract, InstructionResult}; #[test] diff --git a/tests/if.rs b/tests/if.rs index 7ae1536ae..5c03229f6 100644 --- a/tests/if.rs +++ b/tests/if.rs @@ -1,6 +1,6 @@ //! if-else tests for the zink compiler. use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Bytes32, Contract, InstructionResult}; #[test] diff --git a/tests/log.rs b/tests/log.rs index aa86da831..2f45c9812 100644 --- a/tests/log.rs +++ b/tests/log.rs @@ -1,7 +1,7 @@ //! Tests for instruction `select`. use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Bytes32, Contract}; #[test] diff --git a/tests/loop.rs b/tests/loop.rs index e1866de9d..a3edb7d5c 100644 --- a/tests/loop.rs +++ b/tests/loop.rs @@ -1,7 +1,7 @@ //! loop tests use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Bytes32, Contract, InstructionResult}; #[test] diff --git a/tests/recursion.rs b/tests/recursion.rs index 1ad1ed580..52d14beb9 100644 --- a/tests/recursion.rs +++ b/tests/recursion.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Bytes32, Contract}; #[test] diff --git a/tests/select.rs b/tests/select.rs index 9e3b0d0a9..eda2598a9 100644 --- a/tests/select.rs +++ b/tests/select.rs @@ -1,7 +1,7 @@ //! Tests for instruction `select`. use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Bytes32, Contract}; #[test] diff --git a/tests/storage.rs b/tests/storage.rs index e58cebe76..0cb956a58 100644 --- a/tests/storage.rs +++ b/tests/storage.rs @@ -2,7 +2,7 @@ #![cfg(test)] use anyhow::Result; -use zinkc_filetests::Test; +use filetests::Test; use zint::{Bytes32, Contract, InstructionResult, U256}; #[test] diff --git a/tests/sub.rs b/tests/sub.rs index dc4b175ae..0b8f8c93f 100644 --- a/tests/sub.rs +++ b/tests/sub.rs @@ -2,7 +2,7 @@ #![cfg(test)] use anyhow::Result; -use zinkc_filetests::{impl_tests, Test}; +use filetests::{impl_tests, Test}; use zint::{Bytes32, Contract}; fn params(module: &str) -> Result<()> { diff --git a/zint/Cargo.toml b/zint/Cargo.toml index 39a4fb6e3..7410dd7b4 100644 --- a/zint/Cargo.toml +++ b/zint/Cargo.toml @@ -17,6 +17,7 @@ ethers.workspace = true hex.workspace = true revm.workspace = true serde = { workspace = true, features = [ "derive" ] } +serde_json.workspace = true thiserror.workspace = true tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"]} diff --git a/zint/src/contract.rs b/zint/src/contract.rs index 06aeb63fa..21ff43ef9 100644 --- a/zint/src/contract.rs +++ b/zint/src/contract.rs @@ -8,6 +8,7 @@ use std::{ path::{Path, PathBuf}, }; use wasm_opt::OptimizationOptions; +use zabi::Abi; use zinkc::Compiler; /// Cargo Manifest for parsing package. @@ -33,6 +34,8 @@ pub struct Contract { pub dispatcher: bool, /// If enable constructor. pub constructor: bool, + /// The ABI of the contract. + pub abi: Vec, /// The source WASM of the contract. pub wasm: Vec, } @@ -85,14 +88,21 @@ impl Contract { self } + /// Get the JSON ABI of the contract. + pub fn json_abi(&self) -> Result { + serde_json::to_string_pretty(&self.abi).map_err(Into::into) + } + /// Compile WASM to EVM bytecode. pub fn compile(mut self) -> Result { - self.bytecode = Compiler::default() + let mut compiler = Compiler::default() .constructor(self.constructor) - .dispatcher(self.dispatcher) - .compile(&self.wasm)? - .to_vec(); + .dispatcher(self.dispatcher); + + self.bytecode = compiler.compile(&self.wasm)?.to_vec(); + self.abi = compiler.abi(); + tracing::debug!("abi: {:#}", self.json_abi()?); tracing::debug!("bytecode: {:?}", hex::encode(&self.bytecode)); Ok(self) }