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: added WASM functions & mm test #2380

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
4 changes: 2 additions & 2 deletions autonomi/examples/metamask/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
<body>
<!-- credits: https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html -->
<script type="module">
import {externalSignerPut} from "./index.js";
import {externalSignerPrivateDataPutToVault} from "./index.js";

async function run() {
document.getElementById("btn-run").disabled = true;
const peerAddr = document.getElementById('peer_id').value;
await externalSignerPut(peerAddr);
await externalSignerPrivateDataPutToVault(peerAddr);
}

document.getElementById("btn-run").addEventListener("click", run, false);
Expand Down
188 changes: 136 additions & 52 deletions autonomi/examples/metamask/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import init, * as autonomi from '../../pkg/autonomi.js';

export async function externalSignerPut(peerAddr) {
export async function externalSignerPrivateDataPutToVault(peerAddr) {
try {
// Check if MetaMask (window.ethereum) is available
if (typeof window.ethereum === 'undefined') {
Expand All @@ -21,76 +21,107 @@ export async function externalSignerPut(peerAddr) {
// Generate 1MB of random bytes in a Uint8Array
const data = new Uint8Array(1024 * 1024).map(() => Math.floor(Math.random() * 256));

// Get quotes and payment information (this would need actual implementation)
const [quotes, quotePayments, free_chunks] = await client.getQuotes(data);
// Encrypt the data to chunks
const [dataMapChunk, dataChunks, dataMapChunkAddress, dataChunkAddresses] = autonomi.encryptData(data);

// Get the EVM network
let evmNetwork = autonomi.getEvmNetwork();
// Fetch quotes for the chunks
const [quotes, quotePayments, _freeChunks] = await client.getQuotes(dataChunkAddresses);

// Form quotes payment calldata
const payForQuotesCalldata = autonomi.getPayForQuotesCalldata(
evmNetwork,
quotePayments
);
// Pay for data chunks (not the data map)
const receipt = await executeQuotePayments(sender, quotes, quotePayments);

// Form approve to spend tokens calldata
const approveCalldata = autonomi.getApproveToSpendTokensCalldata(
evmNetwork,
payForQuotesCalldata.approve_spender,
payForQuotesCalldata.approve_amount
);
// Wait for a few seconds to allow tx to confirm
await new Promise(resolve => setTimeout(resolve, 5000));

console.log("Sending approve transaction..");
// Upload the data
const privateDataAccess = await client.privateDataPutWithReceipt(data, receipt);

// Approve to spend tokens
let txHash = await sendTransaction({
from: sender,
to: approveCalldata[1],
data: approveCalldata[0]
});
// Create a private archive
const privateArchive = new autonomi.PrivateArchive();

// Add our data's data map chunk to the private archive
privateArchive.addNewFile("test", privateDataAccess);

// Get the private archive's bytes
const privateArchiveBytes = privateArchive.bytes();

// Encrypt the private archive to chunks
const [paDataMapChunk, paDataChunks, paDataMapChunkAddress, paDataChunkAddresses] = autonomi.encryptData(privateArchiveBytes);

await waitForTransactionConfirmation(txHash);
// Fetch quotes for the private archive chunks
const [paQuotes, paQuotePayments, _paFreeChunks] = await client.getQuotes(paDataChunkAddresses);

let payments = {};
// Pay for the private archive chunks (not the data map)
const paReceipt = await executeQuotePayments(sender, paQuotes, paQuotePayments);

// Execute batched quote payment transactions
for (const [calldata, quoteHashes] of payForQuotesCalldata.batched_calldata_map) {
console.log("Sending batched data payment transaction..");
// Wait for a few seconds to allow tx to confirm
await new Promise(resolve => setTimeout(resolve, 5000));

let txHash = await sendTransaction({
from: sender,
to: payForQuotesCalldata.to,
data: calldata
});
// Upload the private archive
const privateArchiveAccess = await client.privateArchivePutWithReceipt(privateArchive, paReceipt);

await waitForTransactionConfirmation(txHash);
// Generate a random vault key (should normally be derived from a constant signature)
const vaultKey = autonomi.genSecretKey();

// Record the transaction hashes for each quote
quoteHashes.forEach(quoteHash => {
payments[quoteHash] = txHash;
});
// Fetch user data from vault (won't exist, so will be empty)
let userData;

try {
userData = await client.getUserDataFromVault(vaultKey);
} catch (err) {
userData = new autonomi.UserData();
}

// Generate payment proof
const proof = autonomi.getPaymentProofFromQuotesAndPayments(quotes, payments);
// Add archive to user data
userData.addPrivateFileArchive(privateArchiveAccess, "test-archive");

// Submit the data with proof of payment
const addr = await client.dataPutWithProof(data, proof);
// Get or create a scratchpad for the user data
let scratchpad = await client.getOrCreateUserDataScratchpad(vaultKey);

// Wait for a few seconds to allow data to propagate
await new Promise(resolve => setTimeout(resolve, 10000));
// Content address of the scratchpad
let scratchPadAddress = scratchpad.xorName();

// Fetch the data back
const fetchedData = await client.dataGet(addr);
// Fetch quotes for the scratchpad
const [spQuotes, spQuotePayments, _spFreeChunks] = await client.getQuotes(scratchPadAddress ? [scratchPadAddress] : []);

if (fetchedData.toString() === data.toString()) {
console.log("Fetched data matches the original data!");
} else {
throw new Error("Fetched data does not match original data!")
}
// Pay for the private archive chunks (not the data map)
const spReceipt = await executeQuotePayments(sender, spQuotes, spQuotePayments);

// Wait for a few seconds to allow tx to confirm
await new Promise(resolve => setTimeout(resolve, 5000));

console.log("Data successfully put and verified!");
// Update vault
await client.putUserDataToVaultWithReceipt(userData, spReceipt, vaultKey);

// VERIFY UPLOADED DATA

// Fetch user data
let fetchedUserData = await client.getUserDataFromVault(vaultKey);

// Get the first key
let fetchedPrivateArchiveAccess = fetchedUserData.privateFileArchives().keys().next().value;

// Get private archive
let fetchedPrivateArchive = await client.privateArchiveGet(fetchedPrivateArchiveAccess);

// Select first file in private archive
let [fetchedFilePath, [fetchedPrivateFileAccess, fetchedFileMetadata]] = fetchedPrivateArchive.map().entries().next().value;

console.log(fetchedFilePath);
console.log(fetchedPrivateFileAccess);
console.log(fetchedFileMetadata);

// Fetch private file/data
let fetchedPrivateFile = await client.privateDataGet(fetchedPrivateFileAccess);

// Compare to original data
console.log("Comparing fetched data to original data..");

if (fetchedPrivateFile.toString() === data.toString()) {
console.log("Data matches! Private file upload to vault was successful!");
} else {
console.log("Data does not match!! Something went wrong..")
}
} catch (error) {
console.error("An error occurred:", error);
}
Expand Down Expand Up @@ -146,4 +177,57 @@ async function waitForTransactionConfirmation(txHash) {
// Wait for 1 second before checking again
await delay(1000);
}
}

const executeQuotePayments = async (sender, quotes, quotePayments) => {
// Get the EVM network
let evmNetwork = autonomi.getEvmNetwork();

// Form quotes payment calldata
const payForQuotesCalldata = autonomi.getPayForQuotesCalldata(
evmNetwork,
quotePayments
);

// Form approve to spend tokens calldata
const approveCalldata = autonomi.getApproveToSpendTokensCalldata(
evmNetwork,
payForQuotesCalldata.approve_spender,
payForQuotesCalldata.approve_amount
);

console.log("Sending approve transaction..");

// Approve to spend tokens
let hash = await sendTransaction({
from: sender,
to: approveCalldata[1],
data: approveCalldata[0]
});

// Wait for approve tx to confirm
await waitForTransactionConfirmation(hash);

let payments = {};

// Execute batched quote payment transactions
for (const [calldata, quoteHashes] of payForQuotesCalldata.batched_calldata_map) {
console.log("Sending batched data payment transaction..");

let hash = await sendTransaction({
from: sender,
to: payForQuotesCalldata.to,
data: calldata
});

await waitForTransactionConfirmation(hash);

// Record the transaction hashes for each quote
quoteHashes.forEach(quoteHash => {
payments[quoteHash] = hash;
});
}

// Generate receipt
return autonomi.getReceiptFromQuotesAndPayments(quotes, payments);
}
13 changes: 3 additions & 10 deletions autonomi/src/client/external_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,12 @@ impl Client {

/// Encrypts data as chunks.
///
/// Returns the data map chunk, file chunks and a list of all content addresses including the data map.
pub fn encrypt_data(data: Bytes) -> Result<(Chunk, Vec<Chunk>, Vec<XorName>), PutError> {
/// Returns the data map chunk and file chunks.
pub fn encrypt_data(data: Bytes) -> Result<(Chunk, Vec<Chunk>), 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))
Ok((result.0, result.1))
}
Loading
Loading