Luke Parker 2022-08-22 08:57:36 -04:00
parent 5751204a98
commit 5a1f011db8
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
4 changed files with 38 additions and 14 deletions

View file

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::collections::{HashSet, HashMap};
use zeroize::{Zeroize, ZeroizeOnDrop}; use zeroize::{Zeroize, ZeroizeOnDrop};
@ -124,19 +124,25 @@ impl ViewPair {
pub struct Scanner { pub struct Scanner {
pair: ViewPair, pair: ViewPair,
network: Network, network: Network,
guaranteed: bool,
pub(crate) subaddresses: HashMap<CompressedEdwardsY, (u32, u32)>, pub(crate) subaddresses: HashMap<CompressedEdwardsY, (u32, u32)>,
pub(crate) burning_bug: Option<HashSet<CompressedEdwardsY>>,
} }
impl Zeroize for Scanner { impl Zeroize for Scanner {
fn zeroize(&mut self) { fn zeroize(&mut self) {
self.pair.zeroize(); self.pair.zeroize();
self.network.zeroize(); self.network.zeroize();
self.guaranteed.zeroize();
// These may not be effective, unfortunately
for (mut key, mut value) in self.subaddresses.drain() { for (mut key, mut value) in self.subaddresses.drain() {
key.zeroize(); key.zeroize();
value.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 ZeroizeOnDrop for Scanner {}
impl 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<HashSet<CompressedEdwardsY>>,
) -> Scanner {
let mut subaddresses = HashMap::new(); let mut subaddresses = HashMap::new();
subaddresses.insert(pair.spend.compress(), (0, 0)); subaddresses.insert(pair.spend.compress(), (0, 0));
Scanner { pair, network, guaranteed, subaddresses } Scanner { pair, network, subaddresses, burning_bug }
} }
pub fn address(&self) -> Address { pub fn address(&self) -> Address {
Address::new( Address::new(
AddressMeta { AddressMeta {
network: self.network, network: self.network,
kind: if self.guaranteed { kind: if self.burning_bug.is_none() {
AddressType::Featured(false, None, true) AddressType::Featured(false, None, true)
} else { } else {
AddressType::Standard AddressType::Standard
@ -181,7 +193,7 @@ impl Scanner {
Address::new( Address::new(
AddressMeta { AddressMeta {
network: self.network, network: self.network,
kind: if self.guaranteed { kind: if self.burning_bug.is_none() {
AddressType::Featured(true, None, true) AddressType::Featured(true, None, true)
} else { } else {
AddressType::Subaddress AddressType::Subaddress

View file

@ -95,7 +95,7 @@ impl SpendableOutput {
} }
impl Scanner { 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 extra = Extra::deserialize(&mut Cursor::new(&tx.prefix.extra));
let keys; let keys;
let extra = if let Ok(extra) = extra { let extra = if let Ok(extra) = extra {
@ -108,9 +108,17 @@ impl Scanner {
let mut res = vec![]; let mut res = vec![];
for (o, output) in tx.prefix.outputs.iter().enumerate() { 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 { for key in &keys {
let (view_tag, key_offset, payment_id_xor) = shared_key( 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, &self.pair.view,
key, key,
o, o,
@ -173,6 +181,10 @@ impl Scanner {
subaddress: (0, 0), subaddress: (0, 0),
payment_id, 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 // Break to prevent public keys from being included multiple times, triggering multiple
// inclusions of the same output // inclusions of the same output

View file

@ -1,4 +1,4 @@
use std::sync::Mutex; use std::{sync::Mutex, collections::HashSet};
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
use std::collections::HashMap; 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 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 addr = scanner.address();
let fee = rpc.get_fee().await.unwrap(); let fee = rpc.get_fee().await.unwrap();

View file

@ -72,7 +72,7 @@ impl Monero {
} }
fn scanner(&self, spend: dfg::EdwardsPoint) -> Scanner { 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)] #[cfg(test)]
@ -81,7 +81,7 @@ impl Monero {
Scanner::from_view( Scanner::from_view(
ViewPair::new(*dfg::EdwardsPoint::generator(), Scalar::one()), ViewPair::new(*dfg::EdwardsPoint::generator(), Scalar::one()),
Network::Mainnet, 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<Self::Output> { async fn get_outputs(&self, block: &Self::Block, key: dfg::EdwardsPoint) -> Vec<Self::Output> {
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() block.iter().flat_map(|tx| scanner.scan(tx).not_locked()).map(Output::from).collect()
} }