Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions ant-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,16 @@ serial_test = "3"
anyhow = "1"
alloy = { version = "1.6", features = ["node-bindings"] }
tokio-test = "0.4"
dirs = "5"

[[example]]
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"
Expand Down
120 changes: 120 additions & 0 deletions ant-core/examples/start-devnet-sepolia.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//! 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 ant-gui config directory so the GUI
//! auto-detects Sepolia mode on startup.
//!
//! # Usage
//!
//! ```bash
//! cargo run --release --example start-devnet-sepolia
//! ```

use ant_core::data::EvmNetwork;
use ant_node::devnet::{Devnet, DevnetConfig};
use std::path::PathBuf;

fn gui_manifest_path() -> PathBuf {
let config_dir = dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("autonomi")
.join("ant-gui");
std::fs::create_dir_all(&config_dir).ok();
config_dir.join("devnet-manifest.json")
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
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<String> = devnet
.bootstrap_addrs()
.iter()
.filter_map(|ma| {
let s = ma.to_string();
let parts: Vec<&str> = s.split('/').collect();
let ip = parts
.iter()
.position(|&p| p == "ip4")
.and_then(|i| parts.get(i + 1))?;
let port = parts
.iter()
.position(|&p| p == "udp")
.and_then(|i| parts.get(i + 1))?;
Some(format!("{ip}:{port}"))
})
.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::<Vec<_>>(),
"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 manifest_path = gui_manifest_path();
std::fs::write(&manifest_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: {}", manifest_path.display());
println!();
println!("Start ant-gui with: npm run tauri:dev");
println!("Import wallet key in Settings > Advanced > Direct Wallet.");
println!();
println!("Press Ctrl+C to stop.");

tokio::signal::ctrl_c().await?;
println!("Shutting down...");

if manifest_path.exists() {
std::fs::remove_file(&manifest_path).ok();
}

Ok(())
})
}
99 changes: 60 additions & 39 deletions ant-core/examples/start-local-devnet.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
//! 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 ant-gui config directory, and waits for Ctrl+C.
//!
//! The ant-gui desktop app detects the manifest on startup and automatically
//! enters devnet mode.
//!
//! # Usage
//!
//! ```bash
//! cargo run --example start-local-devnet
//! cargo run --release --example start-local-devnet
//! ```

use ant_core::data::LocalDevnet;
use ant_node::devnet::DevnetConfig;
use std::path::PathBuf;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
fn gui_manifest_path() -> PathBuf {
let config_dir = dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("autonomi")
.join("ant-gui");
std::fs::create_dir_all(&config_dir).ok();
config_dir.join("devnet-manifest.json")
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
Expand All @@ -23,36 +34,46 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.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 <YOUR_FILE>",
manifest_path.display()
);
println!();
println!("# Download it back:");
println!(
"cargo run --example cli -- --devnet-manifest {} download --datamap <HEX> --output <OUTPUT_PATH>",
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(); // 25 nodes, 3 bootstrap
println!("Starting devnet with {} nodes...", config.node_count);

let devnet = LocalDevnet::start(config).await?;

let path = gui_manifest_path();
devnet.write_manifest(&path).await?;

let manifest = devnet.manifest();
let evm = manifest.evm.as_ref().expect("EVM info present");

println!();
println!("=== Devnet is running! ===");
println!();
println!("Nodes: {}", manifest.node_count);
println!("Bootstrap peers: {:?}", devnet.bootstrap_addrs());
println!("Manifest: {}", path.display());
println!();
println!("EVM RPC: {}", evm.rpc_url);
println!("Token contract: {}", evm.payment_token_address);
println!("Vault contract: {}", evm.payment_vault_address);
println!();
println!("Start ant-gui with: $env:VITE_DEVNET=\"1\"; npm run tauri:dev");
println!();
println!("Press Ctrl+C to stop.");

tokio::signal::ctrl_c().await?;
println!("Shutting down...");

if path.exists() {
std::fs::remove_file(&path).ok();
println!("Removed manifest: {}", path.display());
}

Ok(())
})
}
Loading