Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into test-js-wasm
Browse files Browse the repository at this point in the history
  • Loading branch information
b-zee committed Oct 18, 2024
2 parents b6bbb24 + 0ccde60 commit 68060f9
Show file tree
Hide file tree
Showing 33 changed files with 796 additions and 172 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions autonomi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ repository = "https://github.com/maidsafe/safe_network"
crate-type = ["cdylib", "rlib"]

[features]
default = ["data"]
default = ["data", "vault"]
full = ["data", "registers", "vault"]
data = []
vault = ["data"]
vault = ["data", "registers"]
fs = ["tokio/fs", "data"]
local = ["sn_networking/local", "test_utils/local", "sn_evm/local"]
registers = ["data"]
loud = []
external-signer = ["sn_evm/external-signer", "data"]

[dependencies]
bip39 = "2.0.0"
Expand Down Expand Up @@ -53,6 +54,7 @@ wasm-bindgen-futures = "0.4.43"
serde-wasm-bindgen = "0.6.5"

[dev-dependencies]
alloy = { version = "0.4.2", default-features = false, features = ["std", "reqwest-rustls-tls", "provider-anvil-node", "sol-types", "json", "signers", "contract", "signer-local", "network"] }
eyre = "0.6.5"
sha2 = "0.10.6"
sn_logging = { path = "../sn_logging", version = "0.2.33" }
Expand Down
41 changes: 23 additions & 18 deletions autonomi/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,59 @@

aut.logInit("sn_networking=warn,autonomi=trace");

console.log("connecting...");
updateProgress("Connecting...");
const client = await aut.Client.connect([peer_addr]);
console.log("connected");
updateProgress("Connected");

console.log("getting wallet...");
updateProgress("Getting wallet...");
const wallet = aut.getFundedWallet();
console.log("wallet retrieved");
updateProgress("Wallet retrieved");

// Random 32 byte data
const data = [...Array(16)].map(() => Math.floor(Math.random() * 9));
console.log("our data: ", data);
updateProgress("Our data: " + data);

console.log("calculating cost...");
updateProgress("Calculating cost...");
let result = await client.dataCost(data, wallet);
console.log("calculated cost: ", result.toString());
updateProgress("Calculated cost: " + result.toString());

console.log("putting...");
updateProgress("Putting data...");
const dataAddr = await client.dataPut(data, wallet);
console.log("put done: ", dataAddr.toString());
updateProgress("Put done: " + dataAddr.toString());

let archive = new Map([["README.md", dataAddr]]);
console.log("putting data as archive... ", archive);
updateProgress("Putting data as archive... " + archive);
let archiveAddr = await client.archivePut(archive, wallet);
console.log("archive put done: ", archiveAddr);
updateProgress("Archive put done: " + archiveAddr);

console.log("fetching archive...");
updateProgress("Fetching archive...");
let archiveFetched = await client.archiveGet(archiveAddr);
console.log("archive fetched: ", archiveFetched);
updateProgress("Archive fetched: " + archiveFetched);

for (const [path, addr] of archiveFetched) {
console.log("fetching data: ", path, ", addr: ", addr);
updateProgress("Fetching data: " + path + ", addr: " + addr);
const dataFetched = await client.dataGet(addr);
console.log("data fetched: ", dataFetched);
updateProgress("Data fetched: " + dataFetched);
}

// Generate random secret key
const secretKey = [...Array(32)].map(() => Math.floor(Math.random() * 9));

await client.writeBytesToVault(data, wallet, secretKey);

const vault = await client.fetchAndDecryptVault(secretKey);
console.log("vault: ", vault);
updateProgress("Vault: " + vault);
}

function updateProgress(message) {
document.getElementById('progress').textContent = message;
}

document.getElementById("btn-run").addEventListener("click", run, false);
</script>

<label for="peer_id">Peer ID: <input type="text" id="peer_id" /></label>
<label for="peer_id">Peer MultiAddr: <input type="text" id="peer_id" /></label>
<button id="btn-run">Run</button>
<span id="progress"></span>
</body>

