From 73a9202d056c6e50cdb0effdce64269e3607fbee Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 10:59:29 -0300 Subject: [PATCH 01/33] Replace install script with Makefile --- Makefile | 19 +++++++++++++++++++ install.sh | 3 --- 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 Makefile delete mode 100755 install.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c63a85a --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +.PHONY: build +build: + cargo build + +.PHONY: test +test: + cargo test + +.PHONY: fmt +fmt: + cargo fmt + +.PHONY: lint +lint: + cargo clippy --package cargo-stylus --package cargo-stylus-example + +.PHONY: install +install: fmt lint + cargo install --path main diff --git a/install.sh b/install.sh deleted file mode 100755 index fbed7b3..0000000 --- a/install.sh +++ /dev/null @@ -1,3 +0,0 @@ -cargo fmt -cargo clippy --package cargo-stylus --package cargo-stylus-example -cargo install --path main From 00c6367791c318cc6cb9f14404d26916a8717483 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 11:24:18 -0300 Subject: [PATCH 02/33] Fix lint by replacing match with matches! --- main/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/src/main.rs b/main/src/main.rs index 82e781a..e6c3f87 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -487,10 +487,10 @@ fn main() -> Result<()> { // supported. These extensions are now incorporated as part of the `cargo-stylus` command itself and // will be the preferred method of running them. fn is_deprecated_extension(subcommand: &str) -> bool { - match subcommand { - "cargo-stylus-check" | "cargo-stylus-cgen" | "cargo-stylus-replay" => true, - _ => false, - } + matches!( + subcommand, + "cargo-stylus-check" | "cargo-stylus-cgen" | "cargo-stylus-replay" + ) } async fn main_impl(args: Opts) -> Result<()> { From 455d31cfa13b6e336a4df53a20219bd6bc38f91a Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 11:20:04 -0300 Subject: [PATCH 03/33] Fix inconsistent docker image naming Create a new auxiliary function to generate the correct image name. --- main/src/docker.rs | 49 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/main/src/docker.rs b/main/src/docker.rs index 62ad2c1..7accd54 100644 --- a/main/src/docker.rs +++ b/main/src/docker.rs @@ -12,8 +12,14 @@ use crate::constants::TOOLCHAIN_FILE_NAME; use crate::macros::greyln; use crate::project::extract_toolchain_channel; -fn image_exists(cargo_stylus_version: &str) -> Result { - let image_name = format!("cargo-stylus-base:{}", cargo_stylus_version); +fn image_name(cargo_stylus_version: &str, toolchain_version: &str) -> String { + format!( + "cargo-stylus-base-{}-toolchain-{}", + cargo_stylus_version, toolchain_version + ) +} + +fn image_exists(image_name: &str) -> Result { let output = Command::new("docker") .arg("images") .arg(image_name) @@ -37,21 +43,19 @@ a reproducible deployment, or opt out by using the --no-verify flag for local bu Ok(output.stdout.iter().filter(|c| **c == b'\n').count() > 1) } -fn create_image(cargo_stylus_version: Option, version: &str) -> Result<()> { - let cargo_stylus_version = - cargo_stylus_version.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string()); - if image_exists(&cargo_stylus_version)? { +fn create_image(cargo_stylus_version: &str, toolchain_version: &str) -> Result<()> { + let image_name = image_name(cargo_stylus_version, toolchain_version); + if image_exists(&image_name)? { return Ok(()); } - let name = format!( - "cargo-stylus-base-{}-toolchain-{}", - cargo_stylus_version, version + println!( + "Building Docker image for Rust toolchain {}", + toolchain_version ); - println!("Building Docker image for Rust toolchain {}", version,); let mut child = Command::new("docker") .arg("build") .arg("-t") - .arg(name) + .arg(image_name) .arg(".") .arg("-f-") .stdin(Stdio::piped()) @@ -67,25 +71,20 @@ fn create_image(cargo_stylus_version: Option, version: &str) -> Result<( RUN rustup component add rust-src --toolchain {}-x86_64-unknown-linux-gnu ", cargo_stylus_version, - version, - version, - version, + toolchain_version, + toolchain_version, + toolchain_version, )?; child.wait().map_err(|e| eyre!("wait failed: {e}"))?; Ok(()) } fn run_in_docker_container( - cargo_stylus_version: Option, + cargo_stylus_version: &str, toolchain_version: &str, command_line: &[&str], ) -> Result<()> { - let cargo_stylus_version = - cargo_stylus_version.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string()); - let name = format!( - "cargo-stylus-base-{}-toolchain-{}", - cargo_stylus_version, toolchain_version - ); + let image_name = image_name(cargo_stylus_version, toolchain_version); let dir = std::env::current_dir().map_err(|e| eyre!("failed to find current directory: {e}"))?; Command::new("docker") @@ -96,7 +95,7 @@ fn run_in_docker_container( .arg("/source") .arg("-v") .arg(format!("{}:/source", dir.as_os_str().to_str().unwrap())) - .arg(name) + .arg(image_name) .args(command_line) .spawn() .map_err(|e| eyre!("failed to execute Docker command: {e}"))? @@ -116,12 +115,14 @@ pub fn run_reproducible( "Running reproducible Stylus command with toolchain {}", toolchain_channel.mint() ); + let cargo_stylus_version = + cargo_stylus_version.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string()); let mut command = vec!["cargo", "stylus"]; for s in command_line.iter() { command.push(s); } - create_image(cargo_stylus_version.clone(), &toolchain_channel)?; - run_in_docker_container(cargo_stylus_version, &toolchain_channel, &command) + create_image(&cargo_stylus_version, &toolchain_channel)?; + run_in_docker_container(&cargo_stylus_version, &toolchain_channel, &command) } fn verify_valid_host() -> Result<()> { From ba5ee7e20fd66c84301447e8879e08af9dedc719 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 12:40:08 -0300 Subject: [PATCH 04/33] Move version from docker image name to tag --- main/src/docker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/docker.rs b/main/src/docker.rs index 7accd54..5822558 100644 --- a/main/src/docker.rs +++ b/main/src/docker.rs @@ -14,7 +14,7 @@ use crate::project::extract_toolchain_channel; fn image_name(cargo_stylus_version: &str, toolchain_version: &str) -> String { format!( - "cargo-stylus-base-{}-toolchain-{}", + "cargo-stylus:base-{}-toolchain-{}", cargo_stylus_version, toolchain_version ) } From 13ae7aadb01b67ba52d9c56a5271b10c533e36bb Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 13:02:05 -0300 Subject: [PATCH 05/33] Fix warnings in generated dockerfile --- main/src/docker.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/src/docker.rs b/main/src/docker.rs index 5822558..fb6d1ab 100644 --- a/main/src/docker.rs +++ b/main/src/docker.rs @@ -64,7 +64,8 @@ fn create_image(cargo_stylus_version: &str, toolchain_version: &str) -> Result<( write!( child.stdin.as_mut().unwrap(), "\ - FROM --platform=linux/amd64 offchainlabs/cargo-stylus-base:{} as base + ARG BUILD_PLATFORM=linux/amd64 + FROM --platform=${{BUILD_PLATFORM}} offchainlabs/cargo-stylus-base:{} AS base RUN rustup toolchain install {}-x86_64-unknown-linux-gnu RUN rustup default {}-x86_64-unknown-linux-gnu RUN rustup target add wasm32-unknown-unknown From 04b93794b19796a1813c09d00516535bb6b1d160 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 13:02:28 -0300 Subject: [PATCH 06/33] Add test that builds docker image --- main/src/docker.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/main/src/docker.rs b/main/src/docker.rs index fb6d1ab..4645356 100644 --- a/main/src/docker.rs +++ b/main/src/docker.rs @@ -148,3 +148,31 @@ fn verify_valid_host() -> Result<()> { } Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_image_and_check_it_exists() { + let toolchain_version = "1.80.0"; + let cargo_stylus_version = "0.5.3"; + let image_name = image_name(&cargo_stylus_version, toolchain_version); + println!("image name: {}", image_name); + + // Remove existing docker image + Command::new("docker") + .arg("image") + .arg("rm") + .arg("-f") + .arg(&image_name) + .spawn() + .expect("failed to spawn docker image rm") + .wait() + .expect("failed to run docker image rm"); + + assert!(!image_exists(&image_name).unwrap()); + create_image(&cargo_stylus_version, toolchain_version).unwrap(); + assert!(image_exists(&image_name).unwrap()); + } +} From 4822031090ed3999867da484356f0d374d599965 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 13:56:03 -0300 Subject: [PATCH 07/33] Install docker on macos CI workflow --- .github/workflows/mac.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 49918f7..f98879f 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -30,5 +30,10 @@ jobs: sh rustup-init.sh -y --default-toolchain none rustup target add ${{ matrix.target }} + - name: install docker + run: | + brew install docker + colima start + - name: Build and Test run: ./.ci/build_and_test.sh From 6f81d17d24fb4e44a59d42ad5c4e8e22c57bb9f9 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 14:30:52 -0300 Subject: [PATCH 08/33] Install colima in macos CI to run docker --- .github/workflows/mac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index f98879f..0740a7e 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -32,7 +32,7 @@ jobs: - name: install docker run: | - brew install docker + brew install docker colima colima start - name: Build and Test From d4edcaaac990e0539c00721c1e80e039ae04c9a6 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 14:53:31 -0300 Subject: [PATCH 09/33] Revert "Install colima in macos CI to run docker" This reverts commit 6f81d17d24fb4e44a59d42ad5c4e8e22c57bb9f9. --- .github/workflows/mac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 0740a7e..f98879f 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -32,7 +32,7 @@ jobs: - name: install docker run: | - brew install docker colima + brew install docker colima start - name: Build and Test From e366b6116698b3dce8bac90263b633abcc7c7858 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 14:53:45 -0300 Subject: [PATCH 10/33] Revert "Install docker on macos CI workflow" This reverts commit 4822031090ed3999867da484356f0d374d599965. --- .github/workflows/mac.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index f98879f..49918f7 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -30,10 +30,5 @@ jobs: sh rustup-init.sh -y --default-toolchain none rustup target add ${{ matrix.target }} - - name: install docker - run: | - brew install docker - colima start - - name: Build and Test run: ./.ci/build_and_test.sh From 0f960b035b3f2ec47e668c9c20159ef77f7a0193 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 16:44:57 -0300 Subject: [PATCH 11/33] Add feature to disable docker tests on MacOS CI --- .ci/build_and_test.sh | 9 ++++++++- main/Cargo.toml | 4 ++++ main/src/docker.rs | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.ci/build_and_test.sh b/.ci/build_and_test.sh index 8277240..67f2378 100755 --- a/.ci/build_and_test.sh +++ b/.ci/build_and_test.sh @@ -15,4 +15,11 @@ if [ "$CFG_RELEASE_CHANNEL" == "nightly" ]; then else cargo build --locked fi -cargo test \ No newline at end of file + +UNAME=$(uname -s) +if [ "$UNAME" == "Darwin" ]; then + # Disable docker tests on MacOS CI + cargo test --no-default-features +else + cargo test +fi diff --git a/main/Cargo.toml b/main/Cargo.toml index 98215aa..edca95e 100644 --- a/main/Cargo.toml +++ b/main/Cargo.toml @@ -10,6 +10,10 @@ license.workspace = true version.workspace = true repository.workspace = true +[features] +default = ["docker-test"] +docker-test = [] + [dependencies] alloy-primitives.workspace = true alloy-json-abi.workspace = true diff --git a/main/src/docker.rs b/main/src/docker.rs index 4645356..eb15a1b 100644 --- a/main/src/docker.rs +++ b/main/src/docker.rs @@ -149,6 +149,7 @@ fn verify_valid_host() -> Result<()> { Ok(()) } +#[cfg(feature="docker-test")] #[cfg(test)] mod tests { use super::*; From fffea3d3cf4c5c6854bb2ae32d4543f9a4f22260 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 30 Oct 2024 16:49:26 -0300 Subject: [PATCH 12/33] Fix cargo fmt --- main/src/docker.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main/src/docker.rs b/main/src/docker.rs index eb15a1b..5530d92 100644 --- a/main/src/docker.rs +++ b/main/src/docker.rs @@ -149,8 +149,7 @@ fn verify_valid_host() -> Result<()> { Ok(()) } -#[cfg(feature="docker-test")] -#[cfg(test)] +#[cfg(all(test, feature = "docker-test"))] mod tests { use super::*; From e6b6ce1d911d5cf1b25d24e78ffdffc2ab929a3f Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Thu, 31 Oct 2024 10:40:17 -0300 Subject: [PATCH 13/33] Use correct cargo stylus version in docker image --- .dockerignore | 1 + Dockerfile | 16 +++++++++++----- Makefile | 6 ++++++ 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9fe342c --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +./target diff --git a/Dockerfile b/Dockerfile index 48fe819..a3a8515 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,15 @@ -FROM --platform=linux/amd64 rust:1.80 as builder +ARG BUILD_PLATFORM=linux/amd64 +ARG RUST_VERSION=1.80 +ARG CARGO_STYLUS_VERSION + +FROM --platform=${BUILD_PLATFORM} rust:${RUST_VERSION} AS builder RUN apt-get update && apt-get install -y git RUN rustup target add x86_64-unknown-linux-gnu -RUN git clone https://github.com/offchainlabs/cargo-stylus.git +ARG CARGO_STYLUS_VERSION +RUN test -n "$CARGO_STYLUS_VERSION" +RUN git clone --branch v$CARGO_STYLUS_VERSION https://github.com/offchainlabs/cargo-stylus.git WORKDIR /cargo-stylus -RUN git checkout v0.5.2 RUN cargo build --release --manifest-path main/Cargo.toml -FROM --platform=linux/amd64 rust:1.80 -COPY --from=builder /cargo-stylus/target/release/cargo-stylus /usr/local/bin/cargo-stylus \ No newline at end of file + +FROM --platform=${BUILD_PLATFORM} rust:${RUST_VERSION} AS cargo-stylus-base +COPY --from=builder /cargo-stylus/target/release/cargo-stylus /usr/local/bin/cargo-stylus diff --git a/Makefile b/Makefile index c63a85a..b4fbed4 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +CARGO_STYLUS_VERSION := $(shell cargo pkgid --manifest-path main/Cargo.toml | cut -d '@' -f 2) + .PHONY: build build: cargo build @@ -17,3 +19,7 @@ lint: .PHONY: install install: fmt lint cargo install --path main + +.PHONY: docker +docker: + docker build -t cargo-stylus-base:$(CARGO_STYLUS_VERSION) --build-arg CARGO_STYLUS_VERSION=$(CARGO_STYLUS_VERSION) . From 6dfbd5548539c52860edf3acd01e1e5a25992190 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Thu, 31 Oct 2024 10:59:15 -0300 Subject: [PATCH 14/33] Update version to 0.5.6 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2056791..ba112b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1030,7 +1030,7 @@ dependencies = [ [[package]] name = "cargo-stylus" -version = "0.5.5" +version = "0.5.6" dependencies = [ "alloy-contract", "alloy-ethers-typecast", @@ -1072,7 +1072,7 @@ dependencies = [ [[package]] name = "cargo-stylus-example" -version = "0.5.5" +version = "0.5.6" dependencies = [ "clap", ] diff --git a/Cargo.toml b/Cargo.toml index 37ce93a..f838050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] authors = ["Offchain Labs"] -version = "0.5.5" +version = "0.5.6" edition = "2021" homepage = "https://arbitrum.io" license = "MIT OR Apache-2.0" From 91211f6986c08cf074e6435f361204db835951c0 Mon Sep 17 00:00:00 2001 From: Derek Lee <103802618+leeederek@users.noreply.github.com> Date: Fri, 1 Nov 2024 03:30:19 -0400 Subject: [PATCH 15/33] update link for contract verification to point to our docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62c2523..0ce501f 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ See `--help` for all available flags and default values. ## Verifying Stylus Contracts -See [here](https://hackmd.io/bpeMnrzbSvO4mohhvkrKqw) +See the formal Arbitrum docs on verifying Stylus contracts [here](https://docs.arbitrum.io/stylus/how-tos/verifying-contracts#reproducible-verification) ## Deploying Non-Rust WASM Projects From fae5773da15efcbb3b47bf0e3161af457f45aa91 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 1 Nov 2024 11:41:38 -0300 Subject: [PATCH 16/33] Add unit test for hash_files function --- main/src/project.rs | 64 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/main/src/project.rs b/main/src/project.rs index 0f0fd4e..1aca759 100644 --- a/main/src/project.rs +++ b/main/src/project.rs @@ -405,10 +405,39 @@ fn strip_user_metadata(wasm_file_bytes: &[u8]) -> Result> { #[cfg(test)] mod test { use super::*; - use std::fs::{self, File}; - use std::io::Write; + use std::{ + env, + fs::{self, File}, + io::Write, + path::Path, + }; use tempfile::tempdir; + fn write_valid_toolchain_file(toolchain_file_path: &Path) -> Result<()> { + let toolchain_contents = r#" + [toolchain] + channel = "nightly-2020-07-10" + components = [ "rustfmt", "rustc-dev" ] + targets = [ "wasm32-unknown-unknown", "thumbv2-none-eabi" ] + profile = "minimal" + "#; + fs::write(&toolchain_file_path, toolchain_contents)?; + Ok(()) + } + + fn write_mock_rust_files(base_path: &Path, num_files: usize, num_lines: u64) -> Result<()> { + fs::create_dir(base_path.join("src"))?; + let mut contents = String::new(); + for _ in 0..num_lines { + contents.push_str("// foo"); + } + for i in 0..num_files { + let file_path = base_path.join(format!("src/f{i}.rs")); + fs::write(&file_path, &contents)?; + } + Ok(()) + } + #[test] fn test_extract_toolchain_channel() -> Result<()> { let dir = tempdir()?; @@ -438,15 +467,7 @@ mod test { }; assert!(err_details.to_string().contains("is not a string"),); - let toolchain_contents = r#" - [toolchain] - channel = "nightly-2020-07-10" - components = [ "rustfmt", "rustc-dev" ] - targets = [ "wasm32-unknown-unknown", "thumbv2-none-eabi" ] - profile = "minimal" - "#; - std::fs::write(&toolchain_file_path, toolchain_contents)?; - + write_valid_toolchain_file(&toolchain_file_path)?; let channel = extract_toolchain_channel(&toolchain_file_path)?; assert_eq!(channel, "nightly-2020-07-10"); Ok(()) @@ -496,4 +517,25 @@ mod test { Ok(()) } + + #[test] + pub fn test_hash_files() -> Result<()> { + let dir = tempdir()?; + env::set_current_dir(dir.path())?; + + let toolchain_file_path = dir.path().join(TOOLCHAIN_FILE_NAME); + write_valid_toolchain_file(&toolchain_file_path)?; + write_mock_rust_files(dir.path(), 10, 100)?; + fs::write(dir.path().join("Cargo.toml"), "")?; + fs::write(dir.path().join("Cargo.lock"), "")?; + + let cfg = BuildConfig::new(false); + let hash = hash_files(vec![], cfg)?; + + assert_eq!( + hex::encode(hash), + "06b50fcc53e0804f043eac3257c825226e59123018b73895cb946676148cb262" + ); + Ok(()) + } } From cdab4857c02e588b457259e0a534dd9a973be1e8 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 1 Nov 2024 14:38:13 -0300 Subject: [PATCH 17/33] Revert "Move version from docker image name to tag" This reverts commit ba5ee7e20fd66c84301447e8879e08af9dedc719. --- main/src/docker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/docker.rs b/main/src/docker.rs index 5530d92..c293727 100644 --- a/main/src/docker.rs +++ b/main/src/docker.rs @@ -14,7 +14,7 @@ use crate::project::extract_toolchain_channel; fn image_name(cargo_stylus_version: &str, toolchain_version: &str) -> String { format!( - "cargo-stylus:base-{}-toolchain-{}", + "cargo-stylus-base-{}-toolchain-{}", cargo_stylus_version, toolchain_version ) } From 4dfc1163771bedfc9692adca4664f9c0b9f5a1fe Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 1 Nov 2024 19:11:56 -0300 Subject: [PATCH 18/33] Add benchmark for hash_files --- main/Cargo.toml | 3 +++ main/src/main.rs | 3 +++ main/src/project.rs | 46 ++++++++++++++++++++++++++++----------------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/main/Cargo.toml b/main/Cargo.toml index 98215aa..b784754 100644 --- a/main/Cargo.toml +++ b/main/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true version.workspace = true repository.workspace = true +[features] +nightly = [] + [dependencies] alloy-primitives.workspace = true alloy-json-abi.workspace = true diff --git a/main/src/main.rs b/main/src/main.rs index 82e781a..c34e4dd 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -1,6 +1,9 @@ // Copyright 2023-2024, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md +// Enable unstable test feature for benchmarks when nightly is available +#![cfg_attr(feature = "nightly", feature(test))] + use alloy_primitives::TxHash; use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand}; use constants::DEFAULT_ENDPOINT; diff --git a/main/src/project.rs b/main/src/project.rs index 1aca759..71d4d09 100644 --- a/main/src/project.rs +++ b/main/src/project.rs @@ -411,7 +411,10 @@ mod test { io::Write, path::Path, }; - use tempfile::tempdir; + use tempfile::{tempdir, TempDir}; + + #[cfg(feature = "nightly")] + extern crate test; fn write_valid_toolchain_file(toolchain_file_path: &Path) -> Result<()> { let toolchain_contents = r#" @@ -425,17 +428,26 @@ mod test { Ok(()) } - fn write_mock_rust_files(base_path: &Path, num_files: usize, num_lines: u64) -> Result<()> { - fs::create_dir(base_path.join("src"))?; + fn write_hash_files(num_files: usize, num_lines: usize) -> Result { + let dir = tempdir()?; + env::set_current_dir(dir.path())?; + + let toolchain_file_path = dir.path().join(TOOLCHAIN_FILE_NAME); + write_valid_toolchain_file(&toolchain_file_path)?; + + fs::create_dir(dir.path().join("src"))?; let mut contents = String::new(); for _ in 0..num_lines { contents.push_str("// foo"); } for i in 0..num_files { - let file_path = base_path.join(format!("src/f{i}.rs")); + let file_path = dir.path().join(format!("src/f{i}.rs")); fs::write(&file_path, &contents)?; } - Ok(()) + fs::write(dir.path().join("Cargo.toml"), "")?; + fs::write(dir.path().join("Cargo.lock"), "")?; + + Ok(dir) } #[test] @@ -520,22 +532,22 @@ mod test { #[test] pub fn test_hash_files() -> Result<()> { - let dir = tempdir()?; - env::set_current_dir(dir.path())?; - - let toolchain_file_path = dir.path().join(TOOLCHAIN_FILE_NAME); - write_valid_toolchain_file(&toolchain_file_path)?; - write_mock_rust_files(dir.path(), 10, 100)?; - fs::write(dir.path().join("Cargo.toml"), "")?; - fs::write(dir.path().join("Cargo.lock"), "")?; - - let cfg = BuildConfig::new(false); - let hash = hash_files(vec![], cfg)?; - + let _dir = write_hash_files(10, 100)?; + let hash = hash_files(vec![], BuildConfig::new(false))?; assert_eq!( hex::encode(hash), "06b50fcc53e0804f043eac3257c825226e59123018b73895cb946676148cb262" ); Ok(()) } + + #[cfg(feature = "nightly")] + #[bench] + pub fn bench_hash_files(b: &mut test::Bencher) -> Result<()> { + let _dir = write_hash_files(1000, 10000)?; + b.iter(|| { + hash_files(vec![], BuildConfig::new(false)).expect("failed to hash files"); + }); + Ok(()) + } } From 3fce6759e64d60a263f9494527f176ad2be50dc7 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 1 Nov 2024 17:40:11 -0300 Subject: [PATCH 19/33] Improve hash_files performance Read the files in a separate thread and then send the keccak preimage through a channel to the main thread. This improves the performance in ~13% without changing the hash output. --- main/src/project.rs | 57 ++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/main/src/project.rs b/main/src/project.rs index 71d4d09..a5d6fa0 100644 --- a/main/src/project.rs +++ b/main/src/project.rs @@ -18,6 +18,8 @@ use std::{ io::Read, path::{Path, PathBuf}, process, + sync::mpsc, + thread, }; use std::{ops::Range, process::Command}; use tiny_keccak::{Hasher, Keccak}; @@ -228,6 +230,21 @@ pub fn extract_cargo_toml_version(cargo_toml_path: &PathBuf) -> Result { Ok(version.to_string()) } +pub fn read_file_preimage(filename: &Path) -> Result> { + let mut contents = Vec::with_capacity(1024); + { + let filename = filename.as_os_str(); + contents.extend_from_slice(&(filename.len() as u64).to_be_bytes()); + contents.extend_from_slice(filename.as_encoded_bytes()); + } + let mut file = std::fs::File::open(filename) + .map_err(|e| eyre!("failed to open file {}: {e}", filename.display()))?; + contents.extend_from_slice(&file.metadata().unwrap().len().to_be_bytes()); + file.read_to_end(&mut contents) + .map_err(|e| eyre!("Unable to read file {}: {e}", filename.display()))?; + Ok(contents) +} + pub fn hash_files(source_file_patterns: Vec, cfg: BuildConfig) -> Result<[u8; 32]> { let mut keccak = Keccak::v256(); let mut cmd = Command::new("cargo"); @@ -245,26 +262,6 @@ pub fn hash_files(source_file_patterns: Vec, cfg: BuildConfig) -> Result keccak.update(&[1]); } - let mut buf = vec![0u8; 0x100000]; - - let mut hash_file = |filename: &Path| -> Result<()> { - keccak.update(&(filename.as_os_str().len() as u64).to_be_bytes()); - keccak.update(filename.as_os_str().as_encoded_bytes()); - let mut file = std::fs::File::open(filename) - .map_err(|e| eyre!("failed to open file {}: {e}", filename.display()))?; - keccak.update(&file.metadata().unwrap().len().to_be_bytes()); - loop { - let bytes_read = file - .read(&mut buf) - .map_err(|e| eyre!("Unable to read file {}: {e}", filename.display()))?; - if bytes_read == 0 { - break; - } - keccak.update(&buf[..bytes_read]); - } - Ok(()) - }; - // Fetch the Rust toolchain toml file from the project root. Assert that it exists and add it to the // files in the directory to hash. let toolchain_file_path = PathBuf::from(".").as_path().join(TOOLCHAIN_FILE_NAME); @@ -277,12 +274,20 @@ pub fn hash_files(source_file_patterns: Vec, cfg: BuildConfig) -> Result paths.push(toolchain_file_path); paths.sort(); - for filename in paths.iter() { - greyln!( - "File used for deployment hash: {}", - filename.as_os_str().to_string_lossy() - ); - hash_file(filename)?; + // Read the file contents in another thread and process the keccak in the main thread. + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + for filename in paths.iter() { + greyln!( + "File used for deployment hash: {}", + filename.as_os_str().to_string_lossy() + ); + tx.send(read_file_preimage(&filename)) + .expect("failed to send preimage (impossible)"); + } + }); + for result in rx { + keccak.update(result?.as_slice()); } let mut hash = [0u8; 32]; From 2e0cd0c53478c65ab3f1b10f97b16e3dbfb7d51d Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Fri, 1 Nov 2024 18:47:53 -0700 Subject: [PATCH 20/33] Use rust-lldb for macos or if rust-gdb not available --- main/src/main.rs | 61 +++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/main/src/main.rs b/main/src/main.rs index 82e781a..99d8812 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -7,8 +7,7 @@ use constants::DEFAULT_ENDPOINT; use ethers::abi::Bytes; use ethers::types::{H160, U256}; use eyre::{bail, eyre, Context, Result}; -use std::path::PathBuf; -use std::{fmt, path::Path}; +use std::{fmt, path::{Path, PathBuf}}; use tokio::runtime::Builder; use trace::Trace; use util::{color::Color, sys}; @@ -487,10 +486,7 @@ fn main() -> Result<()> { // supported. These extensions are now incorporated as part of the `cargo-stylus` command itself and // will be the preferred method of running them. fn is_deprecated_extension(subcommand: &str) -> bool { - match subcommand { - "cargo-stylus-check" | "cargo-stylus-cgen" | "cargo-stylus-replay" => true, - _ => false, - } + matches!(subcommand, "cargo-stylus-check" | "cargo-stylus-cgen" | "cargo-stylus-replay") } async fn main_impl(args: Opts) -> Result<()> { @@ -612,35 +608,52 @@ async fn simulate(args: SimulateArgs) -> Result<()> { async fn replay(args: ReplayArgs) -> Result<()> { if !args.child { - let rust_gdb = sys::command_exists("rust-gdb"); - if !rust_gdb { - println!( - "{} not installed, falling back to {}", - "rust-gdb".red(), - "gdb".red() - ); - } - - let mut cmd = match rust_gdb { - true => sys::new_command("rust-gdb"), - false => sys::new_command("gdb"), + let macos = cfg!(target_os = "macos"); + let gdb_args = [ + "--quiet", + "-ex=set breakpoint pending on", + "-ex=b user_entrypoint", + "-ex=r", + "--args", + ].as_slice(); + let lldb_args = [ + "--source-quietly", + "-o", + "b user_entrypoint", + "-o", + "r", + "--", + ].as_slice(); + let (cmd_name, args) = if sys::command_exists("rust-gdb") && !macos { + ("rust-gdb", &gdb_args) + } else if sys::command_exists("rust-lldb") { + ("rust-lldb", &lldb_args) + } else { + println!("rust specific debugger not installed, falling back to generic debugger"); + if sys::command_exists("gdb") && !macos { + ("gdb", &gdb_args) + } else if sys::command_exists("lldb") { + ("lldb", &lldb_args) + } else { + bail!("no debugger found") + } }; - cmd.arg("--quiet"); - cmd.arg("-ex=set breakpoint pending on"); - cmd.arg("-ex=b user_entrypoint"); - cmd.arg("-ex=r"); - cmd.arg("--args"); + let mut cmd = sys::new_command(cmd_name); + for arg in args.iter() { + cmd.arg(arg); + } for arg in std::env::args() { cmd.arg(arg); } cmd.arg("--child"); + #[cfg(unix)] let err = cmd.exec(); #[cfg(windows)] let err = cmd.status(); - bail!("failed to exec gdb {:?}", err); + bail!("failed to exec {cmd_name} {:?}", err); } let provider = sys::new_provider(&args.trace.endpoint)?; From 42be9a2ddd84549869768df2e12094cfff519ccb Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Sun, 3 Nov 2024 19:27:06 -0700 Subject: [PATCH 21/33] cargo fmt fixes --- main/src/main.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/main/src/main.rs b/main/src/main.rs index 99d8812..578e546 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -7,7 +7,10 @@ use constants::DEFAULT_ENDPOINT; use ethers::abi::Bytes; use ethers::types::{H160, U256}; use eyre::{bail, eyre, Context, Result}; -use std::{fmt, path::{Path, PathBuf}}; +use std::{ + fmt, + path::{Path, PathBuf}, +}; use tokio::runtime::Builder; use trace::Trace; use util::{color::Color, sys}; @@ -486,7 +489,10 @@ fn main() -> Result<()> { // supported. These extensions are now incorporated as part of the `cargo-stylus` command itself and // will be the preferred method of running them. fn is_deprecated_extension(subcommand: &str) -> bool { - matches!(subcommand, "cargo-stylus-check" | "cargo-stylus-cgen" | "cargo-stylus-replay") + matches!( + subcommand, + "cargo-stylus-check" | "cargo-stylus-cgen" | "cargo-stylus-replay" + ) } async fn main_impl(args: Opts) -> Result<()> { @@ -615,7 +621,8 @@ async fn replay(args: ReplayArgs) -> Result<()> { "-ex=b user_entrypoint", "-ex=r", "--args", - ].as_slice(); + ] + .as_slice(); let lldb_args = [ "--source-quietly", "-o", @@ -623,7 +630,8 @@ async fn replay(args: ReplayArgs) -> Result<()> { "-o", "r", "--", - ].as_slice(); + ] + .as_slice(); let (cmd_name, args) = if sys::command_exists("rust-gdb") && !macos { ("rust-gdb", &gdb_args) } else if sys::command_exists("rust-lldb") { From b57487f435891fe259c0de71e6d49eeae28fb848 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Mon, 4 Nov 2024 10:15:15 -0300 Subject: [PATCH 22/33] Add bench target in makefile --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index b4fbed4..38f0a23 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,10 @@ build: test: cargo test +.PHONY: bench +bench: + rustup run nightly cargo bench -F nightly + .PHONY: fmt fmt: cargo fmt From bcfb70386fabafdc923e8120f0b5617773ed0b7c Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Mon, 4 Nov 2024 10:58:51 -0300 Subject: [PATCH 23/33] Move cargo version out of hash_files This makes the unit tests more resilient to changes in the rust environment. --- main/src/check.rs | 2 +- main/src/project.rs | 28 ++++++++++++++++++++++------ main/src/verify.rs | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/main/src/check.rs b/main/src/check.rs index 5c31ea6..572eb50 100644 --- a/main/src/check.rs +++ b/main/src/check.rs @@ -128,7 +128,7 @@ impl CheckConfig { let cfg = BuildConfig::new(rust_stable); let wasm = project::build_dylib(cfg.clone())?; let project_hash = - project::hash_files(self.common_cfg.source_files_for_project_hash.clone(), cfg)?; + project::hash_project(self.common_cfg.source_files_for_project_hash.clone(), cfg)?; Ok((wasm, project_hash)) } } diff --git a/main/src/project.rs b/main/src/project.rs index a5d6fa0..45cfa65 100644 --- a/main/src/project.rs +++ b/main/src/project.rs @@ -245,8 +245,7 @@ pub fn read_file_preimage(filename: &Path) -> Result> { Ok(contents) } -pub fn hash_files(source_file_patterns: Vec, cfg: BuildConfig) -> Result<[u8; 32]> { - let mut keccak = Keccak::v256(); +pub fn hash_project(source_file_patterns: Vec, cfg: BuildConfig) -> Result<[u8; 32]> { let mut cmd = Command::new("cargo"); cmd.arg("--version"); let output = cmd @@ -255,7 +254,21 @@ pub fn hash_files(source_file_patterns: Vec, cfg: BuildConfig) -> Result if !output.status.success() { bail!("cargo version command failed"); } - keccak.update(&output.stdout); + + hash_files(&output.stdout, source_file_patterns, cfg) +} + +pub fn hash_files( + cargo_version_output: &[u8], + source_file_patterns: Vec, + cfg: BuildConfig, +) -> Result<[u8; 32]> { + let mut keccak = Keccak::v256(); + println!( + "> {}", + String::from_utf8(cargo_version_output.into()).unwrap() + ); + keccak.update(cargo_version_output); if cfg.opt_level == OptLevel::Z { keccak.update(&[0]); } else { @@ -282,7 +295,7 @@ pub fn hash_files(source_file_patterns: Vec, cfg: BuildConfig) -> Result "File used for deployment hash: {}", filename.as_os_str().to_string_lossy() ); - tx.send(read_file_preimage(&filename)) + tx.send(read_file_preimage(filename)) .expect("failed to send preimage (impossible)"); } }); @@ -538,7 +551,8 @@ mod test { #[test] pub fn test_hash_files() -> Result<()> { let _dir = write_hash_files(10, 100)?; - let hash = hash_files(vec![], BuildConfig::new(false))?; + let rust_version = "cargo 1.80.0 (376290515 2024-07-16)\n".as_bytes(); + let hash = hash_files(rust_version, vec![], BuildConfig::new(false))?; assert_eq!( hex::encode(hash), "06b50fcc53e0804f043eac3257c825226e59123018b73895cb946676148cb262" @@ -550,8 +564,10 @@ mod test { #[bench] pub fn bench_hash_files(b: &mut test::Bencher) -> Result<()> { let _dir = write_hash_files(1000, 10000)?; + let rust_version = "cargo 1.80.0 (376290515 2024-07-16)\n".as_bytes(); b.iter(|| { - hash_files(vec![], BuildConfig::new(false)).expect("failed to hash files"); + hash_files(rust_version, vec![], BuildConfig::new(false)) + .expect("failed to hash files"); }); Ok(()) } diff --git a/main/src/verify.rs b/main/src/verify.rs index c65223b..23fb9af 100644 --- a/main/src/verify.rs +++ b/main/src/verify.rs @@ -65,7 +65,7 @@ pub async fn verify(cfg: VerifyConfig) -> eyre::Result<()> { let wasm_file: PathBuf = project::build_dylib(build_cfg.clone()) .map_err(|e| eyre!("could not build project to WASM: {e}"))?; let project_hash = - project::hash_files(cfg.common_cfg.source_files_for_project_hash, build_cfg)?; + project::hash_project(cfg.common_cfg.source_files_for_project_hash, build_cfg)?; let (_, init_code) = project::compress_wasm(&wasm_file, project_hash)?; let deployment_data = deploy::contract_deployment_calldata(&init_code); if deployment_data == *result.input { From 122469436bf9331784a17a251d1b85bcc8dbac3e Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Mon, 4 Nov 2024 11:09:25 -0300 Subject: [PATCH 24/33] Run nightly CI with nightly toolchain --- .ci/build_and_test.sh | 2 +- .github/workflows/linux.yml | 2 +- .github/workflows/mac.yml | 2 +- Makefile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.ci/build_and_test.sh b/.ci/build_and_test.sh index 8277240..656b7d3 100755 --- a/.ci/build_and_test.sh +++ b/.ci/build_and_test.sh @@ -15,4 +15,4 @@ if [ "$CFG_RELEASE_CHANNEL" == "nightly" ]; then else cargo build --locked fi -cargo test \ No newline at end of file +cargo test diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 78b333d..0591fd3 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: - name: install rustup run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh - sh rustup-init.sh -y --default-toolchain none + sh rustup-init.sh -y --default-toolchain ${{ matrix.cfg_release_channel }} rustup target add ${{ matrix.target }} - name: Build and Test diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 49918f7..5a3fb27 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -27,7 +27,7 @@ jobs: - name: install rustup run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh - sh rustup-init.sh -y --default-toolchain none + sh rustup-init.sh -y --default-toolchain ${{ matrix.cfg_release_channel }} rustup target add ${{ matrix.target }} - name: Build and Test diff --git a/Makefile b/Makefile index 38f0a23..6b94b6d 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ test: .PHONY: bench bench: - rustup run nightly cargo bench -F nightly + cargo +nightly bench -F nightly .PHONY: fmt fmt: From e574ac50b84722d3d4aee8748381cd9437d94c56 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Mon, 4 Nov 2024 11:22:37 -0300 Subject: [PATCH 25/33] Remove debug println --- main/src/project.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main/src/project.rs b/main/src/project.rs index 45cfa65..4df27d1 100644 --- a/main/src/project.rs +++ b/main/src/project.rs @@ -264,10 +264,6 @@ pub fn hash_files( cfg: BuildConfig, ) -> Result<[u8; 32]> { let mut keccak = Keccak::v256(); - println!( - "> {}", - String::from_utf8(cargo_version_output.into()).unwrap() - ); keccak.update(cargo_version_output); if cfg.opt_level == OptLevel::Z { keccak.update(&[0]); From 6bcb17c5eb6b8bdd374966fbbd647622e5d7f834 Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Mon, 4 Nov 2024 11:57:58 -0700 Subject: [PATCH 26/33] Use `.dylib` for macos --- main/src/main.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/main/src/main.rs b/main/src/main.rs index 578e546..59c4b9b 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -613,8 +613,8 @@ async fn simulate(args: SimulateArgs) -> Result<()> { } async fn replay(args: ReplayArgs) -> Result<()> { + let macos = cfg!(target_os = "macos"); if !args.child { - let macos = cfg!(target_os = "macos"); let gdb_args = [ "--quiet", "-ex=set breakpoint pending on", @@ -667,8 +667,9 @@ async fn replay(args: ReplayArgs) -> Result<()> { let provider = sys::new_provider(&args.trace.endpoint)?; let trace = Trace::new(provider, args.trace.tx, args.trace.use_native_tracer).await?; - build_so(&args.trace.project)?; - let so = find_so(&args.trace.project)?; + build_shared_library(&args.trace.project)?; + let library_extension = if macos { ".dylib" } else { ".so" }; + let shared_library = find_shared_library(&args.trace.project, library_extension)?; // TODO: don't assume the contract is top-level let args_len = trace.tx.input.len(); @@ -677,7 +678,7 @@ async fn replay(args: ReplayArgs) -> Result<()> { *hostio::FRAME.lock() = Some(trace.reader()); type Entrypoint = unsafe extern "C" fn(usize) -> usize; - let lib = libloading::Library::new(so)?; + let lib = libloading::Library::new(shared_library)?; let main: libloading::Symbol = lib.get(b"user_entrypoint")?; match main(args_len) { @@ -689,7 +690,7 @@ async fn replay(args: ReplayArgs) -> Result<()> { Ok(()) } -pub fn build_so(path: &Path) -> Result<()> { +pub fn build_shared_library(path: &Path) -> Result<()> { let mut cargo = sys::new_command("cargo"); cargo @@ -703,7 +704,7 @@ pub fn build_so(path: &Path) -> Result<()> { Ok(()) } -pub fn find_so(project: &Path) -> Result { +pub fn find_shared_library(project: &Path, extension: &str) -> Result { let triple = rustc_host::from_cli()?; let so_dir = project.join(format!("target/{triple}/debug/")); let so_dir = std::fs::read_dir(&so_dir) @@ -719,7 +720,7 @@ pub fn find_so(project: &Path) -> Result { }; let ext = ext.to_string_lossy(); - if ext.contains(".so") { + if ext.contains(extension) { if let Some(other) = file { let other = other.file_name().unwrap().to_string_lossy(); bail!("more than one .so found: {ext} and {other}",); From fccba8bf691c307523117e71109f7e7ae7ed122a Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 6 Nov 2024 15:52:59 -0300 Subject: [PATCH 27/33] Add deploy option to disable activate This is useful when we want to run the cargo stylus activate command separately. --- main/src/deploy.rs | 7 +++++++ main/src/main.rs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/main/src/deploy.rs b/main/src/deploy.rs index c6d1887..994fefa 100644 --- a/main/src/deploy.rs +++ b/main/src/deploy.rs @@ -91,6 +91,13 @@ pub async fn deploy(cfg: DeployConfig) -> Result<()> { return Ok(()); } + if cfg.no_activate { + mintln!( + r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: +cargo stylus activate --address {}"#, hex::encode(contract_addr)); + return Ok(()); + } + match contract { ContractCheck::Ready { .. } => { cfg.activate(sender, contract_addr, data_fee, &client) diff --git a/main/src/main.rs b/main/src/main.rs index 59c4b9b..8e9bc11 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -225,6 +225,9 @@ struct DeployConfig { /// If not set, uses the default version of the local cargo stylus binary. #[arg(long)] cargo_stylus_version: Option, + /// If set, do not activate the program after deploying it + #[arg(long)] + no_activate: bool, } #[derive(Args, Clone, Debug)] From a01ae4d6f6a712e1fe4203836dba9e0d0833b9d8 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 6 Nov 2024 15:59:42 -0300 Subject: [PATCH 28/33] Remove run macro to make code more idiomatic --- main/src/deploy.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/main/src/deploy.rs b/main/src/deploy.rs index 994fefa..fa25430 100644 --- a/main/src/deploy.rs +++ b/main/src/deploy.rs @@ -39,20 +39,13 @@ pub type SignerClient = SignerMiddleware, Wallet>; /// Deploys a stylus contract, activating if needed. pub async fn deploy(cfg: DeployConfig) -> Result<()> { - macro_rules! run { - ($expr:expr) => { - $expr.await? - }; - ($expr:expr, $($msg:expr),+) => { - $expr.await.wrap_err_with(|| eyre!($($msg),+))? - }; - } - - let contract = run!(check::check(&cfg.check_config), "cargo stylus check failed"); + let contract = check::check(&cfg.check_config) + .await + .expect("cargo stylus check failed"); let verbose = cfg.check_config.common_cfg.verbose; let client = sys::new_provider(&cfg.check_config.common_cfg.endpoint)?; - let chain_id = run!(client.get_chainid(), "failed to get chain id"); + let chain_id = client.get_chainid().await.expect("failed to get chain id"); let wallet = cfg.auth.wallet().wrap_err("failed to load wallet")?; let wallet = wallet.with_chain_id(chain_id.as_u64()); @@ -67,7 +60,10 @@ pub async fn deploy(cfg: DeployConfig) -> Result<()> { if let ContractCheck::Ready { .. } = &contract { // check balance early - let balance = run!(client.get_balance(sender, None), "failed to get balance"); + let balance = client + .get_balance(sender, None) + .await + .expect("failed to get balance"); let balance = alloy_ethers_typecast::ethers_u256_to_alloy(balance); if balance < data_fee && !cfg.estimate_gas { @@ -94,7 +90,9 @@ pub async fn deploy(cfg: DeployConfig) -> Result<()> { if cfg.no_activate { mintln!( r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: -cargo stylus activate --address {}"#, hex::encode(contract_addr)); +cargo stylus activate --address {}"#, + hex::encode(contract_addr) + ); return Ok(()); } From b4bd15a489db1d2e6f1b5a5e0a0a121a9773cdc0 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Thu, 7 Nov 2024 11:52:26 -0300 Subject: [PATCH 29/33] Unify the activation fee bump Now the deploy and activate commands have the same option to set the activation fee bump value. --- main/src/activate.rs | 32 +++++++------------------------- main/src/check.rs | 44 +++++++++++++++++++++++++++++--------------- main/src/deploy.rs | 21 ++++++++++----------- main/src/main.rs | 14 +++++++++++--- main/src/verify.rs | 5 ++++- 5 files changed, 61 insertions(+), 55 deletions(-) diff --git a/main/src/activate.rs b/main/src/activate.rs index 8526d8f..ccb59e5 100644 --- a/main/src/activate.rs +++ b/main/src/activate.rs @@ -1,24 +1,22 @@ // Copyright 2023-2024, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/stylus/licenses/COPYRIGHT.md +use crate::check::check_activate; +use crate::constants::ARB_WASM_H160; +use crate::macros::greyln; use crate::util::color::{Color, DebugColor}; use crate::util::sys; +use crate::ActivateConfig; use alloy_primitives::Address; use alloy_sol_macro::sol; use alloy_sol_types::SolCall; use ethers::middleware::{Middleware, SignerMiddleware}; use ethers::signers::Signer; use ethers::types::transaction::eip2718::TypedTransaction; -use ethers::types::{Eip1559TransactionRequest, U256}; +use ethers::types::Eip1559TransactionRequest; use ethers::utils::format_units; use eyre::{bail, Context, Result}; -use crate::check::check_activate; -use crate::constants::ARB_WASM_H160; -use crate::macros::greyln; - -use crate::ActivateConfig; - sol! { interface ArbWasm { function activateProgram(address program) @@ -41,25 +39,14 @@ pub async fn activate_contract(cfg: &ActivateConfig) -> Result<()> { let client = SignerMiddleware::new(provider.clone(), wallet); let code = client.get_code(cfg.address, None).await?; - let data_fee = check_activate(code, cfg.address, &provider).await?; - let mut data_fee = alloy_ethers_typecast::alloy_u256_to_ethers(data_fee); - - greyln!( - "obtained estimated activation data fee {}", - format_units(data_fee, "ether")?.debug_lavender() - ); - greyln!( - "bumping estimated activation data fee by {}%", - cfg.data_fee_bump_percent.debug_lavender() - ); - data_fee = bump_data_fee(data_fee, cfg.data_fee_bump_percent); + let data_fee = check_activate(code, cfg.address, &cfg.data_fee, &provider).await?; let contract: Address = cfg.address.to_fixed_bytes().into(); let data = ArbWasm::activateProgramCall { program: contract }.abi_encode(); let tx = Eip1559TransactionRequest::new() .from(client.address()) .to(*ARB_WASM_H160) - .value(data_fee) + .value(alloy_ethers_typecast::alloy_u256_to_ethers(data_fee)) .data(data); let tx = TypedTransaction::Eip1559(tx); if cfg.estimate_gas { @@ -96,8 +83,3 @@ pub async fn activate_contract(cfg: &ActivateConfig) -> Result<()> { } Ok(()) } - -fn bump_data_fee(fee: U256, pct: u64) -> U256 { - let num = 100 + pct; - fee * U256::from(num) / U256::from(100) -} diff --git a/main/src/check.rs b/main/src/check.rs index 5c31ea6..767153f 100644 --- a/main/src/check.rs +++ b/main/src/check.rs @@ -1,13 +1,13 @@ // Copyright 2023-2024, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md -use crate::util::{color::Color, sys, text}; use crate::{ check::ArbWasm::ArbWasmErrors, constants::{ARB_WASM_H160, ONE_ETH, TOOLCHAIN_FILE_NAME}, macros::*, project::{self, extract_toolchain_channel, BuildConfig}, - CheckConfig, + util::{color::Color, sys, text}, + CheckConfig, DataFeeOpts, }; use alloy_primitives::{Address, B256, U256}; use alloy_sol_macro::sol; @@ -87,9 +87,7 @@ pub async fn check(cfg: &CheckConfig) -> Result { } let address = cfg.contract_address.unwrap_or(H160::random()); - let fee = check_activate(code.clone().into(), address, &provider).await?; - let visual_fee = format_data_fee(fee).unwrap_or("???".red()); - greyln!("wasm data fee: {visual_fee} ETH"); + let fee = check_activate(code.clone().into(), address, &cfg.data_fee, &provider).await?; Ok(ContractCheck::Ready { code, fee }) } @@ -112,7 +110,7 @@ impl ContractCheck { pub fn suggest_fee(&self) -> U256 { match self { Self::Active { .. } => U256::default(), - Self::Ready { fee, .. } => fee * U256::from(120) / U256::from(100), + Self::Ready { fee, .. } => *fee, } } } @@ -148,17 +146,19 @@ pub fn format_file_size(len: usize, mid: u64, max: u64) -> String { } /// Pretty-prints a data fee. -fn format_data_fee(fee: U256) -> Result { - let fee: u64 = (fee / U256::from(1e9)).try_into()?; +fn format_data_fee(fee: U256) -> String { + let Ok(fee): Result = (fee / U256::from(1e9)).try_into() else { + return ("???").red(); + }; let fee: f64 = fee as f64 / 1e9; - let text = format!("{fee:.6}"); - Ok(if fee <= 5e14 { + let text = format!("{fee:.6} ETH"); + if fee <= 5e14 { text.mint() } else if fee <= 5e15 { text.yellow() } else { text.pink() - }) + } } pub struct EthCallError { @@ -247,7 +247,12 @@ Perhaps the Arbitrum node for the endpoint you are connecting to has not yet upg } /// Checks contract activation, returning the data fee. -pub async fn check_activate(code: Bytes, address: H160, provider: &Provider) -> Result { +pub async fn check_activate( + code: Bytes, + address: H160, + opts: &DataFeeOpts, + provider: &Provider, +) -> Result { let contract = Address::from(address.to_fixed_bytes()); let data = ArbWasm::activateProgramCall { program: contract }.abi_encode(); let tx = Eip1559TransactionRequest::new() @@ -256,8 +261,17 @@ pub async fn check_activate(code: Bytes, address: H160, provider: &Provider Result<()> { return Ok(()); } - if cfg.no_activate { - mintln!( - r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: -cargo stylus activate --address {}"#, - hex::encode(contract_addr) - ); - return Ok(()); - } - match contract { ContractCheck::Ready { .. } => { - cfg.activate(sender, contract_addr, data_fee, &client) - .await? + if cfg.no_activate { + mintln!( + r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: +cargo stylus activate --address {}"#, + hex::encode(contract_addr) + ); + } else { + cfg.activate(sender, contract_addr, data_fee, &client) + .await? + } } ContractCheck::Active { .. } => greyln!("wasm already activated!"), } diff --git a/main/src/main.rs b/main/src/main.rs index 8e9bc11..846ce97 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -181,15 +181,14 @@ pub struct CacheSuggestionsConfig { pub struct ActivateConfig { #[command(flatten)] common_cfg: CommonConfig, + #[command(flatten)] + data_fee: DataFeeOpts, /// Wallet source to use. #[command(flatten)] auth: AuthOpts, /// Deployed Stylus contract address to activate. #[arg(long)] address: H160, - /// Percent to bump the estimated activation data fee by. Default of 20% - #[arg(long, default_value = "20")] - data_fee_bump_percent: u64, /// Whether or not to just estimate gas without sending a tx. #[arg(long)] estimate_gas: bool, @@ -199,6 +198,8 @@ pub struct ActivateConfig { pub struct CheckConfig { #[command(flatten)] common_cfg: CommonConfig, + #[command(flatten)] + data_fee: DataFeeOpts, /// The WASM to check (defaults to any found in the current directory). #[arg(long)] wasm_file: Option, @@ -314,6 +315,13 @@ pub struct SimulateArgs { use_native_tracer: bool, } +#[derive(Clone, Debug, Args)] +struct DataFeeOpts { + /// Percent to bump the estimated activation data fee by. + #[arg(long, default_value = "20")] + data_fee_bump_percent: u64, +} + #[derive(Clone, Debug, Args)] #[clap(group(ArgGroup::new("key").required(true).args(&["private_key_path", "private_key", "keystore_path"])))] struct AuthOpts { diff --git a/main/src/verify.rs b/main/src/verify.rs index c65223b..3ba5f68 100644 --- a/main/src/verify.rs +++ b/main/src/verify.rs @@ -18,7 +18,7 @@ use crate::{ constants::TOOLCHAIN_FILE_NAME, deploy::{self, extract_compressed_wasm, extract_contract_evm_deployment_prelude}, project::{self, extract_toolchain_channel}, - CheckConfig, VerifyConfig, + CheckConfig, DataFeeOpts, VerifyConfig, }; #[derive(Debug, Deserialize, Serialize)] @@ -52,6 +52,9 @@ pub async fn verify(cfg: VerifyConfig) -> eyre::Result<()> { } let check_cfg = CheckConfig { common_cfg: cfg.common_cfg.clone(), + data_fee: DataFeeOpts { + data_fee_bump_percent: 20, + }, wasm_file: None, contract_address: None, }; From 4c9472ce51b9d922497c2bfb05614bcbb260e1a8 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 8 Nov 2024 11:41:44 -0300 Subject: [PATCH 30/33] Unify the code for new and init commands * The new command creates the project directory and calls the init command. * The init code removes the git origin from the project directory. --- main/src/new.rs | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/main/src/new.rs b/main/src/new.rs index a9ecc2b..6a1fdf8 100644 --- a/main/src/new.rs +++ b/main/src/new.rs @@ -7,31 +7,18 @@ use crate::util::{ sys, }; use eyre::{bail, Context, Result}; -use std::{env::current_dir, path::Path}; +use std::{env, fs, path::Path}; -/// Creates a new Stylus project in the current directory -pub fn new(name: &Path, minimal: bool) -> Result<()> { - let repo = match minimal { - true => GITHUB_TEMPLATE_REPO_MINIMAL, - false => GITHUB_TEMPLATE_REPO, - }; - let output = sys::new_command("git") - .arg("clone") - .arg(repo) - .arg(name) - .output() - .wrap_err("git clone failed")?; - - if !output.status.success() { - bail!("git clone command failed"); - } - let path = current_dir().wrap_err("no current dir")?.join(name); - println!("{GREY}new project at: {}", path.to_string_lossy().mint()); - Ok(()) +/// Creates a new directory given the path and then initialize a stylus project. +pub fn new(path: &Path, minimal: bool) -> Result<()> { + fs::create_dir_all(path).wrap_err("failed to create project dir")?; + env::set_current_dir(path).wrap_err("failed to set project dir")?; + init(minimal) } +/// Creates a new Stylus project in the current directory. pub fn init(minimal: bool) -> Result<()> { - let current_dir = current_dir().wrap_err("no current dir")?; + let current_dir = env::current_dir().wrap_err("no current dir")?; let repo = if minimal { GITHUB_TEMPLATE_REPO_MINIMAL } else { @@ -51,6 +38,17 @@ pub fn init(minimal: bool) -> Result<()> { bail!("git clone command failed"); } + let output = sys::new_command("git") + .arg("remote") + .arg("remove") + .arg("origin") + .output() + .wrap_err("git remote remove failed")?; + + if !output.status.success() { + bail!("git remote remove command failed"); + } + println!( "{GREY}initialized project in: {}", current_dir.to_string_lossy().mint() From 22d944c34e51b75334c239a2cac0da26cba24061 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 8 Nov 2024 11:55:16 -0300 Subject: [PATCH 31/33] Improve activation-fee log message --- main/src/check.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/main/src/check.rs b/main/src/check.rs index 767153f..9c171a2 100644 --- a/main/src/check.rs +++ b/main/src/check.rs @@ -6,7 +6,10 @@ use crate::{ constants::{ARB_WASM_H160, ONE_ETH, TOOLCHAIN_FILE_NAME}, macros::*, project::{self, extract_toolchain_channel, BuildConfig}, - util::{color::Color, sys, text}, + util::{ + color::{Color, GREY, LAVENDER}, + sys, text, + }, CheckConfig, DataFeeOpts, }; use alloy_primitives::{Address, B256, U256}; @@ -267,11 +270,11 @@ pub async fn check_activate( let bump = opts.data_fee_bump_percent; let adjusted_data_fee = data_fee * U256::from(100 + bump) / U256::from(100); - print!("{}", "wasm data fee: ".grey()); - print!("{}", format_data_fee(adjusted_data_fee)); - print!("{}", " (originally ".grey()); - print!("{}", format_data_fee(data_fee)); - greyln!(" with {bump}% bump)"); + greyln!( + "wasm data fee: {} {GREY}(originally {}{GREY} with {LAVENDER}{bump}%{GREY} bump)", + format_data_fee(adjusted_data_fee), + format_data_fee(data_fee) + ); Ok(adjusted_data_fee) } From 2ace743fdf118fafc289514d26c28762792082d3 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Thu, 21 Nov 2024 18:32:06 -0300 Subject: [PATCH 32/33] Fix docker tests in CI --- .ci/build_and_test.sh | 12 ++++++------ main/Cargo.toml | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.ci/build_and_test.sh b/.ci/build_and_test.sh index 67f2378..404589f 100755 --- a/.ci/build_and_test.sh +++ b/.ci/build_and_test.sh @@ -16,10 +16,10 @@ else cargo build --locked fi -UNAME=$(uname -s) -if [ "$UNAME" == "Darwin" ]; then - # Disable docker tests on MacOS CI - cargo test --no-default-features -else - cargo test +cargo test + +if [ "$(uname -s)" != "Darwin" ]; then + # The MacOS CI doesn't support Docker because of licensing issues, so only run them on Linux. + # Also, run the docker tests on a single thread to avoid concurrency issues. + cargo test -F docker-test -- --test-threads 1 docker fi diff --git a/main/Cargo.toml b/main/Cargo.toml index d146957..d09fc9b 100644 --- a/main/Cargo.toml +++ b/main/Cargo.toml @@ -11,7 +11,6 @@ version.workspace = true repository.workspace = true [features] -default = ["docker-test"] docker-test = [] nightly = [] From 7e7db659eced940eb99a6ec5d1777f381692437e Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 22 Nov 2024 16:05:24 -0300 Subject: [PATCH 33/33] Add cargo stylus version to dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a3a8515..7143a4e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ ARG BUILD_PLATFORM=linux/amd64 ARG RUST_VERSION=1.80 -ARG CARGO_STYLUS_VERSION +ARG CARGO_STYLUS_VERSION=0.5.6 FROM --platform=${BUILD_PLATFORM} rust:${RUST_VERSION} AS builder RUN apt-get update && apt-get install -y git