diff --git a/core/src/core/block.rs b/core/src/core/block.rs index e18013171..5d3ee716e 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -60,7 +60,7 @@ impl Default for BlockHeader { // Only Writeable implementation is required for hashing, which is part of // core. Readable is in the ser package. impl Writeable for BlockHeader { - fn write(&self, writer: &mut Writer) -> Option { + fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { ser_multiwrite!(writer, [write_u64, self.height], [write_fixed_bytes, &self.previous], @@ -70,10 +70,10 @@ impl Writeable for BlockHeader { [write_u64, self.total_fees]); // make sure to not introduce any variable length data before the nonce to // avoid complicating PoW - try_o!(writer.write_u64(self.nonce)); + try!(writer.write_u64(self.nonce)); // cuckoo cycle of 42 nodes for n in 0..42 { - try_o!(writer.write_u32(self.pow.0[n])); + try!(writer.write_u32(self.pow.0[n])); } writer.write_u64(self.td) } @@ -101,23 +101,23 @@ pub struct Block { /// Implementation of Writeable for a block, defines how to write the full /// block as binary. impl Writeable for Block { - fn write(&self, writer: &mut Writer) -> Option { - try_o!(self.header.write(writer)); + fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { + try!(self.header.write(writer)); ser_multiwrite!(writer, [write_u64, self.inputs.len() as u64], [write_u64, self.outputs.len() as u64], [write_u64, self.proofs.len() as u64]); for inp in &self.inputs { - try_o!(inp.write(writer)); + try!(inp.write(writer)); } for out in &self.outputs { - try_o!(out.write(writer)); + try!(out.write(writer)); } for proof in &self.proofs { - try_o!(proof.write(writer)); + try!(proof.write(writer)); } - None + Ok(()) } } diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 98566c83a..a250cb90e 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -39,8 +39,8 @@ pub struct TxProof { } impl Writeable for TxProof { - fn write(&self, writer: &mut Writer) -> Option { - try_o!(writer.write_fixed_bytes(&self.remainder)); + fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { + try!(writer.write_fixed_bytes(&self.remainder)); writer.write_vec(&mut self.sig.clone()) } } @@ -68,19 +68,19 @@ pub struct Transaction { /// Implementation of Writeable for a fully blinded transaction, defines how to /// write the transaction as binary. impl Writeable for Transaction { - fn write(&self, writer: &mut Writer) -> Option { + fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { ser_multiwrite!(writer, [write_u64, self.fee], [write_vec, &mut self.zerosig.clone()], [write_u64, self.inputs.len() as u64], [write_u64, self.outputs.len() as u64]); for inp in &self.inputs { - try_o!(inp.write(writer)); + try!(inp.write(writer)); } for out in &self.outputs { - try_o!(out.write(writer)); + try!(out.write(writer)); } - None + Ok(()) } } @@ -235,7 +235,7 @@ pub enum Input { /// Implementation of Writeable for a transaction Input, defines how to write /// an Input as binary. impl Writeable for Input { - fn write(&self, writer: &mut Writer) -> Option { + fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { writer.write_fixed_bytes(&self.output_hash()) } } @@ -302,8 +302,8 @@ pub enum Output { /// Implementation of Writeable for a transaction Output, defines how to write /// an Output as binary. impl Writeable for Output { - fn write(&self, writer: &mut Writer) -> Option { - try_o!(writer.write_fixed_bytes(&self.commitment().unwrap())); + fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { + try!(writer.write_fixed_bytes(&self.commitment().unwrap())); writer.write_vec(&mut self.proof().unwrap().bytes().to_vec()) } } @@ -407,9 +407,7 @@ mod test { let tx = tx2i1o(secp, &mut rng); let btx = tx.blind(&secp).unwrap(); let mut vec = Vec::new(); - if let Some(e) = serialize(&mut vec, &btx) { - panic!(e); - } + serialize(&mut vec, &btx).expect("serialized failed"); assert!(vec.len() > 5320); assert!(vec.len() < 5340); } @@ -422,9 +420,7 @@ mod test { let tx = tx2i1o(secp, &mut rng); let mut btx = tx.blind(&secp).unwrap(); let mut vec = Vec::new(); - if let Some(e) = serialize(&mut vec, &btx) { - panic!(e); - } + serialize(&mut vec, &btx).expect("serialization failed"); // let mut dtx = Transaction::read(&mut BinReader { source: &mut &vec[..] // }).unwrap(); let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); @@ -444,11 +440,11 @@ mod test { let mut btx = tx.blind(&secp).unwrap(); let mut vec = Vec::new(); - assert!(serialize(&mut vec, &btx).is_none()); + assert!(serialize(&mut vec, &btx).is_ok()); let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); let mut vec2 = Vec::new(); - assert!(serialize(&mut vec2, &btx).is_none()); + assert!(serialize(&mut vec2, &btx).is_ok()); let mut dtx2: Transaction = deserialize(&mut &vec2[..]).unwrap(); assert_eq!(btx.hash(), dtx.hash()); diff --git a/core/src/macros.rs b/core/src/macros.rs index 59d3017e4..0f13a999e 100644 --- a/core/src/macros.rs +++ b/core/src/macros.rs @@ -61,19 +61,6 @@ macro_rules! tee { } } -/// Simple equivalent of try! but for an Option. Motivated mostly by the -/// io package and our serialization as an alternative to silly Result<(), -/// Error>. -#[macro_export] -macro_rules! try_o { - ($trying:expr) => { - let tried = $trying; - if let Some(_) = tried { - return tried; - } - } -} - #[macro_export] macro_rules! try_to_o { ($trying:expr) => {{ @@ -109,6 +96,6 @@ macro_rules! ser_multiread { #[macro_export] macro_rules! ser_multiwrite { ($wrtr:ident, $([ $write_call:ident, $val:expr ]),* ) => { - $( try_o!($wrtr.$write_call($val)) );* + $( try!($wrtr.$write_call($val)) );* } } diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index b06383aea..d17da511b 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -71,16 +71,16 @@ struct PowHeader { /// the data that gets hashed for PoW calculation. The nonce is written first /// to make incrementing from the serialized form trivial. impl Writeable for PowHeader { - fn write(&self, writer: &mut Writer) -> Option { - try_o!(writer.write_u64(self.nonce)); - try_o!(writer.write_u64(self.height)); - try_o!(writer.write_fixed_bytes(&self.previous)); - try_o!(writer.write_i64(self.timestamp.to_timespec().sec)); - try_o!(writer.write_fixed_bytes(&self.utxo_merkle)); - try_o!(writer.write_fixed_bytes(&self.tx_merkle)); - try_o!(writer.write_u64(self.total_fees)); - try_o!(writer.write_u64(self.n_in)); - try_o!(writer.write_u64(self.n_out)); + fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { + try!(writer.write_u64(self.nonce)); + try!(writer.write_u64(self.height)); + try!(writer.write_fixed_bytes(&self.previous)); + try!(writer.write_i64(self.timestamp.to_timespec().sec)); + try!(writer.write_fixed_bytes(&self.utxo_merkle)); + try!(writer.write_fixed_bytes(&self.tx_merkle)); + try!(writer.write_u64(self.total_fees)); + try!(writer.write_u64(self.n_in)); + try!(writer.write_u64(self.n_out)); writer.write_u64(self.n_proofs) } } diff --git a/core/src/ser.rs b/core/src/ser.rs index 99680c5f7..e1f5e4e3e 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -19,8 +19,8 @@ //! To use it simply implement `Writeable` or `Readable` and then use the //! `serialize` or `deserialize` functions on them as appropriate. -use std::io; -use std::io::{Write, Read}; +use std::{error, fmt}; +use std::io::{self, Write, Read}; use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; /// Possible errors deriving from serializing or deserializing. @@ -33,12 +33,47 @@ pub enum Error { expected: Vec, received: Vec, }, - /// Data wasn't in a consumable format - CorruptedData, + /// Data wasn't in a consumable format + CorruptedData, /// When asked to read too much data TooLargeReadErr(String), } +impl From for Error { + fn from(e: io::Error) -> Error { + Error::IOErr(e) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::IOErr(ref e) => write!(f, "{}", e), + Error::UnexpectedData { expected: ref e, received: ref r } => write!(f, "expected {:?}, got {:?}", e, r), + Error::CorruptedData => f.write_str("corrupted data"), + Error::TooLargeReadErr(ref s) => f.write_str(&s) + } + } +} + +impl error::Error for Error { + fn cause(&self) -> Option<&error::Error> { + match *self { + Error::IOErr(ref e) => Some(e), + _ => None + } + } + + fn description(&self) -> &str { + match *self { + Error::IOErr(ref e) => error::Error::description(e), + Error::UnexpectedData { expected: _, received: _ } => "unexpected data", + Error::CorruptedData => "corrupted data", + Error::TooLargeReadErr(ref s) => s + } + } +} + /// Useful trait to implement on types that can be translated to byte slices /// directly. Allows the use of `write_fixed_bytes` on them. pub trait AsFixedBytes { @@ -50,21 +85,21 @@ pub trait AsFixedBytes { /// written to an underlying stream or container (depending on implementation). pub trait Writer { /// Writes a u8 as bytes - fn write_u8(&mut self, n: u8) -> Option; + fn write_u8(&mut self, n: u8) -> Result<(), Error>; /// Writes a u16 as bytes - fn write_u16(&mut self, n: u16) -> Option; + fn write_u16(&mut self, n: u16) -> Result<(), Error>; /// Writes a u32 as bytes - fn write_u32(&mut self, n: u32) -> Option; + fn write_u32(&mut self, n: u32) -> Result<(), Error>; /// Writes a u64 as bytes - fn write_u64(&mut self, n: u64) -> Option; + fn write_u64(&mut self, n: u64) -> Result<(), Error>; /// Writes a i64 as bytes - fn write_i64(&mut self, n: i64) -> Option; + fn write_i64(&mut self, n: i64) -> Result<(), Error>; /// Writes a variable length `Vec`, the length of the `Vec` is encoded as a /// prefix. - fn write_vec(&mut self, vec: &mut Vec) -> Option; + fn write_vec(&mut self, vec: &mut Vec) -> Result<(), Error>; /// Writes a fixed number of bytes from something that can turn itself into /// a `&[u8]`. The reader is expected to know the actual length on read. - fn write_fixed_bytes(&mut self, b32: &AsFixedBytes) -> Option; + fn write_fixed_bytes(&mut self, b32: &AsFixedBytes) -> Result<(), Error>; } /// Implementations defined how different numbers and binary structures are @@ -98,7 +133,7 @@ pub trait Reader { /// underlying Write implementation. pub trait Writeable { /// Write the data held by this Writeable to the provided writer - fn write(&self, writer: &mut Writer) -> Option; + fn write(&self, writer: &mut Writer) -> Result<(), Error>; } /// Trait that every type that can be deserialized from binary must implement. @@ -116,7 +151,7 @@ pub fn deserialize>(mut source: &mut Read) -> Result { } /// Serializes a Writeable into any std::io::Write implementation. -pub fn serialize(mut sink: &mut Write, thing: &Writeable) -> Option { +pub fn serialize(mut sink: &mut Write, thing: &Writeable) -> Result<(), Error> { let mut writer = BinWriter { sink: sink }; thing.write(&mut writer) } @@ -125,9 +160,7 @@ pub fn serialize(mut sink: &mut Write, thing: &Writeable) -> Option { /// Vec. pub fn ser_vec(thing: &Writeable) -> Result, Error> { let mut vec = Vec::new(); - if let Some(err) = serialize(&mut vec, thing) { - return Err(err); - } + try!(serialize(&mut vec, thing)); Ok(vec) } @@ -192,33 +225,40 @@ struct BinWriter<'a> { } impl<'a> Writer for BinWriter<'a> { - fn write_u8(&mut self, n: u8) -> Option { - self.sink.write_u8(n).err().map(Error::IOErr) + fn write_u8(&mut self, n: u8) -> Result<(), Error> { + try!(self.sink.write_u8(n)); + Ok(()) } - fn write_u16(&mut self, n: u16) -> Option { - self.sink.write_u16::(n).err().map(Error::IOErr) + fn write_u16(&mut self, n: u16) -> Result<(), Error> { + try!(self.sink.write_u16::(n)); + Ok(()) } - fn write_u32(&mut self, n: u32) -> Option { - self.sink.write_u32::(n).err().map(Error::IOErr) + fn write_u32(&mut self, n: u32) -> Result<(), Error> { + try!(self.sink.write_u32::(n)); + Ok(()) } - fn write_u64(&mut self, n: u64) -> Option { - self.sink.write_u64::(n).err().map(Error::IOErr) + fn write_u64(&mut self, n: u64) -> Result<(), Error> { + try!(self.sink.write_u64::(n)); + Ok(()) } - fn write_i64(&mut self, n: i64) -> Option { - self.sink.write_i64::(n).err().map(Error::IOErr) + fn write_i64(&mut self, n: i64) -> Result<(), Error> { + try!(self.sink.write_i64::(n)); + Ok(()) } - fn write_vec(&mut self, vec: &mut Vec) -> Option { - try_o!(self.write_u64(vec.len() as u64)); - self.sink.write_all(vec).err().map(Error::IOErr) + fn write_vec(&mut self, vec: &mut Vec) -> Result<(), Error> { + try!(self.write_u64(vec.len() as u64)); + try!(self.sink.write_all(vec)); + Ok(()) } - fn write_fixed_bytes(&mut self, b32: &AsFixedBytes) -> Option { + fn write_fixed_bytes(&mut self, b32: &AsFixedBytes) -> Result<(), Error> { let bs = b32.as_fixed_bytes(); - self.sink.write_all(bs).err().map(Error::IOErr) + try!(self.sink.write_all(bs)); + Ok(()) } }