diff --git a/coins/monero/src/wallet/extra.rs b/coins/monero/src/wallet/extra.rs index f213fecf..867c5189 100644 --- a/coins/monero/src/wallet/extra.rs +++ b/coins/monero/src/wallet/extra.rs @@ -112,16 +112,20 @@ impl ExtraField { #[derive(Clone, PartialEq, Eq, Debug, Zeroize)] pub(crate) struct Extra(Vec); impl Extra { - pub(crate) fn keys(&self) -> Vec { - let mut keys = Vec::with_capacity(2); + pub(crate) fn keys(&self) -> Option<(EdwardsPoint, Option>)> { + let mut key = None; + let mut additional = None; for field in &self.0 { match field.clone() { - ExtraField::PublicKey(key) => keys.push(key), - ExtraField::PublicKeys(additional) => keys.extend(additional), + ExtraField::PublicKey(this_key) => key = key.or(Some(this_key)), + ExtraField::PublicKeys(these_additional) => { + additional = additional.or(Some(these_additional)) + } _ => (), } } - keys + // Don't return any keys if this was non-standard and didn't include the primary key + key.map(|key| (key, additional)) } pub(crate) fn payment_id(&self) -> Option { diff --git a/coins/monero/src/wallet/scan.rs b/coins/monero/src/wallet/scan.rs index a86cbf9b..5d1d4e2b 100644 --- a/coins/monero/src/wallet/scan.rs +++ b/coins/monero/src/wallet/scan.rs @@ -272,13 +272,18 @@ impl Scanner { /// Scan a transaction to discover the received outputs. pub fn scan_transaction(&mut self, tx: &Transaction) -> Timelocked { let extra = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref()); - let keys; let extra = if let Ok(extra) = extra { - keys = extra.keys(); extra } else { return Timelocked(tx.prefix.timelock, vec![]); }; + + let (tx_key, additional) = if let Some((tx_key, additional)) = extra.keys() { + (tx_key, additional) + } else { + return Timelocked(tx.prefix.timelock, vec![]); + }; + let payment_id = extra.payment_id(); let mut res = vec![]; @@ -296,8 +301,19 @@ impl Scanner { } let output_key = output_key.unwrap(); - // TODO: Only use THE key or the matching additional key. Not any key - for key in &keys { + for key in [Some(Some(&tx_key)), additional.as_ref().map(|additional| additional.get(o))] { + let key = if let Some(Some(key)) = key { + key + } else if let Some(None) = key { + // This is non-standard. There were additional keys, yet not one for this output + // https://github.com/monero-project/monero/ + // blob/04a1e2875d6e35e27bb21497988a6c822d319c28/ + // src/cryptonote_basic/cryptonote_format_utils.cpp#L1062 + // TODO: Should this return? Where does Monero set the trap handler for this exception? + continue; + } else { + break; + }; let (view_tag, shared_key, payment_id_xor) = shared_key( if self.burning_bug.is_none() { Some(uniqueness(&tx.prefix.inputs)) } else { None }, &self.pair.view,