diff --git a/src/bin/ant-node/cli.rs b/src/bin/ant-node/cli.rs index 8eb9fa51..b1d68c66 100644 --- a/src/bin/ant-node/cli.rs +++ b/src/bin/ant-node/cli.rs @@ -50,6 +50,9 @@ pub struct Cli { pub rewards_address: Option, /// EVM network for payment processing. + /// + /// Ignored when `--evm-rpc-url` is set (which selects a custom EVM + /// network instead — used by the local-Anvil testnet flow). #[arg( long, value_enum, @@ -58,6 +61,22 @@ pub struct Cli { )] pub evm_network: CliEvmNetwork, + /// HTTP RPC URL of a custom EVM (e.g. a local Anvil instance). + /// When set, --evm-payment-token and --evm-payment-vault must also + /// be set, and they together override --evm-network. + #[arg(long, env = "ANT_EVM_RPC_URL")] + pub evm_rpc_url: Option, + + /// ANT token contract address on the custom EVM. + /// Required iff --evm-rpc-url is set. + #[arg(long, env = "ANT_EVM_PAYMENT_TOKEN")] + pub evm_payment_token: Option, + + /// Payment vault contract address on the custom EVM. + /// Required iff --evm-rpc-url is set. + #[arg(long, env = "ANT_EVM_PAYMENT_VAULT")] + pub evm_payment_vault: Option, + /// Metrics port for Prometheus scraping (0 to disable). #[arg(long, default_value = "9100", env = "ANT_METRICS_PORT")] pub metrics_port: u16, @@ -239,10 +258,27 @@ impl Cli { config.upgrade.stop_on_upgrade = self.stop_on_upgrade; // Payment config (payment verification is always on) + // Custom EVM (--evm-rpc-url) overrides the --evm-network preset. + let evm_network = if let Some(rpc_url) = self.evm_rpc_url { + let payment_token_address = self.evm_payment_token.ok_or_else(|| { + color_eyre::eyre::eyre!("--evm-payment-token is required when --evm-rpc-url is set") + })?; + let payment_vault_address = self.evm_payment_vault.ok_or_else(|| { + color_eyre::eyre::eyre!("--evm-payment-vault is required when --evm-rpc-url is set") + })?; + EvmNetworkConfig::Custom { + rpc_url, + payment_token_address, + payment_vault_address, + } + } else { + self.evm_network.into() + }; + config.payment = PaymentConfig { cache_capacity: self.cache_capacity, rewards_address: self.rewards_address, - evm_network: self.evm_network.into(), + evm_network, metrics_port: self.metrics_port, }; diff --git a/src/config.rs b/src/config.rs index a9f569c3..e1d9d743 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ //! Configuration for ant-node. +use evmlib::Network as EvmNetwork; use serde::{Deserialize, Serialize}; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::path::{Path, PathBuf}; @@ -182,14 +183,47 @@ pub struct UpgradeConfig { } /// EVM network for payment processing. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +/// +/// `Custom` is used by the local-Anvil testnet flow in +/// `deploy/testnet-v2/`: when an operator stands up a private Anvil +/// instance with the ANT token + payment vault contracts deployed, +/// every node points at the Anvil RPC and the deployed addresses +/// instead of one of the public Arbitrum networks. +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case", tag = "type")] pub enum EvmNetworkConfig { /// Arbitrum One mainnet. #[default] ArbitrumOne, /// Arbitrum Sepolia testnet. ArbitrumSepolia, + /// Local / private EVM (e.g. Anvil) with operator-supplied + /// contract addresses. + Custom { + /// HTTP RPC URL of the EVM node (e.g. `http://1.2.3.4:8545`). + rpc_url: String, + /// Deployed ANT token (ERC-20) contract address. + payment_token_address: String, + /// Deployed payment vault contract address. + payment_vault_address: String, + }, +} + +impl EvmNetworkConfig { + /// Resolve this config into the concrete `evmlib` network used by + /// the payment verifier and the rewards wallet. + #[must_use] + pub fn into_evm_network(self) -> EvmNetwork { + match self { + Self::ArbitrumOne => EvmNetwork::ArbitrumOne, + Self::ArbitrumSepolia => EvmNetwork::ArbitrumSepoliaTest, + Self::Custom { + rpc_url, + payment_token_address, + payment_vault_address, + } => EvmNetwork::new_custom(&rpc_url, &payment_token_address, &payment_vault_address), + } + } } /// Payment verification configuration. diff --git a/src/node.rs b/src/node.rs index 4dc2a6d8..43d58298 100644 --- a/src/node.rs +++ b/src/node.rs @@ -2,8 +2,7 @@ use crate::ant_protocol::CHUNK_PROTOCOL_ID; use crate::config::{ - default_nodes_dir, default_root_dir, EvmNetworkConfig, NetworkMode, NodeConfig, - NODE_IDENTITY_FILENAME, + default_nodes_dir, default_root_dir, NetworkMode, NodeConfig, NODE_IDENTITY_FILENAME, }; use crate::error::{Error, Result}; use crate::event::{create_event_channel, NodeEvent, NodeEventsChannel, NodeEventsSender}; @@ -18,7 +17,6 @@ use crate::storage::{AntProtocol, LmdbStorage, LmdbStorageConfig}; use crate::upgrade::{ upgrade_cache_dir, AutoApplyUpgrader, BinaryCache, ReleaseCache, UpgradeMonitor, UpgradeResult, }; -use evmlib::Network as EvmNetwork; use rand::Rng; use saorsa_core::identity::NodeIdentity; use saorsa_core::{ @@ -375,10 +373,7 @@ impl NodeBuilder { }; // Create payment verifier - let evm_network = match config.payment.evm_network { - EvmNetworkConfig::ArbitrumOne => EvmNetwork::ArbitrumOne, - EvmNetworkConfig::ArbitrumSepolia => EvmNetwork::ArbitrumSepoliaTest, - }; + let evm_network = config.payment.evm_network.clone().into_evm_network(); let payment_config = PaymentVerifierConfig { evm: EvmVerifierConfig { network: evm_network, diff --git a/src/payment/wallet.rs b/src/payment/wallet.rs index 7ec53dff..756aedf5 100644 --- a/src/payment/wallet.rs +++ b/src/payment/wallet.rs @@ -30,11 +30,7 @@ impl WalletConfig { /// Returns an error if the address string is invalid. pub fn new(rewards_address: Option<&str>, evm_network: EvmNetworkConfig) -> Result { let rewards_address = rewards_address.map(parse_rewards_address).transpose()?; - - let network = match evm_network { - EvmNetworkConfig::ArbitrumOne => EvmNetwork::ArbitrumOne, - EvmNetworkConfig::ArbitrumSepolia => EvmNetwork::ArbitrumSepoliaTest, - }; + let network = evm_network.into_evm_network(); Ok(Self { rewards_address,