mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-22 02:34:55 +00:00
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).
This commit is contained in:
parent
d45473b2bd
commit
5ca0945cbf
3 changed files with 39 additions and 5 deletions
|
@ -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()]
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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<u8> {
|
||||
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: std::io::Read>(r: &mut R) -> std::io::Result<SpendableOutput> {
|
||||
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
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in a new issue