diff --git a/README.md b/README.md index 317758d..c94ef59 100644 --- a/README.md +++ b/README.md @@ -30,17 +30,16 @@ irm https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.ps1 | ### Store and retrieve a file (local devnet) ```bash -# 1. Start a local devnet (spins up nodes + a local Anvil EVM chain) -# See "Local Development with Devnet" below for details. +# 1. Start a local devnet (spins up 25 nodes + a local Anvil EVM chain) +cargo run --release --example start-local-devnet -# 2. Upload a file (public — anyone with the address can download) +# 2. Upload a file (the manifest is auto-written to the shared data dir) SECRET_KEY=0x... ant file upload photo.jpg --public \ - --devnet-manifest /tmp/devnet.json --allow-loopback --evm-network local -# Output: ADDRESS=abc123... + --devnet-manifest ~/.local/share/ant/devnet-manifest.json --allow-loopback --evm-network local # 3. Download it back ant file download abc123... -o photo_copy.jpg \ - --devnet-manifest /tmp/devnet.json --allow-loopback --evm-network local + --devnet-manifest ~/.local/share/ant/devnet-manifest.json --allow-loopback --evm-network local ``` ### Store and retrieve a file (Arbitrum mainnet) @@ -454,29 +453,41 @@ Autonomi supports two payment strategies: ### Local Development with Devnet -`LocalDevnet` spins up a local Autonomi network with an embedded Anvil EVM blockchain for development and testing: +Two ready-made examples spin up a local network and write a manifest to the shared data directory (`~/.local/share/ant/` on Linux, `~/Library/Application Support/ant/` on macOS, `%APPDATA%\ant\` on Windows). Any consumer that checks this directory — ant-gui, ant-cli, ant-tui — will auto-detect the devnet. + +```bash +# Local Anvil devnet (25 nodes + embedded EVM blockchain) +# Includes a pre-funded wallet key in the manifest +cargo run --release --example start-local-devnet + +# Sepolia testnet devnet (25 nodes + real Arbitrum Sepolia contracts) +# No wallet key — connect your own funded Sepolia wallet +cargo run --release --example start-devnet-sepolia +``` + +Both write `devnet-manifest.json` to the shared data dir and clean it up on Ctrl+C. + +The [ant-gui](https://github.com/WithAutonomi/ant-ui) desktop app auto-detects the manifest on startup and switches to devnet/Sepolia mode — no manual configuration needed. The manifest can also be passed to the CLI explicitly via `--devnet-manifest `. + +#### Programmatic usage ```rust use ant_core::data::LocalDevnet; -// Start a minimal devnet (5 nodes + Anvil EVM chain) -let mut devnet = LocalDevnet::start_minimal().await?; +// Start a devnet (25 nodes + Anvil EVM chain) +let devnet = LocalDevnet::start(DevnetConfig::default()).await?; // Create a client with a pre-funded wallet (ready to upload) let client = devnet.create_funded_client().await?; -// Upload data -let result = client.data_upload(Bytes::from("test payload")).await?; - -// Write manifest for CLI usage -devnet.write_manifest(Path::new("/tmp/devnet.json")).await?; +// Write manifest for discovery by other tools +let manifest_path = ant_core::config::data_dir()?.join("devnet-manifest.json"); +devnet.write_manifest(&manifest_path).await?; // Clean up devnet.shutdown().await?; ``` -The manifest JSON can be passed to the CLI via `--devnet-manifest` for local testing. - ### Chunk Cache The client includes an in-memory LRU cache for recently accessed chunks: @@ -617,7 +628,7 @@ cargo fmt --all -- --check # Run the CLI cargo run --bin ant -- --help -cargo run --bin ant -- file upload photo.jpg --public --devnet-manifest /tmp/devnet.json --allow-loopback +cargo run --bin ant -- file upload photo.jpg --public --devnet-manifest ~/.local/share/ant/devnet-manifest.json --allow-loopback cargo run --bin ant -- node daemon status ``` diff --git a/ant-core/Cargo.toml b/ant-core/Cargo.toml index 6d3aa04..3f2c7ca 100644 --- a/ant-core/Cargo.toml +++ b/ant-core/Cargo.toml @@ -62,6 +62,10 @@ tokio-test = "0.4" name = "start-local-devnet" path = "examples/start-local-devnet.rs" +[[example]] +name = "start-devnet-sepolia" +path = "examples/start-devnet-sepolia.rs" + [[test]] name = "e2e_chunk" path = "tests/e2e_chunk.rs" diff --git a/ant-core/examples/start-devnet-sepolia.rs b/ant-core/examples/start-devnet-sepolia.rs new file mode 100644 index 0000000..d07737c --- /dev/null +++ b/ant-core/examples/start-devnet-sepolia.rs @@ -0,0 +1,107 @@ +//! Start a local devnet with 25 nodes using Arbitrum Sepolia for payments. +//! +//! Uses the existing deployed contracts on Arbitrum Sepolia. +//! Nodes verify payments against the real Sepolia PaymentVault. +//! +//! Writes a manifest to the shared ant data directory so any consumer +//! (ant-gui, ant-cli, ant-tui) auto-detects Sepolia mode on startup. +//! +//! Unlike the local Anvil devnet, no wallet key is provided — the user +//! must connect their own funded Sepolia wallet (e.g. via WalletConnect +//! in ant-gui, or SECRET_KEY env var in ant-cli). +//! +//! # Usage +//! +//! ```bash +//! cargo run --release --example start-devnet-sepolia +//! ``` + +use ant_core::data::EvmNetwork; +use ant_node::devnet::{Devnet, DevnetConfig}; + +fn manifest_path() -> std::path::PathBuf { + let data_dir = ant_core::config::data_dir().expect("Could not determine data directory"); + std::fs::create_dir_all(&data_dir).ok(); + data_dir.join("devnet-manifest.json") +} + +fn main() -> Result<(), Box> { + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), + ) + .with_writer(std::io::stderr) + .init(); + + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .thread_stack_size(8 * 1024 * 1024) + .build()?; + + runtime.block_on(async { + let evm_network = EvmNetwork::ArbitrumSepoliaTest; + + let rpc_url = evm_network.rpc_url().to_string(); + let token_addr = format!("{}", evm_network.payment_token_address()); + let vault_addr = format!("{}", evm_network.payment_vault_address()); + + println!("Starting Sepolia devnet..."); + println!("RPC: {rpc_url}"); + println!("Token: {token_addr}"); + println!("Vault: {vault_addr}"); + + let config = DevnetConfig { + evm_network: Some(evm_network), + ..DevnetConfig::default() + }; + + println!("Starting {} nodes...", config.node_count); + + let mut devnet = Devnet::new(config).await?; + devnet.start().await?; + + let bootstrap_addrs: Vec = devnet + .bootstrap_addrs() + .iter() + .map(|a| format!("{a}")) + .collect(); + + let manifest = serde_json::json!({ + "base_port": 0, + "node_count": devnet.config().node_count, + "bootstrap": devnet.bootstrap_addrs().iter().map(|a| a.to_string()).collect::>(), + "data_dir": devnet.config().data_dir.to_string_lossy(), + "created_at": "", + "evm": { + "rpc_url": rpc_url, + "wallet_private_key": "", + "payment_token_address": token_addr, + "payment_vault_address": vault_addr, + } + }); + + let path = manifest_path(); + std::fs::write(&path, serde_json::to_string_pretty(&manifest)?)?; + + println!(); + println!("=== Sepolia Devnet is running! ==="); + println!(); + println!("Nodes: {}", devnet.config().node_count); + println!("Bootstrap peers: {:?}", bootstrap_addrs); + println!("Manifest: {}", path.display()); + println!(); + println!("No wallet key is embedded — connect your own funded Sepolia wallet."); + println!(); + println!("Press Ctrl+C to stop."); + + tokio::signal::ctrl_c().await?; + println!("Shutting down..."); + + if path.exists() { + std::fs::remove_file(&path).ok(); + } + + Ok(()) + }) +} diff --git a/ant-core/examples/start-local-devnet.rs b/ant-core/examples/start-local-devnet.rs index 5867e5d..246e6db 100644 --- a/ant-core/examples/start-local-devnet.rs +++ b/ant-core/examples/start-local-devnet.rs @@ -1,20 +1,28 @@ -//! Start a local devnet with EVM payments. +//! Start a local devnet with 25 nodes and EVM payments. //! -//! Launches a minimal Autonomi network (5 nodes) with an embedded Anvil -//! blockchain, writes a manifest to `/tmp/ant-devnet-manifest.json`, -//! and waits for Ctrl+C. +//! Launches an Autonomi network with an embedded Anvil blockchain, +//! writes a manifest to the shared ant data directory, and waits for Ctrl+C. +//! +//! Any consumer that checks `ant_core::config::data_dir()` for +//! `devnet-manifest.json` (ant-gui, ant-cli, ant-tui) will auto-detect +//! devnet mode on startup. //! //! # Usage //! //! ```bash -//! cargo run --example start-local-devnet +//! cargo run --release --example start-local-devnet //! ``` use ant_core::data::LocalDevnet; -use std::path::PathBuf; +use ant_node::devnet::DevnetConfig; + +fn manifest_path() -> std::path::PathBuf { + let data_dir = ant_core::config::data_dir().expect("Could not determine data directory"); + std::fs::create_dir_all(&data_dir).ok(); + data_dir.join("devnet-manifest.json") +} -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() @@ -23,36 +31,40 @@ async fn main() -> Result<(), Box> { .with_writer(std::io::stderr) .init(); - // Start a minimal local devnet with EVM payments - let devnet = LocalDevnet::start_minimal().await?; - - // Write manifest so the CLI example can use it - let manifest_path = PathBuf::from("/tmp/ant-devnet-manifest.json"); - devnet.write_manifest(&manifest_path).await?; - - println!(); - println!("=== Devnet is running! ==="); - println!(); - println!("Bootstrap peers: {:?}", devnet.bootstrap_addrs()); - println!("Wallet key: {}", devnet.wallet_private_key()); - println!("Manifest: {}", manifest_path.display()); - println!(); - println!("# Upload a file:"); - println!( - "cargo run --example cli -- --devnet-manifest {} upload --file ", - manifest_path.display() - ); - println!(); - println!("# Download it back:"); - println!( - "cargo run --example cli -- --devnet-manifest {} download --datamap --output ", - manifest_path.display() - ); - println!(); - println!("Press Ctrl+C to stop."); - - tokio::signal::ctrl_c().await?; - println!("Shutting down..."); - - Ok(()) + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .thread_stack_size(8 * 1024 * 1024) + .build()?; + + runtime.block_on(async { + let config = DevnetConfig::default(); + println!( + "Starting local Anvil devnet with {} nodes...", + config.node_count + ); + + let devnet = LocalDevnet::start(config).await?; + + let path = manifest_path(); + devnet.write_manifest(&path).await?; + + println!(); + println!("=== Local Devnet is running! ==="); + println!(); + println!("Nodes: {}", devnet.manifest().node_count); + println!("Bootstrap peers: {:?}", devnet.bootstrap_addrs()); + println!("Wallet key: {}", devnet.wallet_private_key()); + println!("Manifest: {}", path.display()); + println!(); + println!("Press Ctrl+C to stop."); + + tokio::signal::ctrl_c().await?; + println!("Shutting down..."); + + if path.exists() { + std::fs::remove_file(&path).ok(); + } + + Ok(()) + }) }