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

feat: cache contracts before benching #254

Merged
merged 11 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/gas-bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
with:
key: "gas-bench"

- name: install cargo-stylus
run: cargo install [email protected]

- name: install solc
run: |
curl -LO https://github.com/ethereum/solidity/releases/download/v0.8.24/solc-static-linux
Expand Down
6 changes: 4 additions & 2 deletions benches/src/access_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ pub async fn bench() -> eyre::Result<Report> {
.wallet(EthereumWallet::from(bob.signer.clone()))
.on_http(bob.url().parse()?);

let contract_addr = deploy(&alice).await;
let contract_addr = deploy(&alice).await?;
crate::cache_contract(&alice, contract_addr)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can be part of deploy? And maybe change deploy's signature to receive a enum CachePolicy { None, Bid(u64) } which determines whether we cache or not? (this may seem overengineered, but it is forward-compatible)

Copy link
Member

@qalisander qalisander Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also deploy_with_cache(&alice).await? could make, sense since 0 bid is always acceptable on nitro test node.

But I was thinking to have it separate for now. And later add analytics of how contracts perform with cache and without it in the single report. Also to track improvements of caching separate from contracts


let contract = AccessControl::new(contract_addr, &alice_wallet);
let contract_bob = AccessControl::new(contract_addr, &bob_wallet);

Expand All @@ -72,7 +74,7 @@ pub async fn bench() -> eyre::Result<Report> {
Ok(report)
}

async fn deploy(account: &Account) -> Address {
async fn deploy(account: &Account) -> eyre::Result<Address> {
let args = AccessControl::constructorCall {};
let args = alloy::hex::encode(args.abi_encode());
crate::deploy(account, "access-control", Some(args)).await
Expand Down
6 changes: 4 additions & 2 deletions benches/src/erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ pub async fn bench() -> eyre::Result<Report> {
.wallet(EthereumWallet::from(bob.signer.clone()))
.on_http(bob.url().parse()?);

let contract_addr = deploy(&alice).await;
let contract_addr = deploy(&alice).await?;
crate::cache_contract(&alice, contract_addr)?;

let contract = Erc20::new(contract_addr, &alice_wallet);
let contract_bob = Erc20::new(contract_addr, &bob_wallet);

Expand Down Expand Up @@ -84,7 +86,7 @@ pub async fn bench() -> eyre::Result<Report> {
Ok(report)
}

async fn deploy(account: &Account) -> Address {
async fn deploy(account: &Account) -> eyre::Result<Address> {
let args = Erc20Example::constructorCall {
name_: TOKEN_NAME.to_owned(),
symbol_: TOKEN_SYMBOL.to_owned(),
Expand Down
6 changes: 4 additions & 2 deletions benches/src/erc721.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ pub async fn bench() -> eyre::Result<Report> {
let bob = Account::new().await?;
let bob_addr = bob.address();

let contract_addr = deploy(&alice).await;
let contract_addr = deploy(&alice).await?;
crate::cache_contract(&alice, contract_addr)?;

let contract = Erc721::new(contract_addr, &alice_wallet);

let token_1 = uint!(1_U256);
Expand Down Expand Up @@ -75,7 +77,7 @@ pub async fn bench() -> eyre::Result<Report> {
Ok(report)
}

async fn deploy(account: &Account) -> Address {
async fn deploy(account: &Account) -> eyre::Result<Address> {
let args = Erc721Example::constructorCall {};
let args = alloy::hex::encode(args.abi_encode());
crate::deploy(account, "erc721", Some(args)).await
Expand Down
45 changes: 34 additions & 11 deletions benches/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::process::Command;

use alloy::{
primitives::Address,
rpc::types::{
Expand All @@ -7,6 +9,7 @@ use alloy::{
};
use alloy_primitives::U128;
use e2e::{Account, ReceiptExt};
use eyre::WrapErr;
use koba::config::{Deploy, Generate, PrivateKey};
use serde::Deserialize;

Expand All @@ -16,8 +19,6 @@ pub mod erc721;
pub mod merkle_proofs;
pub mod report;

const RPC_URL: &str = "http://localhost:8547";

#[derive(Debug, Deserialize)]
struct ArbOtherFields {
#[serde(rename = "gasUsedForL1")]
Expand All @@ -41,9 +42,9 @@ async fn deploy(
account: &Account,
contract_name: &str,
args: Option<String>,
) -> Address {
) -> eyre::Result<Address> {
let manifest_dir =
std::env::current_dir().expect("should get current dir from env");
std::env::current_dir().context("should get current dir from env")?;

let wasm_path = manifest_dir
.join("target")
Expand All @@ -53,7 +54,7 @@ async fn deploy(
let sol_path = args.as_ref().map(|_| {
manifest_dir
.join("examples")
.join(format!("{}", contract_name))
.join(contract_name)
.join("src")
.join("constructor.sol")
});
Expand All @@ -72,14 +73,36 @@ async fn deploy(
keystore_path: None,
keystore_password_path: None,
},
endpoint: RPC_URL.to_owned(),
endpoint: env("RPC_URL")?,
deploy_only: false,
quiet: true,
};

koba::deploy(&config)
.await
.expect("should deploy contract")
.address()
.expect("should return contract address")
koba::deploy(&config).await.expect("should deploy contract").address()
}

/// Try to cache a contract on the stylus network.
/// Already cached contracts won't be cached, and this function will not return
/// an error.
/// Output will be forwarded to the child process.
fn cache_contract(
account: &Account,
contract_addr: Address,
) -> eyre::Result<()> {
// We don't need a status code.
// Since it is not zero when the contract is already cached.
let _ = Command::new("cargo")
.args(["stylus", "cache", "bid"])
.args(["-e", &env("RPC_URL")?])
.args(["--private-key", &format!("0x{}", account.pk())])
.arg(contract_addr.to_string())
.arg("0")
.status()
.context("failed to execute `cargo stylus cache bid` command")?;
Ok(())
}

/// Load the `name` environment variable.
fn env(name: &str) -> eyre::Result<String> {
std::env::var(name).wrap_err(format!("failed to load {name}"))
}
1 change: 1 addition & 0 deletions benches/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ async fn main() -> eyre::Result<()> {
let report =
reports.into_iter().fold(Reports::default(), Reports::merge_with);

println!();
println!("{report}");
Comment on lines +18 to 19
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
println!();
println!("{report}");
println!("\n{report}");

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's kinda personal. For me add or remove another line is faster, then change a specific symbol:)


Ok(())
Expand Down
6 changes: 4 additions & 2 deletions benches/src/merkle_proofs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ pub async fn bench() -> eyre::Result<Report> {
.wallet(EthereumWallet::from(alice.signer.clone()))
.on_http(alice.url().parse()?);

let contract_addr = deploy(&alice).await;
let contract_addr = deploy(&alice).await?;
crate::cache_contract(&alice, contract_addr)?;

let contract = Verifier::new(contract_addr, &alice_wallet);

let proof = PROOF.map(|h| h.into()).to_vec();
Expand All @@ -81,6 +83,6 @@ pub async fn bench() -> eyre::Result<Report> {
Ok(report)
}

async fn deploy(account: &Account) -> Address {
async fn deploy(account: &Account) -> eyre::Result<Address> {
crate::deploy(account, "merkle-proofs", None).await
}
6 changes: 4 additions & 2 deletions lib/e2e/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use crate::{
system::{fund_account, Wallet, RPC_URL_ENV_VAR_NAME},
};

const DEFAULT_FUNDING_ETH: u32 = 100;

/// Type that corresponds to a test account.
#[derive(Clone, Debug)]
pub struct Account {
Expand All @@ -23,7 +25,7 @@ pub struct Account {
}

impl Account {
/// Create a new account.
/// Create a new account with default funding of 100 ETH.
qalisander marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Errors
///
Expand Down Expand Up @@ -103,7 +105,7 @@ impl AccountFactory {

let signer = PrivateKeySigner::random();
let addr = signer.address();
fund_account(addr, "100")?;
fund_account(addr, DEFAULT_FUNDING_ETH)?;

let rpc_url = std::env::var(RPC_URL_ENV_VAR_NAME)
.expect("failed to load RPC_URL var from env")
Expand Down
4 changes: 2 additions & 2 deletions lib/e2e/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub fn provider() -> Provider {
}

/// Send `amount` eth to `address` in the nitro-tesnode.
pub fn fund_account(address: Address, amount: &str) -> eyre::Result<()> {
pub fn fund_account(address: Address, amount: u32) -> eyre::Result<()> {
let node_script = get_node_path()?.join("test-node.bash");
if !node_script.exists() {
bail!("Test nitro node wasn't setup properly. Try to setup it first with `./scripts/nitro-testnode.sh -i -d`")
Expand All @@ -73,7 +73,7 @@ pub fn fund_account(address: Address, amount: &str) -> eyre::Result<()> {
.arg("--to")
.arg(format!("address_{address}"))
.arg("--ethamount")
.arg(amount)
.arg(amount.to_string())
.output()?;

if !output.status.success() {
Expand Down
9 changes: 7 additions & 2 deletions scripts/bench.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ MYDIR=$(realpath "$(dirname "$0")")
cd "$MYDIR"
cd ..

NIGHTLY_TOOLCHAIN=${NIGHTLY_TOOLCHAIN:-nightly}
NIGHTLY_TOOLCHAIN=${NIGHTLY_TOOLCHAIN:-nightly-2024-01-01}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use a more recent nightly like 2024-09-01?

Copy link
Member

@qalisander qalisander Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alas, recent nightly versions do not work. That is another problem why we're pinned. Also I've checked that on stable not just binary size is bigger, but benchmarks perform %2-3 worse

Copy link
Member

@qalisander qalisander Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this pr we will update nightly till minimal working version everywhere in the project

cargo +"$NIGHTLY_TOOLCHAIN" build --release --target wasm32-unknown-unknown -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort

export RPC_URL=http://localhost:8547
cargo run --release -p benches

# No need to run benchmark infrastructure with release.
# Just compilation will take longer.
# Contracts already built with release.
cargo run -p benches
qalisander marked this conversation as resolved.
Show resolved Hide resolved

echo "Finished running benches!"
15 changes: 11 additions & 4 deletions scripts/nitro-testnode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ do
shift
;;
-q|--quit)
docker container stop $(docker container ls -q --filter name=nitro-testnode)
NITRO_CONTAINERS=$(docker container ls -q --filter name=nitro-testnode)

if [ -z "$NITRO_CONTAINERS" ]; then
echo "No running nitro test node containers"
qalisander marked this conversation as resolved.
Show resolved Hide resolved
else
docker container stop $NITRO_CONTAINERS || exit
fi

exit 0
;;
*)
Expand All @@ -41,10 +48,10 @@ then
cd "$MYDIR" || exit
cd ..

git clone --recurse-submodules https://github.com/OffchainLabs/nitro-testnode.git
git clone -b release --recurse-submodules https://github.com/OffchainLabs/nitro-testnode.git
cd ./nitro-testnode || exit
# `release` branch.
git checkout 8cb6b84e31909157d431e7e4af9fb83799443e00 || exit
git pull origin release --recurse-submodules
git checkout 43861b001713db3526b74e905e383d41e9272760 || exit
qalisander marked this conversation as resolved.
Show resolved Hide resolved

./test-node.bash --no-run --init || exit
fi
Expand Down
Loading