Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Miscellaneous Issue Resolution #70

Merged
merged 15 commits into from
Aug 15, 2024
19 changes: 1 addition & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,24 +156,7 @@ See `--help` for all available flags and default values.

## Verifying Stylus Contracts

**cargo stylus verify**

Verifies that a deployed smart contract is identical to that produced by the
current project. Since Stylus smart contracts include a hash of all project
files, this additionally verifies that code comments and other files are
identical. To ensure build reproducibility, if a contract is to be verified,
it should be both deployed and verified using `cargo stylus reproducible`.

See `--help` for all available flags and default values.

## Reproducibly Deploying and Verifying

**cargo stylus reproducible**

Runs a `cargo stylus` command in a Docker container to ensure build
reproducibility.

See `--help` for all available flags and default values.
See [here](https://hackmd.io/bpeMnrzbSvO4mohhvkrKqw)

## Deploying Non-Rust WASM Projects

Expand Down
7 changes: 0 additions & 7 deletions check/Dockerfile

This file was deleted.

4 changes: 4 additions & 0 deletions check/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sol! {
error AlreadyCached(bytes32 codehash);
error BidTooSmall(uint192 bid, uint192 min);
error BidsArePaused();
error ProgramNotActivated();
}
}

Expand Down Expand Up @@ -88,6 +89,9 @@ pub async fn cache_contract(cfg: &CacheConfig) -> Result<()> {
C::BidTooSmall(_) => {
bail!("Bid amount {} (wei) too small", cfg.bid.unwrap_or_default())
}
C::ProgramNotActivated(_) => {
bail!("Your Stylus contract {} is not yet activated. To activate it, use the `cargo stylus activate` subcommand", hex::encode(contract))
}
}
}
let verbose = cfg.common_cfg.verbose;
Expand Down
6 changes: 6 additions & 0 deletions check/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ async fn contract_exists(codehash: B256, provider: &Provider<Http>) -> Result<bo

