diff --git a/Cargo.lock b/Cargo.lock index d8ebc70d4..0b0aecbdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6116,6 +6116,79 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "maptos-execution-util" +version = "0.3.0" +dependencies = [ + "anyhow", + "aptos-crypto", + "aptos-sdk", + "aptos-types", + "m1-da-light-node-util", + "rand 0.7.3", + "serde_json", + "tempfile", +] + +[[package]] +name = "maptos-opt-executor" +version = "0.3.0" +dependencies = [ + "anyhow", + "aptos-api", + "aptos-api-types", + "aptos-bitvec", + "aptos-block-executor", + "aptos-cached-packages", + "aptos-config", + "aptos-consensus-types", + "aptos-crypto", + "aptos-db", + "aptos-executor", + "aptos-executor-test-helpers", + "aptos-executor-types", + "aptos-faucet-core", + "aptos-framework", + "aptos-language-e2e-tests", + "aptos-mempool", + "aptos-sdk", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "aptos-vm-logging", + "aptos-vm-types", + "async-channel", + "async-trait", + "bcs 0.1.4", + "borsh", + "bytes", + "chrono", + "clap 4.5.4", + "derive_more", + "dirs 3.0.2", + "env_logger 0.11.3", + "fail 0.5.1", + "futures", + "hex", + "lazy_static", + "log", + "maptos-execution-util", + "poem", + "poem-openapi", + "rand 0.7.3", + "rand_core 0.5.1", + "schemars", + "serde", + "serde_json", + "tempfile", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber 0.3.18", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -6237,7 +6310,7 @@ version = "0.3.0" dependencies = [ "anyhow", "aptos-sdk", - "monza-execution-util", + "maptos-execution-util", "once_cell", "rand 0.7.3", "tokio", @@ -6249,24 +6322,10 @@ name = "monza-config" version = "0.3.0" dependencies = [ "anyhow", - "monza-execution-util", + "maptos-execution-util", "tokio", ] -[[package]] -name = "monza-execution-util" -version = "0.3.0" -dependencies = [ - "anyhow", - "aptos-crypto", - "aptos-sdk", - "aptos-types", - "m1-da-light-node-util", - "rand 0.7.3", - "serde_json", - "tempfile", -] - [[package]] name = "monza-executor" version = "0.3.0" @@ -6309,8 +6368,8 @@ dependencies = [ "hex", "lazy_static", "log", - "monza-execution-util", - "monza-opt-executor", + "maptos-execution-util", + "maptos-opt-executor", "poem-openapi", "rand 0.7.3", "rand_core 0.5.1", @@ -6393,65 +6452,6 @@ dependencies = [ "tracing-subscriber 0.3.18", ] -[[package]] -name = "monza-opt-executor" -version = "0.3.0" -dependencies = [ - "anyhow", - "aptos-api", - "aptos-api-types", - "aptos-bitvec", - "aptos-block-executor", - "aptos-cached-packages", - "aptos-config", - "aptos-consensus-types", - "aptos-crypto", - "aptos-db", - "aptos-executor", - "aptos-executor-test-helpers", - "aptos-executor-types", - "aptos-faucet-core", - "aptos-framework", - "aptos-language-e2e-tests", - "aptos-mempool", - "aptos-sdk", - "aptos-storage-interface", - "aptos-temppath", - "aptos-types", - "aptos-vm", - "aptos-vm-genesis", - "aptos-vm-logging", - "aptos-vm-types", - "async-channel", - "async-trait", - "bcs 0.1.4", - "borsh", - "bytes", - "chrono", - "clap 4.5.4", - "derive_more", - "dirs 3.0.2", - "env_logger 0.11.3", - "fail 0.5.1", - "futures", - "hex", - "lazy_static", - "log", - "monza-execution-util", - "poem", - "poem-openapi", - "rand 0.7.3", - "rand_core 0.5.1", - "schemars", - "serde", - "serde_json", - "tempfile", - "thiserror", - "tokio", - "tracing", - "tracing-subscriber 0.3.18", -] - [[package]] name = "more-asserts" version = "0.3.1" @@ -10010,6 +10010,153 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" +[[package]] +name = "suzuka-client" +version = "0.3.0" +dependencies = [ + "anyhow", + "aptos-sdk", + "maptos-execution-util", + "once_cell", + "rand 0.7.3", + "tokio", + "url", +] + +[[package]] +name = "suzuka-config" +version = "0.3.0" +dependencies = [ + "anyhow", + "maptos-execution-util", + "tokio", +] + +[[package]] +name = "suzuka-executor" +version = "0.3.0" +dependencies = [ + "anyhow", + "aptos-api", + "aptos-api-types", + "aptos-bitvec", + "aptos-block-executor", + "aptos-config", + "aptos-consensus-types", + "aptos-crypto", + "aptos-db", + "aptos-executor", + "aptos-executor-test-helpers", + "aptos-executor-types", + "aptos-framework", + "aptos-language-e2e-tests", + "aptos-mempool", + "aptos-sdk", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "aptos-vm-logging", + "aptos-vm-types", + "async-channel", + "async-trait", + "bcs 0.1.4", + "borsh", + "bytes", + "chrono", + "clap 4.5.4", + "derive_more", + "dirs 5.0.1", + "fail 0.5.1", + "futures", + "hex", + "lazy_static", + "log", + "maptos-execution-util", + "maptos-opt-executor", + "poem-openapi", + "rand 0.7.3", + "rand_core 0.5.1", + "schemars", + "serde", + "serde_json", + "tempfile", + "thiserror", + "tokio", + "tonic", + "tracing", +] + +[[package]] +name = "suzuka-fin-executor" +version = "0.3.0" +dependencies = [ + "anyhow", + "aptos-api", + "aptos-api-types", + "aptos-bitvec", + "aptos-block-executor", + "aptos-config", + "aptos-consensus-types", + "aptos-crypto", + "aptos-db", + "aptos-executor", + "aptos-executor-test-helpers", + "aptos-executor-types", + "aptos-framework", + "aptos-language-e2e-tests", + "aptos-mempool", + "aptos-sdk", + "aptos-storage-interface", + "aptos-temppath", + "aptos-types", + "aptos-vm", + "aptos-vm-genesis", + "aptos-vm-logging", + "aptos-vm-types", + "async-trait", + "borsh", + "bytes", + "chrono", + "clap 4.5.4", + "derive_more", + "dirs 5.0.1", + "fail 0.5.1", + "hex", + "lazy_static", + "log", + "poem-openapi", + "rand 0.7.3", + "rand_core 0.5.1", + "schemars", + "serde", + "serde_json", + "tempfile", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "suzuka-full-node" +version = "0.3.0" +dependencies = [ + "anyhow", + "async-channel", + "env_logger 0.11.3", + "m1-da-light-node-client", + "movement-types", + "serde_json", + "sha2 0.10.8", + "suzuka-executor", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-subscriber 0.3.18", +] + [[package]] name = "syn" version = "0.15.44" diff --git a/Cargo.toml b/Cargo.toml index c80f46065..69826273c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,12 @@ [workspace] +resolver = "2" + members = [ + "protocol-units/execution/maptos/opt-executor", + "protocol-units/execution/maptos/util", "protocol-units/execution/monza/*", + "protocol-units/execution/suzuka/*", "protocol-units/da/m1/*", @@ -13,7 +18,13 @@ members = [ "util/buildtime/buildtime-helpers", "util/buildtime/buildtime-macros", - "networks/monza/*", + "networks/monza/monza-client", + "networks/monza/monza-config", + "networks/monza/monza-full-node", + + "networks/suzuka/suzuka-client", + "networks/suzuka/suzuka-config", + "networks/suzuka/suzuka-full-node", ] [workspace.package] @@ -29,11 +40,15 @@ rust-version = "1.75" [workspace.dependencies] # internal +## execution/aptos +maptos-opt-executor = { path = "protocol-units/execution/maptos/opt-executor" } +maptos-execution-util = { path = "protocol-units/execution/maptos/util" } ## execution/monza -monza-opt-executor = { path = "protocol-units/execution/monza/opt-executor" } monza-fin-executor = { path = "protocol-units/execution/monza/fin-executor" } -monza-execution-util = { path = "protocol-units/execution/monza/util" } monza-executor = { path = "protocol-units/execution/monza/executor" } +## execution/suzuka +suzuka-fin-executor = { path = "protocol-units/execution/suzuka/fin-executor" } +suzuka-executor = { path = "protocol-units/execution/suzuka/executor" } ## types movement-types = { path = "util/movement-types"} diff --git a/networks/monza/monza-client/Cargo.toml b/networks/monza/monza-client/Cargo.toml index 3cd893358..f732bc204 100644 --- a/networks/monza/monza-client/Cargo.toml +++ b/networks/monza/monza-client/Cargo.toml @@ -18,7 +18,7 @@ once_cell = { workspace = true } tokio = { workspace = true } url = { workspace = true } rand = { workspace = true } -monza-execution-util = { workspace = true } +maptos-execution-util = { workspace = true } [lints] workspace = true diff --git a/networks/monza/monza-client/src/tests/mod.rs b/networks/monza/monza-client/src/tests/mod.rs index 5d8365c6c..8c0912b61 100644 --- a/networks/monza/monza-client/src/tests/mod.rs +++ b/networks/monza/monza-client/src/tests/mod.rs @@ -8,15 +8,15 @@ use once_cell::sync::Lazy; use std::str::FromStr; use url::Url; -static MONZA_CONFIG : Lazy = Lazy::new(|| { - monza_execution_util::config::Config::try_from_env().context("Failed to create the config").unwrap() +static MONZA_CONFIG : Lazy = Lazy::new(|| { + maptos_execution_util::config::Config::try_from_env().context("Failed to create the config").unwrap() }); // :!:>section_1c static NODE_URL: Lazy = Lazy::new(|| { Url::from_str( - format!("http://{}", MONZA_CONFIG.monza_config.aptos_rest_listen_url.as_str()).as_str() + format!("http://{}", MONZA_CONFIG.aptos_config.aptos_rest_listen_url.as_str()).as_str() ).unwrap() }); @@ -24,7 +24,7 @@ static NODE_URL: Lazy = Lazy::new(|| { static FAUCET_URL: Lazy = Lazy::new(|| { Url::from_str( - format!("http://{}", MONZA_CONFIG.monza_config.aptos_faucet_listen_url.as_str()).as_str() + format!("http://{}", MONZA_CONFIG.aptos_config.aptos_faucet_listen_url.as_str()).as_str() ).unwrap() }); diff --git a/networks/monza/monza-config/Cargo.toml b/networks/monza/monza-config/Cargo.toml index 87f561974..0a27c293c 100644 --- a/networks/monza/monza-config/Cargo.toml +++ b/networks/monza/monza-config/Cargo.toml @@ -17,6 +17,6 @@ name = "monza-config" path = "src/bin/monza_config.rs" [dependencies] -monza-execution-util = { workspace = true } +maptos-execution-util = { workspace = true } anyhow = { workspace = true } tokio = { workspace = true } \ No newline at end of file diff --git a/networks/monza/monza-config/src/lib.rs b/networks/monza/monza-config/src/lib.rs index a69fb82ad..b5af4fea5 100644 --- a/networks/monza/monza-config/src/lib.rs +++ b/networks/monza/monza-config/src/lib.rs @@ -1,11 +1,11 @@ #[derive(Debug, Clone, PartialEq, Eq)] pub struct Config { - pub execution_config : monza_execution_util::config::Config, + pub execution_config : maptos_execution_util::config::Config, } impl Config { - pub fn new(execution_config : monza_execution_util::config::Config) -> Self { + pub fn new(execution_config : maptos_execution_util::config::Config) -> Self { Self { execution_config, } @@ -13,7 +13,7 @@ impl Config { pub fn try_from_env() -> Result { - let execution_config = monza_execution_util::config::Config::try_from_env()?; + let execution_config = maptos_execution_util::config::Config::try_from_env()?; Ok(Self { execution_config, diff --git a/networks/monza/monza-full-node/src/main.rs b/networks/monza/monza-full-node/src/main.rs index 7275f03ef..4a292db7b 100644 --- a/networks/monza/monza-full-node/src/main.rs +++ b/networks/monza/monza-full-node/src/main.rs @@ -1,7 +1,7 @@ use anyhow::Context; use monza_full_node::{ MonzaFullNode, - partial::MonzaPartialFullNode, + partial::MonzaPartialNode, }; #[tokio::main] @@ -18,7 +18,7 @@ async fn main() -> Result<(), anyhow::Error> { } - let executor = MonzaPartialFullNode::try_from_env().await.context( + let executor = MonzaPartialNode::try_from_env().await.context( "Failed to create the executor" )?; diff --git a/networks/monza/monza-full-node/src/partial.rs b/networks/monza/monza-full-node/src/partial.rs index 1695a54e9..95094b924 100644 --- a/networks/monza/monza-full-node/src/partial.rs +++ b/networks/monza/monza-full-node/src/partial.rs @@ -22,14 +22,14 @@ use movement_types::Block; #[derive(Clone)] -pub struct MonzaPartialFullNode { +pub struct MonzaPartialNode { executor: T, transaction_sender : Sender, pub transaction_receiver : Receiver, light_node_client: Arc>>, } -impl MonzaPartialFullNode { +impl MonzaPartialNode { pub fn new(executor : T, light_node_client: LightNodeServiceClient) -> Self { let (transaction_sender, transaction_receiver) = async_channel::unbounded(); @@ -180,7 +180,7 @@ impl MonzaPartialFullNode { } -impl MonzaFullNode for MonzaPartialFullNode { +impl MonzaFullNode for MonzaPartialNode { /// Runs the services until crash or shutdown. async fn run_services(&self) -> Result<(), anyhow::Error> { @@ -217,7 +217,7 @@ impl MonzaFullNode for MonzaPartialFull } -impl MonzaPartialFullNode { +impl MonzaPartialNode { pub async fn try_from_env() -> Result { let (tx, _) = async_channel::unbounded(); diff --git a/networks/suzuka/suzuka-client/Cargo.toml b/networks/suzuka/suzuka-client/Cargo.toml new file mode 100644 index 000000000..316106b3e --- /dev/null +++ b/networks/suzuka/suzuka-client/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "suzuka-client" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +publish = { workspace = true } +rust-version = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aptos-sdk = { workspace = true } +anyhow = { workspace = true } +once_cell = { workspace = true } +tokio = { workspace = true } +url = { workspace = true } +rand = { workspace = true } +maptos-execution-util = { workspace = true } + +[lints] +workspace = true diff --git a/networks/suzuka/suzuka-client/src/lib.rs b/networks/suzuka/suzuka-client/src/lib.rs new file mode 100644 index 000000000..5f975ac85 --- /dev/null +++ b/networks/suzuka/suzuka-client/src/lib.rs @@ -0,0 +1,4 @@ +#[cfg(test)] +pub mod tests; + +pub use aptos_sdk::*; \ No newline at end of file diff --git a/networks/suzuka/suzuka-client/src/tests/mod.rs b/networks/suzuka/suzuka-client/src/tests/mod.rs new file mode 100644 index 000000000..5676c069c --- /dev/null +++ b/networks/suzuka/suzuka-client/src/tests/mod.rs @@ -0,0 +1,138 @@ +use anyhow::{Context, Result}; +use crate::{ + coin_client::CoinClient, + rest_client::{Client, FaucetClient}, + types::LocalAccount, +}; +use once_cell::sync::Lazy; +use std::str::FromStr; +use url::Url; + +static SUZUKA_CONFIG : Lazy = Lazy::new(|| { + maptos_execution_util::config::Config::try_from_env().context("Failed to create the config").unwrap() +}); + +// :!:>section_1c +static NODE_URL: Lazy = Lazy::new(|| { + + Url::from_str( + format!("http://{}", SUZUKA_CONFIG.aptos_config.aptos_rest_listen_url.as_str()).as_str() + ).unwrap() + +}); + +static FAUCET_URL: Lazy = Lazy::new(|| { + + Url::from_str( + format!("http://{}", SUZUKA_CONFIG.aptos_config.aptos_faucet_listen_url.as_str()).as_str() + ).unwrap() + +}); +// <:!:section_1c + +#[tokio::test] +async fn test_example_interaction() -> Result<()> { + // :!:>section_1a + let rest_client = Client::new(NODE_URL.clone()); + let faucet_client = FaucetClient::new(FAUCET_URL.clone(), NODE_URL.clone()); // <:!:section_1a + + // :!:>section_1b + let coin_client = CoinClient::new(&rest_client); // <:!:section_1b + + // Create two accounts locally, Alice and Bob. + // :!:>section_2 + let mut alice = LocalAccount::generate(&mut rand::rngs::OsRng); + let bob = LocalAccount::generate(&mut rand::rngs::OsRng); // <:!:section_2 + + // Print account addresses. + println!("\n=== Addresses ==="); + println!("Alice: {}", alice.address().to_hex_literal()); + println!("Bob: {}", bob.address().to_hex_literal()); + + // Create the accounts on chain, but only fund Alice. + // :!:>section_3 + faucet_client + .fund(alice.address(), 100_000_000) + .await + .context("Failed to fund Alice's account")?; + faucet_client + .create_account(bob.address()) + .await + .context("Failed to fund Bob's account")?; // <:!:section_3 + + // Print initial balances. + println!("\n=== Initial Balances ==="); + println!( + "Alice: {:?}", + coin_client + .get_account_balance(&alice.address()) + .await + .context("Failed to get Alice's account balance")? + ); + println!( + "Bob: {:?}", + coin_client + .get_account_balance(&bob.address()) + .await + .context("Failed to get Bob's account balance")? + ); + + // Have Alice send Bob some coins. + let txn_hash = coin_client + .transfer(&mut alice, bob.address(), 1_000, None) + .await + .context("Failed to submit transaction to transfer coins")?; + rest_client + .wait_for_transaction(&txn_hash) + .await + .context("Failed when waiting for the transfer transaction")?; + + // Print intermediate balances. + println!("\n=== Intermediate Balances ==="); + // :!:>section_4 + println!( + "Alice: {:?}", + coin_client + .get_account_balance(&alice.address()) + .await + .context("Failed to get Alice's account balance the second time")? + ); + println!( + "Bob: {:?}", + coin_client + .get_account_balance(&bob.address()) + .await + .context("Failed to get Bob's account balance the second time")? + ); // <:!:section_4 + + // Have Alice send Bob some more coins. + // :!:>section_5 + let txn_hash = coin_client + .transfer(&mut alice, bob.address(), 1_000, None) + .await + .context("Failed to submit transaction to transfer coins")?; // <:!:section_5 + // :!:>section_6 + rest_client + .wait_for_transaction(&txn_hash) + .await + .context("Failed when waiting for the transfer transaction")?; // <:!:section_6 + + // Print final balances. + println!("\n=== Final Balances ==="); + println!( + "Alice: {:?}", + coin_client + .get_account_balance(&alice.address()) + .await + .context("Failed to get Alice's account balance the second time")? + ); + println!( + "Bob: {:?}", + coin_client + .get_account_balance(&bob.address()) + .await + .context("Failed to get Bob's account balance the second time")? + ); + + Ok(()) +} \ No newline at end of file diff --git a/networks/suzuka/suzuka-config/Cargo.toml b/networks/suzuka/suzuka-config/Cargo.toml new file mode 100644 index 000000000..e453567a0 --- /dev/null +++ b/networks/suzuka/suzuka-config/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "suzuka-config" +description = "Config tool for Suzuka" +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } + +version = { workspace = true } + +[lib] +path = "src/lib.rs" + +[[bin]] +name = "suzuka-config" +path = "src/bin/suzuka_config.rs" + +[dependencies] +maptos-execution-util = { workspace = true } +anyhow = { workspace = true } +tokio = { workspace = true } \ No newline at end of file diff --git a/networks/suzuka/suzuka-config/src/bin/suzuka_config.rs b/networks/suzuka/suzuka-config/src/bin/suzuka_config.rs new file mode 100644 index 000000000..8416af390 --- /dev/null +++ b/networks/suzuka/suzuka-config/src/bin/suzuka_config.rs @@ -0,0 +1,10 @@ +use suzuka_config::Config; + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + // read any values from env, but populate the default values if they are not present + let config = Config::try_from_env()?; + // write the values to the env + print!("{}", config.write_bash_export_string()?); + Ok(()) +} \ No newline at end of file diff --git a/networks/suzuka/suzuka-config/src/lib.rs b/networks/suzuka/suzuka-config/src/lib.rs new file mode 100644 index 000000000..b5af4fea5 --- /dev/null +++ b/networks/suzuka/suzuka-config/src/lib.rs @@ -0,0 +1,36 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Config { + pub execution_config : maptos_execution_util::config::Config, +} + +impl Config { + + pub fn new(execution_config : maptos_execution_util::config::Config) -> Self { + Self { + execution_config, + } + } + + pub fn try_from_env() -> Result { + + let execution_config = maptos_execution_util::config::Config::try_from_env()?; + + Ok(Self { + execution_config, + }) + + } + + pub fn write_to_env(&self) -> Result<(), anyhow::Error>{ + self.execution_config.write_to_env()?; + Ok(()) + } + + pub fn write_bash_export_string(&self) -> Result { + Ok(format!( + "{}", + self.execution_config.write_bash_export_string()? + )) + } + +} \ No newline at end of file diff --git a/networks/suzuka/suzuka-full-node/Cargo.toml b/networks/suzuka/suzuka-full-node/Cargo.toml new file mode 100644 index 000000000..32f734865 --- /dev/null +++ b/networks/suzuka/suzuka-full-node/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "suzuka-full-node" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +publish = { workspace = true } +rust-version = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +suzuka-executor = { workspace = true } +m1-da-light-node-client = { workspace = true } +async-channel = { workspace = true } +serde_json = { workspace = true } +anyhow = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +sha2 = { workspace = true } +tonic = { workspace = true } +movement-types = { workspace = true } + +env_logger = { workspace = true, optional = true } +tracing = { workspace = true, optional = true } +tracing-subscriber = { workspace = true, optional = true } + +[features] +default = [ + "logging" +] +logging = [ + "env_logger", + "tracing", + "tracing-subscriber" +] + + +[lints] +workspace = true diff --git a/networks/suzuka/suzuka-full-node/src/lib.rs b/networks/suzuka/suzuka-full-node/src/lib.rs new file mode 100644 index 000000000..544469b95 --- /dev/null +++ b/networks/suzuka/suzuka-full-node/src/lib.rs @@ -0,0 +1,30 @@ +pub mod partial; + +#[cfg(test)] +pub mod tests; + +pub trait SuzukaFullNode { + + /// Runs the services until crash or shutdown. + async fn run_services(&self) -> Result<(), anyhow::Error>; + + /// Runs the background tasks until crash or shutdown. + async fn run_background_tasks(&self) -> Result<(), anyhow::Error>; + + /// Runs the executor until crash or shutdown. + async fn run_executor(&self) -> Result<(), anyhow::Error>; + + /// Runs the full node until crash or shutdown. + async fn run(&self) -> Result<(), anyhow::Error> { + + // run services and executor concurrently + tokio::try_join!( + self.run_background_tasks(), + self.run_services(), + self.run_executor() + )?; + + Ok(()) + } + +} \ No newline at end of file diff --git a/networks/suzuka/suzuka-full-node/src/main.rs b/networks/suzuka/suzuka-full-node/src/main.rs new file mode 100644 index 000000000..d98db0ea3 --- /dev/null +++ b/networks/suzuka/suzuka-full-node/src/main.rs @@ -0,0 +1,31 @@ +use anyhow::Context; +use suzuka_full_node::{ + SuzukaFullNode, + partial::SuzukaPartialNode, +}; + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + + #[cfg(feature = "logging")] + { + use tracing_subscriber::EnvFilter; + + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("info"))) + .init(); + + } + + let executor = SuzukaPartialNode::try_from_env().await.context( + "Failed to create the executor" + )?; + + executor.run().await.context( + "Failed to run the executor" + )?; + + Ok(()) + +} diff --git a/networks/suzuka/suzuka-full-node/src/partial.rs b/networks/suzuka/suzuka-full-node/src/partial.rs new file mode 100644 index 000000000..6d880cc49 --- /dev/null +++ b/networks/suzuka/suzuka-full-node/src/partial.rs @@ -0,0 +1,231 @@ +use std::{sync::Arc, time::Duration}; + +use anyhow::Context; +use suzuka_executor::{ + SuzukaExecutor, + ExecutableBlock, + HashValue, + FinalityMode, + Transaction, + SignatureVerifiedTransaction, + SignedTransaction, + ExecutableTransactions, + v1::SuzukaExecutorV1, +}; +use m1_da_light_node_client::*; +use async_channel::{Sender, Receiver}; +use sha2::Digest; +use crate::*; +use tokio_stream::StreamExt; +use tokio::sync::RwLock; +use movement_types::Block; + + +#[derive(Clone)] +pub struct SuzukaPartialNode { + executor: T, + transaction_sender : Sender, + pub transaction_receiver : Receiver, + light_node_client: Arc>>, +} + +impl SuzukaPartialNode { + + pub fn new(executor : T, light_node_client: LightNodeServiceClient) -> Self { + let (transaction_sender, transaction_receiver) = async_channel::unbounded(); + Self { + executor : executor, + transaction_sender, + transaction_receiver, + light_node_client : Arc::new(RwLock::new(light_node_client)), + } + } + + pub async fn bind_transaction_channel(&mut self) -> Result<(), anyhow::Error> { + self.executor.set_tx_channel(self.transaction_sender.clone()).await?; + Ok(()) + } + + pub async fn bound(executor : T, light_node_client: LightNodeServiceClient) -> Result { + let mut node = Self::new(executor, light_node_client); + node.bind_transaction_channel().await?; + Ok(node) + } + + pub async fn tick_write_transactions_to_da(&self) -> Result<(), anyhow::Error> { + + // limit the total time batching transactions + let start_time = std::time::Instant::now(); + let end_time = start_time + std::time::Duration::from_millis(100); + + let mut transactions = Vec::new(); + + + while let Ok(transaction_result) = tokio::time::timeout(Duration::from_millis(100), self.transaction_receiver.recv()).await { + + match transaction_result { + Ok(transaction) => { + println!("Got transaction: {:?}", transaction); + let serialized_transaction = serde_json::to_vec(&transaction)?; + transactions.push(BlobWrite { + data: serialized_transaction + }); + }, + Err(_) => { + break; + } + } + + if std::time::Instant::now() > end_time { + break; + } + } + + if transactions.len() > 0 { + let client_ptr = self.light_node_client.clone(); + let mut light_node_client = client_ptr.write().await; + light_node_client.batch_write( + BatchWriteRequest { + blobs: transactions + } + ).await?; + println!("Wrote transactions to DA"); + } + + Ok(()) + + + } + + pub async fn write_transactions_to_da(&self) -> Result<(), anyhow::Error> { + + loop { + self.tick_write_transactions_to_da().await?; + } + + } + + // receive transactions from the transaction channel and send them to be executed + // ! This assumes the m1 da light node is running sequencer mode + pub async fn read_blocks_from_da(&self) -> Result<(), anyhow::Error> { + + let block_head_height = self.executor.get_block_head_height().await?; + + let mut stream = { + let client_ptr = self.light_node_client.clone(); + let mut light_node_client = client_ptr.write().await; + light_node_client.stream_read_from_height( + StreamReadFromHeightRequest { + height: block_head_height, + } + ).await? + }.into_inner(); + + while let Some(blob) = stream.next().await { + + println!("Stream hot!"); + // get the block + let block_bytes = match blob?.blob.ok_or(anyhow::anyhow!("No blob in response"))?.blob_type.ok_or(anyhow::anyhow!("No blob type in response"))? { + blob_response::BlobType::SequencedBlobBlock(blob) => { + blob.data + }, + _ => { anyhow::bail!("Invalid blob type in response") } + }; + + // get the block + let block : Block = serde_json::from_slice(&block_bytes)?; + println!("Received block: {:?}", block); + + // get the transactions + let mut block_transactions = Vec::new(); + for transaction in block.transactions { + let signed_transaction : SignedTransaction = serde_json::from_slice(&transaction.0)?; + let signature_verified_transaction = SignatureVerifiedTransaction::Valid( + Transaction::UserTransaction( + signed_transaction + ) + ); + block_transactions.push(signature_verified_transaction); + } + + // form the executable transactions vec + let block = ExecutableTransactions::Unsharded( + block_transactions + ); + + // hash the block bytes + let mut hasher = sha2::Sha256::new(); + hasher.update(&block_bytes); + let slice = hasher.finalize(); + let block_hash = HashValue::from_slice(slice.as_slice())?; + + // form the executable block and execute it + let executable_block = ExecutableBlock::new( + block_hash, + block + ); + let block_id = executable_block.block_id; + self.executor.execute_block( + &FinalityMode::Opt, + executable_block + ).await?; + + println!("Executed block: {:?}", block_id); + + } + + Ok(()) + + } + +} + +impl SuzukaFullNode for SuzukaPartialNode { + + /// Runs the services until crash or shutdown. + async fn run_services(&self) -> Result<(), anyhow::Error> { + + self.executor.run_service().await?; + + Ok(()) + + } + + /// Runs the background tasks until crash or shutdown. + async fn run_background_tasks(&self) -> Result<(), anyhow::Error> { + + self.executor.run_background_tasks().await?; + + Ok(()) + + } + + // ! Currently this only implements opt. + /// Runs the executor until crash or shutdown. + async fn run_executor(&self) -> Result<(), anyhow::Error> { + + // wait for both tasks to finish + tokio::try_join!( + self.write_transactions_to_da(), + self.read_blocks_from_da() + )?; + + Ok(()) + + + } + +} + +impl SuzukaPartialNode { + + pub async fn try_from_env() -> Result { + let (tx, _) = async_channel::unbounded(); + let light_node_client = LightNodeServiceClient::connect("http://[::1]:30730").await?; + let executor = SuzukaExecutorV1::try_from_env(tx).await.context( + "Failed to get executor from environment" + )?; + Self::bound(executor, light_node_client).await + } + +} \ No newline at end of file diff --git a/networks/suzuka/suzuka-full-node/src/tests/e2e/mod.rs b/networks/suzuka/suzuka-full-node/src/tests/e2e/mod.rs new file mode 100644 index 000000000..27122b9b2 --- /dev/null +++ b/networks/suzuka/suzuka-full-node/src/tests/e2e/mod.rs @@ -0,0 +1 @@ +pub mod remote_da; \ No newline at end of file diff --git a/networks/suzuka/suzuka-full-node/src/tests/e2e/remote_da.rs b/networks/suzuka/suzuka-full-node/src/tests/e2e/remote_da.rs new file mode 100644 index 000000000..e69de29bb diff --git a/networks/suzuka/suzuka-full-node/src/tests/mod.rs b/networks/suzuka/suzuka-full-node/src/tests/mod.rs new file mode 100644 index 000000000..8b8581b98 --- /dev/null +++ b/networks/suzuka/suzuka-full-node/src/tests/mod.rs @@ -0,0 +1 @@ +pub mod e2e; \ No newline at end of file diff --git a/protocol-units/execution/monza/opt-executor/Cargo.toml b/protocol-units/execution/maptos/opt-executor/Cargo.toml similarity index 96% rename from protocol-units/execution/monza/opt-executor/Cargo.toml rename to protocol-units/execution/maptos/opt-executor/Cargo.toml index 30bc48cd6..40afd1406 100644 --- a/protocol-units/execution/monza/opt-executor/Cargo.toml +++ b/protocol-units/execution/maptos/opt-executor/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "monza-opt-executor" +name = "maptos-opt-executor" description = "Executor for the AptosVM" authors = { workspace = true } edition = { workspace = true } @@ -60,7 +60,7 @@ aptos-faucet-core = { workspace = true } aptos-cached-packages = { workspace = true } futures = { workspace = true } async-channel = { workspace = true } -monza-execution-util = { workspace = true } +maptos-execution-util = { workspace = true } dirs = { workspace = true } tempfile = { workspace = true } diff --git a/protocol-units/execution/monza/opt-executor/src/executor.rs b/protocol-units/execution/maptos/opt-executor/src/executor.rs similarity index 94% rename from protocol-units/execution/monza/opt-executor/src/executor.rs rename to protocol-units/execution/maptos/opt-executor/src/executor.rs index cf2694190..a85866d24 100644 --- a/protocol-units/execution/monza/opt-executor/src/executor.rs +++ b/protocol-units/execution/maptos/opt-executor/src/executor.rs @@ -61,8 +61,8 @@ pub struct Executor { pub node_config: NodeConfig, /// Context pub context : Arc, - /// The Monza configuration. - pub monza_config : monza_execution_util::config::just_monza::Config, + /// The Aptos VM configuration. + pub aptos_config : maptos_execution_util::config::just_aptos::Config, } @@ -75,10 +75,10 @@ impl Executor { mempool_client_sender: MempoolClientSender, mempool_client_receiver: futures_mpsc::Receiver, node_config: NodeConfig, - monza_config : monza_execution_util::config::just_monza::Config + aptos_config : maptos_execution_util::config::just_aptos::Config ) -> Self { - let (_aptos_db, reader_writer) = DbReaderWriter::wrap(AptosDB::new_for_test(&monza_config.aptos_db_path)); + let (_aptos_db, reader_writer) = DbReaderWriter::wrap(AptosDB::new_for_test(&aptos_config.aptos_db_path)); let core_mempool = Arc::new(RwLock::new(CoreMempool::new(&node_config))); let reader = reader_writer.reader.clone(); Self { @@ -90,13 +90,13 @@ impl Executor { node_config : node_config.clone(), mempool_client_receiver : Arc::new(RwLock::new(mempool_client_receiver)), context : Arc::new(Context::new( - monza_config.chain_id.clone(), + aptos_config.chain_id.clone(), reader, mempool_client_sender, node_config , None )), - monza_config + aptos_config } } @@ -179,10 +179,10 @@ impl Executor { mempool_client_sender: MempoolClientSender, mempool_client_receiver: futures_mpsc::Receiver, node_config: NodeConfig, - monza_config : monza_execution_util::config::just_monza::Config + aptos_config : maptos_execution_util::config::just_aptos::Config ) -> Result { - let (db_rw, signer) = Self::bootstrap_empty_db(&monza_config.aptos_db_path, monza_config.chain_id.clone(), &monza_config.aptos_public_key)?; + let (db_rw, signer) = Self::bootstrap_empty_db(&aptos_config.aptos_db_path, aptos_config.chain_id.clone(), &aptos_config.aptos_public_key)?; let reader = db_rw.reader.clone(); let core_mempool = Arc::new(RwLock::new(CoreMempool::new(&node_config))); @@ -195,13 +195,13 @@ impl Executor { mempool_client_receiver : Arc::new(RwLock::new(mempool_client_receiver)), node_config : node_config.clone(), context : Arc::new(Context::new( - monza_config.chain_id.clone(), + aptos_config.chain_id.clone(), reader, mempool_client_sender, node_config, None )), - monza_config + aptos_config }) } @@ -211,15 +211,15 @@ impl Executor { // use the default signer, block executor, and mempool let (mempool_client_sender, mempool_client_receiver) = futures_mpsc::channel::(10); let node_config = NodeConfig::default(); - let monza_config = monza_execution_util::config::just_monza::Config::try_from_env().context( - "Failed to create Monza config" + let aptos_config = maptos_execution_util::config::just_aptos::Config::try_from_env().context( + "Failed to create Aptos config" )?; Self::bootstrap( mempool_client_sender, mempool_client_receiver, node_config, - monza_config + aptos_config ) } @@ -307,8 +307,8 @@ impl Executor { { // log out to tracing tracing::info!( - "Starting monza-opt-executor services at: {:?}", - self.monza_config.aptos_rest_listen_url + "Starting maptos-opt-executor services at: {:?}", + self.aptos_config.aptos_rest_listen_url ); } @@ -316,7 +316,7 @@ impl Executor { let context = self.try_get_context().await?; let api_service = get_api_service(context).server( - format!("http://{:?}", self.monza_config.aptos_rest_listen_url) + format!("http://{:?}", self.aptos_config.aptos_rest_listen_url) ); let ui = api_service.swagger_ui(); @@ -326,7 +326,7 @@ impl Executor { .nest("/v1", api_service) .nest("/spec", ui); Server::new(TcpListener::bind( - self.monza_config.aptos_rest_listen_url.clone() + self.aptos_config.aptos_rest_listen_url.clone() )) .run(app) .await.map_err( @@ -502,7 +502,7 @@ mod tests { let executor = Executor::try_from_env()?; let block_id = HashValue::random(); let tx = SignatureVerifiedTransaction::Valid(Transaction::UserTransaction( - create_signed_transaction(0, executor.monza_config.chain_id.clone()), + create_signed_transaction(0, executor.aptos_config.chain_id.clone()), )); let txs = ExecutableTransactions::Unsharded(vec![tx]); let block = ExecutableBlock::new(block_id.clone(), txs); @@ -520,7 +520,7 @@ mod tests { // Initialize a root account using a predefined keypair and the test root address. let root_account = LocalAccount::new( aptos_test_root_address(), - AccountKey::from_private_key(executor.monza_config.aptos_private_key.clone()), + AccountKey::from_private_key(executor.aptos_config.aptos_private_key.clone()), 0, ); @@ -529,7 +529,7 @@ mod tests { let mut rng = ::rand::rngs::StdRng::from_seed(seed); // Create a transaction factory with the chain ID of the executor, used for creating transactions. - let tx_factory = TransactionFactory::new(executor.monza_config.chain_id.clone()); + let tx_factory = TransactionFactory::new(executor.aptos_config.chain_id.clone()); // Loop to simulate the execution of multiple blocks. for _ in 0..10 { @@ -615,7 +615,7 @@ mod tests { // Initialize a root account using a predefined keypair and the test root address. let root_account = LocalAccount::new( aptos_test_root_address(), - AccountKey::from_private_key(executor.monza_config.aptos_private_key.clone()), + AccountKey::from_private_key(executor.aptos_config.aptos_private_key.clone()), 0, ); @@ -624,7 +624,7 @@ mod tests { let mut rng = ::rand::rngs::StdRng::from_seed(seed); // Create a transaction factory with the chain ID of the executor. - let tx_factory = TransactionFactory::new(executor.monza_config.chain_id.clone()); + let tx_factory = TransactionFactory::new(executor.aptos_config.chain_id.clone()); // Simulate the execution of multiple blocks. for _ in 0..10 { // For example, create and execute 3 blocks. @@ -668,7 +668,7 @@ mod tests { // header let mut executor = Executor::try_from_env()?; - let user_transaction = create_signed_transaction(0, executor.monza_config.chain_id.clone()); + let user_transaction = create_signed_transaction(0, executor.aptos_config.chain_id.clone()); // send transaction to mempool let (req_sender, callback) = oneshot::channel(); @@ -702,7 +702,7 @@ mod tests { Ok(()) as Result<(), anyhow::Error> }); - let user_transaction = create_signed_transaction(0, executor.monza_config.chain_id.clone()); + let user_transaction = create_signed_transaction(0, executor.aptos_config.chain_id.clone()); // send transaction to mempool let (req_sender, callback) = oneshot::channel(); @@ -744,7 +744,7 @@ mod tests { }); let api = executor.try_get_apis().await?; - let user_transaction = create_signed_transaction(0, executor.monza_config.chain_id.clone()); + let user_transaction = create_signed_transaction(0, executor.aptos_config.chain_id.clone()); let comparison_user_transaction = user_transaction.clone(); let bcs_user_transaction = bcs::to_bytes(&user_transaction)?; let request = SubmitTransactionPost::Bcs( @@ -779,7 +779,7 @@ mod tests { let mut comparison_user_transactions = BTreeSet::new(); for _ in 0..25 { - let user_transaction = create_signed_transaction(0, executor.monza_config.chain_id.clone()); + let user_transaction = create_signed_transaction(0, executor.aptos_config.chain_id.clone()); let bcs_user_transaction = bcs::to_bytes(&user_transaction)?; user_transactions.insert(bcs_user_transaction.clone()); diff --git a/protocol-units/execution/monza/opt-executor/src/lib.rs b/protocol-units/execution/maptos/opt-executor/src/lib.rs similarity index 100% rename from protocol-units/execution/monza/opt-executor/src/lib.rs rename to protocol-units/execution/maptos/opt-executor/src/lib.rs diff --git a/protocol-units/execution/monza/util/Cargo.toml b/protocol-units/execution/maptos/util/Cargo.toml similarity index 95% rename from protocol-units/execution/monza/util/Cargo.toml rename to protocol-units/execution/maptos/util/Cargo.toml index f60773585..fd592b22d 100644 --- a/protocol-units/execution/monza/util/Cargo.toml +++ b/protocol-units/execution/maptos/util/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "monza-execution-util" +name = "maptos-execution-util" description = "Executor for the AptosVM" authors = { workspace = true } edition = { workspace = true } diff --git a/protocol-units/execution/monza/util/src/config.rs b/protocol-units/execution/maptos/util/src/config.rs similarity index 94% rename from protocol-units/execution/monza/util/src/config.rs rename to protocol-units/execution/maptos/util/src/config.rs index 36ce704b0..1bbd86faf 100644 --- a/protocol-units/execution/monza/util/src/config.rs +++ b/protocol-units/execution/maptos/util/src/config.rs @@ -1,4 +1,4 @@ -pub mod just_monza { +pub mod just_aptos { use std::path::PathBuf; @@ -121,33 +121,33 @@ pub mod just_monza { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Config { - pub monza_config : just_monza::Config, + pub aptos_config : just_aptos::Config, pub light_node_config : m1_da_light_node_util::Config, } impl Config { - pub fn new(monza_config : just_monza::Config, light_node_config : m1_da_light_node_util::Config) -> Self { + pub fn new(aptos_config : just_aptos::Config, light_node_config : m1_da_light_node_util::Config) -> Self { Self { - monza_config, + aptos_config, light_node_config, } } pub fn try_from_env() -> Result { - let monza_config = just_monza::Config::try_from_env()?; + let aptos_config = just_aptos::Config::try_from_env()?; let light_node_config = m1_da_light_node_util::Config::try_from_env()?; Ok(Self { - monza_config, + aptos_config, light_node_config, }) } pub fn write_to_env(&self) -> Result<(), anyhow::Error>{ - self.monza_config.write_to_env()?; + self.aptos_config.write_to_env()?; self.light_node_config.write_to_env()?; Ok(()) } @@ -155,7 +155,7 @@ impl Config { pub fn write_bash_export_string(&self) -> Result { Ok(format!( "{}\n{}", - self.monza_config.write_bash_export_string()?, + self.aptos_config.write_bash_export_string()?, self.light_node_config.write_bash_export_string()? )) } diff --git a/protocol-units/execution/monza/util/src/finality_mode.rs b/protocol-units/execution/maptos/util/src/finality_mode.rs similarity index 100% rename from protocol-units/execution/monza/util/src/finality_mode.rs rename to protocol-units/execution/maptos/util/src/finality_mode.rs diff --git a/protocol-units/execution/monza/util/src/lib.rs b/protocol-units/execution/maptos/util/src/lib.rs similarity index 100% rename from protocol-units/execution/monza/util/src/lib.rs rename to protocol-units/execution/maptos/util/src/lib.rs diff --git a/protocol-units/execution/monza/executor/Cargo.toml b/protocol-units/execution/monza/executor/Cargo.toml index f628f64ba..c67d12f0b 100644 --- a/protocol-units/execution/monza/executor/Cargo.toml +++ b/protocol-units/execution/monza/executor/Cargo.toml @@ -61,8 +61,8 @@ aptos-mempool = { workspace = true } aptos-temppath = { workspace = true } aptos-proptest-helpers = { workspace = true } -monza-opt-executor = { workspace = true } -monza-execution-util = { workspace = true } +maptos-opt-executor = { workspace = true } +maptos-execution-util = { workspace = true } dirs = "5.0.1" tempfile = "3.10.1" diff --git a/protocol-units/execution/monza/executor/src/lib.rs b/protocol-units/execution/monza/executor/src/lib.rs index 28a8fa4c0..dbd7e9b87 100644 --- a/protocol-units/execution/monza/executor/src/lib.rs +++ b/protocol-units/execution/monza/executor/src/lib.rs @@ -9,7 +9,7 @@ pub use aptos_types::{ pub use aptos_crypto::hash::HashValue; use async_channel::Sender; use aptos_api::runtime::Apis; -pub use monza_execution_util::FinalityMode; +pub use maptos_execution_util::FinalityMode; #[tonic::async_trait] diff --git a/protocol-units/execution/monza/executor/src/v1.rs b/protocol-units/execution/monza/executor/src/v1.rs index 7dac6068f..43f3f3faf 100644 --- a/protocol-units/execution/monza/executor/src/v1.rs +++ b/protocol-units/execution/monza/executor/src/v1.rs @@ -1,7 +1,7 @@ use crate::*; use aptos_types::transaction::SignedTransaction; use async_channel::Sender; -use monza_opt_executor::Executor; +use maptos_opt_executor::Executor; #[derive(Clone)] pub struct MonzaExecutorV1 { @@ -331,7 +331,7 @@ mod tests { // Initialize a root account using a predefined keypair and the test root address. let root_account = LocalAccount::new( aptos_test_root_address(), - AccountKey::from_private_key(executor.monza_config.aptos_private_key.clone()), + AccountKey::from_private_key(executor.aptos_config.aptos_private_key.clone()), 0, ); @@ -340,7 +340,7 @@ mod tests { let mut rng = ::rand::rngs::StdRng::from_seed(seed); // Create a transaction factory with the chain ID of the executor. - let tx_factory = TransactionFactory::new(executor.monza_config.chain_id.clone()); + let tx_factory = TransactionFactory::new(executor.aptos_config.chain_id.clone()); // Simulate the execution of multiple blocks. for _ in 0..10 { diff --git a/protocol-units/execution/suzuka/executor/Cargo.toml b/protocol-units/execution/suzuka/executor/Cargo.toml new file mode 100644 index 000000000..91391697d --- /dev/null +++ b/protocol-units/execution/suzuka/executor/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "suzuka-executor" +description = "Suzuka executor containing TBD execution paths." +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } + +version = { workspace = true } + +[lib] +path = "src/lib.rs" + +[dependencies] +anyhow = { workspace = true } +chrono = { workspace = true } +fail = { workspace = true } +log = { workspace = true } +thiserror = { workspace = true } +bytes = { workspace = true } +schemars = { workspace = true, optional = true } +clap = { workspace = true, optional = true } +serde = { workspace = true } +serde_json = { workspace = true } +borsh = { workspace = true, features = ["rc"] } +hex = { workspace = true } +poem-openapi = { workspace = true } +tracing = { workspace = true } +derive_more = { workspace = true, default-features = true } +lazy_static = "1.4.0" +tokio = { workspace = true } +rand = { workspace = true } +rand_core = { workspace = true } +async-channel = { workspace = true } +tonic = { workspace = true } +futures = { workspace = true} +bcs = { workspace = true } + +aptos-vm = { workspace = true } +aptos-config = { workspace = true } +aptos-sdk = { workspace = true } +aptos-crypto = { workspace = true } +aptos-consensus-types = { workspace = true } +aptos-db = { workspace = true } +aptos-api = { workspace = true } +aptos-api-types = { workspace = true } +aptos-types = { workspace = true } +aptos-storage-interface = { workspace = true } +aptos-block-executor = { workspace = true } +aptos-vm-types = { workspace = true } +aptos-vm-logging = { workspace = true } +aptos-vm-genesis = { workspace = true } +aptos-language-e2e-tests = { workspace = true } +aptos-framework = { workspace = true } +aptos-executor = { workspace = true } +aptos-executor-types = { workspace = true } +aptos-executor-test-helpers = { workspace = true } +aptos-bitvec = { workspace = true } +aptos-mempool = { workspace = true } +aptos-temppath = { workspace = true } + +maptos-opt-executor = { workspace = true } +maptos-execution-util = { workspace = true } + +dirs = "5.0.1" +tempfile = "3.10.1" +async-trait = "0.1.80" diff --git a/protocol-units/execution/suzuka/executor/src/lib.rs b/protocol-units/execution/suzuka/executor/src/lib.rs new file mode 100644 index 000000000..affbb9f26 --- /dev/null +++ b/protocol-units/execution/suzuka/executor/src/lib.rs @@ -0,0 +1,43 @@ +pub mod v1; + +pub use aptos_types::{ + transaction::signature_verified_transaction::SignatureVerifiedTransaction, + block_executor::partitioner::ExecutableBlock, + block_executor::partitioner::ExecutableTransactions, + transaction::{SignedTransaction, Transaction} +}; +pub use aptos_crypto::hash::HashValue; +use async_channel::Sender; +use aptos_api::runtime::Apis; +pub use maptos_execution_util::FinalityMode; + + +#[tonic::async_trait] +pub trait SuzukaExecutor { + + /// Runs the service + async fn run_service(&self) -> Result<(), anyhow::Error>; + + /// Runs the necessary background tasks. + async fn run_background_tasks(&self) -> Result<(), anyhow::Error>; + + /// Executes a block dynamically + async fn execute_block( + &self, + mode : &FinalityMode, + block: ExecutableBlock, + ) -> Result<(), anyhow::Error>; + + /// Sets the transaction channel. + async fn set_tx_channel(&mut self, tx_channel: Sender) -> Result<(), anyhow::Error>; + + /// Gets the dyn API. + async fn get_api( + &self, + mode : &FinalityMode, + ) -> Result; + + /// Get block head height. + async fn get_block_head_height(&self) -> Result; + +} diff --git a/protocol-units/execution/suzuka/executor/src/v1.rs b/protocol-units/execution/suzuka/executor/src/v1.rs new file mode 100644 index 000000000..fc07e6e3c --- /dev/null +++ b/protocol-units/execution/suzuka/executor/src/v1.rs @@ -0,0 +1,239 @@ +use crate::*; +use maptos_opt_executor::Executor; +use async_channel::Sender; +use aptos_types::transaction::SignedTransaction; + +#[derive(Clone)] +pub struct SuzukaExecutorV1 { + // this rwlock may be somewhat redundant + pub executor: Executor, + pub transaction_channel: Sender, +} + +impl SuzukaExecutorV1 { + pub fn new(executor : Executor, transaction_channel: Sender) -> Self { + Self { + executor, + transaction_channel, + } + } + + pub async fn try_from_env(transaction_channel : Sender) -> Result { + let executor = Executor::try_from_env()?; + Ok(Self::new(executor, transaction_channel)) + } + +} + +#[tonic::async_trait] +impl SuzukaExecutor for SuzukaExecutorV1 { + + /// Runs the service. + async fn run_service(&self) -> Result<(), anyhow::Error> { + self.executor.run_service().await + } + + /// Runs the necessary background tasks. + async fn run_background_tasks(&self) -> Result<(), anyhow::Error> { + + loop { + // readers should be able to run concurrently + self.executor.tick_transaction_pipe(self.transaction_channel.clone()).await?; + } + + Ok(()) + + } + + /// Executes a block dynamically + async fn execute_block( + &self, + mode : &FinalityMode, + block: ExecutableBlock, + ) -> Result<(), anyhow::Error> { + + match mode { + FinalityMode::Dyn => unimplemented!(), + FinalityMode::Opt => { + println!("Executing opt block: {:?}", block.block_id); + self.executor.execute_block(block).await + }, + FinalityMode::Fin => unimplemented!(), + } + + } + + /// Sets the transaction channel. + async fn set_tx_channel(&mut self, tx_channel: Sender) -> Result<(), anyhow::Error> { + self.transaction_channel = tx_channel; + Ok(()) + } + + /// Gets the API. + async fn get_api( + &self, + _mode : &FinalityMode, + ) -> Result { + match _mode { + FinalityMode::Dyn => unimplemented!(), + FinalityMode::Opt => { + Ok(self.executor.try_get_apis().await?) + }, + FinalityMode::Fin => unimplemented!(), + } + } + + /// Get block head height. + async fn get_block_head_height(&self) -> Result { + // ideally, this should read from the ledger + Ok(1) + } + +} + +#[cfg(test)] +mod opt_tests { + + use super::*; + use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519Signature}, + HashValue, PrivateKey, Uniform, + }; + use aptos_types::{ + account_address::AccountAddress, + block_executor::partitioner::ExecutableTransactions, + chain_id::ChainId, + transaction::{ + signature_verified_transaction::SignatureVerifiedTransaction, RawTransaction, Script, + SignedTransaction, Transaction, TransactionPayload + } + }; + use aptos_api::{ + accept_type::AcceptType, + transactions::SubmitTransactionPost + }; + use futures::SinkExt; + use aptos_mempool::{ + MempoolClientRequest, MempoolClientSender, + }; + use futures::channel::oneshot; + + fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { + let private_key = Ed25519PrivateKey::generate_for_testing(); + let public_key = private_key.public_key(); + let transaction_payload = TransactionPayload::Script(Script::new(vec![0], vec![], vec![])); + let raw_transaction = RawTransaction::new( + AccountAddress::random(), + 0, + transaction_payload, + 0, + gas_unit_price, + 0, + ChainId::test(), // This is the value used in aptos testing code. + ); + SignedTransaction::new(raw_transaction, public_key, Ed25519Signature::dummy_signature()) + } + + + #[tokio::test] + async fn test_execute_opt_block() -> Result<(), anyhow::Error> { + let (tx, rx) = async_channel::unbounded(); + let mut executor = SuzukaExecutorV1::try_from_env(tx).await?; + let block_id = HashValue::random(); + let tx = SignatureVerifiedTransaction::Valid(Transaction::UserTransaction( + create_signed_transaction(0), + )); + let txs = ExecutableTransactions::Unsharded(vec![tx]); + let block = ExecutableBlock::new(block_id.clone(), txs); + executor.execute_block(&FinalityMode::Opt, block).await?; + Ok(()) + } + + + #[tokio::test] + async fn test_pipe_transactions_from_api() -> Result<(), anyhow::Error> { + + let (tx, rx) = async_channel::unbounded(); + let executor = SuzukaExecutorV1::try_from_env(tx).await?; + let services_executor = executor.clone(); + let background_executor = executor.clone(); + + let services_handle = tokio::spawn(async move { + services_executor.run_service().await?; + Ok(()) as Result<(), anyhow::Error> + }); + + let background_handle = tokio::spawn(async move { + background_executor.run_background_tasks().await?; + Ok(()) as Result<(), anyhow::Error> + }); + + // Start the background tasks + let user_transaction = create_signed_transaction(0); + let comparison_user_transaction = user_transaction.clone(); + let bcs_user_transaction = bcs::to_bytes(&user_transaction)?; + + + let request = SubmitTransactionPost::Bcs( + aptos_api::bcs_payload::Bcs(bcs_user_transaction) + ); + let api = executor.get_api(&FinalityMode::Opt).await?; + api.transactions.submit_transaction(AcceptType::Bcs, request).await?; + + services_handle.abort(); + background_handle.abort(); + let received_transaction = rx.recv().await?; + assert_eq!(received_transaction, comparison_user_transaction); + + Ok(()) + } + + #[tokio::test] + async fn test_pipe_transactions_from_api_and_execute() -> Result<(), anyhow::Error> { + + let (tx, rx) = async_channel::unbounded(); + let executor = SuzukaExecutorV1::try_from_env(tx).await?; + let services_executor = executor.clone(); + let background_executor = executor.clone(); + + let services_handle = tokio::spawn(async move { + services_executor.run_service().await?; + Ok(()) as Result<(), anyhow::Error> + }); + + let background_handle = tokio::spawn(async move { + background_executor.run_background_tasks().await?; + Ok(()) as Result<(), anyhow::Error> + }); + + // Start the background tasks + let user_transaction = create_signed_transaction(0); + let comparison_user_transaction = user_transaction.clone(); + let bcs_user_transaction = bcs::to_bytes(&user_transaction)?; + + + let request = SubmitTransactionPost::Bcs( + aptos_api::bcs_payload::Bcs(bcs_user_transaction) + ); + let api = executor.get_api(&FinalityMode::Opt).await?; + api.transactions.submit_transaction(AcceptType::Bcs, request).await?; + + let received_transaction = rx.recv().await?; + assert_eq!(received_transaction, comparison_user_transaction); + + // Now execute the block + let block_id = HashValue::random(); + let tx = SignatureVerifiedTransaction::Valid(Transaction::UserTransaction( + received_transaction + )); + let txs = ExecutableTransactions::Unsharded(vec![tx]); + let block = ExecutableBlock::new(block_id.clone(), txs); + executor.execute_block(&FinalityMode::Opt, block).await?; + + services_handle.abort(); + background_handle.abort(); + + Ok(()) + } + +} diff --git a/protocol-units/execution/suzuka/fin-executor/Cargo.toml b/protocol-units/execution/suzuka/fin-executor/Cargo.toml new file mode 100644 index 000000000..6129ca31d --- /dev/null +++ b/protocol-units/execution/suzuka/fin-executor/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "suzuka-fin-executor" +description = "Executor for the AptosVM" +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } + +version = { workspace = true } + +[lib] +path = "src/lib.rs" + +[dependencies] +anyhow = { workspace = true } +chrono = { workspace = true } +fail = { workspace = true } +log = { workspace = true } +thiserror = { workspace = true } +bytes = { workspace = true } +schemars = { workspace = true, optional = true } +clap = { workspace = true, optional = true } +serde = { workspace = true } +serde_json = { workspace = true } +borsh = { workspace = true, features = ["rc"] } +hex = { workspace = true } +poem-openapi = { workspace = true } +tracing = { workspace = true } +derive_more = { workspace = true, default-features = true } +lazy_static = "1.4.0" +tokio = { workspace = true } +rand = { workspace = true } +rand_core = { workspace = true } + +aptos-vm = { workspace = true } +aptos-config = { workspace = true } +aptos-sdk = { workspace = true } +aptos-crypto = { workspace = true } +aptos-consensus-types = { workspace = true } +aptos-db = { workspace = true } +aptos-api = { workspace = true } +aptos-api-types = { workspace = true } +aptos-types = { workspace = true } +aptos-storage-interface = { workspace = true } +aptos-block-executor = { workspace = true } +aptos-vm-types = { workspace = true } +aptos-vm-logging = { workspace = true } +aptos-vm-genesis = { workspace = true } +aptos-language-e2e-tests = { workspace = true } +aptos-framework = { workspace = true } +aptos-executor = { workspace = true } +aptos-executor-types = { workspace = true } +aptos-executor-test-helpers = { workspace = true } +aptos-bitvec = { workspace = true } +aptos-mempool = { workspace = true } +aptos-temppath = { workspace = true } + +dirs = "5.0.1" +tempfile = "3.10.1" +async-trait = "0.1.80" diff --git a/protocol-units/execution/suzuka/fin-executor/src/executor.rs b/protocol-units/execution/suzuka/fin-executor/src/executor.rs new file mode 100644 index 000000000..9d953b292 --- /dev/null +++ b/protocol-units/execution/suzuka/fin-executor/src/executor.rs @@ -0,0 +1,233 @@ +use aptos_db::AptosDB; +use aptos_executor_types::{state_checkpoint_output::StateCheckpointOutput, BlockExecutorTrait}; +use aptos_mempool::core_mempool::CoreMempool; +use aptos_storage_interface::DbReaderWriter; +use aptos_types::{ + block_executor::config::BlockExecutorConfigFromOnchain, + transaction::{ + Transaction, WriteSetPayload, + }, + validator_signer::ValidatorSigner, + block_executor::partitioner::ExecutableBlock +}; +use aptos_vm::AptosVM; +use std::{path::PathBuf, sync::{Arc, RwLock}}; +use aptos_config::config::NodeConfig; +use aptos_executor::{ + block_executor::BlockExecutor, + db_bootstrapper::{generate_waypoint, maybe_bootstrap}, +}; + +/// The state of `movement-network` execution can exist in three states, +/// `Dynamic`, `Optimistic`, and `Final`. The `Dynamic` state is the state. +pub enum FinalityState { + /// The dynamic state that is subject to change and is not + /// yet finalized. It is the state that is derived from the blocks + /// received before any finality is reached and simply represents a + /// local application of the fork-choice rule (longest chain) + /// of the gossipped blocks. + Dynamic, + /// The optimistic state that is derived from the blocks received after DA finality. + /// It is the state that is derived from the blocks that have been finalized by the DA. + Optimistic, + /// The final state that is derived from the blocks received after the finality is reached. + Final, +} + +/// The current state of the executor and its execution of blocks. +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum ExecutorState { + /// The executor is idle and waiting for a block to be executed. + Idle, + /// The block is executed in a speculative manner and its effects held in memory. + Speculate, + /// The network agrees on the block. + Consensus, + /// The block is committed to the state, at this point + /// fork choices must be resolved otherwise the commitment and subsequent execution will fail. + Commit, +} + +/// The `Executor` is responsible for executing blocks and managing the state of the execution +/// against the `AptosVM`. +pub struct Executor { + /// The executing type. + pub block_executor: Arc>>, + /// The current state of the executor. + pub status: ExecutorState, + /// The access to db. + pub db: Arc>, + /// The signer of the executor's transactions. + pub signer: ValidatorSigner, + /// The access to the core mempool. + pub mempool: CoreMempool, +} + +impl Executor { + + const DB_PATH_ENV_VAR: &'static str = "DB_DIR"; + + /// Create a new `Executor` instance. + pub fn new( + db_dir : PathBuf, + block_executor: BlockExecutor, + signer: ValidatorSigner, + mempool: CoreMempool + ) -> Self { + + let (_aptos_db, reader_writer) = DbReaderWriter::wrap(AptosDB::new_for_test(&db_dir)); + Self { + block_executor: Arc::new(RwLock::new(block_executor)), + status: ExecutorState::Idle, + db: Arc::new(RwLock::new(reader_writer)), + signer, + mempool, + } + } + + pub fn bootstrap_empty_db(db_dir : PathBuf) -> Result { + let genesis = aptos_vm_genesis::test_genesis_change_set_and_validators(Some(1)); + let genesis_txn = Transaction::GenesisTransaction(WriteSetPayload::Direct(genesis.0)); + let db_rw = DbReaderWriter::new(AptosDB::new_for_test(&db_dir)); + assert!(db_rw.reader.get_latest_ledger_info_option()?.is_none()); + + // Bootstrap empty DB. + let waypoint = + generate_waypoint::(&db_rw, &genesis_txn).expect("Should not fail."); + maybe_bootstrap::(&db_rw, &genesis_txn, waypoint)?; + assert!(db_rw.reader.get_latest_ledger_info_option()?.is_some()); + + Ok(db_rw) + } + + pub fn bootstrap( + db_dir : PathBuf, + signer: ValidatorSigner, + mempool: CoreMempool + ) -> Result { + + let db_rw = Self::bootstrap_empty_db(db_dir)?; + + Ok(Self { + block_executor: Arc::new(RwLock::new(BlockExecutor::new(db_rw.clone()))), + status: ExecutorState::Idle, + db: Arc::new(RwLock::new(db_rw)), + signer, + mempool, + }) + + } + + pub fn try_from_env() -> Result { + + // read the db dir from env or use a tempfile + let db_dir = match std::env::var(Self::DB_PATH_ENV_VAR) { + Ok(dir) => PathBuf::from(dir), + Err(_) => { + let temp_dir = tempfile::tempdir()?; + temp_dir.path().to_path_buf() + } + }; + + // use the default signer, block executor, and mempool + let signer = ValidatorSigner::random(None); + let mempool = CoreMempool::new(&NodeConfig::default()); + + Self::bootstrap( + db_dir, + signer, + mempool + ) + + } + + + pub fn set_commit_state(&mut self) { + self.status = ExecutorState::Commit; + } + + /// Execute a block which gets committed to the state. + /// `ExecutorState` must be set to `Commit` before calling this method. + pub async fn execute_block( + &mut self, + block: ExecutableBlock, + ) -> Result { + if self.status != ExecutorState::Commit { + return Err(anyhow::anyhow!("Executor is not in the Commit state")); + } + + let parent_block_id = { + let block_executor = self.block_executor.read().map_err( + |e| anyhow::anyhow!("Failed to acquire block executor read lock: {:?}", e) + )?; // acquire read lock + block_executor.committed_block_id() + }; + + + let state_checkpoint = { + let block_executor = self.block_executor.write().map_err( + |e| anyhow::anyhow!("Failed to acquire block executor write lock: {:?}", e) + )?; // acquire write lock + block_executor.execute_and_state_checkpoint( + block, + parent_block_id, + BlockExecutorConfigFromOnchain::new_no_block_limit(), + )? + }; + + // Update the executor state + self.status = ExecutorState::Idle; + + Ok(state_checkpoint) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519Signature}, + HashValue, PrivateKey, Uniform, + }; + use aptos_types::{ + account_address::AccountAddress, + block_executor::partitioner::ExecutableTransactions, + chain_id::ChainId, + transaction::{ + signature_verified_transaction::SignatureVerifiedTransaction, RawTransaction, Script, + SignedTransaction, Transaction, TransactionPayload + } + }; + + fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { + let private_key = Ed25519PrivateKey::generate_for_testing(); + let public_key = private_key.public_key(); + + let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![])); + let raw_transaction = RawTransaction::new( + AccountAddress::random(), + 0, + transaction_payload, + 0, + gas_unit_price, + 0, + ChainId::new(10), // This is the value used in aptos testing code. + ); + SignedTransaction::new(raw_transaction, public_key, Ed25519Signature::dummy_signature()) + } + + + #[tokio::test] + async fn test_execute_block() -> Result<(), anyhow::Error> { + let mut executor = Executor::try_from_env()?; + executor.set_commit_state(); + let block_id = HashValue::random(); + let tx = SignatureVerifiedTransaction::Valid(Transaction::UserTransaction( + create_signed_transaction(0), + )); + let txs = ExecutableTransactions::Unsharded(vec![tx]); + let block = ExecutableBlock::new(block_id.clone(), txs); + executor.execute_block(block).await?; + Ok(()) + } +} diff --git a/protocol-units/execution/suzuka/fin-executor/src/lib.rs b/protocol-units/execution/suzuka/fin-executor/src/lib.rs new file mode 100644 index 000000000..17bc341a0 --- /dev/null +++ b/protocol-units/execution/suzuka/fin-executor/src/lib.rs @@ -0,0 +1,2 @@ +pub mod executor; +pub use executor::*;