Skip to content

Commit

Permalink
feat: wip new quoting payment integration
Browse files Browse the repository at this point in the history
  • Loading branch information
grumbach committed Dec 9, 2024
1 parent 34d7004 commit 38b851c
Show file tree
Hide file tree
Showing 16 changed files with 179 additions and 208 deletions.
3 changes: 2 additions & 1 deletion ant-cli/src/commands/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ pub async fn cost(name: &str, peers: Vec<Multiaddr>) -> Result<()> {
let register_key = crate::keys::get_register_signing_key()
.wrap_err("The register key is required to perform this action")?;
let client = crate::actions::connect_to_network(peers).await?;
let wallet = load_wallet()?;

let cost = client
.register_cost(name.to_string(), register_key)
.register_cost(&wallet.network(), name.to_string(), register_key)
.await
.wrap_err("Failed to get cost for register")?;
info!("Estimated cost to create a register with name {name}: {cost}");
Expand Down
60 changes: 51 additions & 9 deletions ant-evm/src/data_payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

use crate::EvmError;
use evmlib::{
common::{Address as RewardsAddress, QuoteHash, TxHash}, quoting_metrics::QuotingMetrics, utils::dummy_address
common::{Address as RewardsAddress, QuoteHash}, quoting_metrics::QuotingMetrics, utils::dummy_address
};
use libp2p::{identity::PublicKey, PeerId};
use serde::{Deserialize, Serialize};
Expand All @@ -24,19 +24,61 @@ pub const QUOTE_EXPIRATION_SECS: u64 = 3600;
/// The margin allowed for live_time
const LIVE_TIME_MARGIN: u64 = 10;

#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct EncodedPeerId(Vec<u8>);

impl EncodedPeerId {
pub fn to_peer_id(&self) -> Result<PeerId, libp2p::identity::DecodingError> {
match PublicKey::try_decode_protobuf(&self.0) {
Ok(pub_key) => Ok(PeerId::from_public_key(&pub_key)),
Err(e) => Err(e)
}
}
}

/// The proof of payment for a data payment
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct ProofOfPayment {
/// The Quote we're paying for
pub quote: PaymentQuote,
/// The transaction hash
pub tx_hash: TxHash,
peer_quotes: Vec<(EncodedPeerId, PaymentQuote)>
}

impl ProofOfPayment {
pub fn to_peer_id_payee(&self) -> Option<PeerId> {
let pub_key = PublicKey::try_decode_protobuf(&self.quote.pub_key).ok()?;
Some(PeerId::from_public_key(&pub_key))
/// returns a short digest of the proof of payment to use for verification
pub fn digest(&self) -> Vec<(QuoteHash, QuotingMetrics, RewardsAddress)> {
self.peer_quotes.clone().into_iter().map(|(_, quote)| (quote.hash(), quote.quoting_metrics, quote.rewards_address)).collect()
}

/// returns the list of payees
pub fn payees(&self) -> Vec<PeerId> {
self.peer_quotes.iter().filter_map(|(peer_id, _)| peer_id.to_peer_id().ok()).collect()
}

/// has the quote expired
pub fn has_expired(&self) -> bool {
self.peer_quotes.iter().any(|(_, quote)| quote.has_expired())
}

/// verifies the proof of payment is valid for the given peer id
pub fn verify_for(&self, peer_id: PeerId) -> bool {
// make sure I am in the list of payees
if !self.payees().contains(&peer_id) {
return false;
}

// verify all signatures
for (encoded_peer_id, quote) in self.peer_quotes.iter() {
let peer_id = match encoded_peer_id.to_peer_id() {
Ok(peer_id) => peer_id,
Err(e) => {
warn!("Invalid encoded peer id: {e}");
return false;
},
};
if !quote.check_is_signed_by_claimed_peer(peer_id) {
return false;
}
}
true
}
}

Expand Down Expand Up @@ -148,7 +190,7 @@ impl PaymentQuote {
true
}

/// Returns true) if the quote has not yet expired
/// Returns true if the quote has expired
pub fn has_expired(&self) -> bool {
let now = SystemTime::now();

Expand Down
15 changes: 5 additions & 10 deletions ant-networking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,6 @@ use {
std::collections::HashSet,
};

/// 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]
pub const fn close_group_majority() -> usize {
Expand Down Expand Up @@ -384,7 +379,7 @@ impl Network {
&self,
record_address: NetworkAddress,
ignore_peers: Vec<PeerId>,
) -> Result<SelectedQuotes> {
) -> Result<Vec<(PeerId, PaymentQuote)>> {
// The requirement of having at least CLOSE_GROUP_SIZE
// close nodes will be checked internally automatically.
let mut close_nodes = self
Expand All @@ -408,9 +403,9 @@ impl Network {
.send_and_get_responses(&close_nodes, &request, true)
.await;

// consider data to be already paid for if 1/3 of the close nodes already have it
// consider data to be already paid for if 1/2 of the close nodes already have it
let mut peer_already_have_it = 0;
let enough_peers_already_have_it = close_nodes.len() / 3;
let enough_peers_already_have_it = close_nodes.len() / 2;

// loop over responses
let mut all_quotes = vec![];
Expand Down Expand Up @@ -448,7 +443,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(SelectedQuotes { quotes: vec![] });
return Ok(vec![]);
}
}
Err(err) => {
Expand All @@ -460,7 +455,7 @@ impl Network {
}
}

Ok(SelectedQuotes { quotes: quotes_to_pay })
Ok(quotes_to_pay)
}

/// Get register from network.
Expand Down
30 changes: 8 additions & 22 deletions ant-node/src/put_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,42 +603,28 @@ impl Node {

// check if the quote is valid
let self_peer_id = self.network().peer_id();
if !payment.quote.check_is_signed_by_claimed_peer(self_peer_id) {
warn!("Payment quote signature is not valid for record {pretty_key}");
if !payment.verify_for(self_peer_id) {
warn!("Payment is not valid for record {pretty_key}");
return Err(Error::InvalidRequest(format!(
"Payment quote signature is not valid for record {pretty_key}"
"Payment is not valid for record {pretty_key}"
)));
}
debug!("Payment quote signature is valid for record {pretty_key}");

// 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
.duration_since(UNIX_EPOCH)
.map_err(|e| {
Error::InvalidRequest(format!(
"Payment quote timestamp is invalid for record {pretty_key}: {e}"
))
})?
.as_secs();
debug!("Payment is valid for record {pretty_key}");

if quote_expiration_time < SystemTime::now() {
// verify quote expiration
if payment.has_expired() {
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
let payments_to_verify = payment.digest();
debug!("Verifying payment for record {pretty_key}");
let reward_amount = self
.evm_network()
.verify_data_payment(
payment.quote.hash(),
payment.quote.quoting_metrics,
*self.reward_address(),
)
.verify_data_payment(payments_to_verify)
.await
.map_err(|e| Error::EvmNetwork(format!("Failed to verify chunk payment: {e}")))?;
debug!("Payment of {reward_amount:?} is valid for record {pretty_key}");
Expand Down
6 changes: 4 additions & 2 deletions autonomi/src/client/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,12 @@ pub enum CostError {
CouldNotGetStoreQuote(XorName),
#[error("Could not get store costs: {0:?}")]
CouldNotGetStoreCosts(NetworkError),
#[error("Not enough node quotes for {0:?}, got: {1:?} and need at least {2:?}")]
NotEnoughNodeQuotes(XorName, usize, usize),
#[error("Failed to serialize {0}")]
Serialization(String),
#[error("Payment vault error: {0:?}")]
PaymentVaultError(ant_evm::payment_vault::error::Error),
#[error("Market price error: {0:?}")]
MarketPriceError(#[from] ant_evm::payment_vault::error::Error),
}

impl Client {
Expand Down
8 changes: 4 additions & 4 deletions autonomi/src/client/payment.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::client::data::PayError;
use crate::Client;
use ant_evm::{EvmWallet, ProofOfPayment};
use ant_evm::{AttoTokens, EvmWallet, ProofOfPayment};
use std::collections::HashMap;
use xor_name::XorName;

/// Contains the proof of payment for XOR addresses.
pub type Receipt = HashMap<XorName, ProofOfPayment>;
/// Contains the proof of payments for each XOR address and the amount paid
pub type Receipt = HashMap<XorName, Vec<(ProofOfPayment, AttoTokens)>>;

/// Payment options for data payments.
#[derive(Clone)]
Expand Down Expand Up @@ -40,7 +40,7 @@ impl Client {
) -> Result<Receipt, PayError> {
match payment_option {
PaymentOption::Wallet(wallet) => {
let (receipt, _) = self.pay(content_addrs, &wallet).await?;
let receipt = self.pay(content_addrs, &wallet).await?;
Ok(receipt)
}
PaymentOption::Receipt(receipt) => Ok(receipt),
Expand Down
Loading

0 comments on commit 38b851c

Please sign in to comment.