let program_version = match outs {
Ok(outs) => {
if outs.is_empty() {
bail!(
r#"No data returned from the ArbWasm precompile when checking if your Stylus contract exists.
Perhaps the Arbitrum node for the endpoint you are connecting to has not yet upgraded to Stylus"#
);
}
let ArbWasm::codehashVersionReturn { version } =
ArbWasm::codehashVersionCall::abi_decode_returns(&outs, true)?;
version
Expand Down
5 changes: 0 additions & 5 deletions check/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,3 @@ pub const PROJECT_HASH_SECTION_NAME: &str = "project_hash";

/// Name of the toolchain file used to specify the Rust toolchain version for a project.
pub const TOOLCHAIN_FILE_NAME: &str = "rust-toolchain.toml";

/// Base Rust image version to be used for reproducible builds. This simply installs cargo and the Rust
/// compiler, but the user will specify the exact version of the Rust toolchain to use for building within
/// the docker container.
pub const RUST_BASE_IMAGE_VERSION: &str = "1.79.0";
2 changes: 1 addition & 1 deletion check/src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ impl DeployConfig {
let tx_hash = receipt.transaction_hash.debug_lavender();
greyln!("deployment tx hash: {tx_hash}");
println!(
r#"we recommend running cargo stylus cache --address={} to cache your activated contract in ArbOS.
r#"INFO: Your program is not yet part of the Stylus contract cache. We recommend running `cargo stylus cache --address={}` to cache your activated contract in ArbOS.
Cached contracts benefit from cheaper calls. To read more about the Stylus contract cache, see
https://docs.arbitrum.io/stylus/concepts/stylus-cache-manager"#,
hex::encode(contract)
Expand Down
25 changes: 6 additions & 19 deletions check/src/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::process::{Command, Stdio};
use cargo_stylus_util::color::Color;
use eyre::{bail, eyre, Result};

use crate::constants::{RUST_BASE_IMAGE_VERSION, TOOLCHAIN_FILE_NAME};
use crate::constants::TOOLCHAIN_FILE_NAME;
use crate::macros::greyln;
use crate::project::extract_toolchain_channel;

Expand Down Expand Up @@ -45,15 +45,7 @@ fn create_image(version: &str) -> Result<()> {
if image_exists(&name)? {
return Ok(());
}
let cargo_stylus_version = env!("CARGO_PKG_VERSION");
let cargo_stylus_version: String = cargo_stylus_version
.chars()
.filter(|c| c.is_alphanumeric() || *c == '-' || *c == '.')
.collect();
println!(
"Building Docker image for cargo-stylus version {} and Rust toolchain {}",
cargo_stylus_version, version,
);
println!("Building Docker image for Rust toolchain {}", version,);
let mut child = Command::new("docker")
.arg("build")
.arg("-t")
Expand All @@ -62,24 +54,19 @@ fn create_image(version: &str) -> Result<()> {
.arg("-f-")
.stdin(Stdio::piped())
.spawn()
.map_err(|e| eyre!("failed to execure Docker command: {e}"))?;
.map_err(|e| eyre!("failed to execute Docker command: {e}"))?;
write!(
child.stdin.as_mut().unwrap(),
"\
FROM --platform=linux/amd64 rust:{} as builder\n\
FROM --platform=linux/amd64 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
RUN rustup target add wasm32-wasi
RUN rustup target add x86_64-unknown-linux-gnu
RUN cargo install cargo-stylus-check --version {} --force
RUN cargo install cargo-stylus --version {} --force
RUN rustup component add rust-src --toolchain {}-x86_64-unknown-linux-gnu
",
RUST_BASE_IMAGE_VERSION,
version,
version,
cargo_stylus_version,
cargo_stylus_version,
version,
)?;
child.wait().map_err(|e| eyre!("wait failed: {e}"))?;
Ok(())
Expand Down
58 changes: 29 additions & 29 deletions check/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ struct Opts {

#[derive(Parser, Debug, Clone)]
enum Apis {
/// Create a new Rust project.
/// Create a new Stylus project.
New {
/// Project name.
name: PathBuf,
/// Create a minimal contract.
#[arg(long)]
minimal: bool,
},
/// Initializes a Stylus project in the current directory.
Init {
/// Create a minimal contract.
#[arg(long)]
minimal: bool,
},
/// Export a Solidity ABI.
ExportAbi {
/// The output file (defaults to stdout).
Expand Down Expand Up @@ -127,10 +133,6 @@ pub struct CheckConfig {
/// Where to deploy and activate the contract (defaults to a random address).
#[arg(long)]
contract_address: Option<H160>,
/// If specified, will not run the command in a reproducible docker container. Useful for local
/// builds, but at the risk of not having a reproducible contract for verification purposes.
#[arg(long)]
no_verify: bool,
}

#[derive(Args, Clone, Debug)]
Expand All @@ -143,6 +145,10 @@ struct DeployConfig {
/// Only perform gas estimation.
#[arg(long)]
estimate_gas: bool,
/// If specified, will not run the command in a reproducible docker container. Useful for local
/// builds, but at the risk of not having a reproducible contract for verification purposes.
#[arg(long)]
no_verify: bool,
}

#[derive(Args, Clone, Debug)]
Expand Down Expand Up @@ -206,7 +212,7 @@ impl fmt::Display for CheckConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{} {} {} {}",
"{} {} {}",
self.common_cfg,
match &self.wasm_file {
Some(path) => format!("--wasm-file={}", path.display()),
Expand All @@ -216,10 +222,6 @@ impl fmt::Display for CheckConfig {
Some(addr) => format!("--contract-address={:?}", addr),
None => "".to_string(),
},
match self.no_verify {
true => "--no-verify".to_string(),
false => "".to_string(),
},
)
}
}
Expand All @@ -228,13 +230,17 @@ impl fmt::Display for DeployConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{} {} {}",
"{} {} {} {}",
self.check_config,
self.auth,
match self.estimate_gas {
true => "--estimate-gas".to_string(),
false => "".to_string(),
},
match self.no_verify {
true => "--no-verify".to_string(),
false => "".to_string(),
},
)
}
}
Expand Down Expand Up @@ -296,6 +302,9 @@ async fn main_impl(args: Opts) -> Result<()> {
Apis::New { name, minimal } => {
run!(new::new(&name, minimal), "failed to open new project");
}
Apis::Init { minimal } => {
run!(new::init(minimal), "failed to initialize project");
}
Apis::ExportAbi { json, output } => {
run!(export_abi::export_abi(output, json), "failed to export abi");
}
Expand All @@ -309,28 +318,16 @@ async fn main_impl(args: Opts) -> Result<()> {
run!(cache::cache_contract(&config).await, "stylus cache failed");
}
Apis::Check(config) => {
if config.no_verify {
run!(check::check(&config).await, "stylus checks failed");
} else {
let mut commands: Vec<String> =
vec![String::from("check"), String::from("--no-verify")];
let config_args = config
.to_string()
.split(' ')
.map(|s| s.to_string())
.filter(|s| !s.is_empty())
.collect::<Vec<String>>();
commands.extend(config_args);
run!(
docker::run_reproducible(&commands),
"failed reproducible run"
);
}
run!(check::check(&config).await, "stylus checks failed");
}
Apis::Deploy(config) => {
if config.check_config.no_verify {
if config.no_verify {
run!(deploy::deploy(config).await, "stylus deploy failed");
} else {
println!(
"Running in a Docker container for reproducibility, this may take a while",
);
println!("NOTE: You can opt out by doing --no-verify");
let mut commands: Vec<String> =
vec![String::from("deploy"), String::from("--no-verify")];
let config_args = config
Expand All @@ -350,6 +347,9 @@ async fn main_impl(args: Opts) -> Result<()> {
if config.no_verify {
run!(verify::verify(config).await, "failed to verify");
} else {
println!(
"Running in a Docker container for reproducibility, this may take a while",
);
let mut commands: Vec<String> =
vec![String::from("verify"), String::from("--no-verify")];
let config_args = config
Expand Down
28 changes: 28 additions & 0 deletions check/src/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,31 @@ pub fn new(name: &Path, minimal: bool) -> Result<()> {
println!("{GREY}new project at: {}", path.to_string_lossy().mint());
Ok(())
}

pub fn init(minimal: bool) -> Result<()> {
let current_dir = current_dir().wrap_err("no current dir")?;
let repo = if minimal {
GITHUB_TEMPLATE_REPO_MINIMAL
} else {
GITHUB_TEMPLATE_REPO
};

let output = sys::new_command("git")
.arg("clone")
.arg("--depth")
.arg("1")
.arg(repo)
.arg(".")
.output()
.wrap_err("git clone failed")?;

if !output.status.success() {
bail!("git clone command failed");
}

println!(
"{GREY}initialized project in: {}",
current_dir.to_string_lossy().mint()
);
Ok(())
}
7 changes: 0 additions & 7 deletions check/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ pub fn build_dylib(cfg: BuildConfig) -> Result<PathBuf> {

let mut cmd = sys::new_command("cargo");

if !cfg.stable {
cmd.arg("+nightly");
}

cmd.arg("build");
cmd.arg("--lib");

Expand Down Expand Up @@ -208,9 +204,6 @@ pub fn extract_toolchain_channel(toolchain_file_path: &PathBuf) -> Result<String
pub fn hash_files(source_file_patterns: Vec<String>, cfg: BuildConfig) -> Result<[u8; 32]> {
let mut keccak = Keccak::v256();
let mut cmd = Command::new("cargo");
if !cfg.stable {
cmd.arg("+nightly");
}
cmd.arg("--version");
let output = cmd
.output()
Expand Down
1 change: 0 additions & 1 deletion check/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ pub async fn verify(cfg: VerifyConfig) -> eyre::Result<()> {
common_cfg: cfg.common_cfg.clone(),
wasm_file: None,
contract_address: None,
no_verify: cfg.no_verify,
};
let _ = check::check(&check_cfg)
.await
Expand Down
8 changes: 7 additions & 1 deletion main/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ struct Opts {
#[derive(Parser, Debug, Clone)]
enum Subcommands {
#[command(alias = "n")]
/// Create a new Rust project.
/// Create a new Stylus project.
New,
#[command(alias = "i")]
/// Initializes a Stylus project in the current directory.
Init,
#[command(alias = "x")]
/// Export a Solidity ABI.
ExportAbi,
Expand Down Expand Up @@ -69,12 +72,15 @@ const COMMANDS: &[Binary] = &[
name: "cargo-stylus-check",
apis: &[
"new",
"init",
"activate",
"export-abi",
"cache",
"check",
"deploy",
"verify",
"a",
"i",
"n",
"x",
"c",
Expand Down
7 changes: 2 additions & 5 deletions replay/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ async fn replay(args: ReplayArgs) -> Result<()> {
let provider = sys::new_provider(&args.endpoint)?;
let trace = Trace::new(provider, args.tx).await?;

build_so(&args.project, args.stable_rust)?;
build_so(&args.project)?;
let so = find_so(&args.project)?;

// TODO: don't assume the contract is top-level
Expand All @@ -169,12 +169,9 @@ async fn replay(args: ReplayArgs) -> Result<()> {
Ok(())
}

pub fn build_so(path: &Path, stable: bool) -> Result<()> {
pub fn build_so(path: &Path) -> Result<()> {
let mut cargo = sys::new_command("cargo");

if !stable {
cargo.arg("+nightly");
}
cargo
.current_dir(path)
.arg("build")
Expand Down
Loading