Skip to content

Commit

Permalink
Merge pull request #16 from OffchainLabs/dylibs
Browse files Browse the repository at this point in the history
Switch to cdylibs
  • Loading branch information
rauljordan authored Oct 2, 2023
2 parents 088a5c7 + 5f953fa commit ae43fa8
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 160 deletions.
4 changes: 2 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ Please provide a summary of the changes and any backward incompatibilities.
- [ ] I have read the [DCO][DCO] and ensured that these changes comply.
- [ ] I assign this work under its [open source licensing][terms].

[DCO]: licenses/DCO.txt
[terms]: licenses/COPYRIGHT.md
[DCO]: https://github.com/OffchainLabs/cargo-stylus/licenses/DCO.txt
[terms]: https://github.com/OffchainLabs/cargo-stylus/licenses/COPYRIGHT.md
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cargo-stylus"
version = "0.1.8"
version = "0.2.0"
edition = "2021"
authors = ["Offchain Labs"]
license = "MIT OR Apache-2.0"
Expand Down
11 changes: 5 additions & 6 deletions src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl std::fmt::Display for FileByteSize {
pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<bool> {
let wasm_file_path: PathBuf = match &cfg.wasm_file_path {
Some(path) => PathBuf::from_str(path).unwrap(),
None => project::build_project_to_wasm(BuildConfig {
None => project::build_project_dylib(BuildConfig {
opt_level: project::OptLevel::default(),
nightly: cfg.nightly,
rebuild: true,
Expand All @@ -72,14 +72,13 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<bool> {
};
println!("Reading WASM file at {}", wasm_file_path.display().grey());

let (precompressed_bytes, deploy_ready_code) =
project::get_compressed_wasm_bytes(&wasm_file_path)
.map_err(|e| eyre!("failed to get compressed WASM bytes: {e}"))?;
let (precompressed_bytes, init_code) = project::compress_wasm(&wasm_file_path)
.map_err(|e| eyre!("failed to get compressed WASM bytes: {e}"))?;

let precompressed_size = FileByteSize::new(precompressed_bytes.len() as u64);
println!("Uncompressed WASM size: {precompressed_size}");

let compressed_size = FileByteSize::new(deploy_ready_code.len() as u64);
let compressed_size = FileByteSize::new(init_code.len() as u64);
println!("Compressed WASM size to be deployed onchain: {compressed_size}",);

println!(
Expand Down Expand Up @@ -110,7 +109,7 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<bool> {

expected_program_addr = get_contract_address(wallet.address(), nonce);
}
check_can_activate(provider, &expected_program_addr, deploy_ready_code).await
check_can_activate(provider, &expected_program_addr, init_code).await
}

/// Checks if a program can be successfully activated onchain before it is deployed
Expand Down
8 changes: 6 additions & 2 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ pub const ARBWASM_ACTIVATE_METHOD_HASH: &str = "58c780c2";
/// Target for compiled WASM folder in a Rust project
pub const RUST_TARGET: &str = "wasm32-unknown-unknown";

/// URL of the template repository to start a boilerplate Stylus project in Rust.
pub const GITHUB_TEMPLATE_REPOSITORY: &str = "https://github.com/OffchainLabs/stylus-hello-world";
/// The default repo to clone when creating new projects
pub const GITHUB_TEMPLATE_REPO: &str = "https://github.com/OffchainLabs/stylus-hello-world";

/// The minimal entrypoint repo
pub const GITHUB_TEMPLATE_REPO_MINIMAL: &str =
"https://github.com/OffchainLabs/stylus-hello-world-minimal";

/// The error returned from a call onchain if a program is up to date and requires no activation.
pub const PROGRAM_UP_TO_DATE_ERR: &str = "ProgramUpToDate";
8 changes: 4 additions & 4 deletions src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ on the --mode flag under cargo stylus deploy --help"#
}
};

// Whether or not to send the transactions to the endpoint.
// Whether to send the transactions to the endpoint.
let dry_run = cfg.tx_sending_opts.dry_run;

// If we are attempting to send a real transaction, we check if the deployer has any funds
Expand Down Expand Up @@ -113,21 +113,21 @@ programs to Stylus chains here https://docs.arbitrum.io/stylus/stylus-quickstart
if deploy {
let wasm_file_path: PathBuf = match &cfg.check_cfg.wasm_file_path {
Some(path) => PathBuf::from_str(path).unwrap(),
None => project::build_project_to_wasm(BuildConfig {
None => project::build_project_dylib(BuildConfig {
opt_level: project::OptLevel::default(),
nightly: cfg.check_cfg.nightly,
rebuild: false, // The check step at the start of this command rebuilt.
})
.map_err(|e| eyre!("could not build project to WASM: {e}"))?,
};
let (_, deploy_ready_code) = project::get_compressed_wasm_bytes(&wasm_file_path)?;
let (_, init_code) = project::compress_wasm(&wasm_file_path)?;
println!("");
println!("{}", "====DEPLOYMENT====".grey());
println!(
"Deploying program to address {}",
to_checksum(&expected_program_addr, None).mint(),
);
let deployment_calldata = program_deployment_calldata(&deploy_ready_code);
let deployment_calldata = program_deployment_calldata(&init_code);

// Output the tx data to a user's specified directory if desired.
if let Some(tx_data_output_dir) = output_dir {
Expand Down
134 changes: 13 additions & 121 deletions src/new.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,28 @@
// Copyright 2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md
use eyre::{bail, eyre};
use std::{
env::current_dir,
io::Write,
path::PathBuf,
process::{Command, Stdio},
};
use std::{env::current_dir, path::PathBuf};

use crate::{color::Color, constants::GITHUB_TEMPLATE_REPOSITORY};
use crate::{
color::Color,
constants::{GITHUB_TEMPLATE_REPO, GITHUB_TEMPLATE_REPO_MINIMAL},
util,
};

/// Initializes a new Stylus project in the current directory by git cloning
/// the stylus-hello-world template repository and renaming
/// it to the user's choosing.
/// Creates a new Stylus project in the current directory
pub fn new_stylus_project(name: &str, minimal: bool) -> eyre::Result<()> {
if name.is_empty() {
bail!("cannot have an empty project name");
}
let cwd: PathBuf = current_dir().map_err(|e| eyre!("could not get current dir: {e}"))?;
if minimal {
let output = Command::new("cargo")
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.arg("new")
.arg("--bin")
.arg(name)
.output()
.map_err(|e| eyre!("failed to execute cargo new: {e}"))?;
if !output.status.success() {
bail!("cargo new command failed");
}

let cargo_config_dir_path = cwd.join(name).join(".cargo");
std::fs::create_dir_all(&cargo_config_dir_path)
.map_err(|e| eyre!("could not create .cargo folder in new project: {e}"))?;
let cargo_config_path = cargo_config_dir_path.join("config");
let mut f = std::fs::OpenOptions::new()
.create(true)
.write(true)
.open(cargo_config_path)
.map_err(|e| eyre!("could not open config file: {e}"))?;
f.write_all(cargo_config().as_bytes())
.map_err(|e| eyre!("could not write to file: {e}"))?;
f.flush()
.map_err(|e| eyre!("could not write to file: {e}"))?;

let main_path = cwd.join(name).join("src").join("main.rs");

// Overwrite the default main.rs file with the Stylus entrypoint.
let mut f = std::fs::OpenOptions::new()
.write(true)
.open(main_path)
.map_err(|e| eyre!("could not open main.rs file: {e}"))?;
f.write_all(basic_entrypoint().as_bytes())
.map_err(|e| eyre!("could not write to file: {e}"))?;
f.flush()
.map_err(|e| eyre!("could not write to file: {e}"))?;

// Overwrite the default Cargo.toml file.
let cargo_path = cwd.join(name).join("Cargo.toml");
let mut f = std::fs::OpenOptions::new()
.write(true)
.open(cargo_path)
.map_err(|e| eyre!("could not open Cargo.toml file: {e}"))?;
f.write_all(minimal_cargo_toml(name).as_bytes())
.map_err(|e| eyre!("could not write to file: {e}"))?;
f.flush()
.map_err(|e| eyre!("could not write to file: {e}"))?;
return Ok(());
}
let output = Command::new("git")
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
let repo = match minimal {
true => GITHUB_TEMPLATE_REPO_MINIMAL,
false => GITHUB_TEMPLATE_REPO,
};
let output = util::new_command("git")
.arg("clone")
.arg(GITHUB_TEMPLATE_REPOSITORY)
.arg(repo)
.arg(name)
.output()
.map_err(|e| eyre!("failed to execute git clone: {e}"))?;
Expand All @@ -88,60 +37,3 @@ pub fn new_stylus_project(name: &str, minimal: bool) -> eyre::Result<()> {
);
Ok(())
}

fn basic_entrypoint() -> &'static str {
r#"#![no_main]
#![no_std]
extern crate alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
use alloc::vec::Vec;
use stylus_sdk::stylus_proc::entrypoint;
#[entrypoint]
fn user_main(input: Vec<u8>) -> Result<Vec<u8>, Vec<u8>> {
Ok(input)
}
"#
}

fn cargo_config() -> &'static str {
r#"[build]
target = "wasm32-unknown-unknown"
[target.wasm32-unknown-unknown]
rustflags = [
"-C", "link-arg=-zstack-size=32768",
]
"#
}

fn minimal_cargo_toml(name: &str) -> String {
format!(
r#"[package]
name = "{}"
version = "0.1.0"
edition = "2021"
[dependencies]
stylus-sdk = "0.4.1"
wee_alloc = "0.4.5"
[features]
export-abi = ["stylus-sdk/export-abi"]
[profile.release]
codegen-units = 1
strip = true
lto = true
panic = "abort"
opt-level = "s"
[workspace]
"#,
name
)
}
45 changes: 23 additions & 22 deletions src/project.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
// Copyright 2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md
use std::env::current_dir;
use std::io::Read;
use std::path::PathBuf;
use std::process::{Command, Stdio};

use brotli2::read::BrotliEncoder;
use bytesize::ByteSize;
use eyre::{bail, eyre};

use crate::constants::{MAX_PRECOMPRESSED_WASM_SIZE, MAX_PROGRAM_SIZE};
use crate::util;
use crate::{
color::Color,
constants::{BROTLI_COMPRESSION_LEVEL, EOF_PREFIX, RUST_TARGET},
};
use brotli2::read::BrotliEncoder;
use bytesize::ByteSize;
use eyre::{bail, eyre, Result};
use std::{env::current_dir, io::Read, path::PathBuf};

#[derive(Default, PartialEq)]
pub enum OptLevel {
Expand Down Expand Up @@ -55,12 +52,11 @@ https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md"#)
}

/// Build a Rust project to WASM and return the path to the compiled WASM file.
pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result<PathBuf> {
pub fn build_project_dylib(cfg: BuildConfig) -> Result<PathBuf> {
let cwd: PathBuf = current_dir().map_err(|e| eyre!("could not get current dir: {e}"))?;

if cfg.rebuild {
let mut cmd = Command::new("cargo");
cmd.stdout(Stdio::inherit()).stderr(Stdio::inherit());
let mut cmd = util::new_command("cargo");

if cfg.nightly {
cmd.arg("+nightly");
Expand All @@ -69,6 +65,7 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result<PathBuf> {
}

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

if cfg.nightly {
cmd.arg("-Z");
Expand All @@ -84,7 +81,7 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result<PathBuf> {

let output = cmd
.arg("--release")
.arg(format!("--target={}", RUST_TARGET))
.arg(format!("--target={RUST_TARGET}"))
.output()
.map_err(|e| eyre!("failed to execute cargo build: {e}"))?;

Expand All @@ -93,13 +90,17 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result<PathBuf> {
}
}

let release_path = cwd.join("target").join(RUST_TARGET).join("release");
let release_path = cwd
.join("target")
.join(RUST_TARGET)
.join("release")
.join("deps");

// Gets the files in the release folder.
let release_files: Vec<PathBuf> = std::fs::read_dir(&release_path)
.map_err(|e| eyre!("could not read release dir: {e}"))?
.filter(|r| r.is_ok())
.map(|r| r.unwrap().path())
.map_err(|e| eyre!("could not read deps dir: {e}"))?
.filter_map(|r| r.ok())
.map(|r| r.path())
.filter(|r| r.is_file())
.collect();

Expand All @@ -113,7 +114,7 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result<PathBuf> {
})
.ok_or(BuildError::NoWasmFound { path: release_path })?;

if let Err(e) = get_compressed_wasm_bytes(&wasm_file_path) {
if let Err(e) = compress_wasm(&wasm_file_path) {
if let Some(BuildError::MaxCompressedSizeExceeded { got, .. }) = e.downcast_ref() {
match cfg.opt_level {
OptLevel::S => {
Expand All @@ -125,7 +126,7 @@ https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md"#,
got.red(),
);
// Attempt to build again with a bumped-up optimization level.
return build_project_to_wasm(BuildConfig {
return build_project_dylib(BuildConfig {
opt_level: OptLevel::Z,
nightly: cfg.nightly,
rebuild: true,
Expand All @@ -142,7 +143,7 @@ https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md"#,
);
// Attempt to build again with the nightly flag enabled and extra optimizations
// only available with nightly compilation.
return build_project_to_wasm(BuildConfig {
return build_project_dylib(BuildConfig {
opt_level: OptLevel::Z,
nightly: true,
rebuild: true,
Expand All @@ -162,7 +163,7 @@ https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md"#,
}

/// Reads a WASM file at a specified path and returns its brotli compressed bytes.
pub fn get_compressed_wasm_bytes(wasm_path: &PathBuf) -> eyre::Result<(Vec<u8>, Vec<u8>)> {
pub fn compress_wasm(wasm_path: &PathBuf) -> Result<(Vec<u8>, Vec<u8>)> {
let wasm_file_bytes = std::fs::read(wasm_path).map_err(|e| {
eyre!(
"could not read WASM file at target path {}: {e}",
Expand All @@ -172,13 +173,13 @@ pub fn get_compressed_wasm_bytes(wasm_path: &PathBuf) -> eyre::Result<(Vec<u8>,

let wasm_bytes = wasmer::wat2wasm(&wasm_file_bytes)
.map_err(|e| eyre!("could not parse wasm file bytes: {e}"))?;
let wasm_bytes = &*wasm_bytes;

let mut compressor = BrotliEncoder::new(wasm_bytes, BROTLI_COMPRESSION_LEVEL);
let mut compressor = BrotliEncoder::new(&*wasm_bytes, BROTLI_COMPRESSION_LEVEL);
let mut compressed_bytes = vec![];
compressor
.read_to_end(&mut compressed_bytes)
.map_err(|e| eyre!("could not Brotli compress WASM bytes: {e}"))?;

let mut deploy_ready_code = hex::decode(EOF_PREFIX).unwrap();
deploy_ready_code.extend(compressed_bytes);

Expand Down
Loading

0 comments on commit ae43fa8

Please sign in to comment.