diff --git a/components/zcash_encoding/src/lib.rs b/components/zcash_encoding/src/lib.rs index 3e3e2c438..99b8ede53 100644 --- a/components/zcash_encoding/src/lib.rs +++ b/components/zcash_encoding/src/lib.rs @@ -10,9 +10,10 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use nonempty::NonEmpty; +use std::convert::TryFrom; use std::io::{self, Read, Write}; -const MAX_SIZE: usize = 0x02000000; +const MAX_SIZE: u64 = 0x02000000; /// Namespace for functions for compact encoding of integers. /// @@ -22,17 +23,17 @@ pub struct CompactSize; impl CompactSize { /// Reads an integer encoded in compact form. - pub fn read(mut reader: R) -> io::Result { + pub fn read(mut reader: R) -> io::Result { let flag = reader.read_u8()?; - match if flag < 253 { - Ok(flag as usize) + let result = if flag < 253 { + Ok(flag as u64) } else if flag == 253 { match reader.read_u16::()? { n if n < 253 => Err(io::Error::new( io::ErrorKind::InvalidInput, "non-canonical CompactSize", )), - n => Ok(n as usize), + n => Ok(n as u64), } } else if flag == 254 { match reader.read_u32::()? { @@ -40,7 +41,7 @@ impl CompactSize { io::ErrorKind::InvalidInput, "non-canonical CompactSize", )), - n => Ok(n as usize), + n => Ok(n as u64), } } else { match reader.read_u64::()? { @@ -48,9 +49,11 @@ impl CompactSize { io::ErrorKind::InvalidInput, "non-canonical CompactSize", )), - n => Ok(n as usize), + n => Ok(n), } - }? { + }?; + + match result { s if s > MAX_SIZE => Err(io::Error::new( io::ErrorKind::InvalidInput, "CompactSize too large", @@ -59,6 +62,18 @@ impl CompactSize { } } + /// Reads an integer encoded in contact form and performs checked conversion + /// to the target type. + pub fn read_t>(mut reader: R) -> io::Result { + let n = Self::read(&mut reader)?; + ::try_from(n).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidInput, + "CompactSize value exceeds range of target type.", + ) + }) + } + /// Writes the provided `usize` value to the provided Writer in compact form. pub fn write(mut writer: W, size: usize) -> io::Result<()> { match size { @@ -92,7 +107,7 @@ impl Vector { where F: Fn(&mut R) -> io::Result, { - let count = CompactSize::read(&mut reader)?; + let count: usize = CompactSize::read_t(&mut reader)?; Array::read(reader, count, func) } @@ -193,34 +208,38 @@ impl Optional { #[cfg(test)] mod tests { use super::*; + use std::convert::{TryFrom, TryInto}; + use std::fmt::Debug; #[test] fn compact_size() { - macro_rules! eval { - ($value:expr, $expected:expr) => { - let mut data = vec![]; - CompactSize::write(&mut data, $value).unwrap(); - assert_eq!(&data[..], &$expected[..]); - match CompactSize::read(&data[..]) { - Ok(n) => assert_eq!(n, $value), - Err(e) => panic!("Unexpected error: {:?}", e), - } - }; + fn eval + TryInto + Eq + Debug + Copy>(value: T, expected: &[u8]) + where + >::Error: Debug, + { + let mut data = vec![]; + CompactSize::write(&mut data, value.try_into().unwrap()).unwrap(); + assert_eq!(&data[..], expected); + let result: io::Result = CompactSize::read_t(&data[..]); + match result { + Ok(n) => assert_eq!(n, value), + Err(e) => panic!("Unexpected error: {:?}", e), + } } - eval!(0, [0]); - eval!(1, [1]); - eval!(252, [252]); - eval!(253, [253, 253, 0]); - eval!(254, [253, 254, 0]); - eval!(255, [253, 255, 0]); - eval!(256, [253, 0, 1]); - eval!(256, [253, 0, 1]); - eval!(65535, [253, 255, 255]); - eval!(65536, [254, 0, 0, 1, 0]); - eval!(65537, [254, 1, 0, 1, 0]); + eval(0, &[0]); + eval(1, &[1]); + eval(252, &[252]); + eval(253, &[253, 253, 0]); + eval(254, &[253, 254, 0]); + eval(255, &[253, 255, 0]); + eval(256, &[253, 0, 1]); + eval(256, &[253, 0, 1]); + eval(65535, &[253, 255, 255]); + eval(65536, &[254, 0, 0, 1, 0]); + eval(65537, &[254, 1, 0, 1, 0]); - eval!(33554432, [254, 0, 0, 0, 2]); + eval(33554432, &[254, 0, 0, 0, 2]); { let value = 33554433; diff --git a/zcash_primitives/src/transaction/components/tze.rs b/zcash_primitives/src/transaction/components/tze.rs index 48e93b56b..c72dadc2b 100644 --- a/zcash_primitives/src/transaction/components/tze.rs +++ b/zcash_primitives/src/transaction/components/tze.rs @@ -117,15 +117,15 @@ impl TzeIn<::Witness> { pub fn read(mut reader: &mut R) -> io::Result { let prevout = OutPoint::read(&mut reader)?; - let extension_id = CompactSize::read(&mut reader)?; - let mode = CompactSize::read(&mut reader)?; + let extension_id = CompactSize::read_t(&mut reader)?; + let mode = CompactSize::read_t(&mut reader)?; let payload = Vector::read(&mut reader, |r| r.read_u8())?; Ok(TzeIn { prevout, witness: tze::Witness { - extension_id: u32::try_from(extension_id).map_err(to_io_error)?, - mode: u32::try_from(mode).map_err(to_io_error)?, + extension_id, + mode, payload: tze::AuthData(payload), }, }) @@ -159,15 +159,15 @@ impl TzeOut { } .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?; - let extension_id = CompactSize::read(&mut reader)?; - let mode = CompactSize::read(&mut reader)?; + let extension_id = CompactSize::read_t(&mut reader)?; + let mode = CompactSize::read_t(&mut reader)?; let payload = Vector::read(&mut reader, |r| r.read_u8())?; Ok(TzeOut { value, precondition: tze::Precondition { - extension_id: u32::try_from(extension_id).map_err(to_io_error)?, - mode: u32::try_from(mode).map_err(to_io_error)?, + extension_id, + mode, payload, }, })