From 7b0b8a20ec97628a3b9f80516eca587a19695103 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 7 Jan 2023 05:18:35 -0500 Subject: [PATCH] Standardize serialization within the Monero lib read for R: Read write for W: Write serialize for -> Vec Also uses std::io::{self, Read, Write} consistently. --- coins/monero/generators/src/varint.rs | 4 +- coins/monero/src/block.rs | 30 ++++-- coins/monero/src/ringct/bulletproofs/mod.rs | 28 ++++-- coins/monero/src/ringct/clsag/mod.rs | 5 +- coins/monero/src/ringct/clsag/multisig.rs | 11 +- coins/monero/src/ringct/mod.rs | 63 ++++++------ coins/monero/src/rpc.rs | 7 +- coins/monero/src/serialize.rs | 34 +++---- coins/monero/src/transaction.rs | 106 +++++++++----------- coins/monero/src/wallet/extra.rs | 18 ++-- coins/monero/src/wallet/scan.rs | 95 +++++++++++------- coins/monero/src/wallet/send/mod.rs | 4 +- 12 files changed, 219 insertions(+), 186 deletions(-) diff --git a/coins/monero/generators/src/varint.rs b/coins/monero/generators/src/varint.rs index e0aa6a3b..632f658d 100644 --- a/coins/monero/generators/src/varint.rs +++ b/coins/monero/generators/src/varint.rs @@ -1,7 +1,7 @@ -use std::io; +use std::io::{self, Write}; const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000; -pub(crate) fn write_varint(varint: &u64, w: &mut W) -> io::Result<()> { +pub(crate) fn write_varint(varint: &u64, w: &mut W) -> io::Result<()> { let mut varint = *varint; while { let mut b = u8::try_from(varint & u64::from(!VARINT_CONTINUATION_MASK)).unwrap(); diff --git a/coins/monero/src/block.rs b/coins/monero/src/block.rs index ea4f3b98..72178d17 100644 --- a/coins/monero/src/block.rs +++ b/coins/monero/src/block.rs @@ -1,3 +1,5 @@ +use std::io::{self, Read, Write}; + use crate::{serialize::*, transaction::Transaction}; #[derive(Clone, PartialEq, Eq, Debug)] @@ -10,7 +12,7 @@ pub struct BlockHeader { } impl BlockHeader { - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { + pub fn write(&self, w: &mut W) -> io::Result<()> { write_varint(&self.major_version, w)?; write_varint(&self.minor_version, w)?; write_varint(&self.timestamp, w)?; @@ -18,7 +20,13 @@ impl BlockHeader { w.write_all(&self.nonce.to_le_bytes()) } - pub fn deserialize(r: &mut R) -> std::io::Result { + pub fn serialize(&self) -> Vec { + let mut serialized = vec![]; + self.write(&mut serialized).unwrap(); + serialized + } + + pub fn read(r: &mut R) -> io::Result { Ok(BlockHeader { major_version: read_varint(r)?, minor_version: read_varint(r)?, @@ -37,9 +45,9 @@ pub struct Block { } impl Block { - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { - self.header.serialize(w)?; - self.miner_tx.serialize(w)?; + pub fn write(&self, w: &mut W) -> io::Result<()> { + self.header.write(w)?; + self.miner_tx.write(w)?; write_varint(&self.txs.len().try_into().unwrap(), w)?; for tx in &self.txs { w.write_all(tx)?; @@ -47,10 +55,16 @@ impl Block { Ok(()) } - pub fn deserialize(r: &mut R) -> std::io::Result { + pub fn serialize(&self) -> Vec { + let mut serialized = vec![]; + self.write(&mut serialized).unwrap(); + serialized + } + + pub fn read(r: &mut R) -> io::Result { Ok(Block { - header: BlockHeader::deserialize(r)?, - miner_tx: Transaction::deserialize(r)?, + header: BlockHeader::read(r)?, + miner_tx: Transaction::read(r)?, txs: (0 .. read_varint(r)?).map(|_| read_bytes(r)).collect::>()?, }) } diff --git a/coins/monero/src/ringct/bulletproofs/mod.rs b/coins/monero/src/ringct/bulletproofs/mod.rs index 227890d3..866f9018 100644 --- a/coins/monero/src/ringct/bulletproofs/mod.rs +++ b/coins/monero/src/ringct/bulletproofs/mod.rs @@ -1,5 +1,7 @@ #![allow(non_snake_case)] +use std::io::{self, Read, Write}; + use rand_core::{RngCore, CryptoRng}; use zeroize::Zeroize; @@ -93,11 +95,11 @@ impl Bulletproofs { } } - fn serialize_core std::io::Result<()>>( + fn write_core io::Result<()>>( &self, w: &mut W, specific_write_vec: F, - ) -> std::io::Result<()> { + ) -> io::Result<()> { match self { Bulletproofs::Original(bp) => { write_point(&bp.A, w)?; @@ -126,16 +128,22 @@ impl Bulletproofs { } } - pub(crate) fn signature_serialize(&self, w: &mut W) -> std::io::Result<()> { - self.serialize_core(w, |points, w| write_raw_vec(write_point, points, w)) + pub(crate) fn signature_write(&self, w: &mut W) -> io::Result<()> { + self.write_core(w, |points, w| write_raw_vec(write_point, points, w)) } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { - self.serialize_core(w, |points, w| write_vec(write_point, points, w)) + pub fn write(&self, w: &mut W) -> io::Result<()> { + self.write_core(w, |points, w| write_vec(write_point, points, w)) } - /// Deserialize non-plus Bulletproofs. - pub fn deserialize(r: &mut R) -> std::io::Result { + pub fn serialize(&self) -> Vec { + let mut serialized = vec![]; + self.write(&mut serialized).unwrap(); + serialized + } + + /// Read Bulletproofs. + pub fn read(r: &mut R) -> io::Result { Ok(Bulletproofs::Original(OriginalStruct { A: read_point(r)?, S: read_point(r)?, @@ -151,8 +159,8 @@ impl Bulletproofs { })) } - /// Deserialize Bulletproofs+. - pub fn deserialize_plus(r: &mut R) -> std::io::Result { + /// Read Bulletproofs+. + pub fn read_plus(r: &mut R) -> io::Result { Ok(Bulletproofs::Plus(PlusStruct { A: read_point(r)?, A1: read_point(r)?, diff --git a/coins/monero/src/ringct/clsag/mod.rs b/coins/monero/src/ringct/clsag/mod.rs index 2d9ba830..249661f9 100644 --- a/coins/monero/src/ringct/clsag/mod.rs +++ b/coins/monero/src/ringct/clsag/mod.rs @@ -1,6 +1,7 @@ #![allow(non_snake_case)] use core::ops::Deref; +use std::io::{self, Read, Write}; use lazy_static::lazy_static; use thiserror::Error; @@ -313,13 +314,13 @@ impl Clsag { (ring_len * 32) + 32 + 32 } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { + pub fn write(&self, w: &mut W) -> io::Result<()> { write_raw_vec(write_scalar, &self.s, w)?; w.write_all(&self.c1.to_bytes())?; write_point(&self.D, w) } - pub fn deserialize(decoys: usize, r: &mut R) -> std::io::Result { + pub fn read(decoys: usize, r: &mut R) -> io::Result { Ok(Clsag { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? }) } } diff --git a/coins/monero/src/ringct/clsag/multisig.rs b/coins/monero/src/ringct/clsag/multisig.rs index cec1cbd6..0b3472e0 100644 --- a/coins/monero/src/ringct/clsag/multisig.rs +++ b/coins/monero/src/ringct/clsag/multisig.rs @@ -41,18 +41,17 @@ impl ClsagInput { // Doesn't domain separate as this is considered part of the larger CLSAG proof // Ring index - transcript.append_message(b"ring_index", [self.decoys.i]); + transcript.append_message(b"real_spend", [self.decoys.i]); // Ring - let mut ring = vec![]; - for pair in &self.decoys.ring { + for (i, pair) in self.decoys.ring.iter().enumerate() { // Doesn't include global output indexes as CLSAG doesn't care and won't be affected by it // They're just a unreliable reference to this data which will be included in the message // if in use - ring.extend(pair[0].compress().to_bytes()); - ring.extend(pair[1].compress().to_bytes()); + transcript.append_message(b"member", [u8::try_from(i).expect("ring size exceeded 255")]); + transcript.append_message(b"key", pair[0].compress().to_bytes()); + transcript.append_message(b"commitment", pair[1].compress().to_bytes()) } - transcript.append_message(b"ring", ring); // Doesn't include the commitment's parts as the above ring + index includes the commitment // The only potential malleability would be if the G/H relationship is known breaking the diff --git a/coins/monero/src/ringct/mod.rs b/coins/monero/src/ringct/mod.rs index b81e8651..d289b6f1 100644 --- a/coins/monero/src/ringct/mod.rs +++ b/coins/monero/src/ringct/mod.rs @@ -1,4 +1,5 @@ use core::ops::Deref; +use std::io::{self, Read, Write}; use zeroize::Zeroizing; @@ -35,7 +36,7 @@ impl RctBase { 1 + 8 + (outputs * (8 + 32)) } - pub fn serialize(&self, w: &mut W, rct_type: u8) -> std::io::Result<()> { + pub fn write(&self, w: &mut W, rct_type: u8) -> io::Result<()> { w.write_all(&[rct_type])?; match rct_type { 0 => Ok(()), @@ -50,10 +51,7 @@ impl RctBase { } } - pub fn deserialize( - outputs: usize, - r: &mut R, - ) -> std::io::Result<(RctBase, u8)> { + pub fn read(outputs: usize, r: &mut R) -> io::Result<(RctBase, u8)> { let rct_type = read_byte(r)?; Ok(( if rct_type == 0 { @@ -96,46 +94,43 @@ impl RctPrunable { (inputs * (Clsag::fee_weight(protocol.ring_len()) + 32)) } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { + pub fn write(&self, w: &mut W) -> io::Result<()> { match self { RctPrunable::Null => Ok(()), RctPrunable::Clsag { bulletproofs, clsags, pseudo_outs, .. } => { - write_vec(Bulletproofs::serialize, bulletproofs, w)?; - write_raw_vec(Clsag::serialize, clsags, w)?; + write_vec(Bulletproofs::write, bulletproofs, w)?; + write_raw_vec(Clsag::write, clsags, w)?; write_raw_vec(write_point, pseudo_outs, w) } } } - pub fn deserialize( - rct_type: u8, - decoys: &[usize], - r: &mut R, - ) -> std::io::Result { + pub fn serialize(&self) -> Vec { + let mut serialized = vec![]; + self.write(&mut serialized).unwrap(); + serialized + } + + pub fn read(rct_type: u8, decoys: &[usize], r: &mut R) -> io::Result { Ok(match rct_type { 0 => RctPrunable::Null, 5 | 6 => RctPrunable::Clsag { bulletproofs: read_vec( - if rct_type == 5 { Bulletproofs::deserialize } else { Bulletproofs::deserialize_plus }, + if rct_type == 5 { Bulletproofs::read } else { Bulletproofs::read_plus }, r, )?, - clsags: (0 .. decoys.len()) - .map(|o| Clsag::deserialize(decoys[o], r)) - .collect::>()?, + clsags: (0 .. decoys.len()).map(|o| Clsag::read(decoys[o], r)).collect::>()?, pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?, }, - _ => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Tried to deserialize unknown RCT type", - ))?, + _ => Err(io::Error::new(io::ErrorKind::Other, "Tried to deserialize unknown RCT type"))?, }) } - pub(crate) fn signature_serialize(&self, w: &mut W) -> std::io::Result<()> { + pub(crate) fn signature_write(&self, w: &mut W) -> io::Result<()> { match self { RctPrunable::Null => panic!("Serializing RctPrunable::Null for a signature"), RctPrunable::Clsag { bulletproofs, .. } => { - bulletproofs.iter().try_for_each(|bp| bp.signature_serialize(w)) + bulletproofs.iter().try_for_each(|bp| bp.signature_write(w)) } } } @@ -152,17 +147,19 @@ impl RctSignatures { RctBase::fee_weight(outputs) + RctPrunable::fee_weight(protocol, inputs, outputs) } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { - self.base.serialize(w, self.prunable.rct_type())?; - self.prunable.serialize(w) + pub fn write(&self, w: &mut W) -> io::Result<()> { + self.base.write(w, self.prunable.rct_type())?; + self.prunable.write(w) } - pub fn deserialize( - decoys: Vec, - outputs: usize, - r: &mut R, - ) -> std::io::Result { - let base = RctBase::deserialize(outputs, r)?; - Ok(RctSignatures { base: base.0, prunable: RctPrunable::deserialize(base.1, &decoys, r)? }) + pub fn serialize(&self) -> Vec { + let mut serialized = vec![]; + self.write(&mut serialized).unwrap(); + serialized + } + + pub fn read(decoys: Vec, outputs: usize, r: &mut R) -> io::Result { + let base = RctBase::read(outputs, r)?; + Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, &decoys, r)? }) } } diff --git a/coins/monero/src/rpc.rs b/coins/monero/src/rpc.rs index ae4b6a80..06395b31 100644 --- a/coins/monero/src/rpc.rs +++ b/coins/monero/src/rpc.rs @@ -249,7 +249,7 @@ impl Rpc { .txs .iter() .map(|res| { - let tx = Transaction::deserialize::<&[u8]>( + let tx = Transaction::read::<&[u8]>( &mut rpc_hex(if !res.as_hex.is_empty() { &res.as_hex } else { &res.pruned_as_hex })? .as_ref(), ) @@ -312,8 +312,7 @@ impl Rpc { let res: BlockResponse = self.json_rpc_call("get_block", Some(json!({ "hash": hex::encode(hash) }))).await?; - Block::deserialize::<&[u8]>(&mut rpc_hex(&res.blob)?.as_ref()) - .map_err(|_| RpcError::InvalidNode) + Block::read::<&[u8]>(&mut rpc_hex(&res.blob)?.as_ref()).map_err(|_| RpcError::InvalidNode) } pub async fn get_block_by_number(&self, number: usize) -> Result { @@ -487,7 +486,7 @@ impl Rpc { } let mut buf = Vec::with_capacity(2048); - tx.serialize(&mut buf).unwrap(); + tx.write(&mut buf).unwrap(); let res: SendRawResponse = self .rpc_call("send_raw_transaction", Some(json!({ "tx_as_hex": hex::encode(&buf) }))) .await?; diff --git a/coins/monero/src/serialize.rs b/coins/monero/src/serialize.rs index 0767d7f2..bc548737 100644 --- a/coins/monero/src/serialize.rs +++ b/coins/monero/src/serialize.rs @@ -1,4 +1,4 @@ -use std::io; +use std::io::{self, Read, Write}; use curve25519_dalek::{ scalar::Scalar, @@ -11,11 +11,11 @@ pub(crate) fn varint_len(varint: usize) -> usize { ((usize::try_from(usize::BITS - varint.leading_zeros()).unwrap().saturating_sub(1)) / 7) + 1 } -pub(crate) fn write_byte(byte: &u8, w: &mut W) -> io::Result<()> { +pub(crate) fn write_byte(byte: &u8, w: &mut W) -> io::Result<()> { w.write_all(&[*byte]) } -pub(crate) fn write_varint(varint: &u64, w: &mut W) -> io::Result<()> { +pub(crate) fn write_varint(varint: &u64, w: &mut W) -> io::Result<()> { let mut varint = *varint; while { let mut b = u8::try_from(varint & u64::from(!VARINT_CONTINUATION_MASK)).unwrap(); @@ -29,15 +29,15 @@ pub(crate) fn write_varint(varint: &u64, w: &mut W) -> io::Result< Ok(()) } -pub(crate) fn write_scalar(scalar: &Scalar, w: &mut W) -> io::Result<()> { +pub(crate) fn write_scalar(scalar: &Scalar, w: &mut W) -> io::Result<()> { w.write_all(&scalar.to_bytes()) } -pub(crate) fn write_point(point: &EdwardsPoint, w: &mut W) -> io::Result<()> { +pub(crate) fn write_point(point: &EdwardsPoint, w: &mut W) -> io::Result<()> { w.write_all(&point.compress().to_bytes()) } -pub(crate) fn write_raw_vec io::Result<()>>( +pub(crate) fn write_raw_vec io::Result<()>>( f: F, values: &[T], w: &mut W, @@ -48,7 +48,7 @@ pub(crate) fn write_raw_vec io::Result<()> Ok(()) } -pub(crate) fn write_vec io::Result<()>>( +pub(crate) fn write_vec io::Result<()>>( f: F, values: &[T], w: &mut W, @@ -57,25 +57,25 @@ pub(crate) fn write_vec io::Result<()>>( write_raw_vec(f, values, w) } -pub(crate) fn read_bytes(r: &mut R) -> io::Result<[u8; N]> { +pub(crate) fn read_bytes(r: &mut R) -> io::Result<[u8; N]> { let mut res = [0; N]; r.read_exact(&mut res)?; Ok(res) } -pub(crate) fn read_byte(r: &mut R) -> io::Result { +pub(crate) fn read_byte(r: &mut R) -> io::Result { Ok(read_bytes::<_, 1>(r)?[0]) } -pub(crate) fn read_u64(r: &mut R) -> io::Result { +pub(crate) fn read_u64(r: &mut R) -> io::Result { read_bytes(r).map(u64::from_le_bytes) } -pub(crate) fn read_u32(r: &mut R) -> io::Result { +pub(crate) fn read_u32(r: &mut R) -> io::Result { read_bytes(r).map(u32::from_le_bytes) } -pub(crate) fn read_varint(r: &mut R) -> io::Result { +pub(crate) fn read_varint(r: &mut R) -> io::Result { let mut bits = 0; let mut res = 0; while { @@ -100,12 +100,12 @@ pub(crate) fn read_varint(r: &mut R) -> io::Result { // for now. There's also further edge cases as noted by // https://github.com/monero-project/monero/issues/8438, where some scalars had an archaic // reduction applied -pub(crate) fn read_scalar(r: &mut R) -> io::Result { +pub(crate) fn read_scalar(r: &mut R) -> io::Result { Scalar::from_canonical_bytes(read_bytes(r)?) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "unreduced scalar")) } -pub(crate) fn read_point(r: &mut R) -> io::Result { +pub(crate) fn read_point(r: &mut R) -> io::Result { let bytes = read_bytes(r)?; CompressedEdwardsY(bytes) .decompress() @@ -114,14 +114,14 @@ pub(crate) fn read_point(r: &mut R) -> io::Result { .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point")) } -pub(crate) fn read_torsion_free_point(r: &mut R) -> io::Result { +pub(crate) fn read_torsion_free_point(r: &mut R) -> io::Result { read_point(r) .ok() .filter(|point| point.is_torsion_free()) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point")) } -pub(crate) fn read_raw_vec io::Result>( +pub(crate) fn read_raw_vec io::Result>( f: F, len: usize, r: &mut R, @@ -133,7 +133,7 @@ pub(crate) fn read_raw_vec io::Result>( Ok(res) } -pub(crate) fn read_vec io::Result>( +pub(crate) fn read_vec io::Result>( f: F, r: &mut R, ) -> io::Result> { diff --git a/coins/monero/src/transaction.rs b/coins/monero/src/transaction.rs index 6314a97c..e981792a 100644 --- a/coins/monero/src/transaction.rs +++ b/coins/monero/src/transaction.rs @@ -1,4 +1,5 @@ use core::cmp::Ordering; +use std::io::{self, Read, Write}; use zeroize::Zeroize; @@ -27,7 +28,7 @@ impl Input { 1 + 1 + 1 + (8 * ring_len) + 32 } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { + pub fn write(&self, w: &mut W) -> io::Result<()> { match self { Input::Gen(height) => { w.write_all(&[255])?; @@ -43,7 +44,7 @@ impl Input { } } - pub fn deserialize(r: &mut R) -> std::io::Result { + pub fn read(r: &mut R) -> io::Result { Ok(match read_byte(r)? { 255 => Input::Gen(read_varint(r)?), 2 => Input::ToKey { @@ -51,10 +52,9 @@ impl Input { key_offsets: read_vec(read_varint, r)?, key_image: read_torsion_free_point(r)?, }, - _ => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Tried to deserialize unknown/unused input type", - ))?, + _ => { + Err(io::Error::new(io::ErrorKind::Other, "Tried to deserialize unknown/unused input type"))? + } }) } } @@ -72,7 +72,7 @@ impl Output { 1 + 1 + 32 + 1 } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { + pub fn write(&self, w: &mut W) -> io::Result<()> { write_varint(&self.amount, w)?; w.write_all(&[2 + u8::from(self.view_tag.is_some())])?; w.write_all(&self.key.to_bytes())?; @@ -82,13 +82,13 @@ impl Output { Ok(()) } - pub fn deserialize(r: &mut R) -> std::io::Result { + pub fn read(r: &mut R) -> io::Result { let amount = read_varint(r)?; let view_tag = match read_byte(r)? { 2 => false, 3 => true, - _ => Err(std::io::Error::new( - std::io::ErrorKind::Other, + _ => Err(io::Error::new( + io::ErrorKind::Other, "Tried to deserialize unknown/unused output type", ))?, }; @@ -119,7 +119,7 @@ impl Timelock { } } - fn serialize(&self, w: &mut W) -> std::io::Result<()> { + fn write(&self, w: &mut W) -> io::Result<()> { write_varint( &match self { Timelock::None => 0, @@ -163,21 +163,21 @@ impl TransactionPrefix { extra } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { + pub fn write(&self, w: &mut W) -> io::Result<()> { write_varint(&self.version, w)?; - self.timelock.serialize(w)?; - write_vec(Input::serialize, &self.inputs, w)?; - write_vec(Output::serialize, &self.outputs, w)?; + self.timelock.write(w)?; + write_vec(Input::write, &self.inputs, w)?; + write_vec(Output::write, &self.outputs, w)?; write_varint(&self.extra.len().try_into().unwrap(), w)?; w.write_all(&self.extra) } - pub fn deserialize(r: &mut R) -> std::io::Result { + pub fn read(r: &mut R) -> io::Result { let mut prefix = TransactionPrefix { version: read_varint(r)?, timelock: Timelock::from_raw(read_varint(r)?), - inputs: read_vec(Input::deserialize, r)?, - outputs: read_vec(Output::deserialize, r)?, + inputs: read_vec(Input::read, r)?, + outputs: read_vec(Output::read, r)?, extra: vec![], }; prefix.extra = read_vec(read_byte, r)?; @@ -204,8 +204,8 @@ impl Transaction { RctSignatures::fee_weight(protocol, inputs, outputs) } - pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { - self.prefix.serialize(w)?; + pub fn write(&self, w: &mut W) -> io::Result<()> { + self.prefix.write(w)?; if self.prefix.version == 1 { for sig in &self.signatures { write_scalar(&sig.0, w)?; @@ -213,14 +213,14 @@ impl Transaction { } Ok(()) } else if self.prefix.version == 2 { - self.rct_signatures.serialize(w) + self.rct_signatures.write(w) } else { panic!("Serializing a transaction with an unknown version"); } } - pub fn deserialize(r: &mut R) -> std::io::Result { - let prefix = TransactionPrefix::deserialize(r)?; + pub fn read(r: &mut R) -> io::Result { + let prefix = TransactionPrefix::read(r)?; let mut signatures = vec![]; let mut rct_signatures = RctSignatures { base: RctBase { fee: 0, ecdh_info: vec![], commitments: vec![] }, @@ -241,7 +241,7 @@ impl Transaction { .sum::() .saturating_sub(prefix.outputs.iter().map(|output| output.amount).sum()); } else if prefix.version == 2 { - rct_signatures = RctSignatures::deserialize( + rct_signatures = RctSignatures::read( prefix .inputs .iter() @@ -254,64 +254,56 @@ impl Transaction { r, )?; } else { - Err(std::io::Error::new(std::io::ErrorKind::Other, "Tried to deserialize unknown version"))?; + Err(io::Error::new(io::ErrorKind::Other, "Tried to deserialize unknown version"))?; } Ok(Transaction { prefix, signatures, rct_signatures }) } pub fn hash(&self) -> [u8; 32] { - let mut serialized = Vec::with_capacity(2048); + let mut buf = Vec::with_capacity(2048); if self.prefix.version == 1 { - self.serialize(&mut serialized).unwrap(); - hash(&serialized) + self.write(&mut buf).unwrap(); + hash(&buf) } else { - let mut sig_hash = Vec::with_capacity(96); + let mut hashes = Vec::with_capacity(96); - self.prefix.serialize(&mut serialized).unwrap(); - sig_hash.extend(hash(&serialized)); - serialized.clear(); + self.prefix.write(&mut buf).unwrap(); + hashes.extend(hash(&buf)); + buf.clear(); - self - .rct_signatures - .base - .serialize(&mut serialized, self.rct_signatures.prunable.rct_type()) - .unwrap(); - sig_hash.extend(hash(&serialized)); - serialized.clear(); + self.rct_signatures.base.write(&mut buf, self.rct_signatures.prunable.rct_type()).unwrap(); + hashes.extend(hash(&buf)); + buf.clear(); match self.rct_signatures.prunable { - RctPrunable::Null => serialized.resize(32, 0), + RctPrunable::Null => buf.resize(32, 0), _ => { - self.rct_signatures.prunable.serialize(&mut serialized).unwrap(); - serialized = hash(&serialized).to_vec(); + self.rct_signatures.prunable.write(&mut buf).unwrap(); + buf = hash(&buf).to_vec(); } } - sig_hash.extend(&serialized); + hashes.extend(&buf); - hash(&sig_hash) + hash(&hashes) } } /// Calculate the hash of this transaction as needed for signing it. pub fn signature_hash(&self) -> [u8; 32] { - let mut serialized = Vec::with_capacity(2048); + let mut buf = Vec::with_capacity(2048); let mut sig_hash = Vec::with_capacity(96); - self.prefix.serialize(&mut serialized).unwrap(); - sig_hash.extend(hash(&serialized)); - serialized.clear(); + self.prefix.write(&mut buf).unwrap(); + sig_hash.extend(hash(&buf)); + buf.clear(); - self - .rct_signatures - .base - .serialize(&mut serialized, self.rct_signatures.prunable.rct_type()) - .unwrap(); - sig_hash.extend(hash(&serialized)); - serialized.clear(); + self.rct_signatures.base.write(&mut buf, self.rct_signatures.prunable.rct_type()).unwrap(); + sig_hash.extend(hash(&buf)); + buf.clear(); - self.rct_signatures.prunable.signature_serialize(&mut serialized).unwrap(); - sig_hash.extend(hash(&serialized)); + self.rct_signatures.prunable.signature_write(&mut buf).unwrap(); + sig_hash.extend(hash(&buf)); hash(&sig_hash) } diff --git a/coins/monero/src/wallet/extra.rs b/coins/monero/src/wallet/extra.rs index dda72f53..2a0b9aa2 100644 --- a/coins/monero/src/wallet/extra.rs +++ b/coins/monero/src/wallet/extra.rs @@ -32,7 +32,7 @@ impl BitXor<[u8; 8]> for PaymentId { } impl PaymentId { - pub(crate) fn serialize(&self, w: &mut W) -> io::Result<()> { + pub(crate) fn write(&self, w: &mut W) -> io::Result<()> { match self { PaymentId::Unencrypted(id) => { w.write_all(&[0])?; @@ -46,7 +46,7 @@ impl PaymentId { Ok(()) } - fn deserialize(r: &mut R) -> io::Result { + fn read(r: &mut R) -> io::Result { Ok(match read_byte(r)? { 0 => PaymentId::Unencrypted(read_bytes(r)?), 1 => PaymentId::Encrypted(read_bytes(r)?), @@ -65,7 +65,7 @@ pub(crate) enum ExtraField { } impl ExtraField { - fn serialize(&self, w: &mut W) -> io::Result<()> { + fn write(&self, w: &mut W) -> io::Result<()> { match self { ExtraField::PublicKey(key) => { w.write_all(&[1])?; @@ -88,7 +88,7 @@ impl ExtraField { Ok(()) } - fn deserialize(r: &mut R) -> io::Result { + fn read(r: &mut R) -> io::Result { Ok(match read_byte(r)? { 1 => ExtraField::PublicKey(read_point(r)?), 2 => ExtraField::Nonce({ @@ -127,7 +127,7 @@ impl Extra { pub(crate) fn payment_id(&self) -> Option { for field in &self.0 { if let ExtraField::Nonce(data) = field { - return PaymentId::deserialize::<&[u8]>(&mut data.as_ref()).ok(); + return PaymentId::read::<&[u8]>(&mut data.as_ref()).ok(); } } None @@ -176,18 +176,18 @@ impl Extra { data.iter().map(|v| 1 + varint_len(v.len()) + v.len()).sum::() } - pub(crate) fn serialize(&self, w: &mut W) -> io::Result<()> { + pub(crate) fn write(&self, w: &mut W) -> io::Result<()> { for field in &self.0 { - field.serialize(w)?; + field.write(w)?; } Ok(()) } - pub(crate) fn deserialize(r: &mut R) -> io::Result { + pub(crate) fn read(r: &mut R) -> io::Result { let mut res = Extra(vec![]); let mut field; while { - field = ExtraField::deserialize(r); + field = ExtraField::read(r); field.is_ok() } { res.0.push(field.unwrap()); diff --git a/coins/monero/src/wallet/scan.rs b/coins/monero/src/wallet/scan.rs index 5b80dbaf..6ac13f7a 100644 --- a/coins/monero/src/wallet/scan.rs +++ b/coins/monero/src/wallet/scan.rs @@ -1,4 +1,4 @@ -use std::io; +use std::io::{self, Read, Write}; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -24,14 +24,18 @@ pub struct AbsoluteId { } impl AbsoluteId { - pub fn serialize(&self) -> Vec { - let mut res = Vec::with_capacity(32 + 1); - res.extend(self.tx); - res.push(self.o); - res + pub fn write(&self, w: &mut W) -> io::Result<()> { + w.write_all(&self.tx)?; + w.write_all(&[self.o]) } - pub fn read(r: &mut R) -> io::Result { + pub fn serialize(&self) -> Vec { + let mut serialized = Vec::with_capacity(32 + 1); + self.write(&mut serialized).unwrap(); + serialized + } + + pub fn read(r: &mut R) -> io::Result { Ok(AbsoluteId { tx: read_bytes(r)?, o: read_byte(r)? }) } } @@ -46,16 +50,20 @@ pub struct OutputData { } impl OutputData { - pub fn serialize(&self) -> Vec { - let mut res = Vec::with_capacity(32 + 32 + 40); - 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 write(&self, w: &mut W) -> io::Result<()> { + w.write_all(&self.key.compress().to_bytes())?; + w.write_all(&self.key_offset.to_bytes())?; + w.write_all(&self.commitment.mask.to_bytes())?; + w.write_all(&self.commitment.amount.to_le_bytes()) } - pub fn read(r: &mut R) -> io::Result { + pub fn serialize(&self) -> Vec { + let mut serialized = Vec::with_capacity(32 + 32 + 32 + 8); + self.write(&mut serialized).unwrap(); + serialized + } + + pub fn read(r: &mut R) -> io::Result { Ok(OutputData { key: read_point(r)?, key_offset: read_scalar(r)?, @@ -79,26 +87,31 @@ pub struct Metadata { } impl Metadata { - pub fn serialize(&self) -> Vec { - let mut res = Vec::with_capacity(4 + 4 + 8 + 1); + pub fn write(&self, w: &mut W) -> io::Result<()> { if let Some(subaddress) = self.subaddress { - res.push(1); - res.extend(subaddress.account().to_le_bytes()); - res.extend(subaddress.address().to_le_bytes()); + w.write_all(&[1])?; + w.write_all(&subaddress.account().to_le_bytes())?; + w.write_all(&subaddress.address().to_le_bytes())?; } else { - res.push(0); + w.write_all(&[0])?; } - res.extend(self.payment_id); + w.write_all(&self.payment_id)?; - res.extend(u32::try_from(self.arbitrary_data.len()).unwrap().to_le_bytes()); + w.write_all(&u32::try_from(self.arbitrary_data.len()).unwrap().to_le_bytes())?; for part in &self.arbitrary_data { - res.extend([u8::try_from(part.len()).unwrap()]); - res.extend(part); + w.write_all(&[u8::try_from(part.len()).unwrap()])?; + w.write_all(part)?; } - res + Ok(()) } - pub fn read(r: &mut R) -> io::Result { + pub fn serialize(&self) -> Vec { + let mut serialized = Vec::with_capacity(1 + 8 + 1); + self.write(&mut serialized).unwrap(); + serialized + } + + pub fn read(r: &mut R) -> io::Result { let subaddress = if read_byte(r)? == 1 { Some( SubaddressIndex::new(read_u32(r)?, read_u32(r)?) @@ -148,14 +161,19 @@ impl ReceivedOutput { &self.metadata.arbitrary_data } + pub fn write(&self, w: &mut W) -> io::Result<()> { + self.absolute.write(w)?; + self.data.write(w)?; + self.metadata.write(w) + } + pub fn serialize(&self) -> Vec { - let mut serialized = self.absolute.serialize(); - serialized.extend(&self.data.serialize()); - serialized.extend(&self.metadata.serialize()); + let mut serialized = vec![]; + self.write(&mut serialized).unwrap(); serialized } - pub fn deserialize(r: &mut R) -> io::Result { + pub fn read(r: &mut R) -> io::Result { Ok(ReceivedOutput { absolute: AbsoluteId::read(r)?, data: OutputData::read(r)?, @@ -200,14 +218,19 @@ impl SpendableOutput { self.output.commitment() } + pub fn write(&self, w: &mut W) -> io::Result<()> { + self.output.write(w)?; + w.write_all(&self.global_index.to_le_bytes()) + } + pub fn serialize(&self) -> Vec { - let mut serialized = self.output.serialize(); - serialized.extend(self.global_index.to_le_bytes()); + let mut serialized = vec![]; + self.write(&mut serialized).unwrap(); serialized } - pub fn read(r: &mut R) -> io::Result { - Ok(SpendableOutput { output: ReceivedOutput::deserialize(r)?, global_index: read_u64(r)? }) + pub fn read(r: &mut R) -> io::Result { + Ok(SpendableOutput { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? }) } } @@ -248,7 +271,7 @@ impl Timelocked { impl Scanner { /// Scan a transaction to discover the received outputs. pub fn scan_transaction(&mut self, tx: &Transaction) -> Timelocked { - let extra = Extra::deserialize::<&[u8]>(&mut tx.prefix.extra.as_ref()); + let extra = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref()); let keys; let extra = if let Ok(extra) = extra { keys = extra.keys(); diff --git a/coins/monero/src/wallet/send/mod.rs b/coins/monero/src/wallet/send/mod.rs index 9732c051..9aa3e8b8 100644 --- a/coins/monero/src/wallet/send/mod.rs +++ b/coins/monero/src/wallet/send/mod.rs @@ -311,7 +311,7 @@ impl SignableTransaction { let mut extra = Extra::new(outputs.iter().map(|output| output.R).collect()); let mut id_vec = Vec::with_capacity(1 + 8); - PaymentId::Encrypted(id).serialize(&mut id_vec).unwrap(); + PaymentId::Encrypted(id).write(&mut id_vec).unwrap(); extra.push(ExtraField::Nonce(id_vec)); // Include data if present @@ -320,7 +320,7 @@ impl SignableTransaction { } let mut serialized = Vec::with_capacity(Extra::fee_weight(outputs.len(), self.data.as_ref())); - extra.serialize(&mut serialized).unwrap(); + extra.write(&mut serialized).unwrap(); serialized };