diff --git a/antd/Cargo.lock b/antd/Cargo.lock index 619783b..d95c955 100644 --- a/antd/Cargo.lock +++ b/antd/Cargo.lock @@ -2516,9 +2516,9 @@ dependencies = [ [[package]] name = "evmlib" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1338c23c9ce1b4e54ff5cc65e53ce859095f121bfc742e474e1e1f2e03748000" +checksum = "ebd96cf24017cd31cd422c80de3078a821b7884a5b1feb00087e98209326c676" dependencies = [ "alloy", "ant-merkle", @@ -3236,7 +3236,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "system-configuration 0.7.0", "tokio", "tower-service", @@ -3256,7 +3256,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.57.0", ] [[package]] @@ -4576,7 +4576,7 @@ dependencies = [ "quinn-udp 0.5.14", "rustc-hash", "rustls", - "socket2 0.6.3", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -4614,7 +4614,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] @@ -4627,7 +4627,7 @@ checksum = "76150b617afc75e6e21ac5f39bc196e80b65415ae48d62dbef8e2519d040ce42" dependencies = [ "cfg_aliases", "libc", - "socket2 0.6.3", + "socket2 0.5.10", "tracing", "windows-sys 0.61.2", ] @@ -6913,7 +6913,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -6967,19 +6967,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-implement" version = "0.57.0" @@ -7002,17 +6989,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "windows-interface" version = "0.57.0" @@ -7035,17 +7011,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "windows-link" version = "0.2.1" diff --git a/antd/src/evm_defaults.rs b/antd/src/evm_defaults.rs index 2eace91..4aa68b5 100644 --- a/antd/src/evm_defaults.rs +++ b/antd/src/evm_defaults.rs @@ -14,21 +14,16 @@ //! `--network local` is supplied and `arbitrum-one` otherwise — so mainnet is //! the well-lit path and opt-out rather than opt-in. //! -//! Preset values mirror the canonical constants in -//! `autonomi/evmlib/src/lib.rs` so antd reads/writes the same on-chain state -//! as every other Autonomi component pointed at the same network. They are -//! duplicated (rather than pulled from `evmlib` accessors) because the -//! published `evmlib` crate does not yet expose them as `pub const`. - -/// Arbitrum One mainnet defaults — `ARBITRUM_ONE_*` in `evmlib::lib`. -const ARBITRUM_ONE_RPC_URL: &str = "https://arb1.arbitrum.io/rpc"; -const ARBITRUM_ONE_TOKEN: &str = "0xa78d8321B20c4Ef90eCd72f2588AA985A4BDb684"; -const ARBITRUM_ONE_VAULT: &str = "0xB1b5219f8Aaa18037A2506626Dd0406a46f70BcC"; - -/// Arbitrum Sepolia testnet defaults — `ARBITRUM_SEPOLIA_TEST_*` in `evmlib::lib`. -const ARBITRUM_SEPOLIA_RPC_URL: &str = "https://sepolia-rollup.arbitrum.io/rpc"; -const ARBITRUM_SEPOLIA_TOKEN: &str = "0x4bc1aCE0E66170375462cB4E6Af42Ad4D5EC689C"; -const ARBITRUM_SEPOLIA_VAULT: &str = "0x7f0842a78f7d4085d975ba91d630d680f91b1295"; +//! Preset addresses are pulled directly from `evmlib::Network` accessors so +//! antd reads and writes the same on-chain state as every other Autonomi +//! component pointed at the same network. Duplicating the constants here was +//! the cause of a wrong-vault bug after evmlib unified single-node and merkle +//! payments into one contract: the daemon's wallet path used evmlib's +//! `EvmNetwork::ArbitrumOne` (correct), while the external-signer prepare +//! response handed out the stale literal — so external signers paid the old +//! vault and storers verifying against the new vault rejected the payment. + +use evmlib::Network as EvmNetwork; /// Resolved EVM configuration values ready to be passed to /// [`evmlib::Network::new_custom`]. @@ -57,16 +52,10 @@ where }); let (def_rpc, def_token, def_vault) = match preset.as_str() { - "arbitrum-one" => ( - ARBITRUM_ONE_RPC_URL.to_string(), - ARBITRUM_ONE_TOKEN.to_string(), - ARBITRUM_ONE_VAULT.to_string(), - ), - "arbitrum-sepolia" | "arbitrum-sepolia-test" => ( - ARBITRUM_SEPOLIA_RPC_URL.to_string(), - ARBITRUM_SEPOLIA_TOKEN.to_string(), - ARBITRUM_SEPOLIA_VAULT.to_string(), - ), + "arbitrum-one" => preset_addresses(&EvmNetwork::ArbitrumOne), + "arbitrum-sepolia" | "arbitrum-sepolia-test" => { + preset_addresses(&EvmNetwork::ArbitrumSepoliaTest) + } _ => ( "http://127.0.0.1:8545".to_string(), String::new(), @@ -88,6 +77,17 @@ where } } +/// Pull `(rpc_url, payment_token_address, payment_vault_address)` from a +/// typed `evmlib::Network`. Used so the addresses we hand to external +/// signers track evmlib's canonical constants on every version bump. +fn preset_addresses(network: &EvmNetwork) -> (String, String, String) { + ( + network.rpc_url().to_string(), + network.payment_token_address().to_string(), + network.payment_vault_address().to_string(), + ) +} + #[cfg(test)] mod tests { use super::*; @@ -105,18 +105,25 @@ mod tests { fn default_network_picks_arbitrum_one() { let cfg = resolve_with("default", env(&[])); assert_eq!(cfg.preset, "arbitrum-one"); - assert_eq!(cfg.rpc_url, "https://arb1.arbitrum.io/rpc"); + + // Cross-check against evmlib's typed enum: this is the whole point of + // the refactor — these MUST stay in sync, otherwise external signers + // pay the wrong vault. + let canonical = &EvmNetwork::ArbitrumOne; + assert_eq!(cfg.rpc_url, canonical.rpc_url().to_string()); assert!( cfg.token_addr - .eq_ignore_ascii_case("0xa78d8321B20c4Ef90eCd72f2588AA985A4BDb684"), - "unexpected token addr: {}", - cfg.token_addr + .eq_ignore_ascii_case(&canonical.payment_token_address().to_string()), + "token addr drifted from evmlib: cfg={}, evmlib={}", + cfg.token_addr, + canonical.payment_token_address() ); assert!( cfg.vault_addr - .eq_ignore_ascii_case("0xB1b5219f8Aaa18037A2506626Dd0406a46f70BcC"), - "unexpected vault addr: {}", - cfg.vault_addr + .eq_ignore_ascii_case(&canonical.payment_vault_address().to_string()), + "vault addr drifted from evmlib: cfg={}, evmlib={}", + cfg.vault_addr, + canonical.payment_vault_address() ); } @@ -133,12 +140,18 @@ mod tests { fn evm_network_sepolia_picks_sepolia_addresses() { let cfg = resolve_with("default", env(&[("EVM_NETWORK", "arbitrum-sepolia")])); assert_eq!(cfg.preset, "arbitrum-sepolia"); - assert_eq!(cfg.rpc_url, "https://sepolia-rollup.arbitrum.io/rpc"); + + let canonical = &EvmNetwork::ArbitrumSepoliaTest; + assert_eq!(cfg.rpc_url, canonical.rpc_url().to_string()); assert!( cfg.token_addr - .eq_ignore_ascii_case("0x4bc1aCE0E66170375462cB4E6Af42Ad4D5EC689C"), - "unexpected token addr: {}", - cfg.token_addr + .eq_ignore_ascii_case(&canonical.payment_token_address().to_string()), + "sepolia token addr drifted from evmlib" + ); + assert!( + cfg.vault_addr + .eq_ignore_ascii_case(&canonical.payment_vault_address().to_string()), + "sepolia vault addr drifted from evmlib" ); } diff --git a/antd/src/rest/upload.rs b/antd/src/rest/upload.rs index 6cc181f..8828c50 100644 --- a/antd/src/rest/upload.rs +++ b/antd/src/rest/upload.rs @@ -5,21 +5,31 @@ use axum::extract::State; use axum::Json; use crate::error::AntdError; +use crate::evm_defaults; use crate::state::AppState; use crate::types::*; /// Build a [`PrepareUploadResponse`] from a prepared upload, matching on the /// payment variant (wave-batch vs merkle) and serialising the appropriate fields. +/// +/// The EVM addresses returned to the external signer are resolved through +/// [`evm_defaults::resolve`] using the daemon's configured network — the same +/// path that constructs the daemon's own internal-wallet client. Reading the +/// raw `EVM_RPC_URL` / `EVM_PAYMENT_TOKEN_ADDRESS` / `EVM_PAYMENT_VAULT_ADDRESS` +/// env vars here was a bug: an antd launched without those env vars (the +/// expected mainnet shape, where the preset alone is enough) returned an +/// empty token address and `http://127.0.0.1:8545` as the RPC URL, causing +/// external signers (e.g. indelible) to call ERC-20 `approve` against the +/// zero address and revert at gas estimation. fn build_prepare_response( upload_id: String, prepared: &ant_core::data::PreparedUpload, + network: &str, ) -> Result { - let rpc_url = - std::env::var("EVM_RPC_URL").unwrap_or_else(|_| "http://127.0.0.1:8545".to_string()); - let payment_token_address = std::env::var("EVM_PAYMENT_TOKEN_ADDRESS").unwrap_or_default(); - let payment_vault_address = std::env::var("EVM_PAYMENT_VAULT_ADDRESS") - .or_else(|_| std::env::var("EVM_DATA_PAYMENTS_ADDRESS")) - .unwrap_or_default(); + let evm_cfg = evm_defaults::resolve(network); + let rpc_url = evm_cfg.rpc_url; + let payment_token_address = evm_cfg.token_addr; + let payment_vault_address = evm_cfg.vault_addr; match &prepared.payment_info { ant_core::data::ExternalPaymentInfo::WaveBatch { payment_intent, .. } => { @@ -115,7 +125,7 @@ pub async fn prepare_upload( // Generate a unique upload ID and store the prepared state let upload_id = hex::encode(rand::random::<[u8; 16]>()); - let response = build_prepare_response(upload_id.clone(), &prepared)?; + let response = build_prepare_response(upload_id.clone(), &prepared, &state.network)?; state.pending_uploads.lock().await.insert( upload_id, @@ -154,7 +164,7 @@ pub async fn prepare_data_upload( .map_err(|e| AntdError::Internal(format!("task failed: {e}")))??; let upload_id = hex::encode(rand::random::<[u8; 16]>()); - let response = build_prepare_response(upload_id.clone(), &prepared)?; + let response = build_prepare_response(upload_id.clone(), &prepared, &state.network)?; state.pending_uploads.lock().await.insert( upload_id,