Skip to content

Commit

Permalink
refactor: entire transacting flow from CashNote to SignedSpend
Browse files Browse the repository at this point in the history
BREAKING CHANGE: many types gone, replaced by UnsignedTransaction SignedTransaction
  • Loading branch information
grumbach committed Jul 19, 2024
1 parent 20ace52 commit c5d1461
Show file tree
Hide file tree
Showing 26 changed files with 1,454 additions and 2,064 deletions.
2 changes: 1 addition & 1 deletion sn_cli/src/bin/subcommands/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl WalletApiHelper {
}
println!("Available cash notes are:");
if let Ok(available_cnrs) = w.available_cash_notes() {
for (cnr, _key) in available_cnrs.0.iter() {
for cnr in available_cnrs.0.iter() {
println!("{cnr:?}");
}
}
Expand Down
26 changes: 10 additions & 16 deletions sn_cli/src/bin/subcommands/wallet/hot_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use color_eyre::{
};
use dialoguer::Confirm;
use sn_client::transfers::{
HotWallet, MainPubkey, MainSecretKey, NanoTokens, Transfer, TransferError, UnsignedTransfer,
HotWallet, MainPubkey, MainSecretKey, NanoTokens, Transfer, TransferError, UnsignedTransaction,
WalletError,
};
use sn_client::{
Expand Down Expand Up @@ -309,15 +309,15 @@ async fn send(
fn sign_transaction(tx: &str, root_dir: &Path, force: bool) -> Result<()> {
let wallet = load_account_wallet_or_create_with_mnemonic(root_dir, None)?;

let unsigned_transfer: UnsignedTransfer = rmp_serde::from_slice(&hex::decode(tx)?)?;
let unsigned_tx = UnsignedTransaction::from_hex(tx)?;

println!("The unsigned transaction has been successfully decoded:");
for (i, (spend, _)) in unsigned_transfer.spends.iter().enumerate() {
for (i, (unique_pk, amount)) in unsigned_tx.spent_unique_keys().iter().enumerate() {
println!("\nSpending input #{i}:");
println!("\tKey: {}", spend.unique_pubkey.to_hex());
println!("\tAmount: {}", spend.amount());
println!("\tKey: {}", unique_pk.to_hex());
println!("\tAmount: {amount}");

for (descendant, (amount, _purpose)) in spend.descendants.iter() {
for (descendant, amount) in unsigned_tx.output_unique_keys().iter() {
println!("\tOutput Key: {}", descendant.to_hex());
println!("\tAmount: {amount}");
}
Expand All @@ -336,21 +336,15 @@ fn sign_transaction(tx: &str, root_dir: &Path, force: bool) -> Result<()> {
}

println!("Signing the transaction with local hot-wallet...");
let signed_spends = wallet.sign(unsigned_transfer.spends);
let signed_tx = wallet.sign(unsigned_tx)?;

for signed_spend in signed_spends.iter() {
if let Err(err) = signed_spend.verify() {
bail!("Signature or transaction generated is invalid: {err:?}");
}
if let Err(err) = signed_tx.verify(wallet.key()) {
bail!("Signature or transaction generated is invalid: {err:?}");
}

println!(
"The transaction has been successfully signed:\n\n{}\n",
hex::encode(rmp_serde::to_vec(&(
&signed_spends,
unsigned_transfer.output_details,
unsigned_transfer.change_id
))?)
signed_tx.to_hex()?
);
println!(
"Please copy the above text, and broadcast it to the network with 'wallet broadcast' cmd."
Expand Down
62 changes: 24 additions & 38 deletions sn_cli/src/bin/subcommands/wallet/wo_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,11 @@ use color_eyre::{
Result,
};
use dialoguer::Confirm;
use sn_client::transfers::{
DerivationIndex, MainPubkey, NanoTokens, OfflineTransfer, SignedSpend, UniquePubkey,
WatchOnlyWallet,
};
use sn_client::transfers::{MainPubkey, NanoTokens, SignedTransaction, Transfer, WatchOnlyWallet};
use sn_client::Client;
use std::{
collections::{BTreeMap, BTreeSet},
path::Path,
str::FromStr,
};
use std::{path::Path, str::FromStr};
use walkdir::WalkDir;

type SignedTx = (
BTreeSet<SignedSpend>,
BTreeMap<UniquePubkey, (MainPubkey, DerivationIndex, NanoTokens)>,
UniquePubkey,
);

// Please do not remove the blank lines in these doc comments.
// They are used for inserting line breaks when the help menu is rendered in the UI.
#[derive(Parser, Debug)]
Expand Down Expand Up @@ -85,10 +72,9 @@ pub enum WatchOnlyWalletCmds {
#[clap(name = "to")]
to: String,
},
/// This command will create the cash note for the recipient and broadcast it to the network.
///
/// This cash note can then be shared with the recipient, who can then
/// use the 'deposit' command to use/claim the funds.
/// This command turns an offline signed transaction into a valid sendable Transfer
/// The signed transaction's SignedSpends are broadcasted to the Network and the recipient's Transfer is returned
/// This Transfer can then be sent and redeemed by the recipient using the 'receive' command
Broadcast {
/// Hex-encoded signed transaction.
#[clap(name = "signed Tx")]
Expand Down Expand Up @@ -186,7 +172,7 @@ pub(crate) async fn wo_wallet_cmds(
) -> Result<()> {
match cmds {
WatchOnlyWalletCmds::Broadcast { signed_tx, force } => {
broadcast_signed_spends(signed_tx, client, verify_store, force).await
broadcast_signed_tx(signed_tx, client, verify_store, force).await
}
WatchOnlyWalletCmds::Verify {
spend_address,
Expand Down Expand Up @@ -249,17 +235,21 @@ fn build_unsigned_transaction(from: &str, amount: &str, to: &str, root_dir: &Pat
Ok(())
}

async fn broadcast_signed_spends(
async fn broadcast_signed_tx(
signed_tx: String,
client: &Client,
verify_store: bool,
force: bool,
) -> Result<()> {
let (signed_spends, output_details, change_id): SignedTx =
rmp_serde::from_slice(&hex::decode(signed_tx)?)?;

let signed_tx = match SignedTransaction::from_hex(&signed_tx) {
Ok(signed_tx) => signed_tx,
Err(err) => {
bail!("Failed to decode the signed transaction: {err:?}");
}
};
println!("The signed transaction has been successfully decoded:");
for (i, signed_spend) in signed_spends.iter().enumerate() {

for (i, signed_spend) in signed_tx.spends.iter().enumerate() {
println!("\nSpending input #{i}:");
println!("\tKey: {}", signed_spend.unique_pubkey().to_hex());
println!("\tAmount: {}", signed_spend.amount());
Expand Down Expand Up @@ -289,34 +279,30 @@ async fn broadcast_signed_spends(
}

println!("Broadcasting the transaction to the network...");
let transfer = OfflineTransfer::from_transaction(signed_spends, change_id, output_details)?;

// return the first CashNote (assuming there is only one because we only sent to one recipient)
let cash_note = match &transfer.cash_notes_for_recipient[..] {
[cashnote] => cashnote.to_hex()?,
let cash_note = match &signed_tx.output_cashnotes[..] {
[cashnote] => cashnote,
[_multiple, ..] => bail!("Multiple CashNotes were returned from the transaction when only one was expected. This is a BUG."),
[] =>bail!("No CashNotes were built from the Tx.")
};

// send to network
client
.send_spends(transfer.all_spend_requests.iter(), verify_store)
.send_spends(signed_tx.spends.iter(), verify_store)
.await
.map_err(|err| {
eyre!("The transfer was not successfully registered in the network: {err:?}")
})?;

println!("Transaction broadcasted!.");

println!("The recipient's cash note has been successfully created.");
println!("Please share this to the recipient:\n\n{cash_note}\n");
println!("The recipient can then use the wallet 'deposit' command to verify the transfer, and/or be able to use the funds.\n");
let transfer = Transfer::transfer_from_cash_note(cash_note)?.to_hex()?;
println!("Please share this to the recipient:\n\n{transfer}\n");
println!("The recipient can then use the wallet 'receive' command to claim the funds.\n");

if let Some(cash_note) = transfer.change_cash_note {
println!(
"A change cash note has also been created:\n\n{}\n",
cash_note.to_hex()?
);
if let Some(change_cn) = signed_tx.change_cashnote {
let change_transfer = Transfer::transfer_from_cash_note(&change_cn)?.to_hex()?;
println!("Please redeem the change from this Transaction:\n\n{change_transfer}\n");
println!("You should use the wallet 'deposit' command to be able to use these funds.\n");
}

Expand Down
24 changes: 9 additions & 15 deletions sn_client/src/audit/tests/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use bls::SecretKey;
use eyre::{eyre, Result};
use sn_transfers::{
get_genesis_sk, CashNote, DerivationIndex, MainPubkey, MainSecretKey, NanoTokens,
OfflineTransfer, SignedSpend, SpendAddress, SpendReason, GENESIS_CASHNOTE,
OutputPurpose, SignedSpend, SignedTransaction, SpendAddress, SpendReason, GENESIS_CASHNOTE,
GENESIS_OUTPUT_DERIVATION_INDEX,
};

Expand Down Expand Up @@ -90,14 +90,6 @@ impl MockNetwork {
.ok_or_else(|| eyre!("to wallet not found: {to:?}"))?;

// perform offline transfer
let cash_notes_with_keys = from_wallet
.cn
.clone()
.into_iter()
.map(|cn| Ok((cn.clone(), Some(cn.derived_key(&from_wallet.sk)?))))
.collect::<Result<_>>()
.map_err(|e| eyre!("could not get cashnotes for transfer: {e}"))?;

let derivation_index = if is_genesis {
GENESIS_OUTPUT_DERIVATION_INDEX
} else {
Expand All @@ -108,15 +100,17 @@ impl MockNetwork {
NanoTokens::from(amount),
to_wallet.sk.main_pubkey(),
derivation_index,
OutputPurpose::None,
)];
let transfer = OfflineTransfer::new(
cash_notes_with_keys,
let tx = SignedTransaction::new(
from_wallet.cn.clone(),
recipient,
from_wallet.sk.main_pubkey(),
SpendReason::default(),
&from_wallet.sk,
)
.map_err(|e| eyre!("failed to create transfer: {}", e))?;
let spends = transfer.all_spend_requests;
let spends = tx.spends;

// update wallets
let mut updated_from_wallet_cns = from_wallet.cn.clone();
Expand All @@ -125,12 +119,12 @@ impl MockNetwork {
.iter()
.any(|s| s.unique_pubkey() == &cn.unique_pubkey())
});
if let Some(ref change_cn) = transfer.change_cash_note {
if let Some(ref change_cn) = tx.change_cashnote {
if !updated_from_wallet_cns
.iter()
.any(|cn| cn.unique_pubkey() == change_cn.unique_pubkey())
{
updated_from_wallet_cns.extend(transfer.change_cash_note);
updated_from_wallet_cns.extend(tx.change_cashnote);
}
}

Expand All @@ -139,7 +133,7 @@ impl MockNetwork {
.and_modify(|w| w.cn = updated_from_wallet_cns);
self.wallets
.entry(*to)
.and_modify(|w| w.cn.extend(transfer.cash_notes_for_recipient));
.and_modify(|w| w.cn.extend(tx.output_cashnotes));

// update network spends
let spent_addrs = spends.iter().map(|s| s.address()).collect();
Expand Down
2 changes: 1 addition & 1 deletion sn_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub use self::{
folders::{FolderEntry, FoldersApi, Metadata},
register::ClientRegister,
uploader::{UploadCfg, UploadEvent, UploadSummary, Uploader},
wallet::{broadcast_signed_spends, send, StoragePaymentResult, WalletClient},
wallet::{send, StoragePaymentResult, WalletClient},
};
pub(crate) use error::Result;

Expand Down
Loading

0 comments on commit c5d1461

Please sign in to comment.