Skip to content

Commit

Permalink
fix: move tx verification to wallet sign
Browse files Browse the repository at this point in the history
  • Loading branch information
grumbach committed Jul 22, 2024
1 parent ecb17a2 commit 9374151
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 77 deletions.
52 changes: 27 additions & 25 deletions sn_auditor/src/dag_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,67 +415,68 @@ impl SpendDagDb {
// Collect royalties
let royalty_pubkeys: BTreeSet<_> = spend
.spend
.network_royalties
.network_royalties()
.iter()
.map(|derivation_idx| NETWORK_ROYALTIES_PK.new_unique_pubkey(derivation_idx))
.map(|(_, _, derivation_idx)| NETWORK_ROYALTIES_PK.new_unique_pubkey(derivation_idx))
.collect();
let default_royalty_pubkeys: BTreeSet<_> = spend
.spend
.network_royalties
.network_royalties()
.iter()
.map(|derivation_idx| DEFAULT_NETWORK_ROYALTIES_PK.new_unique_pubkey(derivation_idx))
.map(|(_, _, derivation_idx)| {
DEFAULT_NETWORK_ROYALTIES_PK.new_unique_pubkey(derivation_idx)
})
.collect();
let mut royalties = BTreeMap::new();
for output in spend.spend.spent_tx.outputs.iter() {
if default_royalty_pubkeys.contains(&output.unique_pubkey)
|| royalty_pubkeys.contains(&output.unique_pubkey)
{
for (unique_pk, (amount, _)) in spend.spend.descendants.iter() {
if default_royalty_pubkeys.contains(unique_pk) || royalty_pubkeys.contains(unique_pk) {
let _ = royalties.insert(
SpendAddress::from_unique_pubkey(&output.unique_pubkey),
output.amount.as_nano(),
SpendAddress::from_unique_pubkey(unique_pk),
amount.as_nano(),
);
}
}

if royalties.len() > (spend.spend.spent_tx.outputs.len() - 1) / 2 {
if royalties.len() > (spend.spend.descendants.len() - 1) / 2 {
eprintln!(
"Spend: {:?} has incorrect royalty of {}, with amount {} with reason {:?}",
spend.spend.unique_pubkey,
royalties.len(),
spend.spend.amount.as_nano(),
spend.spend.amount().as_nano(),
spend.spend.reason
);
eprintln!(
"Incorrect royalty spend has {} royalties, {:?} - {:?}",
spend.spend.network_royalties.len(),
spend.spend.spent_tx.inputs,
spend.spend.spent_tx.outputs
spend.spend.network_royalties().len(),
spend.spend.ancestors,
spend.spend.descendants
);
warn!(
"Spend: {:?} has incorrect royalty of {}, with amount {} with reason {:?}",
spend.spend.unique_pubkey,
royalties.len(),
spend.spend.amount.as_nano(),
spend.spend.amount().as_nano(),
spend.spend.reason
);
warn!(
"Incorrect royalty spend has {} royalties, {:?} - {:?}",
spend.spend.network_royalties.len(),
spend.spend.spent_tx.inputs,
spend.spend.spent_tx.outputs
spend.spend.network_royalties().len(),
spend.spend.ancestors,
spend.spend.descendants
);
}
beta_tracking.total_royalties.extend(royalties);

let addr = spend.address();
let amount = spend.spend.amount;
let amount = spend.spend.amount();

// check for beta rewards reason
let user_name_hash = match spend.reason().decrypt_discord_cypher(sk) {
Some(n) => n,
None => {
if let Some(default_user_name_hash) =
spend.reason().get_sender_hash(&DEFAULT_PAYMENT_FORWARD_SK)
if let Some(default_user_name_hash) = spend
.reason()
.decrypt_discord_cypher(&DEFAULT_PAYMENT_FORWARD_SK)
{
warn!("With default key, got forwarded reward of {amount} at {addr:?}");
println!("With default key, got forwarded reward of {amount} at {addr:?}");
Expand Down Expand Up @@ -537,7 +538,7 @@ impl SpendDagDb {
sk: &SecretKey,
_utxos_for_further_track: u64,
) {
let user_name_hash = match spend.reason().get_sender_hash(sk) {
let user_name_hash = match spend.reason().decrypt_discord_cypher(sk) {
Some(n) => n,
None => {
return;
Expand All @@ -551,8 +552,9 @@ impl SpendDagDb {
if let Some(user_name) = beta_participants_read.get(&user_name_hash) {
println!("Found double spend from {user_name} at {addr:?}");
} else {
if let Some(default_user_name_hash) =
spend.reason().get_sender_hash(&DEFAULT_PAYMENT_FORWARD_SK)
if let Some(default_user_name_hash) = spend
.reason()
.decrypt_discord_cypher(&DEFAULT_PAYMENT_FORWARD_SK)
{
if let Some(user_name) = beta_participants_read.get(&default_user_name_hash) {
println!("Found double spend from {user_name} at {addr:?} using default key");
Expand Down
9 changes: 1 addition & 8 deletions sn_cli/src/bin/subcommands/wallet/hot_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ use crate::{get_stdin_password_response, get_stdin_response};
use autonomi::utils::is_valid_key_hex;
use bls::SecretKey;
use clap::Parser;
use color_eyre::{
eyre::{bail, eyre},
Result,
};
use color_eyre::{eyre::eyre, Result};
use dialoguer::Confirm;
use sn_client::acc_packet::{load_or_create_mnemonic, secret_key_from_mnemonic};
use sn_client::transfers::{
Expand Down Expand Up @@ -401,10 +398,6 @@ fn sign_transaction(tx: &str, root_dir: &Path, force: bool) -> Result<()> {
println!("Signing the transaction with local hot-wallet...");
let signed_tx = wallet.sign(unsigned_tx)?;

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",
signed_tx.to_hex()?
Expand Down
49 changes: 33 additions & 16 deletions sn_client/src/audit/dag_crawling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use crate::{Client, Error, SpendDag};
use futures::{future::join_all, StreamExt};
use sn_networking::{GetRecordError, NetworkError};
use sn_transfers::{
NanoTokens, OutputPurpose, SignedSpend, SpendAddress, SpendReason, UniquePubkey, WalletError, WalletResult,
GENESIS_SPEND_UNIQUE_KEY, DEFAULT_NETWORK_ROYALTIES_PK, NETWORK_ROYALTIES_PK,
NanoTokens, OutputPurpose, SignedSpend, SpendAddress, SpendReason, UniquePubkey, WalletError,
WalletResult, DEFAULT_NETWORK_ROYALTIES_PK, GENESIS_SPEND_UNIQUE_KEY, NETWORK_ROYALTIES_PK,
};
use std::{
collections::{BTreeMap, BTreeSet},
Expand Down Expand Up @@ -170,9 +170,22 @@ impl Client {
spends.len()
);
for (i, spend) in spends.iter().enumerate() {
warn!("double spend entry {i} reason {:?}, amount {}, inputs: {}, outputs: {}, royties: {}, {:?} - {:?}",
spend.spend.reason, spend.spend.amount, spend.spend.spent_tx.inputs.len(), spend.spend.spent_tx.outputs.len(),
spend.spend.network_royalties.len(), spend.spend.spent_tx.inputs, spend.spend.spent_tx.outputs);
let reason = spend.reason();
let amount = spend.spend.amount();
let ancestors_len = spend.spend.ancestors.len();
let descendants_len = spend.spend.descendants.len();
let roy_len = spend
.spend
.descendants
.iter()
.filter(|(_pk, (_amount, _purpose))| {
matches!(OutputPurpose::RoyaltyFee, _purpose)
})
.count();
warn!(
"double spend entry {i} reason {reason:?}, amount {amount}, ancestors: {ancestors_len}, descendants: {descendants_len}, royalties: {roy_len}, {:?} - {:?}",
spend.spend.ancestors, spend.spend.descendants
);

let for_further_track = beta_track_analyze_spend(spend);
addrs_to_get.extend(for_further_track);
Expand Down Expand Up @@ -541,8 +554,8 @@ fn beta_track_analyze_spend(spend: &SignedSpend) -> BTreeSet<(SpendAddress, Nano
.spend
.descendants
.iter()
.filter(|(pk, (_amount, purpose))| {
if matches!(purpose, OutputPurpose::RoyaltyFee(der)) {
.filter_map(|(_pk, (_amount, purpose))| {
if let OutputPurpose::RoyaltyFee(der) = purpose {
Some(NETWORK_ROYALTIES_PK.new_unique_pubkey(der))
} else {
None
Expand All @@ -551,24 +564,28 @@ fn beta_track_analyze_spend(spend: &SignedSpend) -> BTreeSet<(SpendAddress, Nano
.collect();
let default_royalty_pubkeys: BTreeSet<_> = spend
.spend
.network_royalties
.descendants
.iter()
.map(|derivation_idx| DEFAULT_NETWORK_ROYALTIES_PK.new_unique_pubkey(derivation_idx))
.filter_map(|(_pk, (_amount, purpose))| {
if let OutputPurpose::RoyaltyFee(der) = purpose {
Some(DEFAULT_NETWORK_ROYALTIES_PK.new_unique_pubkey(der))
} else {
None
}
})
.collect();

let new_utxos: BTreeSet<_> = spend
.spend
.descendants
.iter()
.filter_map(|output| {
if default_royalty_pubkeys.contains(&output.unique_pubkey)
|| royalty_pubkeys.contains(&output.unique_pubkey) {
.filter_map(|(unique_pubkey, (amount, _purpose))| {
if default_royalty_pubkeys.contains(unique_pubkey)
|| royalty_pubkeys.contains(unique_pubkey)
{
None
} else {
Some((
SpendAddress::from_unique_pubkey(&output.unique_pubkey),
output.amount,
))
Some((SpendAddress::from_unique_pubkey(unique_pubkey), *amount))
}
})
.collect();
Expand Down
6 changes: 2 additions & 4 deletions sn_node/src/put_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,10 +782,8 @@ impl Node {
for spend in many_spends {
let descendants: BTreeSet<_> = spend
.spend
.spent_tx
.outputs
.iter()
.map(|o| o.unique_pubkey())
.descendants
.keys()
.map(SpendAddress::from_unique_pubkey)
.collect();
for d in descendants {
Expand Down
44 changes: 25 additions & 19 deletions sn_node/tests/double_spend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ async fn parent_and_child_double_spends_should_lead_to_cashnote_being_invalid()
wallet_b.key(),
)?;

info!("spend B to C: {:?}", transfer_to_c.all_spend_requests);
info!("spend B to C: {:?}", transfer_to_c.spends);
client
.send_spends(transfer_to_c.spends.iter(), false)
.await?;
Expand Down Expand Up @@ -438,14 +438,11 @@ async fn parent_and_child_double_spends_should_lead_to_cashnote_being_invalid()
wallet_b.key(),
)?; // reuse the old cash notes

info!("spend B to Y: {:?}", transfer_to_y.all_spend_requests);
info!("spend B to Y: {:?}", transfer_to_y.spends);
client
.send_spends(transfer_to_y.spends.iter(), false)
.await?;
let spend_b_to_y = transfer_to_y
.all_spend_requests
.first()
.expect("should have one");
let spend_b_to_y = transfer_to_y.spends.first().expect("should have one");
let b_spends = client.get_spend_from_network(spend_b_to_y.address()).await;
info!("B spends: {b_spends:?}");

Expand Down Expand Up @@ -498,28 +495,31 @@ async fn spamming_double_spends_should_not_shadow_live_branch() -> Result<()> {
amount,
wallet_b.address(),
DerivationIndex::random(&mut rng),
OutputPurpose::None,
);
let transfer_to_b = OfflineTransfer::new(
let transfer_to_b = SignedTransaction::new(
cash_notes_a.clone(),
vec![to_b_unique_key],
wallet_a.address(),
reason.clone(),
wallet_a.key(),
)?;

info!("Sending A->B to the network...");
client
.send_spends(transfer_to_b.all_spend_requests.iter(), false)
.send_spends(transfer_to_b.spends.iter(), false)
.await?;

// save original A spend
let original_a_spend = if let [spend] = transfer_to_b.all_spend_requests.as_slice() {
let vec_of_spends = transfer_to_b.spends.into_iter().collect::<Vec<_>>();
let original_a_spend = if let [spend] = vec_of_spends.as_slice() {
spend
} else {
panic!("Expected to have one spend here!");
};

info!("Verifying the transfers from A -> B wallet...");
let cash_notes_for_b: Vec<_> = transfer_to_b.cash_notes_for_recipient.clone();
let cash_notes_for_b: Vec<_> = transfer_to_b.output_cashnotes.clone();
client.verify_cashnote(&cash_notes_for_b[0]).await?;
wallet_b.deposit_and_store_to_disk(&cash_notes_for_b)?; // store inside B

Expand All @@ -534,20 +534,22 @@ async fn spamming_double_spends_should_not_shadow_live_branch() -> Result<()> {
wallet_b.balance(),
wallet_c.address(),
DerivationIndex::random(&mut rng),
OutputPurpose::None,
);
let transfer_to_c = OfflineTransfer::new(
let transfer_to_c = SignedTransaction::new(
cash_notes_b.clone(),
vec![to_c_unique_key],
wallet_b.address(),
reason.clone(),
wallet_b.key(),
)?;

client
.send_spends(transfer_to_c.all_spend_requests.iter(), false)
.send_spends(transfer_to_c.spends.iter(), false)
.await?;

info!("Verifying the transfers from B -> C wallet...");
let cash_notes_for_c: Vec<_> = transfer_to_c.cash_notes_for_recipient.clone();
let cash_notes_for_c: Vec<_> = transfer_to_c.output_cashnotes.clone();
client.verify_cashnote(&cash_notes_for_c[0]).await?;
wallet_c.deposit_and_store_to_disk(&cash_notes_for_c.clone())?; // store inside c

Expand All @@ -560,18 +562,20 @@ async fn spamming_double_spends_should_not_shadow_live_branch() -> Result<()> {
amount,
wallet_x.address(),
DerivationIndex::random(&mut rng),
OutputPurpose::None,
);
let transfer_to_x = OfflineTransfer::new(
let transfer_to_x = SignedTransaction::new(
cash_notes_a.clone(),
vec![to_x_unique_key],
wallet_a.address(),
reason.clone(),
wallet_a.key(),
)?; // reuse the old cash notes
client
.send_spends(transfer_to_x.all_spend_requests.iter(), false)
.send_spends(transfer_to_x.spends.iter(), false)
.await?;
info!("Verifying the transfers from A -> X wallet... It should error out.");
let cash_notes_for_x: Vec<_> = transfer_to_x.cash_notes_for_recipient.clone();
let cash_notes_for_x: Vec<_> = transfer_to_x.output_cashnotes.clone();
let result = client.verify_cashnote(&cash_notes_for_x[0]).await;
info!("Got result while verifying double spend from A -> X: {result:?}");
assert_matches!(result, Err(WalletError::CouldNotVerifyTransfer(str)) => {
Expand Down Expand Up @@ -603,18 +607,20 @@ async fn spamming_double_spends_should_not_shadow_live_branch() -> Result<()> {
amount,
wallet_y.address(),
DerivationIndex::random(&mut rng),
OutputPurpose::None,
);
let transfer_to_y = OfflineTransfer::new(
let transfer_to_y = SignedTransaction::new(
cash_notes_a.clone(),
vec![to_y_unique_key],
wallet_a.address(),
reason.clone(),
wallet_a.key(),
)?; // reuse the old cash notes
client
.send_spends(transfer_to_y.all_spend_requests.iter(), false)
.send_spends(transfer_to_y.spends.iter(), false)
.await?;
info!("Verifying the transfers from A -> Y wallet... It should error out.");
let cash_notes_for_y: Vec<_> = transfer_to_y.cash_notes_for_recipient.clone();
let cash_notes_for_y: Vec<_> = transfer_to_y.output_cashnotes.clone();
let result = client.verify_cashnote(&cash_notes_for_y[0]).await;
info!("Got result while verifying double spend from A -> Y: {result:?}");
assert_matches!(result, Err(WalletError::CouldNotVerifyTransfer(str)) => {
Expand Down
Loading

0 comments on commit 9374151

Please sign in to comment.