-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2242 from mickvandijke/metamask-integration
API 3rd party transaction signer integration
- Loading branch information
Showing
19 changed files
with
461 additions
and
78 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
use sn_evm::{ProofOfPayment, QuoteHash, TxHash}; | ||
use sn_networking::PayeeQuote; | ||
use std::collections::{BTreeMap, HashMap}; | ||
use xor_name::XorName; | ||
|
||
pub fn payment_proof_from_quotes_and_payments( | ||
quotes: &HashMap<XorName, PayeeQuote>, | ||
payments: &BTreeMap<QuoteHash, TxHash>, | ||
) -> HashMap<XorName, ProofOfPayment> { | ||
quotes | ||
.iter() | ||
.filter_map(|(xor_name, (_, _, quote))| { | ||
payments.get("e.hash()).map(|tx_hash| { | ||
( | ||
*xor_name, | ||
ProofOfPayment { | ||
quote: quote.clone(), | ||
tx_hash: *tx_hash, | ||
}, | ||
) | ||
}) | ||
}) | ||
.collect() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#![cfg(feature = "external-signer")] | ||
|
||
use alloy::network::TransactionBuilder; | ||
use alloy::providers::Provider; | ||
use autonomi::Client; | ||
use sn_evm::{QuoteHash, TxHash}; | ||
use sn_logging::LogBuilder; | ||
use std::collections::BTreeMap; | ||
use std::time::Duration; | ||
use test_utils::evm::get_funded_wallet; | ||
use test_utils::{gen_random_data, peers_from_env}; | ||
use tokio::time::sleep; | ||
|
||
// Example of how put would be done using external signers. | ||
#[tokio::test] | ||
async fn external_signer_put() -> eyre::Result<()> { | ||
let _log_appender_guard = | ||
LogBuilder::init_single_threaded_tokio_test("external_signer_put", false); | ||
|
||
let client = Client::connect(&peers_from_env()?).await?; | ||
let wallet = get_funded_wallet(); | ||
let data = gen_random_data(1024 * 1024 * 10); | ||
|
||
let (quotes, quote_payments, _free_chunks) = client.get_quotes_for_data(data.clone()).await?; | ||
|
||
// Form quotes payment transaction data | ||
let pay_for_quotes_calldata = autonomi::client::external_signer::pay_for_quotes_calldata( | ||
wallet.network(), | ||
quote_payments.into_iter(), | ||
)?; | ||
|
||
// Init an external wallet provider. In the webapp, this would be MetaMask for example | ||
let provider = wallet.to_provider(); | ||
|
||
// Form approve to spend tokens transaction data | ||
let approve_calldata = autonomi::client::external_signer::approve_to_spend_tokens_calldata( | ||
wallet.network(), | ||
pay_for_quotes_calldata.approve_spender, | ||
pay_for_quotes_calldata.approve_amount, | ||
); | ||
|
||
// Prepare approve to spend tokens transaction | ||
let transaction_request = provider | ||
.transaction_request() | ||
.with_to(approve_calldata.1) | ||
.with_input(approve_calldata.0); | ||
|
||
// Send approve to spend tokens transaction | ||
let _tx_hash = provider | ||
.send_transaction(transaction_request) | ||
.await? | ||
.watch() | ||
.await?; | ||
|
||
let mut payments: BTreeMap<QuoteHash, TxHash> = Default::default(); | ||
|
||
// Execute all quote payment transactions in batches | ||
for (calldata, quote_hashes) in pay_for_quotes_calldata.batched_calldata_map { | ||
// Prepare batched quote payments transaction | ||
let transaction_request = provider | ||
.transaction_request() | ||
.with_to(pay_for_quotes_calldata.to) | ||
.with_input(calldata); | ||
|
||
// Send batched quote payments transaction | ||
let tx_hash = provider | ||
.send_transaction(transaction_request) | ||
.await? | ||
.watch() | ||
.await?; | ||
|
||
// Add to payments to be later use to construct the proofs | ||
for quote_hash in quote_hashes { | ||
payments.insert(quote_hash, tx_hash); | ||
} | ||
} | ||
|
||
// Payment proofs | ||
let proofs = autonomi::payment_proof_from_quotes_and_payments("es, &payments); | ||
|
||
let addr = client | ||
.data_put_with_proof_of_payment(data.clone(), proofs) | ||
.await?; | ||
|
||
sleep(Duration::from_secs(10)).await; | ||
|
||
let data_fetched = client.data_get(addr).await?; | ||
assert_eq!(data, data_fetched, "data fetched should match data put"); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.