From 5ca0945cbf173c2be60813843194dac31b04bc0a Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Thu, 26 May 2022 03:51:27 -0400 Subject: [PATCH] Implement serialization for SpendableOutput Changes the output index to a u8. While it may expand to a u16 at some point, this can remain canonical using little endian serialization while dropping the latter byte if it's 0 (or simply only using u16 when it's actually possible). --- coins/monero/src/wallet/decoys.rs | 2 +- coins/monero/src/wallet/scan.rs | 40 ++++++++++++++++++++++-- coins/monero/src/wallet/send/multisig.rs | 2 +- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/coins/monero/src/wallet/decoys.rs b/coins/monero/src/wallet/decoys.rs index 8ae7b11d..1672e351 100644 --- a/coins/monero/src/wallet/decoys.rs +++ b/coins/monero/src/wallet/decoys.rs @@ -110,7 +110,7 @@ impl Decoys { let mut outputs = Vec::with_capacity(inputs.len()); for input in inputs { outputs.push(( - rpc.get_o_indexes(input.tx).await?[input.o], + rpc.get_o_indexes(input.tx).await?[usize::from(input.o)], [input.key, input.commitment.calculate()] )); } diff --git a/coins/monero/src/wallet/scan.rs b/coins/monero/src/wallet/scan.rs index 625dc34b..df5d1d6f 100644 --- a/coins/monero/src/wallet/scan.rs +++ b/coins/monero/src/wallet/scan.rs @@ -10,7 +10,7 @@ use monero::{consensus::deserialize, blockdata::transaction::ExtraField}; use crate::{ Commitment, - serialize::write_varint, + serialize::{write_varint, read_32, read_scalar, read_point}, transaction::Transaction, wallet::{uniqueness, shared_key, amount_decryption, commitment_mask} }; @@ -18,12 +18,40 @@ use crate::{ #[derive(Clone, PartialEq, Debug)] pub struct SpendableOutput { pub tx: [u8; 32], - pub o: usize, + pub o: u8, pub key: EdwardsPoint, pub key_offset: Scalar, pub commitment: Commitment } +impl SpendableOutput { + pub fn serialize(&self) -> Vec { + let mut res = Vec::with_capacity(32 + 1 + 32 + 32 + 40); + res.extend(&self.tx); + res.push(self.o); + res.extend(self.key.compress().to_bytes()); + res.extend(self.key_offset.to_bytes()); + res.extend(self.commitment.mask.to_bytes()); + res.extend(self.commitment.amount.to_le_bytes()); + res + } + + pub fn deserialize(r: &mut R) -> std::io::Result { + Ok( + SpendableOutput { + tx: read_32(r)?, + o: { let mut o = [0; 1]; r.read_exact(&mut o)?; o[0] }, + key: read_point(r)?, + key_offset: read_scalar(r)?, + commitment: Commitment::new( + read_scalar(r)?, + { let mut amount = [0; 8]; r.read_exact(&mut amount)?; u64::from_le_bytes(amount) } + ) + } + ) + } +} + impl Transaction { pub fn scan( &self, @@ -88,7 +116,13 @@ impl Transaction { } if commitment.amount != 0 { - res.push(SpendableOutput { tx: self.hash(), o, key: output.key, key_offset, commitment }); + res.push(SpendableOutput { + tx: self.hash(), + o: o.try_into().unwrap(), + key: output.key, + key_offset, + commitment + }); } // Break to prevent public keys from being included multiple times, triggering multiple // inclusions of the same output diff --git a/coins/monero/src/wallet/send/multisig.rs b/coins/monero/src/wallet/send/multisig.rs index f218fc8a..12427561 100644 --- a/coins/monero/src/wallet/send/multisig.rs +++ b/coins/monero/src/wallet/send/multisig.rs @@ -71,7 +71,7 @@ impl SignableTransaction { // These outputs can only be spent once. Therefore, it forces all RNGs derived from this // transcript (such as the one used to create one time keys) to be unique transcript.append_message(b"input_hash", &input.tx); - transcript.append_message(b"input_output_index", &u16::try_from(input.o).unwrap().to_le_bytes()); + transcript.append_message(b"input_output_index", &[input.o]); // Not including this, with a doxxed list of payments, would allow brute forcing the inputs // to determine RNG seeds and therefore the true spends transcript.append_message(b"input_shared_key", &input.key_offset.to_bytes());