Skip to content

Commit

Permalink
migrate to using eyre everywhere, add this error
Browse files Browse the repository at this point in the history
  • Loading branch information
rauljordan committed Aug 28, 2023
1 parent 0ca0c0b commit f09d668
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 162 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ ethers = "2.0.8"
serde_json = "1.0.103"
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread" ] }
wasmer = "3.1.0"
thiserror = "1.0.47"
68 changes: 4 additions & 64 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,31 +129,10 @@ Instruments a Rust project using Stylus. This command runs compiled WASM code th

```
Usage: cargo stylus check [OPTIONS]
Options:
-e, --endpoint <ENDPOINT>
The endpoint of the L2 node to connect to [default: http://localhost:8545]
--wasm-file-path <WASM_FILE_PATH>
If desired, it loads a WASM file from a specified path. If not provided, it will try
to find a WASM file under the current working directory's Rust target release
directory and use its contents for the deploy command
--expected-program-address <EXPECTED_PROGRAM_ADDRESS>
Specify the program address we want to check activation for. If unspecified, it
will compute the next program address from the user's wallet address and nonce,
which will require wallet-related flags to be specified
[default: 0x0000000000000000000000000000000000000000]
--private-key-path <PRIVATE_KEY_PATH>
Privkey source to use with the cargo stylus plugin
--keystore-path <KEYSTORE_PATH>
Path to an Ethereum wallet keystore file, such as the one produced by wallets such as clef
--keystore-password-path <KEYSTORE_PASSWORD_PATH>
Path to a text file containing a password to the specified wallet keystore file
--nightly
Whether or not to compile the Rust program using the nightly Rust version.
Nightly can help with reducing compressed WASM sizes, however, can be
a security risk if used liberally
```

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

## Deploying Stylus Programs

**cargo stylus deploy**
Expand All @@ -162,39 +141,10 @@ Instruments a Rust project using Stylus and by outputting its brotli-compressed

```
Usage: cargo stylus deploy [OPTIONS]
Options:
-e, --endpoint <ENDPOINT>
The endpoint of the L2 node to connect to [default: http://localhost:8545]
--wasm-file-path <WASM_FILE_PATH>
If desired, it loads a WASM file from a specified path. If not provided, it will try to find a WASM
file under the current working directory's Rust target release directory and
use its contents for the deploy command
--expected-program-address <EXPECTED_PROGRAM_ADDRESS>
Specify the program address we want to check activation for. If unspecified, it
will compute the next program address from the user's wallet address and nonce,
which will require wallet-related flags to be specified
[default: 0x0000000000000000000000000000000000000000]
--private-key-path <PRIVATE_KEY_PATH>
Privkey source to use with the cargo stylus plugin
--keystore-path <KEYSTORE_PATH>
Path to an Ethereum wallet keystore file, such as the one produced by wallets such as clef
--keystore-password-path <KEYSTORE_PASSWORD_PATH>
Path to a text file containing a password to the specified wallet keystore file
--nightly
Whether or not to compile the Rust program using the nightly Rust version.
Nightly can help with reducing compressed WASM sizes, however, can be a security risk if used liberally
--estimate-gas-only
Does not submit a transaction, but instead estimates the gas required to complete the operation
--mode <MODE>
By default, submits two transactions to deploy and activate the program
to Arbitrum. Otherwise, a user could choose to split up the deploy and activate
steps into individual transactions [possible values: deploy-only, activate-only]
--activate-program-address <ACTIVATE_PROGRAM_ADDRESS>
If only activating an already-deployed, onchain program, the address
of the program to send an activation tx for
```

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

## Deploying Non-Rust WASM Projects

The Stylus tool can also be used to deploy non-Rust, WASM projects to Stylus by specifying the WASM file directly with the `--wasm-file-path` flag to any of the cargo stylus commands.
Expand Down Expand Up @@ -230,16 +180,6 @@ We recommend optimizing your Stylus program's sizes to smaller sizes, but keep i
For a deep-dive into the different options for optimizing binary sizes using cargo stylus, see [OPTIMIZING_BINARIES.md](./OPTIMIZING_BINARIES.md).
## Alternative Installations
### Docker Images
TODO:
### Precompiled Binaries
TODO:
## License
Cargo Stylus is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
79 changes: 44 additions & 35 deletions src/check.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,74 @@
// Copyright 2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md
use bytesize::ByteSize;

