Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat upgradable smart contracts and updated quotation flow 4 #2507

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
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
Loading