From 5a1f011db8b575d831870198ea1837aee34b679c Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Mon, 22 Aug 2022 08:57:36 -0400 Subject: [PATCH] Fix https://github.com/serai-dex/serai/issues/106 --- coins/monero/src/wallet/mod.rs | 26 +++++++++++++++++++------- coins/monero/src/wallet/scan.rs | 16 ++++++++++++++-- coins/monero/tests/send.rs | 4 ++-- processor/src/coin/monero.rs | 6 +++--- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/coins/monero/src/wallet/mod.rs b/coins/monero/src/wallet/mod.rs index 37b9059b..4f5b4534 100644 --- a/coins/monero/src/wallet/mod.rs +++ b/coins/monero/src/wallet/mod.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashSet, HashMap}; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -124,19 +124,25 @@ impl ViewPair { pub struct Scanner { pair: ViewPair, network: Network, - guaranteed: bool, pub(crate) subaddresses: HashMap, + pub(crate) burning_bug: Option>, } impl Zeroize for Scanner { fn zeroize(&mut self) { self.pair.zeroize(); self.network.zeroize(); - self.guaranteed.zeroize(); + + // These may not be effective, unfortunately for (mut key, mut value) in self.subaddresses.drain() { key.zeroize(); value.zeroize(); } + if let Some(ref mut burning_bug) = self.burning_bug.take() { + for mut output in burning_bug.drain() { + output.zeroize(); + } + } } } @@ -149,17 +155,23 @@ impl Drop for Scanner { impl ZeroizeOnDrop for Scanner {} impl Scanner { - pub fn from_view(pair: ViewPair, network: Network, guaranteed: bool) -> Scanner { + // For burning bug immune addresses (Featured Address w/ the Guaranteed feature), pass None + // For traditional Monero address, provide a HashSet of all historically scanned output keys + pub fn from_view( + pair: ViewPair, + network: Network, + burning_bug: Option>, + ) -> Scanner { let mut subaddresses = HashMap::new(); subaddresses.insert(pair.spend.compress(), (0, 0)); - Scanner { pair, network, guaranteed, subaddresses } + Scanner { pair, network, subaddresses, burning_bug } } pub fn address(&self) -> Address { Address::new( AddressMeta { network: self.network, - kind: if self.guaranteed { + kind: if self.burning_bug.is_none() { AddressType::Featured(false, None, true) } else { AddressType::Standard @@ -181,7 +193,7 @@ impl Scanner { Address::new( AddressMeta { network: self.network, - kind: if self.guaranteed { + kind: if self.burning_bug.is_none() { AddressType::Featured(true, None, true) } else { AddressType::Subaddress diff --git a/coins/monero/src/wallet/scan.rs b/coins/monero/src/wallet/scan.rs index ceee25b8..03512b46 100644 --- a/coins/monero/src/wallet/scan.rs +++ b/coins/monero/src/wallet/scan.rs @@ -95,7 +95,7 @@ impl SpendableOutput { } impl Scanner { - pub fn scan(&self, tx: &Transaction) -> Timelocked { + pub fn scan(&mut self, tx: &Transaction) -> Timelocked { let extra = Extra::deserialize(&mut Cursor::new(&tx.prefix.extra)); let keys; let extra = if let Ok(extra) = extra { @@ -108,9 +108,17 @@ impl Scanner { let mut res = vec![]; for (o, output) in tx.prefix.outputs.iter().enumerate() { + // https://github.com/serai-dex/serai/issues/102 + let output_key_compressed = output.key.compress(); + if let Some(burning_bug) = self.burning_bug.as_ref() { + if burning_bug.contains(&output_key_compressed) { + continue; + } + } + for key in &keys { let (view_tag, key_offset, payment_id_xor) = shared_key( - if self.guaranteed { Some(uniqueness(&tx.prefix.inputs)) } else { None }, + if self.burning_bug.is_none() { Some(uniqueness(&tx.prefix.inputs)) } else { None }, &self.pair.view, key, o, @@ -173,6 +181,10 @@ impl Scanner { subaddress: (0, 0), payment_id, }); + + if let Some(burning_bug) = self.burning_bug.as_mut() { + burning_bug.insert(output_key_compressed); + } } // Break to prevent public keys from being included multiple times, triggering multiple // inclusions of the same output diff --git a/coins/monero/tests/send.rs b/coins/monero/tests/send.rs index 4d1bdafb..dbeff426 100644 --- a/coins/monero/tests/send.rs +++ b/coins/monero/tests/send.rs @@ -1,4 +1,4 @@ -use std::sync::Mutex; +use std::{sync::Mutex, collections::HashSet}; #[cfg(feature = "multisig")] use std::collections::HashMap; @@ -75,7 +75,7 @@ async fn send_core(test: usize, multisig: bool) { } let view_pair = ViewPair::new(spend_pub, view); - let scanner = Scanner::from_view(view_pair, Network::Mainnet, false); + let mut scanner = Scanner::from_view(view_pair, Network::Mainnet, Some(HashSet::new())); let addr = scanner.address(); let fee = rpc.get_fee().await.unwrap(); diff --git a/processor/src/coin/monero.rs b/processor/src/coin/monero.rs index d4c2abdf..1abfa9e4 100644 --- a/processor/src/coin/monero.rs +++ b/processor/src/coin/monero.rs @@ -72,7 +72,7 @@ impl Monero { } fn scanner(&self, spend: dfg::EdwardsPoint) -> Scanner { - Scanner::from_view(ViewPair::new(spend.0, self.view), Network::Mainnet, true) + Scanner::from_view(ViewPair::new(spend.0, self.view), Network::Mainnet, None) } #[cfg(test)] @@ -81,7 +81,7 @@ impl Monero { Scanner::from_view( ViewPair::new(*dfg::EdwardsPoint::generator(), Scalar::one()), Network::Mainnet, - false, + Some(std::collections::HashSet::new()), ) } @@ -129,7 +129,7 @@ impl Coin for Monero { } async fn get_outputs(&self, block: &Self::Block, key: dfg::EdwardsPoint) -> Vec { - let scanner = self.scanner(key); + let mut scanner = self.scanner(key); block.iter().flat_map(|tx| scanner.scan(tx).not_locked()).map(Output::from).collect() }