Skip to content
51 changes: 8 additions & 43 deletions Cargo.lock

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

18 changes: 4 additions & 14 deletions ant-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,24 +215,14 @@ fn resolve_evm_network(
.payment_token_address
.parse()
.map_err(|e| anyhow::anyhow!("Invalid token address: {e}"))?;
let payments_addr: EvmAddress = evm
.data_payments_address
let vault_addr: EvmAddress = evm
.payment_vault_address
.parse()
.map_err(|e| anyhow::anyhow!("Invalid payments address: {e}"))?;
let merkle_addr: Option<EvmAddress> = evm
.merkle_payments_address
.as_ref()
.map(|s| {
s.parse().map_err(|e| {
anyhow::anyhow!("Invalid merkle payments address: {e}")
})
})
.transpose()?;
.map_err(|e| anyhow::anyhow!("Invalid payment vault address: {e}"))?;
return Ok(EvmNetwork::Custom(CustomNetwork {
rpc_url_http: rpc_url,
payment_token_address: token_addr,
data_payments_address: payments_addr,
merkle_payments_address: merkle_addr,
payment_vault_address: vault_addr,
}));
}
}
Expand Down
4 changes: 2 additions & 2 deletions ant-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ zip = "2"
tower-http = { version = "0.6.8", features = ["cors"] }

# Data operations
evmlib = "0.5.0"
evmlib = "0.7"
xor_name = "5"
self_encryption = "0.35.0"
futures = "0.3"
Expand All @@ -35,7 +35,7 @@ tracing = "0.1"
bytes = "1"
lru = "0.16"
rand = "0.8"
ant-node = "0.9.0"
ant-node = { git = "https://github.com/WithAutonomi/ant-node.git", branch = "feat/adapt-evmlib-payment-vault" }
saorsa-pqc = "0.5"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

