Skip to content

Commit

Permalink
Merge pull request #2507 from grumbach/feat-upgradable-smart-contract…
Browse files Browse the repository at this point in the history
…s-and-updated-quotation-flow-4

Feat upgradable smart contracts and updated quotation flow 4
  • Loading branch information
mickvandijke authored Dec 9, 2024
2 parents 849de7e + 57ee232 commit 89f1357
Show file tree
Hide file tree
Showing 26 changed files with 272 additions and 361 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
3 changes: 2 additions & 1 deletion ant-evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub use evmlib::common::Address as RewardsAddress;
pub use evmlib::common::Address as EvmAddress;
pub use evmlib::common::QuotePayment;
pub use evmlib::common::{QuoteHash, TxHash};
pub use evmlib::contract::payment_vault;
pub use evmlib::cryptography;
#[cfg(feature = "external-signer")]
pub use evmlib::external_signer;
Expand All @@ -28,8 +29,8 @@ mod amount;
mod data_payments;
mod error;

pub use evmlib::quoting_metrics::QuotingMetrics;
pub use data_payments::{PaymentQuote, ProofOfPayment, QUOTE_EXPIRATION_SECS};
pub use evmlib::quoting_metrics::QuotingMetrics;

/// Types used in the public API
pub use amount::{Amount, AttoTokens};
Expand Down
24 changes: 0 additions & 24 deletions ant-networking/src/event/request_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
31 changes: 13 additions & 18 deletions ant-networking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -83,9 +83,6 @@ use {
std::collections::HashSet,
};

/// The type of quote for a selected payee.
pub type PayeeQuote = (PeerId, PaymentQuote);

/// Majority of a given group (i.e. > 1/2).
#[inline]
pub const fn close_group_majority() -> usize {
Expand Down Expand Up @@ -382,7 +379,7 @@ impl Network {
&self,
record_address: NetworkAddress,
ignore_peers: Vec<PeerId>,
) -> Result<Vec<PayeeQuote>> {
) -> 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 @@ -392,7 +389,7 @@ impl Network {
close_nodes.retain(|peer_id| !ignore_peers.contains(peer_id));

if close_nodes.is_empty() {
error!("Cann't get store_cost of {record_address:?}, as all close_nodes are ignored");
error!("Can't get store_cost of {record_address:?}, as all close_nodes are ignored");
return Err(NetworkError::NoStoreCostResponses);
}

Expand All @@ -406,6 +403,10 @@ impl Network {
.send_and_get_responses(&close_nodes, &request, true)
.await;

// 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() / 2;

// loop over responses
let mut all_quotes = vec![];
let mut quotes_to_pay = vec![];
Expand Down Expand Up @@ -438,8 +439,12 @@ impl Network {
if !storage_proofs.is_empty() {
debug!("Storage proofing during GetStoreQuote to be implemented.");
}
info!("Address {record_address:?} was already paid for according to {peer_address:?}, ending quote request");
return Ok(vec![]);
peer_already_have_it += 1;
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![]);
}
}
Err(err) => {
error!("Got an error while requesting quote from peer {peer:?}: {err}");
Expand All @@ -450,16 +455,6 @@ 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)
}

Expand Down
51 changes: 24 additions & 27 deletions ant-node/src/put_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -603,36 +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 is valid for record {pretty_key}");

// 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}"
)));
}
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();

// 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.tx_hash,
payment.quote.hash(),
payment.quote.quoting_metrics,
*self.reward_address(),
quote_expiration_time_in_secs,
)
let reward_amount = self
.evm_network()
.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 All @@ -651,7 +643,10 @@ impl Node {
.set(new_value);
}
self.events_channel()
.broadcast(crate::NodeEvent::RewardReceived(AttoTokens::from(reward_amount), address.clone()));
.broadcast(crate::NodeEvent::RewardReceived(
AttoTokens::from(reward_amount),
address.clone(),
));

// vdash metric (if modified please notify at https://github.com/happybeing/vdash/issues):
info!("Total payment of {reward_amount:?} atto tokens accepted for record {pretty_key}");
Expand All @@ -660,7 +655,9 @@ impl Node {
#[cfg(feature = "loud")]
{
println!("🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟 RECEIVED REWARD 🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟");
println!("Total payment of {reward_amount:?} atto tokens accepted for record {pretty_key}");
println!(
"Total payment of {reward_amount:?} atto tokens accepted for record {pretty_key}"
);
println!("🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟");
}

Expand Down
19 changes: 0 additions & 19 deletions ant-protocol/src/messages/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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", &quotes.len())
.finish(),
Cmd::PeerConsideredAsBad {
detected_by,
bad_peer,
Expand All @@ -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(),
}
}
Expand All @@ -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,
Expand Down
5 changes: 0 additions & 5 deletions ant-protocol/src/messages/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions autonomi/src/client/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +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("Market price error: {0:?}")]
MarketPriceError(#[from] ant_evm::payment_vault::error::Error),
}

impl Client {
Expand Down
1 change: 1 addition & 0 deletions autonomi/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

pub mod address;
pub mod payment;
pub mod quote;

#[cfg(feature = "data")]
pub mod archive;
Expand Down
Loading

0 comments on commit 89f1357

Please sign in to comment.