</html>
23 changes: 12 additions & 11 deletions autonomi/src/client/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ use std::{
use sn_networking::target_arch::{Duration, SystemTime, UNIX_EPOCH};

use super::{
data::DataAddr,
data::{GetError, PutError},
data::{CostError, DataAddr, GetError, PutError},
Client,
};
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use sn_evm::EvmWallet;
use sn_evm::{AttoTokens, EvmWallet};
use xor_name::XorName;

/// The address of an archive on the network. Points to an [`Archive`].
Expand All @@ -36,13 +35,13 @@ pub enum RenameError {

/// An archive of files that containing file paths, their metadata and the files data addresses
/// Using archives is useful for uploading entire directories to the network, only needing to keep track of a single address.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct Archive {
map: HashMap<PathBuf, (DataAddr, Metadata)>,
}

/// Metadata for a file in an archive
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Metadata {
pub created: u64,
pub modified: u64,
Expand Down Expand Up @@ -147,12 +146,6 @@ impl Archive {
}
}

impl Default for Archive {
fn default() -> Self {
Self::new()
}
}

impl Client {
/// Fetch an archive from the network
pub async fn archive_get(&self, addr: ArchiveAddr) -> Result<Archive, GetError> {
Expand All @@ -171,4 +164,12 @@ impl Client {
.map_err(|e| PutError::Serialization(format!("Failed to serialize archive: {e:?}")))?;
self.data_put(bytes, wallet).await
}

/// Get the cost to upload an archive
pub async fn archive_cost(&self, archive: Archive) -> Result<AttoTokens, CostError> {
let bytes = archive
.into_bytes()
.map_err(|e| CostError::Serialization(format!("Failed to serialize archive: {e:?}")))?;
self.data_cost(bytes).await
}
}
6 changes: 5 additions & 1 deletion autonomi/src/client/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ pub enum PutError {
VaultXorName,
#[error("A network error occurred.")]
Network(#[from] NetworkError),
#[error("Error occurred during cost estimation.")]
CostError(#[from] CostError),
#[error("Error occurred during payment.")]
PayError(#[from] PayError),
#[error("Failed to serialize {0}")]
#[error("Serialization error: {0}")]
Serialization(String),
#[error("A wallet error occurred.")]
Wallet(#[from] sn_evm::EvmError),
Expand Down Expand Up @@ -82,6 +84,8 @@ pub enum CostError {
CouldNotGetStoreQuote(XorName),
#[error("Could not get store costs: {0:?}")]
CouldNotGetStoreCosts(NetworkError),
#[error("Failed to serialize {0}")]
Serialization(String),
}

impl Client {
Expand Down
102 changes: 102 additions & 0 deletions autonomi/src/client/external_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::client::data::{DataAddr, PutError};
use crate::client::utils::extract_quote_payments;
use crate::self_encryption::encrypt;
use crate::Client;
use bytes::Bytes;
use sn_evm::{ProofOfPayment, QuotePayment};
use sn_networking::PayeeQuote;
use sn_protocol::storage::Chunk;
use std::collections::HashMap;
use xor_name::XorName;

#[allow(unused_imports)]
pub use sn_evm::external_signer::*;

impl Client {
/// Upload a piece of data to the network. This data will be self-encrypted.
/// Payment will not be done automatically as opposed to the regular `data_put`, so the proof of payment has to be provided.
/// Returns the Data Address at which the data was stored.
pub async fn data_put_with_proof_of_payment(
&self,
data: Bytes,
proof: HashMap<XorName, ProofOfPayment>,
) -> Result<DataAddr, PutError> {
let (data_map_chunk, chunks, _) = encrypt_data(data)?;
self.upload_data_map(&proof, &data_map_chunk).await?;
self.upload_chunks(&chunks, &proof).await?;
Ok(*data_map_chunk.address().xorname())
}

/// Get quotes for data.
/// Returns a cost map, data payments to be executed and a list of free (already paid for) chunks.
pub async fn get_quotes_for_data(
&self,
data: Bytes,
) -> Result<
(
HashMap<XorName, PayeeQuote>,
Vec<QuotePayment>,
Vec<XorName>,
),
PutError,
> {
// Encrypt the data as chunks
let (_data_map_chunk, _chunks, xor_names) = encrypt_data(data)?;
let cost_map = self.get_store_quotes(xor_names.into_iter()).await?;
let (quote_payments, free_chunks) = extract_quote_payments(&cost_map);
Ok((cost_map, quote_payments, free_chunks))
}

async fn upload_data_map(
&self,
payment_proofs: &HashMap<XorName, ProofOfPayment>,
data_map_chunk: &Chunk,
) -> Result<(), PutError> {
let map_xor_name = data_map_chunk.name();

if let Some(proof) = payment_proofs.get(map_xor_name) {
debug!("Uploading data map chunk: {map_xor_name:?}");
self.chunk_upload_with_payment(data_map_chunk.clone(), proof.clone())
.await
.inspect_err(|err| error!("Error uploading data map chunk: {err:?}"))
} else {
Ok(())
}
}

async fn upload_chunks(
&self,
chunks: &[Chunk],
payment_proofs: &HashMap<XorName, ProofOfPayment>,
) -> Result<(), PutError> {
debug!("Uploading {} chunks", chunks.len());
for chunk in chunks {
if let Some(proof) = payment_proofs.get(chunk.name()) {
let address = *chunk.address();
self.chunk_upload_with_payment(chunk.clone(), proof.clone())
.await
.inspect_err(|err| error!("Error uploading chunk {address:?} :{err:?}"))?;
}
}
Ok(())
}
}

/// Encrypts data as chunks.
///
/// Returns the data map chunk, file chunks and a list of all content addresses including the data map.
fn encrypt_data(data: Bytes) -> Result<(Chunk, Vec<Chunk>, Vec<XorName>), PutError> {
let now = sn_networking::target_arch::Instant::now();
let result = encrypt(data)?;

debug!("Encryption took: {:.2?}", now.elapsed());

let map_xor_name = *result.0.address().xorname();
let mut xor_names = vec![map_xor_name];

for chunk in &result.1 {
xor_names.push(*chunk.name());
}

Ok((result.0, result.1, xor_names))
}
4 changes: 4 additions & 0 deletions autonomi/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ pub mod address;
pub mod archive;
#[cfg(feature = "data")]
pub mod data;
#[cfg(feature = "external-signer")]
pub mod external_signer;
#[cfg(feature = "fs")]
pub mod fs;
#[cfg(feature = "registers")]
pub mod registers;
#[cfg(feature = "vault")]
pub mod vault;
#[cfg(feature = "vault")]
pub mod vault_user_data;

#[cfg(target_arch = "wasm32")]
pub mod wasm;
Expand Down
36 changes: 6 additions & 30 deletions autonomi/src/client/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use std::{
collections::{BTreeMap, HashMap},
num::NonZero,
};
use std::{collections::HashMap, num::NonZero};

use bytes::Bytes;
use libp2p::kad::{Quorum, Record};
use rand::{thread_rng, Rng};
use self_encryption::{decrypt_full_set, DataMap, EncryptedChunk};
use sn_evm::{EvmWallet, ProofOfPayment, QuoteHash, QuotePayment, TxHash};
use sn_evm::{EvmWallet, ProofOfPayment, QuotePayment};
use sn_networking::{
GetRecordCfg, Network, NetworkError, PayeeQuote, PutRecordCfg, VerificationKind,
};
Expand All @@ -26,12 +23,12 @@ use sn_protocol::{
};
use xor_name::XorName;

use crate::self_encryption::DataMapLevel;

use super::{
data::{CostError, GetError, PayError, PutError},
Client,
};
use crate::self_encryption::DataMapLevel;
use crate::utils::payment_proof_from_quotes_and_payments;

impl Client {
/// Fetch and decrypt all chunks in the data map.
Expand Down Expand Up @@ -163,7 +160,7 @@ impl Client {
.await
.map_err(|err| PayError::from(err.0))?;

let proofs = construct_proofs(&cost_map, &payments);
let proofs = payment_proof_from_quotes_and_payments(&cost_map, &payments);

trace!(
"Chunk payments of {} chunks completed. {} chunks were free / already paid for",
Expand Down Expand Up @@ -229,7 +226,7 @@ async fn fetch_store_quote(
}

/// Form to be executed payments and already executed payments from a cost map.
fn extract_quote_payments(
pub(crate) fn extract_quote_payments(
cost_map: &HashMap<XorName, PayeeQuote>,
) -> (Vec<QuotePayment>, Vec<XorName>) {
let mut to_be_paid = vec![];
Expand All @@ -249,24 +246,3 @@ fn extract_quote_payments(

(to_be_paid, already_paid)
}

/// Construct payment proofs from cost map and payments map.
fn construct_proofs(
cost_map: &HashMap<XorName, PayeeQuote>,
payments: &BTreeMap<QuoteHash, TxHash>,
) -> HashMap<XorName, ProofOfPayment> {
cost_map
.iter()
.filter_map(|(xor_name, (_, _, quote))| {
payments.get(&quote.hash()).map(|tx_hash| {
(
*xor_name,
ProofOfPayment {
quote: quote.clone(),
tx_hash: *tx_hash,
},
)
})
})
.collect()
}
Loading

0 comments on commit 68060f9

Please sign in to comment.