Check warning on line 3 in src/check.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `bytesize::ByteSize`

warning: unused import: `bytesize::ByteSize` --> src/check.rs:3:5 | 3 | use bytesize::ByteSize; | ^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
use std::path::PathBuf;
use std::str::FromStr;

use crate::{
color::Color,
constants::{ARB_WASM_ADDRESS, MAX_PROGRAM_SIZE},
deploy::activation_calldata,
project::{self, BuildConfig},
wallet, CheckConfig,
};
use ethers::prelude::*;
use ethers::utils::get_contract_address;
use ethers::{
providers::JsonRpcClient,
types::{transaction::eip2718::TypedTransaction, Address},
};
use std::path::PathBuf;
use std::str::FromStr;

use ethers::types::Eip1559TransactionRequest;
use ethers::{
core::types::spoof,
providers::{Provider, RawCall},
};
use eyre::eyre;

use crate::{
color::Color,
constants::{ARB_WASM_ADDRESS, MAX_PRECOMPRESSED_WASM_SIZE, MAX_PROGRAM_SIZE},

Check warning on line 22 in src/check.rs

View workflow job for this annotation

GitHub Actions / clippy

unused imports: `MAX_PRECOMPRESSED_WASM_SIZE`, `MAX_PROGRAM_SIZE`

warning: unused imports: `MAX_PRECOMPRESSED_WASM_SIZE`, `MAX_PROGRAM_SIZE` --> src/check.rs:22:35 | 22 | constants::{ARB_WASM_ADDRESS, MAX_PRECOMPRESSED_WASM_SIZE, MAX_PROGRAM_SIZE}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
deploy::activation_calldata,
project::{self, BuildConfig},
wallet, CheckConfig,
};

