Skip to content

Commit

Permalink
Merge pull request #59 from flashbots/partial-fill-block
Browse files Browse the repository at this point in the history
partial fill block + UX improvements
  • Loading branch information
zeroXbrock authored Dec 12, 2024
2 parents 69f0021 + 2897312 commit ac8f299
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 37 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

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

17 changes: 15 additions & 2 deletions crates/cli/src/default_scenarios/runconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,22 @@ pub enum BuiltinScenarioConfig {
max_gas_per_block: u128,
num_txs: u64,
sender: Address,
fill_percent: u16,
},
}

impl BuiltinScenarioConfig {
pub fn fill_block(max_gas_per_block: u128, num_txs: u64, sender: Address) -> Self {
pub fn fill_block(
max_gas_per_block: u128,
num_txs: u64,
sender: Address,
fill_percent: u16,
) -> Self {
Self::FillBlock {
max_gas_per_block,
num_txs,
sender,
fill_percent,
}
}
}
Expand All @@ -35,8 +42,14 @@ impl From<BuiltinScenarioConfig> for TestConfig {
max_gas_per_block,
num_txs,
sender,
fill_percent,
} => {
let gas_per_tx = max_gas_per_block / num_txs as u128;
let gas_per_tx =
((max_gas_per_block / num_txs as u128) / 100) * fill_percent as u128;
println!(
"Filling blocks to {}% with {} gas per tx",
fill_percent, gas_per_tx
);
let spam_txs = (0..num_txs)
.map(|_| {
SpamRequest::Tx(FunctionCallDefinition {
Expand Down
6 changes: 6 additions & 0 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod commands;
mod default_scenarios;

use std::{
env,
io::Write,
str::FromStr,
sync::{Arc, LazyLock},
Expand Down Expand Up @@ -286,11 +287,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
None,
))?;

let fill_percent = env::var("C_FILL_PERCENT")
.map(|s| u16::from_str(&s).expect("invalid u16: fill_percent"))
.unwrap_or(100u16);

let scenario_config = match scenario {
BuiltinScenario::FillBlock => BuiltinScenarioConfig::fill_block(
block_gas_limit,
txs_per_duration as u64,
admin_signer.address(),
fill_percent,
),
};
let testconfig: TestConfig = scenario_config.into();
Expand Down
2 changes: 1 addition & 1 deletion crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
futures = { workspace = true }
async-trait = { workspace = true }
tokio = { workspace = true }
tokio = { workspace = true, features = ["signal"]}
alloy-serde = { workspace = true }
serde_json = { workspace = true }
contender_bundle_provider = { workspace = true }
41 changes: 31 additions & 10 deletions crates/core/src/spammer/spammer_trait.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::sync::Mutex;
use std::{pin::Pin, sync::Arc};

use alloy::providers::Provider;
Expand Down Expand Up @@ -39,6 +40,17 @@ where
run_id: Option<u64>,
sent_tx_callback: Arc<F>,
) -> impl std::future::Future<Output = Result<()>> {
let quit = Arc::new(Mutex::new(false));

let quit_clone = quit.clone();
tokio::task::spawn(async move {
loop {
let _ = tokio::signal::ctrl_c().await;
let mut quit = quit_clone.lock().unwrap();
*quit = true;
}
});

async move {
let tx_requests = scenario
.load_txs(crate::generator::PlanType::Spam(
Expand All @@ -57,36 +69,45 @@ where
let mut cursor = self.on_spam(scenario).await?.take(num_periods);

while let Some(trigger) = cursor.next().await {
if *quit.lock().expect("lock failure") {
println!("CTRL-C received, stopping spam and collecting results...");
let mut quit = quit.lock().expect("lock failure");
*quit = false;
break;
}

let trigger = trigger.to_owned();
let payloads = scenario.prepare_spam(tx_req_chunks[tick]).await?;
let spam_tasks = scenario
.execute_spam(trigger, &payloads, sent_tx_callback.clone())
.await?;
for task in spam_tasks {
task.await
.map_err(|e| ContenderError::with_err(e, "spam task failed"))?;
let res = task.await;
if let Err(e) = res {
eprintln!("spam task failed: {:?}", e);
}
}
tick += 1;
}

let mut timeout_counter = 0;
let mut block_counter = 0;
if let Some(run_id) = run_id {
loop {
if timeout_counter > (num_periods * txs_per_period + 1) {
println!("quitting due to timeout");
break;
}
let cache_size = scenario
.msg_handle
.flush_cache(run_id, block_num + timeout_counter as u64)
.flush_cache(run_id, block_num + block_counter as u64)
.await
.expect("failed to flush cache");
if cache_size == 0 {
break;
}
timeout_counter += 1;
if *quit.lock().expect("lock failure") {
println!("CTRL-C received, stopping result collection...");
break;
}
block_counter += 1;
}
println!("done spamming. run_id={}", run_id);
println!("done. run_id={}", run_id);
}

Ok(())
Expand Down
17 changes: 15 additions & 2 deletions crates/core/src/spammer/tx_actor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{sync::Arc, time::Duration};

use alloy::{primitives::TxHash, providers::Provider};
use alloy::{network::ReceiptResponse, primitives::TxHash, providers::Provider};
use tokio::sync::{mpsc, oneshot};

use crate::{
Expand Down Expand Up @@ -117,7 +117,7 @@ where
.await?
.unwrap_or_default();
println!(
"found {} receipts for block #{}",
"found {} receipts for block {}",
receipts.len(),
target_block_num
);
Expand Down Expand Up @@ -150,6 +150,19 @@ where
.iter()
.find(|r| r.transaction_hash == pending_tx.tx_hash)
.expect("this should never happen");
if !receipt.status() {
println!("tx failed: {:?}", pending_tx.tx_hash);
} else {
println!(
"tx landed. hash={}\tgas_used={}\tblock_num={}",
pending_tx.tx_hash,
receipt.gas_used,
receipt
.block_number
.map(|n| n.to_string())
.unwrap_or("N/A".to_owned())
);
}
RunTx {
tx_hash: pending_tx.tx_hash,
start_timestamp: pending_tx.start_timestamp,
Expand Down
35 changes: 15 additions & 20 deletions crates/core/src/test_scenario.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use alloy::consensus::Transaction;
use alloy::eips::eip2718::Encodable2718;
use alloy::hex::ToHexExt;
use alloy::network::{AnyNetwork, EthereumWallet, TransactionBuilder};
use alloy::primitives::{Address, FixedBytes};
use alloy::primitives::{keccak256, Address, FixedBytes};
use alloy::providers::{PendingTransactionConfig, Provider, ProviderBuilder};
use alloy::rpc::types::TransactionRequest;
use alloy::signers::local::PrivateKeySigner;
Expand Down Expand Up @@ -43,7 +43,7 @@ where
pub agent_store: AgentStore,
pub nonces: HashMap<Address, u64>,
pub chain_id: u64,
pub gas_limits: HashMap<FixedBytes<4>, u128>,
pub gas_limits: HashMap<FixedBytes<32>, u128>,
pub msg_handle: Arc<TxActorHandle>,
}

Expand Down Expand Up @@ -295,29 +295,20 @@ where
))?
.to_owned();
self.nonces.insert(from.to_owned(), nonce + 1);
let fn_sig = FixedBytes::<4>::from_slice(
tx_req
.input
.input
.to_owned()
.map(|b| b.split_at(4).0.to_owned())
.ok_or(ContenderError::SetupError(
"invalid function call",
Some(format!("{:?}", tx_req.input.input)),
))?
.as_slice(),
);
if !self.gas_limits.contains_key(fn_sig.as_slice()) {

let key = keccak256(tx_req.input.input.to_owned().unwrap_or_default());

if let std::collections::hash_map::Entry::Vacant(_) = self.gas_limits.entry(key) {
let gas_limit = self
.eth_client
.estimate_gas(tx_req)
.await
.map_err(|e| ContenderError::with_err(e, "failed to estimate gas for tx"))?;
self.gas_limits.insert(fn_sig, gas_limit);
self.gas_limits.insert(key, gas_limit);
}
let gas_limit = self
.gas_limits
.get(&fn_sig)
.get(&key)
.ok_or(ContenderError::SetupError(
"failed to lookup gas limit",
None,
Expand All @@ -337,7 +328,7 @@ where
.with_max_fee_per_gas(gas_price + (gas_price / 5))
.with_max_priority_fee_per_gas(gas_price)
.with_chain_id(self.chain_id)
.with_gas_limit(gas_limit + (gas_limit / 6));
.with_gas_limit(gas_limit);

Ok((full_tx, signer))
}
Expand Down Expand Up @@ -396,7 +387,7 @@ where
})?;

println!(
"sending tx {} from={} to={:?} input={} value={}",
"sending tx {} from={} to={:?} input={} value={} gas_limit={}",
tx_envelope.tx_hash(),
tx_req.from.map(|s| s.encode_hex()).unwrap_or_default(),
tx_envelope.to().to(),
Expand All @@ -409,7 +400,11 @@ where
tx_req
.value
.map(|s| s.to_string())
.unwrap_or_else(|| "0".to_owned())
.unwrap_or_else(|| "0".to_owned()),
tx_req
.gas
.map(|g| g.to_string())
.unwrap_or_else(|| "N/A".to_owned())
);

ExecutionPayload::SignedTx(tx_envelope, req.to_owned())
Expand Down
5 changes: 3 additions & 2 deletions scenarios/mempool.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
to = "{SpamMe2}"
from_pool = "redpool"
signature = "consumeGas(uint256 gasAmount)"
args = ["51000"]
args = ["910000"]
fuzz = [{ param = "gasAmount", min = "10000000", max = "30000000" }]

[[spam]]

[spam.tx]
to = "{SpamMe2}"
from_pool = "bluepool"
signature = "consumeGas(uint256 gasAmount)"
args = ["51000"]
args = ["13500000"]
41 changes: 41 additions & 0 deletions scenarios/stress.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[[create]]
bytecode = "0x608060405234801561001057600080fd5b5060408051808201909152600d81526c48656c6c6f2c20576f726c642160981b602082015260009061004290826100e7565b506101a5565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061007257607f821691505b60208210810361009257634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156100e257806000526020600020601f840160051c810160208510156100bf5750805b601f840160051c820191505b818110156100df57600081556001016100cb565b50505b505050565b81516001600160401b0381111561010057610100610048565b6101148161010e845461005e565b84610098565b6020601f82116001811461014857600083156101305750848201515b600019600385901b1c1916600184901b1784556100df565b600084815260208120601f198516915b828110156101785787850151825560209485019460019092019101610158565b50848210156101965786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b610a72806101b46000396000f3fe6080604052600436106100555760003560e01c806369f86ec81461005a5780638199ba20146100715780639402c00414610091578063a329e8de146100b1578063c5eeaf17146100d1578063fb0e722b146100d9575b600080fd5b34801561006657600080fd5b5061006f610104565b005b34801561007d57600080fd5b5061006f61008c3660046106f6565b61010f565b34801561009d57600080fd5b5061006f6100ac36600461074f565b610413565b3480156100bd57600080fd5b5061006f6100cc3660046107a0565b610444565b61006f6104d7565b3480156100e557600080fd5b506100ee610506565b6040516100fb91906107dd565b60405180910390f35b5b60325a1161010557565b6040805180820190915260068152657373746f726560d01b6020820152610137908390610594565b1561015d5760005b818110156101585761015060008055565b60010161013f565b505050565b6040805180820190915260058152641cdb1bd85960da1b6020820152610184908390610594565b1561019c5760005b818110156101585760010161018c565b6040805180820190915260068152656d73746f726560d01b60208201526101c4908390610594565b156101e55760005b81811015610158576101dd60008052565b6001016101cc565b6040805180820190915260058152641b5b1bd85960da1b602082015261020c908390610594565b1561022157600081156101585760010161018c565b60408051808201909152600381526218591960ea1b6020820152610246908390610594565b1561025b57600081156101585760010161018c565b60408051808201909152600381526239bab160e91b6020820152610280908390610594565b1561029557600081156101585760010161018c565b6040805180820190915260038152621b5d5b60ea1b60208201526102ba908390610594565b156102cf57600081156101585760010161018c565b6040805180820190915260038152623234bb60e91b60208201526102f4908390610594565b1561030957600081156101585760010161018c565b60408051808201909152600981526832b1b932b1b7bb32b960b91b6020820152610334908390610594565b156103545760005b818110156101585761034c6105ee565b60010161033c565b60408051808201909152600981526835b2b1b1b0b5991a9b60b91b602082015261037f908390610594565b1561039457600081156101585760010161018c565b60408051808201909152600781526662616c616e636560c81b60208201526103bd908390610594565b156103d257600081156101585760010161018c565b60408051808201909152600681526531b0b63632b960d11b60208201526103fa908390610594565b1561040f57600081156101585760010161018c565b5050565b60008160405160200161042792919061084a565b6040516020818303038152906040526000908161040f919061091e565b600081116104985760405162461bcd60e51b815260206004820152601a60248201527f476173206d7573742062652067726561746572207468616e2030000000000000604482015260640160405180910390fd5b600060956104a8610a28846109dd565b6104b291906109fe565b9050806000036104c0575060015b60005b8181101561015857600080556001016104c3565b60405141903480156108fc02916000818181858888f19350505050158015610503573d6000803e3d6000fd5b50565b6000805461051390610810565b80601f016020809104026020016040519081016040528092919081815260200182805461053f90610810565b801561058c5780601f106105615761010080835404028352916020019161058c565b820191906000526020600020905b81548152906001019060200180831161056f57829003601f168201915b505050505081565b6000816040516020016105a79190610a20565b60405160208183030381529060405280519060200120836040516020016105ce9190610a20565b604051602081830303815290604052805190602001201490505b92915050565b604080516000808252602082018084527f7b05e003631381b3ecd0222e748a7900c262a008c4b7f002ce4a9f0a190619539052604292820183905260608201839052608082019290925260019060a0016020604051602081039080840390855afa158015610660573d6000803e3d6000fd5b50505050565b634e487b7160e01b600052604160045260246000fd5b60008067ffffffffffffffff84111561069757610697610666565b50604051601f19601f85018116603f0116810181811067ffffffffffffffff821117156106c6576106c6610666565b6040528381529050808284018510156106de57600080fd5b83836020830137600060208583010152509392505050565b6000806040838503121561070957600080fd5b823567ffffffffffffffff81111561072057600080fd5b8301601f8101851361073157600080fd5b6107408582356020840161067c565b95602094909401359450505050565b60006020828403121561076157600080fd5b813567ffffffffffffffff81111561077857600080fd5b8201601f8101841361078957600080fd5b6107988482356020840161067c565b949350505050565b6000602082840312156107b257600080fd5b5035919050565b60005b838110156107d45781810151838201526020016107bc565b50506000910152565b60208152600082518060208401526107fc8160408501602087016107b9565b601f01601f19169190910160400192915050565b600181811c9082168061082457607f821691505b60208210810361084457634e487b7160e01b600052602260045260246000fd5b50919050565b600080845461085881610810565b60018216801561086f5760018114610884576108b4565b60ff19831686528115158202860193506108b4565b87600052602060002060005b838110156108ac57815488820152600190910190602001610890565b505081860193505b50505083516108c78183602088016107b9565b01949350505050565b601f82111561015857806000526020600020601f840160051c810160208510156108f75750805b601f840160051c820191505b818110156109175760008155600101610903565b5050505050565b815167ffffffffffffffff81111561093857610938610666565b61094c816109468454610810565b846108d0565b6020601f82116001811461098057600083156109685750848201515b600019600385901b1c1916600184901b178455610917565b600084815260208120601f198516915b828110156109b05787850151825560209485019460019092019101610990565b50848210156109ce5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b818103818111156105e857634e487b7160e01b600052601160045260246000fd5b600082610a1b57634e487b7160e01b600052601260045260246000fd5b500490565b60008251610a328184602087016107b9565b919091019291505056fea264697066735822122040db52b9a7c8a77f16a18198a6085a3ff5f3e5c378e4a9cd497037d20f775eb864736f6c634300081b0033"
name = "SpamMe3"
from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"

[[spam]]

[spam.tx]
to = "{SpamMe3}"
from_pool = "pool1"
signature = "consumeGas(string memory method, uint256 iterations)"
args = ["sstore", "8000"]
# note: the `fuzz` field will spam the network with `estimateGas` calls; every unique calldata requires a new call
# fuzz = [{ param = "iterations", min = "3000", max = "8000" }]

[[spam]]

[spam.tx]
to = "{SpamMe3}"
from_pool = "pool2"
signature = "consumeGas(string memory method, uint256 iterations)"
args = ["sload", "8000"]
# fuzz = [{ param = "iterations", min = "3000", max = "8000" }]

[[spam]]

[spam.tx]
to = "{SpamMe3}"
from_pool = "pool3"
signature = "consumeGas(string memory method, uint256 iterations)"
args = ["mload", "8000"]
# fuzz = [{ param = "iterations", min = "3000", max = "8000" }]

[[spam]]

[spam.tx]
to = "{SpamMe3}"
from_pool = "pool4"
signature = "consumeGas(string memory method, uint256 iterations)"
args = ["mstore", "8000"]
# fuzz = [{ param = "iterations", min = "3000", max = "8000" }]

0 comments on commit ac8f299

Please sign in to comment.