From db46f56569d9e5bfe97e330da13a10b49c03c899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ci=C4=99=C5=BCarkiewicz?= Date: Sat, 28 May 2022 00:12:28 -0700 Subject: [PATCH] Refactor `trait Decodable` Adjust code afterwards. This is to address #997. --- src/blockdata/script.rs | 8 +- src/blockdata/transaction.rs | 42 +++++---- src/blockdata/witness.rs | 12 ++- src/consensus/encode.rs | 154 ++++++++++++++++++++++--------- src/hash_types.rs | 2 +- src/internal_macros.rs | 14 ++- src/network/address.rs | 10 +- src/network/constants.rs | 4 +- src/network/message.rs | 31 ++++--- src/network/message_blockdata.rs | 23 +++-- src/network/message_bloom.rs | 2 +- src/network/message_network.rs | 2 +- src/util/merkleblock.rs | 23 +++-- src/util/psbt/macros.rs | 9 +- src/util/psbt/mod.rs | 14 ++- src/util/psbt/raw.rs | 29 ++++-- src/util/uint.rs | 10 +- 17 files changed, 263 insertions(+), 126 deletions(-) diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 839a7c2923..2e7f0fd16b 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -23,6 +23,7 @@ //! This module provides the structures and functions needed to support scripts. //! +use crate::consensus::encode::MAX_VEC_SIZE; use crate::prelude::*; use crate::io; @@ -1075,9 +1076,14 @@ impl Encodable for Script { } impl Decodable for Script { + #[inline] + fn consensus_decode_from_finite_reader(d: D) -> Result { + Ok(Script(Decodable::consensus_decode_from_finite_reader(d)?)) + } + #[inline] fn consensus_decode(d: D) -> Result { - Ok(Script(Decodable::consensus_decode(d)?)) + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) } } diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index c35a1b65bd..7600d2c5ad 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -621,10 +621,10 @@ impl Encodable for OutPoint { } } impl Decodable for OutPoint { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(OutPoint { - txid: Decodable::consensus_decode(&mut d)?, - vout: Decodable::consensus_decode(d)?, + txid: Decodable::consensus_decode_from_finite_reader(&mut d)?, + vout: Decodable::consensus_decode_from_finite_reader(d)?, }) } } @@ -639,14 +639,18 @@ impl Encodable for TxIn { } } impl Decodable for TxIn { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(TxIn { - previous_output: Decodable::consensus_decode(&mut d)?, - script_sig: Decodable::consensus_decode(&mut d)?, - sequence: Decodable::consensus_decode(d)?, + previous_output: Decodable::consensus_decode_from_finite_reader(&mut d)?, + script_sig: Decodable::consensus_decode_from_finite_reader(&mut d)?, + sequence: Decodable::consensus_decode_from_finite_reader(d)?, witness: Witness::default(), }) } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } impl Encodable for Transaction { @@ -680,20 +684,20 @@ impl Encodable for Transaction { } impl Decodable for Transaction { - fn consensus_decode(d: D) -> Result { + fn consensus_decode_from_finite_reader(d: D) -> Result { let mut d = d.take(MAX_VEC_SIZE as u64); - let version = i32::consensus_decode(&mut d)?; - let input = Vec::::consensus_decode(&mut d)?; + let version = i32::consensus_decode_from_finite_reader(&mut d)?; + let input = Vec::::consensus_decode_from_finite_reader(&mut d)?; // segwit if input.is_empty() { - let segwit_flag = u8::consensus_decode(&mut d)?; + let segwit_flag = u8::consensus_decode_from_finite_reader(&mut d)?; match segwit_flag { // BIP144 input witnesses 1 => { - let mut input = Vec::::consensus_decode(&mut d)?; - let output = Vec::::consensus_decode(&mut d)?; + let mut input = Vec::::consensus_decode_from_finite_reader(&mut d)?; + let output = Vec::::consensus_decode_from_finite_reader(&mut d)?; for txin in input.iter_mut() { - txin.witness = Decodable::consensus_decode(&mut d)?; + txin.witness = Decodable::consensus_decode_from_finite_reader(&mut d)?; } if !input.is_empty() && input.iter().all(|input| input.witness.is_empty()) { Err(encode::Error::ParseFailed("witness flag set but no witnesses present")) @@ -702,7 +706,7 @@ impl Decodable for Transaction { version, input, output, - lock_time: Decodable::consensus_decode(d)?, + lock_time: Decodable::consensus_decode_from_finite_reader(d)?, }) } } @@ -714,11 +718,15 @@ impl Decodable for Transaction { Ok(Transaction { version, input, - output: Decodable::consensus_decode(&mut d)?, - lock_time: Decodable::consensus_decode(d)?, + output: Decodable::consensus_decode_from_finite_reader(&mut d)?, + lock_time: Decodable::consensus_decode_from_finite_reader(d)?, }) } } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } /// This type is consensus valid but an input including it would prevent the transaction from diff --git a/src/blockdata/witness.rs b/src/blockdata/witness.rs index 095b1ce581..6e97269151 100644 --- a/src/blockdata/witness.rs +++ b/src/blockdata/witness.rs @@ -6,7 +6,7 @@ use crate::blockdata::transaction::EcdsaSighashType; use crate::consensus::encode::{Error, MAX_VEC_SIZE}; use crate::consensus::{Decodable, Encodable, WriteExt}; -use crate::io::{self, Read, Write}; +use crate::io::{self, Write}; use crate::prelude::*; use secp256k1::ecdsa; use crate::VarInt; @@ -46,8 +46,8 @@ pub struct Witness { pub struct Iter<'a>(::core::slice::Iter<'a, u8>); impl Decodable for Witness { - fn consensus_decode(mut d: D) -> Result { - let witness_elements = VarInt::consensus_decode(&mut d)?.0 as usize; + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + let witness_elements = VarInt::consensus_decode_from_finite_reader(&mut d)?.0 as usize; if witness_elements == 0 { Ok(Witness::default()) } else { @@ -62,7 +62,7 @@ impl Decodable for Witness { for _ in 0..witness_elements { second_to_last = last; last = cursor; - let element_size_varint = VarInt::consensus_decode(&mut d)?; + let element_size_varint = VarInt::consensus_decode_from_finite_reader(&mut d)?; let element_size_varint_len = element_size_varint.len(); let element_size = element_size_varint.0 as usize; let required_len = cursor @@ -100,6 +100,10 @@ impl Decodable for Witness { }) } } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } fn resize_if_needed(vec: &mut Vec, required_len: usize) { diff --git a/src/consensus/encode.rs b/src/consensus/encode.rs index 6505df37db..caa011b4bc 100644 --- a/src/consensus/encode.rs +++ b/src/consensus/encode.rs @@ -319,8 +319,28 @@ pub trait Encodable { /// Data which can be encoded in a consensus-consistent way pub trait Decodable: Sized { + /// Decode `Self` from a size-limited reader. + /// + /// Like `consensus_decode` but relies on the reader being + /// limited in the amount of data it returns, e.g. by + /// being wrapped in [`std::io::Take`]. + /// + /// Failling to obide to this requirement might lead to + /// memory exhaustion caused by malicious inputs. + /// + /// Users should default to `consensus_decode`, but + /// when data to be decoded is already in a byte vector + /// of a limited size, calling this function directly + /// might be marginally faster (due to avoiding + /// extra checks). + fn consensus_decode_from_finite_reader(d: D) -> Result; + /// Decode an object with a well-defined format - fn consensus_decode(d: D) -> Result; + fn consensus_decode(d: D) -> Result { + // Note: + // Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + Self::consensus_decode_from_finite_reader(d) + } } /// A variable-length unsigned integer @@ -336,7 +356,7 @@ macro_rules! impl_int_encodable { ($ty:ident, $meth_dec:ident, $meth_enc:ident) => { impl Decodable for $ty { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { ReadExt::$meth_dec(&mut d) } } @@ -403,7 +423,7 @@ impl Encodable for VarInt { impl Decodable for VarInt { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { let n = ReadExt::read_u8(&mut d)?; match n { 0xFF => { @@ -446,7 +466,7 @@ impl Encodable for bool { impl Decodable for bool { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { ReadExt::read_bool(&mut d) } } @@ -464,6 +484,11 @@ impl Encodable for String { impl Decodable for String { #[inline] + fn consensus_decode_from_finite_reader(d: D) -> Result { + String::from_utf8(Decodable::consensus_decode_from_finite_reader(d)?) + .map_err(|_| self::Error::ParseFailed("String was not valid UTF8")) + } + fn consensus_decode(d: D) -> Result { String::from_utf8(Decodable::consensus_decode(d)?) .map_err(|_| self::Error::ParseFailed("String was not valid UTF8")) @@ -483,7 +508,14 @@ impl Encodable for Cow<'static, str> { impl Decodable for Cow<'static, str> { #[inline] - fn consensus_decode(d: D) -> Result, Error> { + fn consensus_decode_from_finite_reader(d: D) -> Result { + String::from_utf8(Decodable::consensus_decode_from_finite_reader(d)?) + .map_err(|_| self::Error::ParseFailed("String was not valid UTF8")) + .map(Cow::Owned) + } + + #[inline] + fn consensus_decode(d: D) -> Result { String::from_utf8(Decodable::consensus_decode(d)?) .map_err(|_| self::Error::ParseFailed("String was not valid UTF8")) .map(Cow::Owned) @@ -504,7 +536,7 @@ macro_rules! impl_array { impl Decodable for [u8; $size] { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { let mut ret = [0; $size]; d.read_slice(&mut ret)?; Ok(ret) @@ -524,10 +556,10 @@ impl_array!(33); impl Decodable for [u16; 8] { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { let mut res = [0; 8]; for item in &mut res { - *item = Decodable::consensus_decode(&mut d)?; + *item = Decodable::consensus_decode_from_finite_reader(&mut d)?; } Ok(res) } @@ -557,24 +589,31 @@ macro_rules! impl_vec { } impl Decodable for Vec<$type> { #[inline] - fn consensus_decode(mut d: D) -> Result { - let len = VarInt::consensus_decode(&mut d)?.0; - let byte_size = (len as usize) - .checked_mul(mem::size_of::<$type>()) - .ok_or(self::Error::ParseFailed("Invalid length"))?; - if byte_size > MAX_VEC_SIZE { - return Err(self::Error::OversizedVectorAllocation { requested: byte_size, max: MAX_VEC_SIZE }) - } - let mut ret = Vec::with_capacity(len as usize); - let mut d = d.take(MAX_VEC_SIZE as u64); + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + let len = VarInt::consensus_decode_from_finite_reader(&mut d)?.0; + // Do not allocate upfront more items than if the sequnce of type + // occupied roughly quarter a block. This should never be the case + // for normal data, but even if that's not true - `push` will just + // reallocate. This should prevent even malicious decoding requests + // sent en masse, from having a big impact on memory utilization. + // Note: OOM protection relies on reader eventually running out of + // data to feed us. + let max_capacity = MAX_VEC_SIZE / 4 / mem::size_of::<$type>(); + let mut ret = Vec::with_capacity(core::cmp::min(len as usize, max_capacity)); for _ in 0..len { - ret.push(Decodable::consensus_decode(&mut d)?); + ret.push(Decodable::consensus_decode_from_finite_reader(&mut d)?); } Ok(ret) } + + #[inline] + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } } } + impl_vec!(BlockHash); impl_vec!(FilterHash); impl_vec!(FilterHeader); @@ -604,16 +643,38 @@ impl Encodable for Vec { } } +/// Read `len` bytes from reader, where `len` could potentially be malicious +/// +/// This function relies on `reader` being bound in amount of data +/// it returns for OOM protection. See [`Decodable::consensus_decode_from_finite_reader`]. +fn consensus_decode_bytes_from_finite_reader(mut d: D, mut len: usize) -> Result, Error> { + // there should never be any byte vectors longer than this, + // at least in real block data, but if they are we'll just re-allocate + let max_capacity = 1024; + let mut ret = vec![]; + + while len > 0 { + let chunk_start = ret.len(); + let chunk_size = core::cmp::min(len, max_capacity); + let chunk_end = chunk_start + chunk_size; + ret.resize(chunk_end, 0u8); + d.read_slice(&mut ret[chunk_start..chunk_end])?; + len -= chunk_size; + } + + Ok(ret) +} + impl Decodable for Vec { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { let len = VarInt::consensus_decode(&mut d)?.0 as usize; - if len > MAX_VEC_SIZE { - return Err(self::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE }) - } - let mut ret = vec![0u8; len]; - d.read_slice(&mut ret)?; - Ok(ret) + consensus_decode_bytes_from_finite_reader(d, len) + } + + #[inline] + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) } } @@ -625,6 +686,11 @@ impl Encodable for Box<[u8]> { } impl Decodable for Box<[u8]> { + #[inline] + fn consensus_decode_from_finite_reader(d: D) -> Result { + >::consensus_decode_from_finite_reader(d).map(From::from) + } + #[inline] fn consensus_decode(d: D) -> Result { >::consensus_decode(d).map(From::from) @@ -651,17 +717,11 @@ impl Encodable for CheckedData { impl Decodable for CheckedData { #[inline] - fn consensus_decode(mut d: D) -> Result { - let len = u32::consensus_decode(&mut d)?; - if len > MAX_VEC_SIZE as u32 { - return Err(self::Error::OversizedVectorAllocation { - requested: len as usize, - max: MAX_VEC_SIZE - }); - } - let checksum = <[u8; 4]>::consensus_decode(&mut d)?; - let mut ret = vec![0u8; len as usize]; - d.read_slice(&mut ret)?; + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + let len = u32::consensus_decode_from_finite_reader(&mut d)?; + + let checksum = <[u8; 4]>::consensus_decode_from_finite_reader(&mut d)?; + let ret = consensus_decode_bytes_from_finite_reader(d, len as usize)?; let expected_checksum = sha2_checksum(&ret); if expected_checksum != checksum { Err(self::Error::InvalidChecksum { @@ -672,6 +732,10 @@ impl Decodable for CheckedData { Ok(CheckedData(ret)) } } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } // References @@ -719,8 +783,8 @@ macro_rules! tuple_encode { impl<$($x: Decodable),*> Decodable for ($($x),*) { #[inline] #[allow(non_snake_case)] - fn consensus_decode(mut d: D) -> Result { - Ok(($({let $x = Decodable::consensus_decode(&mut d)?; $x }),*)) + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + Ok(($({let $x = Decodable::consensus_decode_from_finite_reader(&mut d)?; $x }),*)) } } }; @@ -741,8 +805,8 @@ impl Encodable for sha256d::Hash { } impl Decodable for sha256d::Hash { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_inner(<::Inner>::consensus_decode(d)?)) + fn consensus_decode_from_finite_reader(d: D) -> Result { + Ok(Self::from_inner(<::Inner>::consensus_decode_from_finite_reader(d)?)) } } @@ -753,8 +817,8 @@ impl Encodable for sha256::Hash { } impl Decodable for sha256::Hash { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_inner(<::Inner>::consensus_decode(d)?)) + fn consensus_decode_from_finite_reader(d: D) -> Result { + Ok(Self::from_inner(<::Inner>::consensus_decode_from_finite_reader(d)?)) } } @@ -765,8 +829,8 @@ impl Encodable for TapLeafHash { } impl Decodable for TapLeafHash { - fn consensus_decode(d: D) -> Result { - Ok(Self::from_inner(<::Inner>::consensus_decode(d)?)) + fn consensus_decode_from_finite_reader(d: D) -> Result { + Ok(Self::from_inner(<::Inner>::consensus_decode_from_finite_reader(d)?)) } } diff --git a/src/hash_types.rs b/src/hash_types.rs index 94e46792b1..db51b2f4eb 100644 --- a/src/hash_types.rs +++ b/src/hash_types.rs @@ -31,7 +31,7 @@ macro_rules! impl_hashencode { } impl $crate::consensus::Decodable for $hashtype { - fn consensus_decode(d: D) -> Result { + fn consensus_decode_from_finite_reader(d: D) -> Result { use $crate::hashes::Hash; Ok(Self::from_inner(<<$hashtype as $crate::hashes::Hash>::Inner>::consensus_decode(d)?)) } diff --git a/src/internal_macros.rs b/src/internal_macros.rs index 1814420b2e..6c1d6c68a5 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -33,14 +33,20 @@ macro_rules! impl_consensus_encoding { impl $crate::consensus::Decodable for $thing { #[inline] - fn consensus_decode( - d: D, + fn consensus_decode_from_finite_reader( + mut d: D, ) -> Result<$thing, $crate::consensus::encode::Error> { - let mut d = d.take($crate::consensus::encode::MAX_VEC_SIZE as u64); Ok($thing { - $($field: $crate::consensus::Decodable::consensus_decode(&mut d)?),+ + $($field: $crate::consensus::Decodable::consensus_decode_from_finite_reader(&mut d)?),+ }) } + + #[inline] + fn consensus_decode( + d: D, + ) -> Result<$thing, $crate::consensus::encode::Error> { + Self::consensus_decode_from_finite_reader(d.take($crate::consensus::encode::MAX_VEC_SIZE as u64)) + } } ); } diff --git a/src/network/address.rs b/src/network/address.rs index 9338b3d790..70253ab2fd 100644 --- a/src/network/address.rs +++ b/src/network/address.rs @@ -86,11 +86,11 @@ impl Encodable for Address { impl Decodable for Address { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(Address { - services: Decodable::consensus_decode(&mut d)?, + services: Decodable::consensus_decode_from_finite_reader(&mut d)?, address: read_be_address(&mut d)?, - port: u16::swap_bytes(Decodable::consensus_decode(d)?) + port: u16::swap_bytes(Decodable::consensus_decode_from_finite_reader(d)?) }) } } @@ -168,7 +168,7 @@ impl Encodable for AddrV2 { } impl Decodable for AddrV2 { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { let network_id = u8::consensus_decode(&mut d)?; let len = VarInt::consensus_decode(&mut d)?.0; if len > 512 { @@ -278,7 +278,7 @@ impl Encodable for AddrV2Message { } impl Decodable for AddrV2Message { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(AddrV2Message { time: Decodable::consensus_decode(&mut d)?, services: ServiceFlags::from(VarInt::consensus_decode(&mut d)?.0), diff --git a/src/network/constants.rs b/src/network/constants.rs index 8ccc22cc79..c059131ed4 100644 --- a/src/network/constants.rs +++ b/src/network/constants.rs @@ -281,8 +281,8 @@ impl Encodable for ServiceFlags { impl Decodable for ServiceFlags { #[inline] - fn consensus_decode(mut d: D) -> Result { - Ok(ServiceFlags(Decodable::consensus_decode(&mut d)?)) + fn consensus_decode_from_finite_reader(d: D) -> Result { + Ok(ServiceFlags(Decodable::consensus_decode_from_finite_reader(d)?)) } } diff --git a/src/network/message.rs b/src/network/message.rs index ea676f6bd9..32380ab721 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -20,7 +20,7 @@ use crate::prelude::*; -use core::{mem, fmt, iter}; +use core::{fmt, iter}; use crate::io; use crate::blockdata::block; @@ -29,7 +29,7 @@ use crate::network::address::{Address, AddrV2Message}; use crate::network::{message_network, message_bloom}; use crate::network::message_blockdata; use crate::network::message_filter; -use crate::consensus::encode::{CheckedData, Decodable, Encodable, VarInt, MAX_VEC_SIZE}; +use crate::consensus::encode::{CheckedData, Decodable, Encodable, VarInt}; use crate::consensus::{encode, serialize}; use crate::util::merkleblock::MerkleBlock; @@ -38,6 +38,9 @@ use crate::util::merkleblock::MerkleBlock; /// This limit is not currently enforced by this implementation. pub const MAX_INV_SIZE: usize = 50_000; +/// Maximum size, in bytes, of a an encoded message +pub const MAX_MSG_SIZE: usize = 16_000_000; + /// Serializer for command string #[derive(PartialEq, Eq, Clone, Debug)] pub struct CommandString(Cow<'static, str>); @@ -86,7 +89,7 @@ impl Encodable for CommandString { impl Decodable for CommandString { #[inline] - fn consensus_decode(d: D) -> Result { + fn consensus_decode_from_finite_reader(d: D) -> Result { let rawbytes: [u8; 12] = Decodable::consensus_decode(d)?; let rv = iter::FromIterator::from_iter( rawbytes @@ -341,15 +344,9 @@ struct HeaderDeserializationWrapper(Vec); impl Decodable for HeaderDeserializationWrapper { #[inline] - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { let len = VarInt::consensus_decode(&mut d)?.0; - let byte_size = (len as usize) - .checked_mul(mem::size_of::()) - .ok_or(encode::Error::ParseFailed("Invalid length"))?; - if byte_size > MAX_VEC_SIZE { - return Err(encode::Error::OversizedVectorAllocation { requested: byte_size, max: MAX_VEC_SIZE }) - } - let mut ret = Vec::with_capacity(len as usize); + let mut ret = Vec::with_capacity(1024 * 16); for _ in 0..len { ret.push(Decodable::consensus_decode(&mut d)?); if u8::consensus_decode(&mut d)? != 0u8 { @@ -358,10 +355,15 @@ impl Decodable for HeaderDeserializationWrapper { } Ok(HeaderDeserializationWrapper(ret)) } + + #[inline] + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_MSG_SIZE as u64)) + } } impl Decodable for RawNetworkMessage { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { let magic = Decodable::consensus_decode(&mut d)?; let cmd = CommandString::consensus_decode(&mut d)?; let raw_payload = CheckedData::consensus_decode(&mut d)?.0; @@ -412,6 +414,11 @@ impl Decodable for RawNetworkMessage { payload, }) } + + #[inline] + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_MSG_SIZE as u64)) + } } #[cfg(test)] diff --git a/src/network/message_blockdata.rs b/src/network/message_blockdata.rs index 37af36eac1..e5a13bbc7c 100644 --- a/src/network/message_blockdata.rs +++ b/src/network/message_blockdata.rs @@ -28,6 +28,8 @@ use crate::network::constants; use crate::consensus::encode::{self, Decodable, Encodable}; use crate::hash_types::{BlockHash, Txid, Wtxid}; +use super::message::MAX_MSG_SIZE; + /// An inventory item. #[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)] pub enum Inventory { @@ -74,21 +76,26 @@ impl Encodable for Inventory { impl Decodable for Inventory { #[inline] - fn consensus_decode(mut d: D) -> Result { - let inv_type: u32 = Decodable::consensus_decode(&mut d)?; + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + let inv_type: u32 = Decodable::consensus_decode_from_finite_reader(&mut d)?; Ok(match inv_type { 0 => Inventory::Error, - 1 => Inventory::Transaction(Decodable::consensus_decode(&mut d)?), - 2 => Inventory::Block(Decodable::consensus_decode(&mut d)?), - 5 => Inventory::WTx(Decodable::consensus_decode(&mut d)?), - 0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(&mut d)?), - 0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(&mut d)?), + 1 => Inventory::Transaction(Decodable::consensus_decode_from_finite_reader(&mut d)?), + 2 => Inventory::Block(Decodable::consensus_decode_from_finite_reader(&mut d)?), + 5 => Inventory::WTx(Decodable::consensus_decode_from_finite_reader(&mut d)?), + 0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode_from_finite_reader(&mut d)?), + 0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode_from_finite_reader(&mut d)?), tp => Inventory::Unknown { inv_type: tp, - hash: Decodable::consensus_decode(&mut d)?, + hash: Decodable::consensus_decode_from_finite_reader(&mut d)?, } }) } + + #[inline] + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_MSG_SIZE as u64)) + } } // Some simple messages diff --git a/src/network/message_bloom.rs b/src/network/message_bloom.rs index daae4d14c4..20ac8d348e 100644 --- a/src/network/message_bloom.rs +++ b/src/network/message_bloom.rs @@ -45,7 +45,7 @@ impl Encodable for BloomFlags { } impl Decodable for BloomFlags { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(match d.read_u8()? { 0 => BloomFlags::None, 1 => BloomFlags::All, diff --git a/src/network/message_network.rs b/src/network/message_network.rs index bd9a77202e..627f635ed6 100644 --- a/src/network/message_network.rs +++ b/src/network/message_network.rs @@ -113,7 +113,7 @@ impl Encodable for RejectReason { } impl Decodable for RejectReason { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(match d.read_u8()? { 0x01 => RejectReason::Malformed, 0x10 => RejectReason::Invalid, diff --git a/src/util/merkleblock.rs b/src/util/merkleblock.rs index ddeb3d8fb3..9bb8fff652 100644 --- a/src/util/merkleblock.rs +++ b/src/util/merkleblock.rs @@ -52,6 +52,7 @@ //! assert_eq!(1, index[0]); //! ``` +use crate::consensus::encode::MAX_VEC_SIZE; use crate::prelude::*; use crate::io; @@ -352,11 +353,11 @@ impl Encodable for PartialMerkleTree { } impl Decodable for PartialMerkleTree { - fn consensus_decode(mut d: D) -> Result { - let num_transactions: u32 = Decodable::consensus_decode(&mut d)?; - let hashes: Vec = Decodable::consensus_decode(&mut d)?; + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + let num_transactions: u32 = Decodable::consensus_decode_from_finite_reader(&mut d)?; + let hashes: Vec = Decodable::consensus_decode_from_finite_reader(&mut d)?; - let bytes: Vec = Decodable::consensus_decode(d)?; + let bytes: Vec = Decodable::consensus_decode_from_finite_reader(d)?; let mut bits: Vec = vec![false; bytes.len() * 8]; for (p, bit) in bits.iter_mut().enumerate() { @@ -368,6 +369,10 @@ impl Decodable for PartialMerkleTree { bits, }) } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } /// Data structure that represents a block header paired to a partial merkle tree. @@ -499,12 +504,16 @@ impl Encodable for MerkleBlock { } impl Decodable for MerkleBlock { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(MerkleBlock { - header: Decodable::consensus_decode(&mut d)?, - txn: Decodable::consensus_decode(d)?, + header: Decodable::consensus_decode_from_finite_reader(&mut d)?, + txn: Decodable::consensus_decode_from_finite_reader(d)?, }) } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } #[cfg(test)] diff --git a/src/util/psbt/macros.rs b/src/util/psbt/macros.rs index 78c7f3597a..fc902e8133 100644 --- a/src/util/psbt/macros.rs +++ b/src/util/psbt/macros.rs @@ -68,19 +68,24 @@ macro_rules! impl_psbtmap_consensus_encoding { macro_rules! impl_psbtmap_consensus_decoding { ($thing:ty) => { impl $crate::consensus::Decodable for $thing { - fn consensus_decode( + fn consensus_decode_from_finite_reader( mut d: D, ) -> Result { let mut rv: Self = ::core::default::Default::default(); loop { - match $crate::consensus::Decodable::consensus_decode(&mut d) { + match $crate::consensus::Decodable::consensus_decode_from_finite_reader(&mut d) { Ok(pair) => rv.insert_pair(pair)?, Err($crate::consensus::encode::Error::Psbt($crate::util::psbt::Error::NoMorePairs)) => return Ok(rv), Err(e) => return Err(e), } } } + fn consensus_decode( + d: D, + ) -> Result { + Self::consensus_decode_from_finite_reader(d.take($crate::consensus::encode::MAX_VEC_SIZE as u64)) + } } }; } diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 81aeefa3a9..bea5744bd3 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -295,15 +295,15 @@ impl Encodable for PartiallySignedTransaction { } impl Decodable for PartiallySignedTransaction { - fn consensus_decode(d: D) -> Result { + fn consensus_decode_from_finite_reader(d: D) -> Result { let mut d = d.take(MAX_VEC_SIZE as u64); - let magic: [u8; 4] = Decodable::consensus_decode(&mut d)?; + let magic: [u8; 4] = Decodable::consensus_decode_from_finite_reader(&mut d)?; if *b"psbt" != magic { return Err(Error::InvalidMagic.into()); } - if 0xff_u8 != u8::consensus_decode(&mut d)? { + if 0xff_u8 != u8::consensus_decode_from_finite_reader(&mut d)? { return Err(Error::InvalidSeparator.into()); } @@ -316,7 +316,7 @@ impl Decodable for PartiallySignedTransaction { let mut inputs: Vec = Vec::with_capacity(inputs_len); for _ in 0..inputs_len { - inputs.push(Decodable::consensus_decode(&mut d)?); + inputs.push(Decodable::consensus_decode_from_finite_reader(&mut d)?); } inputs @@ -328,7 +328,7 @@ impl Decodable for PartiallySignedTransaction { let mut outputs: Vec = Vec::with_capacity(outputs_len); for _ in 0..outputs_len { - outputs.push(Decodable::consensus_decode(&mut d)?); + outputs.push(Decodable::consensus_decode_from_finite_reader(&mut d)?); } outputs @@ -338,6 +338,10 @@ impl Decodable for PartiallySignedTransaction { global.outputs = outputs; Ok(global) } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } #[cfg(test)] diff --git a/src/util/psbt/raw.rs b/src/util/psbt/raw.rs index cf7883527f..0b9a5033da 100644 --- a/src/util/psbt/raw.rs +++ b/src/util/psbt/raw.rs @@ -76,8 +76,8 @@ impl fmt::Display for Key { } impl Decodable for Key { - fn consensus_decode(mut d: D) -> Result { - let VarInt(byte_size): VarInt = Decodable::consensus_decode(&mut d)?; + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + let VarInt(byte_size): VarInt = Decodable::consensus_decode_from_finite_reader(&mut d)?; if byte_size == 0 { return Err(Error::NoMorePairs.into()); @@ -92,15 +92,19 @@ impl Decodable for Key { }) } - let type_value: u8 = Decodable::consensus_decode(&mut d)?; + let type_value: u8 = Decodable::consensus_decode_from_finite_reader(&mut d)?; let mut key = Vec::with_capacity(key_byte_size as usize); for _ in 0..key_byte_size { - key.push(Decodable::consensus_decode(&mut d)?); + key.push(Decodable::consensus_decode_from_finite_reader(&mut d)?); } Ok(Key { type_value, key }) } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } impl Encodable for Key { @@ -126,12 +130,15 @@ impl Encodable for Pair { } impl Decodable for Pair { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode_from_finite_reader(mut d: D) -> Result { Ok(Pair { - key: Decodable::consensus_decode(&mut d)?, - value: Decodable::consensus_decode(d)?, + key: Decodable::consensus_decode_from_finite_reader(&mut d)?, + value: Decodable::consensus_decode_from_finite_reader(d)?, }) } + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } impl Encodable for ProprietaryKey where Subtype: Copy + From + Into { @@ -145,13 +152,17 @@ impl Encodable for ProprietaryKey where Subtype: Copy + From Decodable for ProprietaryKey where Subtype: Copy + From + Into { - fn consensus_decode(mut d: D) -> Result { - let prefix = Vec::::consensus_decode(&mut d)?; + fn consensus_decode_from_finite_reader(mut d: D) -> Result { + let prefix = Vec::::consensus_decode_from_finite_reader(&mut d)?; let subtype = Subtype::from(d.read_u8()?); let key = read_to_end(d)?; Ok(ProprietaryKey { prefix, subtype, key }) } + + fn consensus_decode(d: D) -> Result { + Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64)) + } } impl ProprietaryKey where Subtype: Copy + From + Into { diff --git a/src/util/uint.rs b/src/util/uint.rs index ccb702e714..bd0314414b 100644 --- a/src/util/uint.rs +++ b/src/util/uint.rs @@ -433,16 +433,22 @@ macro_rules! construct_uint { } impl $crate::consensus::Decodable for $name { - fn consensus_decode( + fn consensus_decode_from_finite_reader( mut d: D, ) -> Result<$name, $crate::consensus::encode::Error> { use $crate::consensus::Decodable; let mut ret: [u64; $n_words] = [0; $n_words]; for i in 0..$n_words { - ret[i] = Decodable::consensus_decode(&mut d)?; + ret[i] = Decodable::consensus_decode_from_finite_reader(&mut d)?; } Ok($name(ret)) } + + fn consensus_decode( + d: D, + ) -> Result<$name, $crate::consensus::encode::Error> { + Self::consensus_decode_from_finite_reader(d.take($crate::consensus::encode::MAX_VEC_SIZE as u64)) + } } #[cfg(feature = "serde")]