Skip to content
Merged
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
567 changes: 30 additions & 537 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@ saorsa-core = "0.20.0"
saorsa-pqc = "0.5"

# Payment verification - autonomi network lookup + EVM payment
ant-evm = "0.1.19"
evmlib = "0.4.9"
evmlib = "0.5.0"
xor_name = "5"
libp2p = "0.56" # For PeerId in payment proofs
multihash = "0.19" # For identity multihash in PeerId construction

# Caching - LRU cache for verified XorNames
lru = "0.16.3"
Expand Down
8 changes: 6 additions & 2 deletions src/bin/ant-devnet/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ async fn main() -> color_eyre::Result<()> {
// Start Anvil and deploy contracts if EVM is enabled
let evm_info = if cli.enable_evm {
info!("Starting local Anvil blockchain for EVM payment enforcement...");
let testnet = evmlib::testnet::Testnet::new().await;
let testnet = evmlib::testnet::Testnet::new()
.await
.map_err(|e| color_eyre::eyre::eyre!("Failed to start Anvil testnet: {e}"))?;
let network = testnet.to_network();
let wallet_key = testnet.default_wallet_private_key();
let wallet_key = testnet
.default_wallet_private_key()
.map_err(|e| color_eyre::eyre::eyre!("Failed to get wallet key: {e}"))?;

let (rpc_url, token_addr, payments_addr, merkle_addr) = match &network {
evmlib::Network::Custom(custom) => (
Expand Down
30 changes: 8 additions & 22 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,37 +45,23 @@ pub use data_types::{

// Re-export hex_node_id_to_encoded_peer_id for payment operations
use crate::error::{Error, Result};
use ant_evm::EncodedPeerId;

/// Identity multihash code (stores raw bytes without hashing).
const MULTIHASH_IDENTITY_CODE: u64 = 0x00;
use evmlib::EncodedPeerId;

/// Convert a hex-encoded 32-byte node ID to an [`EncodedPeerId`].
///
/// Peer IDs are 64-character hex strings representing 32 raw bytes.
/// libp2p `PeerId` expects a multihash-encoded identity. This function bridges the two
/// formats by wrapping the raw bytes in an identity multihash (code 0x00) and then
/// converting to `EncodedPeerId` via `From<PeerId>`.
/// This function decodes the hex string and wraps the raw bytes directly
/// into an `EncodedPeerId`.
///
/// # Errors
///
/// Returns an error if the hex string is invalid or the peer ID cannot be constructed.
/// Returns an error if the hex string is invalid or not exactly 32 bytes.
pub fn hex_node_id_to_encoded_peer_id(hex_id: &str) -> Result<EncodedPeerId> {
let raw_bytes = hex::decode(hex_id)
.map_err(|e| Error::Payment(format!("Invalid hex peer ID '{hex_id}': {e}")))?;

let multihash =
multihash::Multihash::<64>::wrap(MULTIHASH_IDENTITY_CODE, &raw_bytes).map_err(|e| {
Error::Payment(format!(
"Failed to create multihash for peer '{hex_id}': {e}"
))
})?;

let peer_id = libp2p::PeerId::from_multihash(multihash).map_err(|_| {
Error::Payment(format!(
"Failed to create PeerId from multihash for peer '{hex_id}'"
))
let bytes: [u8; 32] = raw_bytes.try_into().map_err(|v: Vec<u8>| {
let len = v.len();
Error::Payment(format!("Peer ID must be 32 bytes, got {len}"))
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length error path drops the offending input value, making it harder to diagnose which peer ID was malformed. Consider including hex_id (and/or expected hex length of 64 chars) in the "Peer ID must be 32 bytes" error message for easier debugging.

Suggested change
Error::Payment(format!("Peer ID must be 32 bytes, got {len}"))
Error::Payment(format!(
"Peer ID must be 32 bytes (64 hex chars), got {len} bytes from '{hex_id}'"
))

Copilot uses AI. Check for mistakes.
})?;

Ok(EncodedPeerId::from(peer_id))
Ok(EncodedPeerId::new(bytes))
}
2 changes: 1 addition & 1 deletion src/devnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use crate::payment::{
QuotingMetricsTracker,
};
use crate::storage::{AntProtocol, LmdbStorage, LmdbStorageConfig};
use ant_evm::RewardsAddress;
use evmlib::Network as EvmNetwork;
use evmlib::RewardsAddress;
use rand::Rng;
use saorsa_core::identity::NodeIdentity;
use saorsa_core::{
Expand Down
2 changes: 1 addition & 1 deletion src/payment/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! - Storage capacity and usage
//! - Network liveness information

use ant_evm::QuotingMetrics;
use evmlib::quoting_metrics::QuotingMetrics;
use parking_lot::RwLock;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
Expand Down
3 changes: 2 additions & 1 deletion src/payment/pricing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
//! charge the same regardless of remaining capacity. The logarithmic curve ensures
//! the network self-balances as nodes fill up.

use ant_evm::{Amount, QuotingMetrics};
use evmlib::common::Amount;
use evmlib::quoting_metrics::QuotingMetrics;

/// Minimum price floor (matches contract's `minPrice = 3`).
const MIN_PRICE: u64 = 3;
Expand Down
18 changes: 8 additions & 10 deletions src/payment/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
//! on-chain transaction hashes returned by the wallet after payment.

use crate::ant_protocol::{PROOF_TAG_MERKLE, PROOF_TAG_SINGLE_NODE};
use ant_evm::merkle_payments::MerklePaymentProof;
use ant_evm::ProofOfPayment;
use evmlib::common::TxHash;
use evmlib::merkle_payments::MerklePaymentProof;
use evmlib::ProofOfPayment;
use serde::{Deserialize, Serialize};

/// A payment proof that includes both the quote-based proof and on-chain tx hashes.
Expand Down Expand Up @@ -116,15 +116,14 @@ pub fn deserialize_merkle_proof(bytes: &[u8]) -> std::result::Result<MerklePayme
mod tests {
use super::*;
use alloy::primitives::FixedBytes;
use ant_evm::merkle_payments::{
use evmlib::merkle_payments::{
MerklePaymentCandidateNode, MerklePaymentCandidatePool, MerklePaymentProof, MerkleTree,
CANDIDATES_PER_POOL,
};
use ant_evm::RewardsAddress;
use ant_evm::{EncodedPeerId, PaymentQuote};
use evmlib::quoting_metrics::QuotingMetrics;
use libp2p::identity::Keypair;
use libp2p::PeerId;
use evmlib::EncodedPeerId;
use evmlib::PaymentQuote;
use evmlib::RewardsAddress;
use saorsa_core::MlDsa65;
use saorsa_pqc::pqc::types::MlDsaSecretKey;
use saorsa_pqc::pqc::MlDsaOperations;
Expand Down Expand Up @@ -153,10 +152,9 @@ mod tests {
}

fn make_proof_of_payment() -> ProofOfPayment {
let keypair = Keypair::generate_ed25519();
let peer_id = PeerId::from_public_key(&keypair.public());
let random_peer = EncodedPeerId::new(rand::random());
ProofOfPayment {
peer_quotes: vec![(EncodedPeerId::from(peer_id), make_test_quote())],
peer_quotes: vec![(random_peer, make_test_quote())],
}
}

Expand Down
10 changes: 6 additions & 4 deletions src/payment/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

use crate::error::{Error, Result};
use crate::payment::metrics::QuotingMetricsTracker;
use ant_evm::merkle_payments::MerklePaymentCandidateNode;
use ant_evm::{PaymentQuote, QuotingMetrics, RewardsAddress};
use evmlib::merkle_payments::MerklePaymentCandidateNode;
use evmlib::quoting_metrics::QuotingMetrics;
use evmlib::PaymentQuote;
use evmlib::RewardsAddress;
use saorsa_core::MlDsa65;
use saorsa_pqc::pqc::types::{MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature};
use saorsa_pqc::pqc::MlDsaOperations;
Expand Down Expand Up @@ -612,7 +614,7 @@ mod tests {
let quote = PaymentQuote {
content: xor_name::XorName([0u8; 32]),
timestamp: SystemTime::now(),
quoting_metrics: ant_evm::QuotingMetrics {
quoting_metrics: QuotingMetrics {
data_size: 0,
data_type: 0,
close_records_stored: 0,
Expand Down Expand Up @@ -761,7 +763,7 @@ mod tests {
.duration_since(std::time::UNIX_EPOCH)
.expect("system time")
.as_secs();
let metrics = ant_evm::QuotingMetrics {
let metrics = QuotingMetrics {
data_size: 4096,
data_type: 0,
close_records_stored: 10,
Expand Down
33 changes: 24 additions & 9 deletions src/payment/single_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@

use crate::ant_protocol::CLOSE_GROUP_SIZE;
use crate::error::{Error, Result};
use ant_evm::{Amount, PaymentQuote, QuoteHash, QuotingMetrics, RewardsAddress};
use evmlib::common::{Amount, QuoteHash};
use evmlib::contract::payment_vault;
use evmlib::quoting_metrics::QuotingMetrics;
use evmlib::wallet::Wallet;
use evmlib::Network as EvmNetwork;
use evmlib::PaymentQuote;
use evmlib::RewardsAddress;
use tracing::info;

/// Create zero-valued `QuotingMetrics` for payment verification.
Expand Down Expand Up @@ -330,9 +333,13 @@ mod tests {
async fn test_standard_five_quote_payment() {
// Use autonomi's setup pattern with increased timeout for CI
let (node, rpc_url) = start_node_with_timeout();
let network_token = deploy_network_token_contract(&rpc_url, &node).await;
let network_token = deploy_network_token_contract(&rpc_url, &node)
.await
.expect("deploy network token");
let mut payment_vault =
deploy_data_payments_contract(&rpc_url, &node, *network_token.contract.address()).await;
deploy_data_payments_contract(&rpc_url, &node, *network_token.contract.address())
.await
.expect("deploy data payments");

let transaction_config = TransactionConfig::default();

Expand Down Expand Up @@ -397,9 +404,13 @@ mod tests {
#[allow(clippy::expect_used)]
async fn test_single_node_payment_strategy() {
let (node, rpc_url) = start_node_with_timeout();
let network_token = deploy_network_token_contract(&rpc_url, &node).await;
let network_token = deploy_network_token_contract(&rpc_url, &node)
.await
.expect("deploy network token");
let mut payment_vault =
deploy_data_payments_contract(&rpc_url, &node, *network_token.contract.address()).await;
deploy_data_payments_contract(&rpc_url, &node, *network_token.contract.address())
.await
.expect("deploy data payments");

let transaction_config = TransactionConfig::default();

Expand Down Expand Up @@ -608,11 +619,15 @@ mod tests {
#[serial]
async fn test_single_node_with_real_prices() -> Result<()> {
// Setup testnet
let testnet = Testnet::new().await;
let testnet = Testnet::new()
.await
.map_err(|e| Error::Payment(format!("Failed to start testnet: {e}")))?;
let network = testnet.to_network();
let wallet =
Wallet::new_from_private_key(network.clone(), &testnet.default_wallet_private_key())
.map_err(|e| Error::Payment(format!("Failed to create wallet: {e}")))?;
let wallet_key = testnet
.default_wallet_private_key()
.map_err(|e| Error::Payment(format!("Failed to get wallet key: {e}")))?;
let wallet = Wallet::new_from_private_key(network.clone(), &wallet_key)
.map_err(|e| Error::Payment(format!("Failed to create wallet: {e}")))?;

println!("✓ Started Anvil testnet");

Expand Down
Loading
Loading