Skip to content

Commit

Permalink
Add benchmarks for Scilla and ERC-20 transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHinshelwood committed Jan 20, 2025
1 parent fdcccd0 commit d368d5b
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 14 deletions.
32 changes: 26 additions & 6 deletions Cargo.lock

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

6 changes: 3 additions & 3 deletions zilliqa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ anyhow = { version = "1.0.95", features = ["backtrace"] }
vergen = { version = "8.3.1", features = ["git", "git2"] }

[dependencies]
alloy = { version = "0.6.4", default-features = false, features = ["consensus", "eips", "k256", "rlp", "rpc-types", "rpc-types-trace", "serde", "sol-types"] }
alloy = { version = "0.6.4", default-features = false, features = ["consensus", "eips", "json-abi", "dyn-abi", "k256", "rlp", "rpc-types", "rpc-types-trace", "serde", "sol-types"] }
anyhow = { version = "1.0.95", features = ["backtrace"] }
async-trait = "0.1.85"
base64 = "0.22.1"
Expand Down Expand Up @@ -85,18 +85,18 @@ serde_repr = "0.1.19"
thiserror = "2.0.11"
lru-mem = "0.3.0"
opentelemetry-semantic-conventions = { version = "0.27.0", features = ["semconv_experimental"] }
semver = "1.0.23"
foundry-compilers = { version = "0.12.9", features = ["svm-solc"] }

[dev-dependencies]
alloy = { version = "0.6.4", default-features = false, features = ["network", "rand", "signers", "signer-local"] }
async-trait = "0.1.85"
criterion = "0.5.1"
ethers = { version = "2.0.14", default-features = false, features = ["legacy"] }
foundry-compilers = { version = "0.12.9", features = ["svm-solc"] }
fs_extra = "1.3.0"
indicatif = "0.17.9"
pprof = { version = "0.14.0", default-features = false, features = ["criterion", "flamegraph"] }
primitive-types = { version = "0.12.2" }
semver = "1.0.23"
ureq = "2.12.1"
zilliqa = { path = ".", default-features = false, features = ["fake_response_channel", "fake_time"] }
zilliqa-macros = { path = "../zilliqa-macros" }
Expand Down
10 changes: 10 additions & 0 deletions zilliqa/benches/ERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.28;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ERC20FixedSupply is ERC20("token", "TKN") {
constructor() {
_mint(msg.sender, 1_000_000_000);
}
}
128 changes: 123 additions & 5 deletions zilliqa/benches/it.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
use std::{env, iter, path::PathBuf, sync::Arc, time::Duration};

