Standardize serialization within the Monero lib

read for R: Read
write for W: Write
serialize for -> Vec<u8>

Also uses std::io::{self, Read, Write} consistently.
This commit is contained in:
Luke Parker 2023-01-07 05:18:35 -05:00
parent 7508106650
commit 7b0b8a20ec
No known key found for this signature in database
12 changed files with 219 additions and 186 deletions

View file

@ -1,7 +1,7 @@
use std::io;
use std::io::{self, Write};
const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000;
pub(crate) fn write_varint<W: io::Write>(varint: &u64, w: &mut W) -> io::Result<()> {
pub(crate) fn write_varint<W: Write>(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();

View file

@ -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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
pub fn write<W: 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: std::io::Read>(r: &mut R) -> std::io::Result<BlockHeader> {
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<BlockHeader> {
Ok(BlockHeader {
major_version: read_varint(r)?,
minor_version: read_varint(r)?,
@ -37,9 +45,9 @@ pub struct Block {
}
impl Block {
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
self.header.serialize(w)?;
self.miner_tx.serialize(w)?;
pub fn write<W: 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: std::io::Read>(r: &mut R) -> std::io::Result<Block> {
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<Block> {
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::<Result<_, _>>()?,
})
}

View file

@ -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<W: std::io::Write, F: Fn(&[EdwardsPoint], &mut W) -> std::io::Result<()>>(
fn write_core<W: Write, F: Fn(&[EdwardsPoint], &mut W) -> 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<W: std::io::Write>(&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<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.write_core(w, |points, w| write_raw_vec(write_point, points, w))
}
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
self.serialize_core(w, |points, w| write_vec(write_point, points, w))
pub fn write<W: 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: std::io::Read>(r: &mut R) -> std::io::Result<Bulletproofs> {
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
/// Read Bulletproofs.
pub fn read<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
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: std::io::Read>(r: &mut R) -> std::io::Result<Bulletproofs> {
/// Read Bulletproofs+.
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
Ok(Bulletproofs::Plus(PlusStruct {
A: read_point(r)?,
A1: read_point(r)?,

View file

@ -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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
pub fn write<W: 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<R: std::io::Read>(decoys: usize, r: &mut R) -> std::io::Result<Clsag> {
pub fn read<R: Read>(decoys: usize, r: &mut R) -> io::Result<Clsag> {
Ok(Clsag { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? })
}
}

View file

@ -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

View file

@ -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<W: std::io::Write>(&self, w: &mut W, rct_type: u8) -> std::io::Result<()> {
pub fn write<W: 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<R: std::io::Read>(
outputs: usize,
r: &mut R,
) -> std::io::Result<(RctBase, u8)> {
pub fn read<R: 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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
pub fn write<W: 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<R: std::io::Read>(
rct_type: u8,
decoys: &[usize],
r: &mut R,
) -> std::io::Result<RctPrunable> {
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(rct_type: u8, decoys: &[usize], r: &mut R) -> io::Result<RctPrunable> {
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::<Result<_, _>>()?,
clsags: (0 .. decoys.len()).map(|o| Clsag::read(decoys[o], r)).collect::<Result<_, _>>()?,
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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
pub(crate) fn signature_write<W: 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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
self.base.serialize(w, self.prunable.rct_type())?;
self.prunable.serialize(w)
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.base.write(w, self.prunable.rct_type())?;
self.prunable.write(w)
}
pub fn deserialize<R: std::io::Read>(
decoys: Vec<usize>,
outputs: usize,
r: &mut R,
) -> std::io::Result<RctSignatures> {
let base = RctBase::deserialize(outputs, r)?;
Ok(RctSignatures { base: base.0, prunable: RctPrunable::deserialize(base.1, &decoys, r)? })
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> io::Result<RctSignatures> {
let base = RctBase::read(outputs, r)?;
Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, &decoys, r)? })
}
}

View file

@ -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<Block, RpcError> {
@ -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?;

View file

@ -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<W: io::Write>(byte: &u8, w: &mut W) -> io::Result<()> {
pub(crate) fn write_byte<W: Write>(byte: &u8, w: &mut W) -> io::Result<()> {
w.write_all(&[*byte])
}
pub(crate) fn write_varint<W: io::Write>(varint: &u64, w: &mut W) -> io::Result<()> {
pub(crate) fn write_varint<W: Write>(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<W: io::Write>(varint: &u64, w: &mut W) -> io::Result<
Ok(())
}
pub(crate) fn write_scalar<W: io::Write>(scalar: &Scalar, w: &mut W) -> io::Result<()> {
pub(crate) fn write_scalar<W: Write>(scalar: &Scalar, w: &mut W) -> io::Result<()> {
w.write_all(&scalar.to_bytes())
}
pub(crate) fn write_point<W: io::Write>(point: &EdwardsPoint, w: &mut W) -> io::Result<()> {
pub(crate) fn write_point<W: Write>(point: &EdwardsPoint, w: &mut W) -> io::Result<()> {
w.write_all(&point.compress().to_bytes())
}
pub(crate) fn write_raw_vec<T, W: io::Write, F: Fn(&T, &mut W) -> io::Result<()>>(
pub(crate) fn write_raw_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>(
f: F,
values: &[T],
w: &mut W,
@ -48,7 +48,7 @@ pub(crate) fn write_raw_vec<T, W: io::Write, F: Fn(&T, &mut W) -> io::Result<()>
Ok(())
}
pub(crate) fn write_vec<T, W: io::Write, F: Fn(&T, &mut W) -> io::Result<()>>(
pub(crate) fn write_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>(
f: F,
values: &[T],
w: &mut W,
@ -57,25 +57,25 @@ pub(crate) fn write_vec<T, W: io::Write, F: Fn(&T, &mut W) -> io::Result<()>>(
write_raw_vec(f, values, w)
}
pub(crate) fn read_bytes<R: io::Read, const N: usize>(r: &mut R) -> io::Result<[u8; N]> {
pub(crate) fn read_bytes<R: Read, const N: usize>(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: io::Read>(r: &mut R) -> io::Result<u8> {
pub(crate) fn read_byte<R: Read>(r: &mut R) -> io::Result<u8> {
Ok(read_bytes::<_, 1>(r)?[0])
}
pub(crate) fn read_u64<R: io::Read>(r: &mut R) -> io::Result<u64> {
pub(crate) fn read_u64<R: Read>(r: &mut R) -> io::Result<u64> {
read_bytes(r).map(u64::from_le_bytes)
}
pub(crate) fn read_u32<R: io::Read>(r: &mut R) -> io::Result<u32> {
pub(crate) fn read_u32<R: Read>(r: &mut R) -> io::Result<u32> {
read_bytes(r).map(u32::from_le_bytes)
}
pub(crate) fn read_varint<R: io::Read>(r: &mut R) -> io::Result<u64> {
pub(crate) fn read_varint<R: Read>(r: &mut R) -> io::Result<u64> {
let mut bits = 0;
let mut res = 0;
while {
@ -100,12 +100,12 @@ pub(crate) fn read_varint<R: io::Read>(r: &mut R) -> io::Result<u64> {
// 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: io::Read>(r: &mut R) -> io::Result<Scalar> {
pub(crate) fn read_scalar<R: Read>(r: &mut R) -> io::Result<Scalar> {
Scalar::from_canonical_bytes(read_bytes(r)?)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "unreduced scalar"))
}
pub(crate) fn read_point<R: io::Read>(r: &mut R) -> io::Result<EdwardsPoint> {
pub(crate) fn read_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
let bytes = read_bytes(r)?;
CompressedEdwardsY(bytes)
.decompress()
@ -114,14 +114,14 @@ pub(crate) fn read_point<R: io::Read>(r: &mut R) -> io::Result<EdwardsPoint> {
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point"))
}
pub(crate) fn read_torsion_free_point<R: io::Read>(r: &mut R) -> io::Result<EdwardsPoint> {
pub(crate) fn read_torsion_free_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
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<R: io::Read, T, F: Fn(&mut R) -> io::Result<T>>(
pub(crate) fn read_raw_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>(
f: F,
len: usize,
r: &mut R,
@ -133,7 +133,7 @@ pub(crate) fn read_raw_vec<R: io::Read, T, F: Fn(&mut R) -> io::Result<T>>(
Ok(res)
}
pub(crate) fn read_vec<R: io::Read, T, F: Fn(&mut R) -> io::Result<T>>(
pub(crate) fn read_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>(
f: F,
r: &mut R,
) -> io::Result<Vec<T>> {

View file

@ -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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
pub fn write<W: 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: std::io::Read>(r: &mut R) -> std::io::Result<Input> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Input> {
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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
pub fn write<W: 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: std::io::Read>(r: &mut R) -> std::io::Result<Output> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Output> {
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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
write_varint(
&match self {
Timelock::None => 0,
@ -163,21 +163,21 @@ impl TransactionPrefix {
extra
}
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
pub fn write<W: 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: std::io::Read>(r: &mut R) -> std::io::Result<TransactionPrefix> {
pub fn read<R: Read>(r: &mut R) -> io::Result<TransactionPrefix> {
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<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
self.prefix.serialize(w)?;
pub fn write<W: 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: std::io::Read>(r: &mut R) -> std::io::Result<Transaction> {
let prefix = TransactionPrefix::deserialize(r)?;
pub fn read<R: Read>(r: &mut R) -> io::Result<Transaction> {
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::<u64>()
.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)
}

View file

@ -32,7 +32,7 @@ impl BitXor<[u8; 8]> for PaymentId {
}
impl PaymentId {
pub(crate) fn serialize<W: Write>(&self, w: &mut W) -> io::Result<()> {
pub(crate) fn write<W: 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: Read>(r: &mut R) -> io::Result<PaymentId> {
fn read<R: Read>(r: &mut R) -> io::Result<PaymentId> {
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<W: Write>(&self, w: &mut W) -> io::Result<()> {
fn write<W: 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: Read>(r: &mut R) -> io::Result<ExtraField> {
fn read<R: Read>(r: &mut R) -> io::Result<ExtraField> {
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<PaymentId> {
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::<usize>()
}
pub(crate) fn serialize<W: Write>(&self, w: &mut W) -> io::Result<()> {
pub(crate) fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
for field in &self.0 {
field.serialize(w)?;
field.write(w)?;
}
Ok(())
}
pub(crate) fn deserialize<R: Read>(r: &mut R) -> io::Result<Extra> {
pub(crate) fn read<R: Read>(r: &mut R) -> io::Result<Extra> {
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());

View file

@ -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<u8> {
let mut res = Vec::with_capacity(32 + 1);
res.extend(self.tx);
res.push(self.o);
res
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.write_all(&self.tx)?;
w.write_all(&[self.o])
}
pub fn read<R: io::Read>(r: &mut R) -> io::Result<AbsoluteId> {
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = Vec::with_capacity(32 + 1);
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<AbsoluteId> {
Ok(AbsoluteId { tx: read_bytes(r)?, o: read_byte(r)? })
}
}
@ -46,16 +50,20 @@ pub struct OutputData {
}
impl OutputData {
pub fn serialize(&self) -> Vec<u8> {
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<W: 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: io::Read>(r: &mut R) -> io::Result<OutputData> {
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = Vec::with_capacity(32 + 32 + 32 + 8);
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<OutputData> {
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<u8> {
let mut res = Vec::with_capacity(4 + 4 + 8 + 1);
pub fn write<W: 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: io::Read>(r: &mut R) -> io::Result<Metadata> {
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = Vec::with_capacity(1 + 8 + 1);
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<Metadata> {
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<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.absolute.write(w)?;
self.data.write(w)?;
self.metadata.write(w)
}
pub fn serialize(&self) -> Vec<u8> {
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: io::Read>(r: &mut R) -> io::Result<ReceivedOutput> {
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
Ok(ReceivedOutput {
absolute: AbsoluteId::read(r)?,
data: OutputData::read(r)?,
@ -200,14 +218,19 @@ impl SpendableOutput {
self.output.commitment()
}
pub fn write<W: 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<u8> {
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: io::Read>(r: &mut R) -> io::Result<SpendableOutput> {
Ok(SpendableOutput { output: ReceivedOutput::deserialize(r)?, global_index: read_u64(r)? })
pub fn read<R: Read>(r: &mut R) -> io::Result<SpendableOutput> {
Ok(SpendableOutput { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? })
}
}
@ -248,7 +271,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
impl Scanner {
/// Scan a transaction to discover the received outputs.
pub fn scan_transaction(&mut self, tx: &Transaction) -> Timelocked<ReceivedOutput> {
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();

View file

@ -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
};