Skip to content

Commit

Permalink
feat(client): verify register uploads and retry and repay if failed
Browse files Browse the repository at this point in the history
BREAKING CHANGE: updates client register creation APIs
  • Loading branch information
joshuef committed Oct 17, 2023
1 parent c73fa4a commit a7d16ee
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 75 deletions.
10 changes: 5 additions & 5 deletions sn_cli/src/subcommands/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use bls::PublicKey;
use clap::Subcommand;
use color_eyre::{eyre::WrapErr, Result, Section};
use sn_client::{Client, ClientRegister, Error as ClientError, WalletClient};
use sn_client::{Client, Error as ClientError, WalletClient};
use sn_protocol::storage::RegisterAddress;
use sn_transfers::LocalWallet;
use std::path::Path;
Expand Down Expand Up @@ -88,11 +88,11 @@ async fn create_register(
let mut wallet_client = WalletClient::new(client.clone(), wallet);

let meta = XorName::from_content(name.as_bytes());
let register =
ClientRegister::create_online(client.clone(), meta, &mut wallet_client, verify_store)
.await?;
let (register, cost) = client
.create_and_pay_for_register(meta, &mut wallet_client, verify_store)
.await?;
println!(
"Successfully created register '{name}' at {}!",
"Successfully created register '{name}' at {} for {cost:?}!",
register.address()
);
Ok(())
Expand Down
55 changes: 47 additions & 8 deletions sn_client/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use sn_protocol::{
},
NetworkAddress, PrettyPrintRecordKey,
};
use sn_registers::SignedRegister;
use sn_transfers::{SignedSpend, Transfer, UniquePubkey};
use sn_registers::{Register, SignedRegister};
use sn_transfers::{NanoTokens, SignedSpend, Transfer, UniquePubkey};
use std::{
collections::{HashMap, HashSet},
time::Duration,
Expand Down Expand Up @@ -287,14 +287,35 @@ impl Client {
}

/// Create a new Register on the Network.
pub async fn create_register(
/// Tops up payments and retries if necessary and verification failed
pub async fn create_and_pay_for_register(
&self,
meta: XorName,
address: XorName,
wallet_client: &mut WalletClient,
verify_store: bool,
) -> Result<ClientRegister> {
info!("Instantiating a new Register replica with meta {meta:?}");
ClientRegister::create_online(self.clone(), meta, wallet_client, verify_store).await
) -> Result<(ClientRegister, NanoTokens)> {
info!("Instantiating a new Register replica with address {address:?}");
let (reg, mut total_cost) =
ClientRegister::create_online(self.clone(), address, wallet_client, false).await?;
let reg_address = reg.address();
if verify_store {
let mut stored = self.verify_register_stored(*reg_address).await.is_ok();

while !stored {
info!("Register not completely stored on the network yet. Retrying...");
let (reg, top_up_cost) =
ClientRegister::create_online(self.clone(), address, wallet_client, false)
.await?;
let reg_address = reg.address();

total_cost = total_cost.checked_add(top_up_cost).ok_or(Error::Transfers(
sn_transfers::WalletError::from(sn_transfers::Error::ExcessiveNanoValue),
))?;
stored = self.verify_register_stored(*reg_address).await.is_ok();
}
}

Ok((reg, total_cost))
}

/// Store `Chunk` as a record.
Expand Down Expand Up @@ -379,7 +400,7 @@ impl Client {

/// Verify if a `Chunk` is stored by expected nodes on the network.
pub async fn verify_chunk_stored(&self, address: ChunkAddress) -> Result<Chunk> {
info!("Getting chunk: {address:?}");
info!("Verifying chunk: {address:?}");
let key = NetworkAddress::from_chunk_address(address).to_record_key();
let record = self
.network
Expand All @@ -394,6 +415,24 @@ impl Client {
}
}

/// Verify if a `Register` is stored by expected nodes on the network.
pub async fn verify_register_stored(&self, address: RegisterAddress) -> Result<Register> {
info!("Verifying register: {address:?}");
let key = NetworkAddress::from_register_address(address).to_record_key();
let record = self
.network
.get_record_from_network(key, None, GetQuorum::All, true, Default::default())
.await?;

let header = RecordHeader::from_record(&record)?;
if let RecordKind::Register = header.kind {
let register: Register = try_deserialize_record(&record)?;
Ok(register)
} else {
Err(ProtocolError::RecordKindMismatch(RecordKind::Register).into())
}
}

/// Send a `SpendCashNote` request to the network
pub(crate) async fn network_store_spend(
&self,
Expand Down
52 changes: 26 additions & 26 deletions sn_client/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use sn_protocol::{
NetworkAddress,
};
use sn_registers::{Entry, EntryHash, Permissions, Register, RegisterAddress, SignedRegister};
use sn_transfers::Transfer;
use sn_transfers::{NanoTokens, Transfer};

use std::collections::{BTreeSet, HashSet, LinkedList};
use xor_name::XorName;
Expand Down Expand Up @@ -70,10 +70,10 @@ impl ClientRegister {
meta: XorName,
wallet_client: &mut WalletClient,
verify_store: bool,
) -> Result<Self> {
) -> Result<(Self, NanoTokens)> {
let mut reg = Self::create_register(client, meta, Permissions::new_owner_only())?;
reg.sync(wallet_client, verify_store).await?;
Ok(reg)
let cost = reg.sync(wallet_client, verify_store).await?;
Ok((reg, cost))
}

/// Retrieve a Register from the network to work on it offline.
Expand Down Expand Up @@ -172,9 +172,10 @@ impl ClientRegister {
&mut self,
wallet_client: &mut WalletClient,
verify_store: bool,
) -> Result<()> {
) -> Result<NanoTokens> {
let addr = *self.address();
debug!("Syncing Register at {addr:?}!");
let mut cost = NanoTokens::zero();
let remote_replica = match Self::get_register_from_network(&self.client, addr).await {
Ok(r) => r,
Err(err) => {
Expand All @@ -187,34 +188,33 @@ impl ClientRegister {

// Let's check if the user has already paid for this address first
let net_addr = sn_protocol::NetworkAddress::RegisterAddress(addr);
let mut payment = wallet_client.get_payment_transfers(&net_addr)?;
if payment.is_empty() {
// Let's make the storage payment
let cost = wallet_client
.pay_for_storage(std::iter::once(net_addr.clone()), verify_store)
.await?;

println!("Successfully made payment of {cost} for a Register (At a cost per record of {cost:?}.)");

if let Err(err) = wallet_client.store_local_wallet() {
println!("Failed to store wallet with cached payment proofs: {err:?}");
} else {
println!(
"Successfully stored wallet with cached payment proofs, and new balance {}.",
wallet_client.balance()
);
}

// Get payment proofs needed to publish the Register
payment = wallet_client.get_payment_transfers(&net_addr)?;
// Let's make the storage payment
cost = wallet_client
.pay_for_storage(std::iter::once(net_addr.clone()), verify_store)
.await?;

println!("Successfully made payment of {cost} for a Register (At a cost per record of {cost:?}.)");

if let Err(err) = wallet_client.store_local_wallet() {
println!("Failed to store wallet with cached payment proofs: {err:?}");
} else {
println!(
"Successfully stored wallet with cached payment proofs, and new balance {}.",
wallet_client.balance()
);
}

// Get payment proofs needed to publish the Register
let payment = wallet_client.get_payment_transfers(&net_addr)?;

self.publish_register(cmd, payment, verify_store).await?;
self.register.clone()
}
};
self.register.merge(remote_replica);
self.push(verify_store).await
self.push(verify_store).await?;

Ok(cost)
}

/// Push all operations made locally to the replicas of this Register on the network.
Expand Down
8 changes: 5 additions & 3 deletions sn_node/examples/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ async fn main() -> Result<()> {
}
Err(_) => {
println!("Register '{reg_nickname}' not found, creating it at {address}");
client
.create_register(meta, &mut wallet_client, true)
.await?
let (register, _cost) = client
.create_and_pay_for_register(meta, &mut wallet_client, true)
.await?;

register
}
};
println!("Register owned by: {:?}", reg_replica.owner());
Expand Down
5 changes: 4 additions & 1 deletion sn_node/tests/data_with_churn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,10 @@ fn create_registers_task(
println!("Creating Register at {addr:?} in {delay:?}");
sleep(delay).await;

match client.create_register(meta, &mut wallet_client, true).await {
match client
.create_and_pay_for_register(meta, &mut wallet_client, true)
.await
{
Ok(_) => content
.write()
.await
Expand Down
34 changes: 6 additions & 28 deletions sn_node/tests/nodes_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use eyre::{eyre, Result};
use sn_client::WalletClient;
use sn_networking::CLOSE_GROUP_SIZE;
use sn_node::NodeEvent;
use sn_protocol::{storage::RegisterAddress, NetworkAddress};
use sn_transfers::{LocalWallet, NanoTokens};
use tokio::{
task::JoinHandle,
Expand Down Expand Up @@ -76,29 +75,18 @@ async fn nodes_rewards_for_storing_registers() -> Result<()> {

println!("Paying for random Register address... rb {rb:?}");
let mut rng = rand::thread_rng();
let owner_pk = client.signer_pk();
let register_addr = XorName::random(&mut rng);

let cost = wallet_client
.pay_for_storage(
std::iter::once(NetworkAddress::RegisterAddress(RegisterAddress::new(
register_addr,
owner_pk,
))),
true,
)
.await?;

let prev_rewards_balance = current_rewards_balance()?;
println!("Cost is {cost:?}, rb: {prev_rewards_balance:?}");

let (_register, cost) = client
.create_and_pay_for_register(register_addr, &mut wallet_client, false)
.await?;
println!("Cost is {cost:?}: {prev_rewards_balance:?}");
let expected_rewards_balance = prev_rewards_balance
.checked_add(cost)
.ok_or_else(|| eyre!("Failed to sum up rewards balance"))?;

let _register = client
.create_register(register_addr, &mut wallet_client, false)
.await?;

verify_rewards(expected_rewards_balance).await?;

Ok(())
Expand Down Expand Up @@ -146,24 +134,14 @@ async fn nodes_rewards_for_register_notifs_over_gossipsub() -> Result<()> {
let mut wallet_client = WalletClient::new(client.clone(), paying_wallet);

let mut rng = rand::thread_rng();
let owner_pk = client.signer_pk();
let register_addr = XorName::random(&mut rng);

println!("Paying for random Register address...");
let _cost = wallet_client
.pay_for_storage(
std::iter::once(NetworkAddress::RegisterAddress(RegisterAddress::new(
register_addr,
owner_pk,
))),
true,
)
.await?;

let handle = spawn_transfer_notifs_listener("https://127.0.0.1:12001".to_string());

let _register = client
.create_register(register_addr, &mut wallet_client, false)
.create_and_pay_for_register(register_addr, &mut wallet_client, false)
.await?;
println!("Random Register created");

Expand Down
8 changes: 4 additions & 4 deletions sn_node/tests/storage_payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,8 @@ async fn storage_payment_register_creation_succeeds() -> Result<()> {
.pay_for_storage(std::iter::once(net_addr), true)
.await?;

let mut register = client
.create_register(xor_name, &mut wallet_client, true)
let (mut register, _cost) = client
.create_and_pay_for_register(xor_name, &mut wallet_client, true)
.await?;

let retrieved_reg = client.get_register(address).await?;
Expand Down Expand Up @@ -344,8 +344,8 @@ async fn storage_payment_register_creation_and_mutation_fails() -> Result<()> {
.local_send_storage_payment(no_data_payments, None)?;

// this should fail to store as the amount paid is not enough
let mut register = client
.create_register(xor_name, &mut wallet_client, false)
let (mut register, _cost) = client
.create_and_pay_for_register(xor_name, &mut wallet_client, false)
.await?;

sleep(Duration::from_secs(5)).await;
Expand Down

0 comments on commit a7d16ee

Please sign in to comment.