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(autonomi): file/vault API in wasm #2244

Merged
merged 4 commits into from
Oct 17, 2024
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
35 changes: 27 additions & 8 deletions autonomi/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,26 @@
<body>
<!-- credits: https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html -->
<script type="module">
import init, { Client, getFundedWallet, logInit } from './pkg/autonomi.js';
import init, * as aut from './pkg/autonomi.js';

async function run() {
document.getElementById("btn-run").disabled = true;
const peer_addr = document.getElementById('peer_id').value;

await init();

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

console.log("connecting...");
const client = await new Client([peer_addr]);
const client = await new aut.Client([peer_addr]);
console.log("connected");

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

const data = new Uint8Array([1, 2, 3]);
// Random 32 byte data
const data = [...Array(16)].map(() => Math.floor(Math.random() * 9));
console.log("our data: ", data);

console.log("calculating cost...");
Expand All @@ -33,10 +34,28 @@
console.log("putting...");
const dataAddr = await client.dataPut(data, wallet);
console.log("put done: ", dataAddr.toString());

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

console.log("getting...");
const data_get = await client.dataGet(dataAddr);
console.log("get done: ", data_get, " (original data: ", data, ")");
console.log("fetching archive...");
let archiveFetched = await client.archiveGet(archiveAddr);
console.log("archive fetched: ", archiveFetched);
for (const [path, addr] of archiveFetched) {
console.log("fetching data: ", path, ", addr: ", addr);
const dataFetched = await client.dataGet(addr);
console.log("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);
}

document.getElementById ("btn-run").addEventListener("click", run, false);
Expand Down
4 changes: 2 additions & 2 deletions autonomi/src/client/vault.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ impl Client {
/// Pays for a new VaultPacket if none yet created for the client. Returns the current version
/// of the data on success.
pub async fn write_bytes_to_vault(
&mut self,
&self,
data: Bytes,
wallet: &mut EvmWallet,
wallet: &EvmWallet,
secret_key: &SecretKey,
) -> Result<u64, PutError> {
let client_pk = secret_key.public_key();
Expand Down
128 changes: 99 additions & 29 deletions autonomi/src/client/wasm.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
use libp2p::Multiaddr;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct Client(super::Client);

#[wasm_bindgen]
pub struct ChunkAddr(xor_name::XorName);
use super::address::{addr_to_str, str_to_addr};

#[wasm_bindgen]
pub struct DataAddr(xor_name::XorName);
#[wasm_bindgen]
impl DataAddr {
#[wasm_bindgen(js_name = toString)]
pub fn to_string(&self) -> String {
crate::client::address::addr_to_str(self.0)
}
}
#[wasm_bindgen(js_name = Client)]
pub struct JsClient(super::Client);

#[wasm_bindgen]
pub struct AttoTokens(sn_evm::AttoTokens);
Expand All @@ -27,41 +16,46 @@ impl AttoTokens {
}
}

#[wasm_bindgen]
impl Client {
#[wasm_bindgen(js_class = Client)]
impl JsClient {
#[wasm_bindgen(constructor)]
pub async fn connect(peers: Vec<String>) -> Result<Client, JsError> {
pub async fn connect(peers: Vec<String>) -> Result<JsClient, JsError> {
let peers = peers
.into_iter()
.map(|peer| peer.parse())
.collect::<Result<Vec<Multiaddr>, _>>()?;

let client = super::Client::connect(&peers).await?;

Ok(Client(client))
Ok(JsClient(client))
}

#[wasm_bindgen(js_name = chunkPut)]
pub async fn chunk_put(&self, _data: Vec<u8>, _wallet: Wallet) -> Result<ChunkAddr, JsError> {
pub async fn chunk_put(&self, _data: Vec<u8>, _wallet: &JsWallet) -> Result<String, JsError> {
async { unimplemented!() }.await
}

#[wasm_bindgen(js_name = chunkGet)]
pub async fn chunk_get(&self, addr: ChunkAddr) -> Result<Vec<u8>, JsError> {
let chunk = self.0.chunk_get(addr.0).await?;
pub async fn chunk_get(&self, addr: String) -> Result<Vec<u8>, JsError> {
let addr = str_to_addr(&addr)?;
let chunk = self.0.chunk_get(addr).await?;

Ok(chunk.value().to_vec())
}

#[wasm_bindgen(js_name = dataPut)]
pub async fn data_put(&self, data: Vec<u8>, wallet: Wallet) -> Result<DataAddr, JsError> {
pub async fn data_put(&self, data: Vec<u8>, wallet: &JsWallet) -> Result<String, JsError> {
let data = crate::Bytes::from(data);
let xorname = self.0.data_put(data, &wallet.0).await?;
Ok(DataAddr(xorname))

Ok(addr_to_str(xorname))
}

#[wasm_bindgen(js_name = dataGet)]
pub async fn data_get(&self, addr: DataAddr) -> Result<Vec<u8>, JsError> {
let data = self.0.data_get(addr.0).await?;
pub async fn data_get(&self, addr: String) -> Result<Vec<u8>, JsError> {
let addr = str_to_addr(&addr)?;
let data = self.0.data_get(addr).await?;

Ok(data.to_vec())
}

Expand All @@ -74,14 +68,90 @@ impl Client {
}
}

#[wasm_bindgen]
pub struct Wallet(evmlib::wallet::Wallet);
mod archive {
use super::*;
use crate::client::{address::str_to_addr, archive::Archive};
use std::{collections::HashMap, path::PathBuf};
use xor_name::XorName;

#[wasm_bindgen(js_class = Client)]
impl JsClient {
#[wasm_bindgen(js_name = archiveGet)]
pub async fn archive_get(&self, addr: String) -> Result<js_sys::Map, JsError> {
let addr = str_to_addr(&addr)?;
let data = self.0.archive_get(addr).await?;

// To `Map<K, V>` (JS)
let data = serde_wasm_bindgen::to_value(&data.map)?;
Ok(data.into())
}

#[wasm_bindgen(js_name = archivePut)]
pub async fn archive_put(
&self,
map: JsValue,
wallet: &JsWallet,
) -> Result<String, JsError> {
// From `Map<K, V>` or `Iterable<[K, V]>` (JS)
let map: HashMap<PathBuf, XorName> = serde_wasm_bindgen::from_value(map)?;
let archive = Archive { map };

let addr = self.0.archive_put(archive, &wallet.0).await?;

Ok(addr_to_str(addr))
}
}
}

#[cfg(feature = "vault")]
mod vault {
use super::*;
use bls::SecretKey;

#[wasm_bindgen(js_class = Client)]
impl JsClient {
#[wasm_bindgen(js_name = fetchAndDecryptVault)]
pub async fn fetch_and_decrypt_vault(
&self,
secret_key: Vec<u8>,
) -> Result<Option<Vec<u8>>, JsError> {
let secret_key: [u8; 32] = secret_key[..].try_into()?;
let secret_key = SecretKey::from_bytes(secret_key)?;

let vault = self.0.fetch_and_decrypt_vault(&secret_key).await?;
let vault = vault.map(|v| v.to_vec());

Ok(vault)
}

#[wasm_bindgen(js_name = writeBytesToVault)]
pub async fn write_bytes_to_vault(
&self,
vault: Vec<u8>,
wallet: &mut JsWallet,
secret_key: Vec<u8>,
) -> Result<(), JsError> {
let secret_key: [u8; 32] = secret_key[..].try_into()?;
let secret_key = SecretKey::from_bytes(secret_key)?;

let vault = bytes::Bytes::from(vault);
self.0
.write_bytes_to_vault(vault, &mut wallet.0, &secret_key)
.await?;

Ok(())
}
}
}

#[wasm_bindgen(js_name = Wallet)]
pub struct JsWallet(evmlib::wallet::Wallet);

/// Get a funded wallet for testing. This either uses a default private key or the `EVM_PRIVATE_KEY`
/// environment variable that was used during the build process of this library.
#[wasm_bindgen(js_name = getFundedWallet)]
pub fn funded_wallet() -> Wallet {
Wallet(test_utils::evm::get_funded_wallet())
pub fn funded_wallet() -> JsWallet {
JsWallet(test_utils::evm::get_funded_wallet())
}

/// Enable tracing logging in the console.
Expand Down
6 changes: 3 additions & 3 deletions autonomi/tests/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ fn compute_dir_sha256(dir: &str) -> Result<String> {
async fn file_into_vault() -> Result<()> {
let _log_appender_guard = LogBuilder::init_single_threaded_tokio_test("file", false);

let mut client = Client::connect(&peers_from_env()?).await?;
let mut wallet = get_funded_wallet();
let client = Client::connect(&peers_from_env()?).await?;
let wallet = get_funded_wallet();
let client_sk = bls::SecretKey::random();

let addr = client
Expand All @@ -92,7 +92,7 @@ async fn file_into_vault() -> Result<()> {

let archive = client.archive_get(addr).await?;
client
.write_bytes_to_vault(archive.into_bytes()?, &mut wallet, &client_sk)
.write_bytes_to_vault(archive.into_bytes()?, &wallet, &client_sk)
.await?;

// now assert over the stored account packet
Expand Down
Loading