use alloy::{
consensus::TxLegacy, network::TxSignerSync, primitives::Address, signers::local::LocalSigner,
consensus::TxLegacy,
dyn_abi::JsonAbiExt,
network::TxSignerSync,
primitives::{Address, U256},
signers::local::LocalSigner,
};
use bitvec::{bitarr, order::Msb0};
use criterion::{
black_box, criterion_group, criterion_main, BatchSize, Criterion, SamplingMode, Throughput,
};
use eth_trie::{MemoryDB, Trie};
use k256::elliptic_curve::sec1::ToEncodedPoint;
use libp2p::PeerId;
use pprof::criterion::{Output, PProfProfiler};
use prost::Message;
use revm::primitives::{Bytes, TxKind};
use sha2::{Digest, Sha256};
use tempfile::tempdir;
use tokio::sync::mpsc;
use zilliqa::{
Expand All @@ -20,8 +27,13 @@ use zilliqa::{
db::Db,
message::{Block, ExternalMessage, Proposal, QuorumCertificate, Vote, MAX_COMMITTEE_SIZE},
node::{MessageSender, RequestId},
schnorr,
test_util::compile_contract,
time::{self, SystemTime},
transaction::{EvmGas, SignedTransaction, VerifiedTransaction},
transaction::{
EvmGas, ScillaGas, SignedTransaction, TxZilliqa, VerifiedTransaction, ZilAmount,
},
zq1_proto::{Nonce, ProtoTransactionCoreInfo},
};

fn process_empty(c: &mut Criterion) {
Expand Down Expand Up @@ -206,11 +218,11 @@ fn full_blocks_evm_transfers(c: &mut Criterion) {
let txns = (0..).map(|nonce| {
let mut tx = TxLegacy {
chain_id: None,
nonce: nonce as u64,
nonce,
gas_price: 1,
gas_limit: 21_000,
to: TxKind::Call(to),
value: alloy::primitives::U256::from(1),
value: U256::from(1),
input: Bytes::new(),
};
let sig = signer.sign_transaction_sync(&mut tx).unwrap();
Expand All @@ -228,6 +240,112 @@ fn full_blocks_evm_transfers(c: &mut Criterion) {
);
}

fn full_blocks_zil_transfers(c: &mut Criterion) {
let signer = schnorr::SecretKey::random(&mut rand::thread_rng());
let key = signer.public_key();
let to = Address::random();
let txns = (1..).map(|nonce| {
let chain_id = 700;
let amount = 1;
let gas_price = 1;
let gas_limit = 50;
let tx = TxZilliqa {
chain_id,
nonce,
gas_price: ZilAmount::from_raw(gas_price),
gas_limit: ScillaGas(gas_limit),
to_addr: to,
amount: ZilAmount::from_raw(amount),
code: String::new(),
data: String::new(),
};
let version = ((chain_id as u32) << 16) | 1u32;
let proto = ProtoTransactionCoreInfo {
version,
toaddr: to.0.to_vec(),
senderpubkey: Some(key.to_sec1_bytes().into()),
amount: Some(amount.to_be_bytes().to_vec().into()),
gasprice: Some(gas_price.to_be_bytes().to_vec().into()),
gaslimit: gas_limit,
oneof2: Some(Nonce::Nonce(nonce)),
oneof8: None,
oneof9: None,
};
let txn_data = proto.encode_to_vec();
let sig = schnorr::sign(&txn_data, &signer);
let txn = SignedTransaction::Zilliqa { tx, key, sig };
txn.verify().unwrap()
});

let hashed = Sha256::digest(key.to_encoded_point(true).as_bytes());
let address = Address::from_slice(&hashed[12..]);

full_transaction_benchmark(
c,
"full-blocks-zil-transfers",
address,
iter::empty(),
txns,
4000,
);
}

fn full_blocks_erc20_transfers(c: &mut Criterion) {
let (abi, input, estimate) = compile_contract("benches/ERC20.sol", "ERC20FixedSupply");
let signer = LocalSigner::random();

let mut tx = TxLegacy {
chain_id: None,
nonce: 0,
gas_price: 1,
gas_limit: 10_000_000,
to: TxKind::Create,
value: U256::ZERO,
input,
};
let sig = signer.sign_transaction_sync(&mut tx).unwrap();
let setup_txn = SignedTransaction::Legacy { tx, sig };
let setup_txn = setup_txn.verify().unwrap();

let to = Address::random();
let contract_address = signer.address().create(0);
let gas_limit = estimate
.external
.get("transfer(address,uint256)")
.unwrap()
.parse()
.unwrap();
let transfer = abi.function("transfer").unwrap()[0].clone();
let input: Bytes = transfer
.abi_encode_input(&[to.into(), U256::from(1).into()])
.unwrap()
.into();

let txns = (1..).map(|nonce| {
let mut tx = TxLegacy {
chain_id: None,
nonce,
gas_price: 1,
gas_limit,
to: TxKind::Call(contract_address),
value: U256::ZERO,
input: input.clone(),
};
let sig = signer.sign_transaction_sync(&mut tx).unwrap();
let txn = SignedTransaction::Legacy { tx, sig };
txn.verify().unwrap()
});

full_transaction_benchmark(
c,
"full-blocks-erc20-transfers",
signer.address(),
iter::once(setup_txn),
txns,
(84_000_000 / gas_limit) as usize,
);
}

/// Run a benchmark which produces blocks full of the provided transactions. `txns` should be infinitely iterable
/// so the benchmark can generate as many transactions as it needs.
fn full_transaction_benchmark(
Expand Down Expand Up @@ -382,6 +500,6 @@ fn c_big_process_block(big: &mut Consensus, from: PeerId, proposal: Proposal) ->
criterion_group!(
name = benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
targets = process_empty, full_blocks_evm_transfers,
targets = process_empty, full_blocks_evm_transfers, full_blocks_zil_transfers, full_blocks_erc20_transfers,
);
criterion_main!(benches);
1 change: 1 addition & 0 deletions zilliqa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod scilla;
mod scilla_proto;
pub mod serde_util;
pub mod state;
pub mod test_util;
pub mod time;
pub mod transaction;
pub mod zq1_proto;
70 changes: 70 additions & 0 deletions zilliqa/src/test_util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use std::path::{Path, PathBuf};

use alloy::{json_abi::JsonAbi, primitives::Bytes};
use foundry_compilers::{
artifacts::{
output_selection::OutputSelection, EvmVersion, GasEstimates, Optimizer, Settings,
SolcInput, Source,
},
solc::{Solc, SolcLanguage},
};

pub fn compile_contract(path: &str, contract: &str) -> (JsonAbi, Bytes, GasEstimates) {
let full_path = format!("{}/{}", env!("CARGO_MANIFEST_DIR"), path);
let source_path = Path::new(&full_path);
let target_file = tempfile::Builder::new()
.suffix(".sol")
.tempfile()
.unwrap()
.into_temp_path();
let target_file = target_file.to_path_buf();

std::fs::copy(source_path, &target_file).unwrap();

let solc_input = SolcInput::new(
SolcLanguage::Solidity,
Source::read_all_files(vec![target_file.clone()]).unwrap(),
Settings {
remappings: vec![format!(
"@openzeppelin/contracts={}/../vendor/openzeppelin-contracts/contracts",
env!("CARGO_MANIFEST_DIR")
)
.parse()
.unwrap()],
optimizer: Optimizer {
enabled: Some(true),
runs: Some(2usize.pow(32) - 1),
details: None,
},
output_selection: OutputSelection::complete_output_selection(),
..Default::default()
},
)
.evm_version(EvmVersion::Shanghai); // ensure compatible with EVM version in exec.rs

let mut solc = Solc::find_or_install(&semver::Version::new(0, 8, 28)).unwrap();
solc.allow_paths
.insert(PathBuf::from("../vendor/openzeppelin-contracts"));
let mut output = solc.compile_exact(&solc_input).unwrap();

if output.has_error() {
for error in output.errors {
eprintln!("{error}");
}
panic!("failed to compile contract");
}

let contract = output
.contracts
.remove(&target_file)
.unwrap()
.remove(contract)
.unwrap();
let evm = contract.evm.unwrap();

(
contract.abi.unwrap(),
evm.bytecode.unwrap().into_bytes().unwrap(),
evm.gas_estimates.unwrap(),
)
}

0 comments on commit d368d5b

Please sign in to comment.