/// Runs a series of checks on the WASM program to ensure it is valid for compilation
/// and code size before being deployed and activated onchain. An optional list of checks
/// to disable can be specified.
pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<(), String> {
pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<()> {
let wasm_file_path: PathBuf = match cfg.wasm_file_path {
Some(path) => PathBuf::from_str(&path).unwrap(),
None => project::build_project_to_wasm(BuildConfig {
opt_level: project::OptLevel::default(),
nightly: cfg.nightly,
clean: true,
})
.map_err(|e| format!("failed to build project to WASM: {e}"))?,
.map_err(|e| eyre!("failed to build project to WASM: {e}"))?,
};
println!("Reading WASM file at {}", wasm_file_path.display().grey());

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

Check warning on line 43 in src/check.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `precompressed_bytes`

warning: unused variable: `precompressed_bytes` --> src/check.rs:43:10 | 43 | let (precompressed_bytes, deploy_ready_code) = | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_precompressed_bytes` | = note: `#[warn(unused_variables)]` on by default
project::get_compressed_wasm_bytes(&wasm_file_path)
.map_err(|e| eyre!("failed to get compressed WASM bytes: {e}"))?;

let compressed_size = ByteSize::b(deploy_ready_code.len() as u64);
if compressed_size > MAX_PROGRAM_SIZE {
return Err(format!(
"brotli-compressed WASM size {} is bigger than program size limit: {}",
compressed_size.to_string().red(),
MAX_PROGRAM_SIZE,
));
}
// let precompressed_size = ByteSize::b(precompressed_bytes.len() as u64);
// if precompressed_size > MAX_PRECOMPRESSED_WASM_SIZE {
// return Err(format!(
// "pre-compressed WASM program size {} is bigger than program size limit: {}",
// precompressed_size.to_string().red(),
// MAX_PRECOMPRESSED_WASM_SIZE,
// ));
// }

println!(
"Compressed WASM size: {}",
compressed_size.to_string().mint(),
);
// let compressed_size = ByteSize::b(deploy_ready_code.len() as u64);
// if compressed_size > MAX_PROGRAM_SIZE {
// return Err(format!(
// "brotli-compressed WASM size {} is bigger than program size limit: {}",
// compressed_size.to_string().red(),
// MAX_PROGRAM_SIZE,
// ));
// }

// println!(
// "Compressed WASM size: {}",
// compressed_size.to_string().mint(),
// );

let provider = Provider::<Http>::try_from(&cfg.endpoint)
.map_err(|e| format!("could not initialize provider from http: {e}"))?;
.map_err(|e| eyre!("could not initialize provider from http: {e}"))?;

let mut expected_program_addr = cfg.expected_program_address;

Expand All @@ -67,7 +78,7 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<(), String> {
let chain_id = provider
.get_chainid()
.await
.map_err(|e| format!("could not get chain id {e}"))?
.map_err(|e| eyre!("could not get chain id {e}"))?
.as_u64();
let client =
SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(chain_id));
Expand All @@ -76,7 +87,7 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<(), String> {
let nonce = client
.get_transaction_count(addr, None)
.await
.map_err(|e| format!("could not get nonce {addr}: {e}"))?;
.map_err(|e| eyre!("could not get nonce {addr}: {e}"))?;

expected_program_addr = get_contract_address(wallet.address(), nonce);
}
Expand All @@ -91,7 +102,7 @@ pub async fn check_can_activate<T>(
client: Provider<T>,
expected_program_address: &Address,
compressed_wasm: Vec<u8>,
) -> eyre::Result<(), String>
) -> eyre::Result<()>
where
T: JsonRpcClient + Send + Sync,
{
Expand All @@ -109,21 +120,19 @@ where
compressed_wasm.into(),
);
let response = client.call_raw(&tx).state(&state).await.map_err(|e| {
format!(
"program predeployment check failed when checking against ARB_WASM_ADDRESS {to}: {e}"
)
eyre!("program predeployment check failed when checking against ARB_WASM_ADDRESS {to}: {e}")
})?;

if response.len() < 2 {
return Err(format!(
return Err(eyre!(
"Stylus version bytes response too short, expected at least 2 bytes but got: {}",
hex::encode(&response)
));
}
let n = response.len();
let version_bytes: [u8; 2] = response[n - 2..]
.try_into()
.map_err(|e| format!("could not parse Stylus version bytes: {e}"))?;
.map_err(|e| eyre!("could not parse Stylus version bytes: {e}"))?;
let version = u16::from_be_bytes(version_bytes);
println!("Program succeeded Stylus onchain activation checks with Stylus version: {version}");
Ok(())
Expand Down
2 changes: 2 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub const BROTLI_COMPRESSION_LEVEL: u32 = 11;
pub const ARB_WASM_ADDRESS: &str = "0000000000000000000000000000000000000071";
/// Maximum allowed size of a program on Arbitrum (and Ethereum).
pub const MAX_PROGRAM_SIZE: ByteSize = ByteSize::kb(24);
/// Maximum allowed size of a precompressed WASM program.
pub const MAX_PRECOMPRESSED_WASM_SIZE: ByteSize = ByteSize::kb(128);

Check warning on line 14 in src/constants.rs

View workflow job for this annotation

GitHub Actions / clippy

constant `MAX_PRECOMPRESSED_WASM_SIZE` is never used

warning: constant `MAX_PRECOMPRESSED_WASM_SIZE` is never used --> src/constants.rs:14:11 | 14 | pub const MAX_PRECOMPRESSED_WASM_SIZE: ByteSize = ByteSize::kb(128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default
/// 4 bytes method selector for the activate method of ArbWasm.
pub const ARBWASM_ACTIVATE_METHOD_HASH: &str = "58c780c2";
/// Target for compiled WASM folder in a Rust project
Expand Down
17 changes: 9 additions & 8 deletions src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use ethers::{
providers::{Http, Middleware, Provider},
signers::Signer,
};
use eyre::eyre;

use crate::project::BuildConfig;
use crate::{constants, project, tx, wallet, DeployConfig, DeployMode};
Expand All @@ -18,28 +19,28 @@ use crate::{constants, project, tx, wallet, DeployConfig, DeployMode};
/// DeployOnly: Sends a signed tx to deploy a Stylus program to a new address.
/// ActivateOnly: Sends a signed tx to activate a Stylus program at a specified address.
/// DeployAndActivate (default): Sends both transactions above.
pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> {
pub async fn deploy(cfg: DeployConfig) -> eyre::Result<()> {
let wallet = wallet::load(cfg.check_cfg.private_key_path, cfg.check_cfg.keystore_opts)
.map_err(|e| format!("could not load wallet: {e}"))?;
.map_err(|e| eyre!("could not load wallet: {e}"))?;

let provider = Provider::<Http>::try_from(&cfg.check_cfg.endpoint).map_err(|e| {
format!(
eyre!(
"could not initialize provider from http endpoint: {}: {e}",
&cfg.check_cfg.endpoint
)
})?;
let chain_id = provider
.get_chainid()
.await
.map_err(|e| format!("could not get chain id: {e}"))?
.map_err(|e| eyre!("could not get chain id: {e}"))?
.as_u64();
let client = SignerMiddleware::new(provider, wallet.clone().with_chain_id(chain_id));

let addr = wallet.address();
let nonce = client
.get_transaction_count(addr, None)
.await
.map_err(|e| format!("could not get nonce for address {addr}: {e}"))?;
.map_err(|e| eyre!("could not get nonce for address {addr}: {e}"))?;

let expected_program_addr = get_contract_address(wallet.address(), nonce);

Expand All @@ -65,7 +66,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> {
nightly: cfg.check_cfg.nightly,
clean: false,
})
.map_err(|e| format!("could not build project to WASM: {e}"))?,
.map_err(|e| eyre!("could not build project to WASM: {e}"))?,
};
let (_, deploy_ready_code) = project::get_compressed_wasm_bytes(&wasm_file_path)?;
println!("Deploying program to address {expected_program_addr:#032x}");
Expand All @@ -80,7 +81,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> {
&mut tx_request,
)
.await
.map_err(|e| format!("could not submit signed deployment tx: {e}"))?;
.map_err(|e| eyre!("could not submit signed deployment tx: {e}"))?;
}
if activate {
let program_addr = cfg
Expand All @@ -103,7 +104,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> {
&mut tx_request,
)
.await
.map_err(|e| format!("could not submit signed deployment tx: {e}"))?;
.map_err(|e| eyre!("could not submit signed deployment tx: {e}"))?;
}
Ok(())
}
Expand Down
9 changes: 5 additions & 4 deletions src/export_abi.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright 2023, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md
use eyre::eyre;
use std::process::{Command, Stdio};

/// Exports the solidity ABI a Stylus Rust program in the current directory to stdout.
pub fn export_abi(release: bool) -> Result<(), String> {
let target_host = rustc_host::from_cli()
.map_err(|e| format!("could not get host target architecture: {e}"))?;
pub fn export_abi(release: bool) -> eyre::Result<()> {
let target_host =
rustc_host::from_cli().map_err(|e| eyre!("could not get host target architecture: {e}"))?;
println!("Exporting Solidity ABI for Stylus Rust program in current directory");
let mut cmd = Command::new("cargo");

Expand All @@ -20,6 +21,6 @@ pub fn export_abi(release: bool) -> Result<(), String> {
}

cmd.output()
.map_err(|e| format!("failed to execute export abi command: {e}"))?;
.map_err(|e| eyre!("failed to execute export abi command: {e}"))?;
Ok(())
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub struct KeystoreOpts {
}

#[tokio::main]
async fn main() -> eyre::Result<(), String> {
async fn main() -> eyre::Result<()> {
let CargoCli::Stylus(args) = CargoCli::parse();

match args.command {
Expand Down
Loading

0 comments on commit f09d668

Please sign in to comment.