-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #65 from ethereum-optimism/0xkitsune/refactor
chore(opt8n): Refactor `opt8n` into `cmd` modules
- Loading branch information
Showing
9 changed files
with
297 additions
and
242 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod repl; | ||
pub mod script; | ||
pub mod server; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
use anvil::cmd::NodeArgs; | ||
use clap::{CommandFactory, FromArgMatches, Parser}; | ||
use futures::StreamExt; | ||
use serde::{Deserialize, Serialize}; | ||
use tokio::io::{AsyncBufReadExt, BufReader}; | ||
|
||
use crate::opt8n::{Opt8n, Opt8nArgs}; | ||
|
||
#[derive(Parser, Clone, Debug)] | ||
pub struct ReplArgs { | ||
#[command(flatten)] | ||
opt8n_args: Opt8nArgs, | ||
#[command(flatten)] | ||
pub node_args: NodeArgs, | ||
} | ||
|
||
impl ReplArgs { | ||
pub async fn run(&self) -> color_eyre::Result<()> { | ||
let mut opt8n = Opt8n::new( | ||
Some(self.node_args.clone()), | ||
self.opt8n_args.output.clone(), | ||
self.opt8n_args.genesis.clone(), | ||
) | ||
.await?; | ||
|
||
repl(&mut opt8n).await?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Parser, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] | ||
#[clap(rename_all = "snake_case", infer_subcommands = true, multicall = true)] | ||
pub enum ReplCommand { | ||
#[command(visible_alias = "a")] | ||
Anvil { | ||
#[arg(index = 1, allow_hyphen_values = true)] | ||
args: Vec<String>, | ||
}, | ||
#[command(visible_alias = "c")] | ||
Cast { | ||
#[arg(index = 1, allow_hyphen_values = true)] | ||
args: Vec<String>, | ||
}, | ||
Dump, | ||
RpcEndpoint, | ||
// TODO: implement clear | ||
// TODO: implement reset | ||
#[command(visible_alias = "e")] | ||
Exit, | ||
} | ||
|
||
/// Listens for commands, and new blocks from the block stream. | ||
pub async fn repl(opt8n: &mut Opt8n) -> color_eyre::Result<()> { | ||
let mut new_blocks = opt8n.eth_api.backend.new_block_notifications(); | ||
|
||
loop { | ||
tokio::select! { | ||
command = receive_command() => { | ||
match command { | ||
Ok(ReplCommand::Exit) => break, | ||
Ok(command) => execute(opt8n, command).await?, | ||
Err(e) => eprintln!("Error: {:?}", e), | ||
} | ||
} | ||
|
||
new_block = new_blocks.next() => { | ||
if let Some(new_block) = new_block { | ||
if let Some(block) = opt8n.eth_api.backend.get_block_by_hash(new_block.hash) { | ||
opt8n.generate_execution_fixture(block).await?; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn receive_command() -> color_eyre::Result<ReplCommand> { | ||
let line = BufReader::new(tokio::io::stdin()) | ||
.lines() | ||
.next_line() | ||
.await? | ||
.unwrap(); | ||
let words = shellwords::split(&line)?; | ||
|
||
let matches = ReplCommand::command().try_get_matches_from(words)?; | ||
Ok(ReplCommand::from_arg_matches(&matches)?) | ||
} | ||
|
||
async fn execute(opt8n: &mut Opt8n, command: ReplCommand) -> color_eyre::Result<()> { | ||
match command { | ||
ReplCommand::Dump => { | ||
opt8n.mine_block().await; | ||
} | ||
ReplCommand::Anvil { mut args } => { | ||
args.insert(0, "anvil".to_string()); | ||
let command = NodeArgs::command_for_update(); | ||
let matches = command.try_get_matches_from(args)?; | ||
let node_args = NodeArgs::from_arg_matches(&matches)?; | ||
node_args.run().await?; | ||
} | ||
ReplCommand::Cast { .. } => {} | ||
ReplCommand::RpcEndpoint => { | ||
println!("{}", opt8n.node_handle.http_endpoint()); | ||
} | ||
ReplCommand::Exit => unreachable!(), | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
use anvil::cmd::NodeArgs; | ||
use clap::Parser; | ||
use color_eyre::eyre::eyre; | ||
use futures::StreamExt; | ||
|
||
use crate::opt8n::{Opt8n, Opt8nArgs}; | ||
|
||
#[derive(Parser, Clone, Debug)] | ||
pub struct ScriptArgs { | ||
#[command(flatten)] | ||
opt8n_args: Opt8nArgs, | ||
#[command(flatten)] | ||
inner: forge_script::ScriptArgs, | ||
#[command(flatten)] | ||
pub node_args: NodeArgs, | ||
} | ||
|
||
impl ScriptArgs { | ||
pub async fn run(mut self) -> color_eyre::Result<()> { | ||
let opt8n = Opt8n::new( | ||
Some(self.node_args.clone()), | ||
self.opt8n_args.output.clone(), | ||
self.opt8n_args.genesis.clone(), | ||
) | ||
.await?; | ||
|
||
foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args( | ||
self.inner.opts.silent, | ||
self.inner.json, | ||
))?; | ||
|
||
self.inner.broadcast = true; | ||
self.inner.evm_opts.sender = Some( | ||
opt8n | ||
.node_handle | ||
.genesis_accounts() | ||
.last() | ||
.expect("Could not get genesis account"), | ||
); | ||
self.inner.unlocked = true; | ||
self.inner.evm_opts.fork_url = Some(opt8n.node_handle.http_endpoint()); | ||
|
||
run_script(opt8n, Box::new(self.inner)).await?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
/// Run a Forge script with the given arguments, and generate an execution fixture | ||
/// from the broadcasted transactions. | ||
pub async fn run_script( | ||
opt8n: Opt8n, | ||
script_args: Box<forge_script::ScriptArgs>, | ||
) -> color_eyre::Result<()> { | ||
let mut new_blocks = opt8n.eth_api.backend.new_block_notifications(); | ||
|
||
// Run the forge script and broadcast the transactions to the anvil node | ||
let mut opt8n = broadcast_transactions(opt8n, script_args).await?; | ||
|
||
// Mine the block and generate the execution fixture | ||
opt8n.mine_block().await; | ||
|
||
let block = new_blocks.next().await.ok_or(eyre!("No new block"))?; | ||
if let Some(block) = opt8n.eth_api.backend.get_block_by_hash(block.hash) { | ||
opt8n.generate_execution_fixture(block).await?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn broadcast_transactions( | ||
opt8n: Opt8n, | ||
script_args: Box<forge_script::ScriptArgs>, | ||
) -> color_eyre::Result<Opt8n> { | ||
// Run the script, compile the transactions and broadcast to the anvil instance | ||
let compiled = script_args.preprocess().await?.compile()?; | ||
|
||
let pre_simulation = compiled | ||
.link() | ||
.await? | ||
.prepare_execution() | ||
.await? | ||
.execute() | ||
.await? | ||
.prepare_simulation() | ||
.await?; | ||
|
||
let bundled = pre_simulation.fill_metadata().await?.bundle().await?; | ||
|
||
let tx_count = bundled | ||
.sequence | ||
.sequences() | ||
.iter() | ||
.fold(0, |sum, sequence| sum + sequence.transactions.len()); | ||
|
||
// TODO: break into function | ||
let broadcast = bundled.broadcast(); | ||
|
||
let pending_transactions = tokio::task::spawn(async move { | ||
loop { | ||
let pending_tx_count = opt8n | ||
.eth_api | ||
.txpool_content() | ||
.await | ||
.expect("Failed to get txpool content") | ||
.pending | ||
.len(); | ||
|
||
if pending_tx_count == tx_count { | ||
return opt8n; | ||
} | ||
} | ||
}); | ||
|
||
let opt8n = tokio::select! { | ||
_ = broadcast => { | ||
// TODO: Gracefully handle this error | ||
return Err(eyre!("Script failed early")); | ||
}, | ||
opt8n = pending_transactions => { | ||
opt8n? | ||
} | ||
}; | ||
|
||
Ok(opt8n) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
use anvil::cmd::NodeArgs; | ||
use clap::Parser; | ||
|
||
use crate::opt8n::Opt8nArgs; | ||
|
||
#[derive(Parser, Clone, Debug)] | ||
pub struct ServerArgs { | ||
#[command(flatten)] | ||
pub opt8n_args: Opt8nArgs, | ||
#[command(flatten)] | ||
pub node_args: NodeArgs, | ||
} | ||
|
||
impl ServerArgs { | ||
pub async fn run(&self) -> color_eyre::Result<()> { | ||
unimplemented!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.