Expand Down
49 changes: 5 additions & 44 deletions ant-core/src/data/client/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use ant_node::core::{MultiAddr, PeerId};
use ant_node::payment::{serialize_single_node_proof, PaymentProof, SingleNodePayment};
use bytes::Bytes;
use evmlib::common::{Amount, QuoteHash, TxHash};
use evmlib::contract::payment_vault::get_market_price;
use evmlib::wallet::PayForQuotesError;
use evmlib::{EncodedPeerId, PaymentQuote, ProofOfPayment, RewardsAddress};
use futures::stream::{self, StreamExt};
Expand Down Expand Up @@ -144,7 +143,7 @@ pub fn finalize_batch_payment(
impl Client {
/// Prepare a single chunk for batch payment.
///
/// Collects quotes and fetches contract prices without making any
/// Collects quotes and uses node-reported prices without making any
/// on-chain transaction. Returns `Ok(None)` if the chunk is already
/// stored on the network.
///
Expand All @@ -168,44 +167,21 @@ impl Client {
Err(e) => return Err(e),
};

let evm_network = self.require_evm_network()?;

// Capture all quoted peers for close-group replication.
let quoted_peers: Vec<(PeerId, Vec<MultiAddr>)> = quotes_with_peers
.iter()
.map(|(peer_id, addrs, _, _)| (*peer_id, addrs.clone()))
.collect();

// Fetch authoritative prices from the on-chain contract.
let metrics_batch: Vec<_> = quotes_with_peers
.iter()
.map(|(_, _, quote, _)| quote.quoting_metrics.clone())
.collect();

let contract_prices = get_market_price(evm_network, metrics_batch)
.await
.map_err(|e| {
Error::Payment(format!("Failed to get market prices from contract: {e}"))
})?;

if contract_prices.len() != quotes_with_peers.len() {
return Err(Error::Payment(format!(
"Contract returned {} prices for {} quotes",
contract_prices.len(),
quotes_with_peers.len()
)));
}

// Build peer_quotes for ProofOfPayment + quotes for SingleNodePayment.
// Use node-reported prices directly — no contract price fetch needed.
let mut peer_quotes = Vec::with_capacity(quotes_with_peers.len());
let mut quotes_for_payment = Vec::with_capacity(quotes_with_peers.len());

for ((peer_id, _addrs, quote, _local_price), contract_price) in
quotes_with_peers.into_iter().zip(contract_prices)
{
for (peer_id, _addrs, quote, price) in quotes_with_peers {
let encoded = peer_id_to_encoded(&peer_id)?;
peer_quotes.push((encoded, quote.clone()));
quotes_for_payment.push((quote, contract_price));
quotes_for_payment.push((quote, price));
}

let payment = SingleNodePayment::from_quotes(quotes_for_payment)
Expand Down Expand Up @@ -437,21 +413,6 @@ mod tests {
use super::*;
use ant_node::payment::single_node::QuotePaymentInfo;
use ant_node::CLOSE_GROUP_SIZE;
use evmlib::quoting_metrics::QuotingMetrics;

fn test_metrics() -> QuotingMetrics {
QuotingMetrics {
data_size: 0,
data_type: 0,
close_records_stored: 0,
records_per_type: vec![],
max_records: 0,
received_payment_count: 0,
live_time: 0,
network_density: None,
network_size: None,
}
}

/// Helper: build a PreparedChunk with specified payment amounts.
fn make_prepared_chunk(amounts: [u64; CLOSE_GROUP_SIZE]) -> PreparedChunk {
Expand All @@ -460,7 +421,7 @@ mod tests {
quote_hash: QuoteHash::from([i as u8 + 1; 32]),
rewards_address: RewardsAddress::new([i as u8 + 10; 20]),
amount: Amount::from(amounts[i]),
quoting_metrics: test_metrics(),
price: Amount::from(amounts[i]),
});

PreparedChunk {
Expand Down
67 changes: 5 additions & 62 deletions ant-core/src/data/client/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,8 @@ impl Client {
candidate_futures.push(fut);
}

self.collect_validated_candidates(
&mut candidate_futures,
merkle_payment_timestamp,
data_type,
)
.await
self.collect_validated_candidates(&mut candidate_futures, merkle_payment_timestamp)
.await
}

/// Collect and validate merkle candidate responses until we have enough.
Expand All @@ -396,7 +392,6 @@ impl Client {
>,
>,
merkle_payment_timestamp: u64,
expected_data_type: u32,
) -> Result<[MerklePaymentCandidateNode; CANDIDATES_PER_POOL]> {
let mut candidates = Vec::with_capacity(CANDIDATES_PER_POOL);
let mut failures: Vec<String> = Vec::new();
Expand All @@ -414,14 +409,6 @@ impl Client {
failures.push(format!("{peer_id}: timestamp mismatch"));
continue;
}
if candidate.quoting_metrics.data_type != expected_data_type {
warn!(
"Data type mismatch from {peer_id}: expected {expected_data_type}, got {}",
candidate.quoting_metrics.data_type
);
failures.push(format!("{peer_id}: wrong data_type"));
continue;
}
candidates.push(candidate);
if candidates.len() >= CANDIDATES_PER_POOL {
break;
Expand Down Expand Up @@ -633,8 +620,8 @@ mod tests {
#[test]
fn test_merkle_proof_serialize_deserialize_roundtrip() {
use ant_node::payment::{deserialize_merkle_proof, serialize_merkle_proof};
use evmlib::common::Amount;
use evmlib::merkle_payments::MerklePaymentCandidateNode;
use evmlib::quoting_metrics::QuotingMetrics;
use evmlib::RewardsAddress;

let addrs = make_test_addresses(4);
Expand All @@ -654,17 +641,7 @@ mod tests {
let candidate_nodes: [MerklePaymentCandidateNode; CANDIDATES_PER_POOL] =
std::array::from_fn(|i| MerklePaymentCandidateNode {
pub_key: vec![i as u8; 32],
quoting_metrics: QuotingMetrics {
data_size: 1024,
data_type: 0,
close_records_stored: 0,
records_per_type: vec![],
max_records: 100,
received_payment_count: 0,
live_time: 0,
network_density: None,
network_size: None,
},
price: Amount::from(1024u64),
reward_address: RewardsAddress::new([i as u8; 20]),
merkle_payment_timestamp: timestamp,
signature: vec![i as u8; 64],
Expand Down Expand Up @@ -702,17 +679,7 @@ mod tests {
// Simulates what collect_validated_candidates checks
let candidate = MerklePaymentCandidateNode {
pub_key: vec![0u8; 32],
quoting_metrics: evmlib::quoting_metrics::QuotingMetrics {
data_size: 0,
data_type: 0,
close_records_stored: 0,
records_per_type: vec![],
max_records: 0,
received_payment_count: 0,
live_time: 0,
network_density: None,
network_size: None,
},
price: evmlib::common::Amount::ZERO,
reward_address: evmlib::RewardsAddress::new([0u8; 20]),
merkle_payment_timestamp: 1000,
signature: vec![0u8; 64],
Expand All @@ -722,30 +689,6 @@ mod tests {
assert_ne!(candidate.merkle_payment_timestamp, 2000);
}

#[test]
fn test_candidate_wrong_data_type_rejected() {
let candidate = MerklePaymentCandidateNode {
pub_key: vec![0u8; 32],
quoting_metrics: evmlib::quoting_metrics::QuotingMetrics {
data_size: 0,
data_type: 1, // scratchpad
close_records_stored: 0,
records_per_type: vec![],
max_records: 0,
received_payment_count: 0,
live_time: 0,
network_density: None,
network_size: None,
},
reward_address: evmlib::RewardsAddress::new([0u8; 20]),
merkle_payment_timestamp: 1000,
signature: vec![0u8; 64],
};

// data_type check: 1 (scratchpad) != 0 (chunk)
assert_ne!(candidate.quoting_metrics.data_type, 0);
}

// =========================================================================
// Batch splitting edge cases
// =========================================================================
Expand Down
6 changes: 3 additions & 3 deletions ant-core/src/data/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,17 @@ impl Client {
/// Set the wallet for payment operations.
///
/// Also populates the EVM network from the wallet so that
/// price queries work without a separate `with_evm_network` call.
/// token approvals work without a separate `with_evm_network` call.
#[must_use]
pub fn with_wallet(mut self, wallet: Wallet) -> Self {
self.evm_network = Some(wallet.network().clone());
self.wallet = Some(Arc::new(wallet));
self
}

/// Set the EVM network for price queries without requiring a wallet.
/// Set the EVM network without requiring a wallet.
///
/// This enables operations like quote collection and cost estimation
/// This enables token approval and contract interactions
/// for external-signer flows where the private key lives outside Rust.
#[must_use]
pub fn with_evm_network(mut self, network: evmlib::Network) -> Self {
Expand Down
Loading
Loading