diff --git a/ant-networking/src/event/request_response.rs b/ant-networking/src/event/request_response.rs index d7a210821b..ce6755e8dc 100644 --- a/ant-networking/src/event/request_response.rs +++ b/ant-networking/src/event/request_response.rs @@ -48,30 +48,6 @@ impl SwarmDriver { self.add_keys_to_replication_fetcher(holder, keys); } - Request::Cmd(ant_protocol::messages::Cmd::QuoteVerification { - quotes, - .. - }) => { - let response = Response::Cmd( - ant_protocol::messages::CmdResponse::QuoteVerification(Ok(())), - ); - self.queue_network_swarm_cmd(NetworkSwarmCmd::SendResponse { - resp: response, - channel: MsgResponder::FromPeer(channel), - }); - - // The keypair is required to verify the quotes, - // hence throw it up to Network layer for further actions. - let quotes = quotes - .iter() - .filter_map(|(peer_address, quote)| { - peer_address - .as_peer_id() - .map(|peer_id| (peer_id, quote.clone())) - }) - .collect(); - self.send_event(NetworkEvent::QuoteVerification { quotes }) - } Request::Cmd(ant_protocol::messages::Cmd::PeerConsideredAsBad { detected_by, bad_peer, diff --git a/ant-networking/src/lib.rs b/ant-networking/src/lib.rs index c2d196e8ae..d0ee5d2c06 100644 --- a/ant-networking/src/lib.rs +++ b/ant-networking/src/lib.rs @@ -51,7 +51,7 @@ use self::{cmd::NetworkSwarmCmd, error::Result}; use ant_evm::{PaymentQuote, QuotingMetrics}; use ant_protocol::{ error::Error as ProtocolError, - messages::{ChunkProof, Cmd, Nonce, Query, QueryResponse, Request, Response}, + messages::{ChunkProof, Nonce, Query, QueryResponse, Request, Response}, storage::{RecordType, RetryStrategy, Scratchpad}, NetworkAddress, PrettyPrintKBucketKey, PrettyPrintRecordKey, CLOSE_GROUP_SIZE, }; @@ -83,8 +83,10 @@ use { std::collections::HashSet, }; -/// The type of quote for a selected payee. -pub type PayeeQuote = (PeerId, PaymentQuote); +/// Selected quotes to pay for a data address +pub struct SelectedQuotes { + pub quotes: Vec<(PeerId, PaymentQuote)>, +} /// Majority of a given group (i.e. > 1/2). #[inline] @@ -382,7 +384,7 @@ impl Network { &self, record_address: NetworkAddress, ignore_peers: Vec, - ) -> Result> { + ) -> Result { // The requirement of having at least CLOSE_GROUP_SIZE // close nodes will be checked internally automatically. let mut close_nodes = self @@ -446,7 +448,7 @@ impl Network { info!("Address {record_address:?} was already paid for according to {peer_address:?} ({peer_already_have_it}/{enough_peers_already_have_it})"); if peer_already_have_it >= enough_peers_already_have_it { info!("Address {record_address:?} was already paid for according to {peer_already_have_it} peers, ending quote request"); - return Ok(vec![]); + return Ok(SelectedQuotes { quotes: vec![] }); } } Err(err) => { @@ -458,17 +460,7 @@ impl Network { } } - // send the quotes to the other peers for verification - for peer_id in close_nodes.iter() { - let request = Request::Cmd(Cmd::QuoteVerification { - target: NetworkAddress::from_peer(*peer_id), - quotes: all_quotes.clone(), - }); - - self.send_req_ignore_reply(request, *peer_id); - } - - Ok(quotes_to_pay) + Ok(SelectedQuotes { quotes: quotes_to_pay }) } /// Get register from network. diff --git a/ant-node/src/put_validation.rs b/ant-node/src/put_validation.rs index 9a757ddf9d..0f6912e9b8 100644 --- a/ant-node/src/put_validation.rs +++ b/ant-node/src/put_validation.rs @@ -19,7 +19,7 @@ use ant_protocol::{ }; use ant_registers::SignedRegister; use libp2p::kad::{Record, RecordKey}; -use std::time::{Duration, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use xor_name::XorName; impl Node { @@ -614,7 +614,7 @@ impl Node { // verify quote timestamp let quote_timestamp = payment.quote.timestamp; let quote_expiration_time = quote_timestamp + Duration::from_secs(QUOTE_EXPIRATION_SECS); - let quote_expiration_time_in_secs = quote_expiration_time + let _quote_expiration_time_in_secs = quote_expiration_time .duration_since(UNIX_EPOCH) .map_err(|e| { Error::InvalidRequest(format!( @@ -622,16 +622,22 @@ impl Node { )) })? .as_secs(); + // NB TODO @mick: can we check if the quote has expired with block time in evmlib? Or should nodes do it manually here? Else keep the block below + // manually check if the quote has expired + if quote_expiration_time < SystemTime::now() { + warn!("Payment quote has expired for record {pretty_key}"); + return Err(Error::InvalidRequest(format!( + "Payment quote has expired for record {pretty_key}" + ))); + } // check if payment is valid on chain debug!("Verifying payment for record {pretty_key}"); let reward_amount = self.evm_network() .verify_data_payment( - payment.tx_hash, payment.quote.hash(), payment.quote.quoting_metrics, *self.reward_address(), - quote_expiration_time_in_secs, ) .await .map_err(|e| Error::EvmNetwork(format!("Failed to verify chunk payment: {e}")))?; diff --git a/ant-protocol/src/messages/cmd.rs b/ant-protocol/src/messages/cmd.rs index cec0629259..1437c6540b 100644 --- a/ant-protocol/src/messages/cmd.rs +++ b/ant-protocol/src/messages/cmd.rs @@ -8,7 +8,6 @@ #![allow(clippy::mutable_key_type)] // for Bytes in NetworkAddress use crate::{storage::RecordType, NetworkAddress}; -pub use ant_evm::PaymentQuote; use serde::{Deserialize, Serialize}; /// Data and CashNote cmds - recording transactions or creating, updating, and removing data. @@ -28,11 +27,6 @@ pub enum Cmd { /// Keys of copy that shall be replicated. keys: Vec<(NetworkAddress, RecordType)>, }, - /// Write operation to notify nodes a list of PaymentQuote collected. - QuoteVerification { - target: NetworkAddress, - quotes: Vec<(NetworkAddress, PaymentQuote)>, - }, /// Notify the peer it is now being considered as BAD due to the included behaviour PeerConsideredAsBad { detected_by: NetworkAddress, @@ -52,11 +46,6 @@ impl std::fmt::Debug for Cmd { .field("first_ten_keys", &first_ten_keys) .finish() } - Cmd::QuoteVerification { target, quotes } => f - .debug_struct("Cmd::QuoteVerification") - .field("target", target) - .field("quotes_len", "es.len()) - .finish(), Cmd::PeerConsideredAsBad { detected_by, bad_peer, @@ -76,7 +65,6 @@ impl Cmd { pub fn dst(&self) -> NetworkAddress { match self { Cmd::Replicate { holder, .. } => holder.clone(), - Cmd::QuoteVerification { target, .. } => target.clone(), Cmd::PeerConsideredAsBad { bad_peer, .. } => bad_peer.clone(), } } @@ -93,13 +81,6 @@ impl std::fmt::Display for Cmd { keys.len() ) } - Cmd::QuoteVerification { target, quotes } => { - write!( - f, - "Cmd::QuoteVerification(sent to {target:?} has {} quotes)", - quotes.len() - ) - } Cmd::PeerConsideredAsBad { detected_by, bad_peer, diff --git a/ant-protocol/src/messages/response.rs b/ant-protocol/src/messages/response.rs index d3fc29ab31..48b332c60b 100644 --- a/ant-protocol/src/messages/response.rs +++ b/ant-protocol/src/messages/response.rs @@ -150,11 +150,6 @@ pub enum CmdResponse { /// Response to replication cmd Replicate(Result<()>), // - // ===== QuoteVerification ===== - // - /// Response to quote verification cmd - QuoteVerification(Result<()>), - // // ===== PeerConsideredAsBad ===== // /// Response to the considered as bad notification diff --git a/autonomi/src/client/quote.rs b/autonomi/src/client/quote.rs index 8b257f74d6..16cd377369 100644 --- a/autonomi/src/client/quote.rs +++ b/autonomi/src/client/quote.rs @@ -8,7 +8,7 @@ use ant_evm::{PaymentQuote, ProofOfPayment, QuoteHash, TxHash}; use ant_evm::{Amount, AttoTokens, QuotePayment}; -use ant_networking::{Network, NetworkError, PayeeQuote}; +use ant_networking::{Network, NetworkError, SelectedQuotes}; use ant_protocol::{ storage::ChunkAddress, NetworkAddress, @@ -21,7 +21,7 @@ use super::{data::CostError, Client}; pub struct QuotesToPay { pub nodes_to_pay: Vec, - pub nodes_to_upload_to: Vec, + pub nodes_to_upload_to: Vec, pub cost_per_node: AttoTokens, pub total_cost: AttoTokens, } @@ -66,7 +66,7 @@ impl Client { async fn fetch_store_quote( network: &Network, content_addr: XorName, -) -> Result, NetworkError> { +) -> Result, NetworkError> { network .get_store_quote_from_network( NetworkAddress::from_chunk_address(ChunkAddress::new(content_addr)), @@ -79,7 +79,7 @@ async fn fetch_store_quote( async fn fetch_store_quote_with_retries( network: &Network, content_addr: XorName, -) -> Result<(XorName, Vec), CostError> { +) -> Result<(XorName, Vec), CostError> { let mut retries = 0; loop { diff --git a/autonomi/src/client/utils.rs b/autonomi/src/client/utils.rs index 7afbdceec1..ba043d3158 100644 --- a/autonomi/src/client/utils.rs +++ b/autonomi/src/client/utils.rs @@ -9,7 +9,7 @@ use crate::client::payment::Receipt; use ant_evm::{EvmWallet, ProofOfPayment, QuotePayment}; use ant_networking::{ - GetRecordCfg, Network, NetworkError, PayeeQuote, PutRecordCfg, VerificationKind, + GetRecordCfg, Network, NetworkError, SelectedQuotes, PutRecordCfg, VerificationKind, }; use ant_protocol::{ messages::ChunkProof, @@ -197,7 +197,7 @@ impl Client { pub(crate) async fn get_store_quotes( &self, content_addrs: impl Iterator, - ) -> Result, CostError> { + ) -> Result, CostError> { let futures: Vec<_> = content_addrs .into_iter() .map(|content_addr| fetch_store_quote_with_retries(&self.network, content_addr)) @@ -205,7 +205,7 @@ impl Client { let quotes = futures::future::try_join_all(futures).await?; - Ok(quotes.into_iter().collect::>()) + Ok(quotes.into_iter().collect::>()) } } @@ -213,7 +213,7 @@ impl Client { async fn fetch_store_quote_with_retries( network: &Network, content_addr: XorName, -) -> Result<(XorName, PayeeQuote), CostError> { +) -> Result<(XorName, SelectedQuotes), CostError> { let mut retries = 0; loop { @@ -239,7 +239,7 @@ async fn fetch_store_quote_with_retries( async fn fetch_store_quote( network: &Network, content_addr: XorName, -) -> Result { +) -> Result { network .get_store_costs_from_network( NetworkAddress::from_chunk_address(ChunkAddress::new(content_addr)), @@ -250,7 +250,7 @@ async fn fetch_store_quote( /// Form to be executed payments and already executed payments from a cost map. pub(crate) fn extract_quote_payments( - cost_map: &HashMap, + cost_map: &HashMap, ) -> (Vec, Vec) { let mut to_be_paid = vec![]; let mut already_paid = vec![]; diff --git a/autonomi/src/utils.rs b/autonomi/src/utils.rs index 1348c0c685..85d8f3f62c 100644 --- a/autonomi/src/utils.rs +++ b/autonomi/src/utils.rs @@ -1,17 +1,17 @@ use crate::client::payment::Receipt; use ant_evm::{PaymentQuote, ProofOfPayment, QuoteHash, TxHash}; -use ant_networking::PayeeQuote; +use ant_networking::SelectedQuotes; use std::collections::{BTreeMap, HashMap}; use xor_name::XorName; pub fn cost_map_to_quotes( - cost_map: HashMap, + cost_map: HashMap, ) -> HashMap { - cost_map.into_iter().map(|(k, (_, _, v))| (k, v)).collect() + cost_map.into_iter().map(|(k, (_, q))| (k, q)).collect() } pub fn receipt_from_cost_map_and_payments( - cost_map: HashMap, + cost_map: HashMap, payments: &BTreeMap, ) -> Receipt { let quotes = cost_map_to_quotes(cost_map); diff --git a/evmlib/src/external_signer.rs b/evmlib/src/external_signer.rs index 20c3aa95df..30186f031d 100644 --- a/evmlib/src/external_signer.rs +++ b/evmlib/src/external_signer.rs @@ -7,9 +7,8 @@ // permissions and limitations relating to use of the SAFE Network Software. use crate::common::{Address, Amount, Calldata, QuoteHash, QuotePayment, U256}; -use crate::contract::data_payments::{DataPaymentsHandler, MAX_TRANSFERS_PER_TRANSACTION}; -use crate::contract::network_token::NetworkToken; -use crate::contract::{data_payments, network_token}; +use crate::contract::network_token::{NetworkToken, self}; +use crate::contract::payment_vault::MAX_TRANSFERS_PER_TRANSACTION; use crate::utils::http_provider; use crate::Network; use serde::{Deserialize, Serialize}; @@ -20,7 +19,7 @@ pub enum Error { #[error("Network token contract error: {0}")] NetworkTokenContract(#[from] network_token::Error), #[error("Data payments contract error: {0}")] - DataPaymentsContract(#[from] data_payments::error::Error), + DataPaymentsContract(#[from] crate::contract::payment_vault::error::Error), } /// Approve an address / smart contract to spend this wallet's payment tokens. @@ -73,7 +72,10 @@ pub fn pay_for_quotes_calldata>( let approve_amount = total_amount; let provider = http_provider(network.rpc_url().clone()); - let data_payments = DataPaymentsHandler::new(*network.data_payments_address(), provider); + let data_payments = crate::contract::payment_vault::handler::PaymentVaultHandler::new( + *network.data_payments_address(), + provider, + ); // Divide transfers over multiple transactions if they exceed the max per transaction. let chunks = payments.chunks(MAX_TRANSFERS_PER_TRANSACTION); diff --git a/evmlib/src/lib.rs b/evmlib/src/lib.rs index 6c1054d600..a37ae2a16e 100644 --- a/evmlib/src/lib.rs +++ b/evmlib/src/lib.rs @@ -10,6 +10,7 @@ use crate::common::{Address, QuoteHash}; use crate::transaction::verify_data_payment; use alloy::primitives::address; use alloy::transports::http::reqwest; +use common::Amount; use quoting_metrics::QuotingMetrics; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; @@ -140,7 +141,7 @@ impl Network { quote_hash: QuoteHash, quoting_metrics: QuotingMetrics, reward_addr: Address, - ) -> Result<(), transaction::Error> { + ) -> Result { verify_data_payment(self, quote_hash, reward_addr, quoting_metrics).await } } diff --git a/evmlib/src/transaction.rs b/evmlib/src/transaction.rs index 998b2051fe..cdc1262ab4 100644 --- a/evmlib/src/transaction.rs +++ b/evmlib/src/transaction.rs @@ -29,16 +29,17 @@ pub async fn verify_data_payment( quote_hash: QuoteHash, reward_addr: Address, quoting_metrics: QuotingMetrics, -) -> Result<(), Error> { +) -> Result { let provider = http_provider(network.rpc_url().clone()); let payment_vault = PaymentVaultHandler::new(*network.data_payments_address(), provider); let is_paid = payment_vault .verify_payment(quoting_metrics, (quote_hash, reward_addr, Amount::ZERO)) .await?; + let amount_paid = Amount::ZERO; // NB TODO @mick we need to get the amount paid from the contract if is_paid { - Ok(()) + Ok(amount_paid) } else { Err(Error::PaymentMissing) } diff --git a/evmlib/tests/wallet.rs b/evmlib/tests/wallet.rs index 905f719fc3..c324f771fc 100644 --- a/evmlib/tests/wallet.rs +++ b/evmlib/tests/wallet.rs @@ -8,7 +8,8 @@ use alloy::providers::ext::AnvilApi; use alloy::providers::{ProviderBuilder, WalletProvider}; use alloy::signers::local::{LocalSigner, PrivateKeySigner}; use evmlib::common::{Amount, TxHash}; -use evmlib::contract::data_payments::MAX_TRANSFERS_PER_TRANSACTION; +use evmlib::contract::payment_vault::MAX_TRANSFERS_PER_TRANSACTION; +use evmlib::quoting_metrics::QuotingMetrics; use evmlib::testnet::{deploy_data_payments_contract, deploy_network_token_contract, start_node}; use evmlib::transaction::verify_data_payment; use evmlib::wallet::{transfer_tokens, wallet_address, Wallet}; @@ -67,7 +68,6 @@ async fn funded_wallet(network: &Network, genesis_wallet: EthereumWallet) -> Wal #[tokio::test] async fn test_pay_for_quotes_and_data_payment_verification() { const TRANSFERS: usize = 600; - const EXPIRATION_TIMESTAMP_IN_SECS: u64 = 4102441200; // The year 2100 let (_anvil, network, genesis_wallet) = local_testnet().await; let wallet = funded_wallet(&network, genesis_wallet).await; @@ -87,23 +87,18 @@ async fn test_pay_for_quotes_and_data_payment_verification() { unique_tx_hashes.len(), TRANSFERS.div_ceil(MAX_TRANSFERS_PER_TRANSACTION) ); - - for quote_payment in quote_payments.iter() { - let tx_hash = *tx_hashes.get("e_payment.0).unwrap(); - + for (quote_hash, reward_addr, _) in quote_payments.iter() { let result = verify_data_payment( &network, - tx_hash, - quote_payment.0, - quote_payment.1, - quote_payment.2, - EXPIRATION_TIMESTAMP_IN_SECS, + *quote_hash, + *reward_addr, + QuotingMetrics::default(), ) .await; assert!( result.is_ok(), - "Verification failed for: {quote_payment:?}. Error: {:?}", + "Verification failed for: {quote_hash:?}. Error: {:?}", result.err() ); }