mirror of
https://github.com/serai-dex/serai.git
synced 2025-03-16 16:42:03 +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());
|
let mut outputs = Vec::with_capacity(inputs.len());
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
outputs.push((
|
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()]
|
[input.key, input.commitment.calculate()]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use monero::{consensus::deserialize, blockdata::transaction::ExtraField};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Commitment,
|
Commitment,
|
||||||
serialize::write_varint,
|
serialize::{write_varint, read_32, read_scalar, read_point},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
wallet::{uniqueness, shared_key, amount_decryption, commitment_mask}
|
wallet::{uniqueness, shared_key, amount_decryption, commitment_mask}
|
||||||
};
|
};
|
||||||
|
@ -18,12 +18,40 @@ use crate::{
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct SpendableOutput {
|
pub struct SpendableOutput {
|
||||||
pub tx: [u8; 32],
|
pub tx: [u8; 32],
|
||||||
pub o: usize,
|
pub o: u8,
|
||||||
pub key: EdwardsPoint,
|
pub key: EdwardsPoint,
|
||||||
pub key_offset: Scalar,
|
pub key_offset: Scalar,
|
||||||
pub commitment: Commitment
|
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 {
|
impl Transaction {
|
||||||
pub fn scan(
|
pub fn scan(
|
||||||
&self,
|
&self,
|
||||||
|
@ -88,7 +116,13 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
if commitment.amount != 0 {
|
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
|
// Break to prevent public keys from being included multiple times, triggering multiple
|
||||||
// inclusions of the same output
|
// 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
|
// 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 (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_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
|
// Not including this, with a doxxed list of payments, would allow brute forcing the inputs
|
||||||
// to determine RNG seeds and therefore the true spends
|
// to determine RNG seeds and therefore the true spends
|
||||||
transcript.append_message(b"input_shared_key", &input.key_offset.to_bytes());
|
transcript.append_message(b"input_shared_key", &input.key_offset.to_bytes());
|
||||||
|
|
Loading…
Reference in a new issue