diff --git a/chain/src/txhashset/bitmap_accumulator.rs b/chain/src/txhashset/bitmap_accumulator.rs
index 2990d21e6..49b472716 100644
--- a/chain/src/txhashset/bitmap_accumulator.rs
+++ b/chain/src/txhashset/bitmap_accumulator.rs
@@ -233,7 +233,7 @@ impl Readable for BitmapChunk {
 	/// Reading is not currently supported, just return an empty one for now.
 	/// We store the underlying roaring bitmap externally for the bitmap accumulator
 	/// and the "hash only" backend means we never actually read these chunks.
-	fn read(_reader: &mut dyn Reader) -> Result<BitmapChunk, ser::Error> {
+	fn read<R: Reader>(_reader: &mut R) -> Result<BitmapChunk, ser::Error> {
 		Ok(BitmapChunk::new())
 	}
 }
diff --git a/chain/src/types.rs b/chain/src/types.rs
index 9e3155ddd..12e2d78b6 100644
--- a/chain/src/types.rs
+++ b/chain/src/types.rs
@@ -312,7 +312,7 @@ pub struct CommitPos {
 }
 
 impl Readable for CommitPos {
-	fn read(reader: &mut dyn Reader) -> Result<CommitPos, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<CommitPos, ser::Error> {
 		let pos = reader.read_u64()?;
 		let height = reader.read_u64()?;
 		Ok(CommitPos { pos, height })
@@ -384,7 +384,7 @@ impl ser::Writeable for Tip {
 }
 
 impl ser::Readable for Tip {
-	fn read(reader: &mut dyn ser::Reader) -> Result<Tip, ser::Error> {
+	fn read<R: ser::Reader>(reader: &mut R) -> Result<Tip, ser::Error> {
 		let height = reader.read_u64()?;
 		let last = Hash::read(reader)?;
 		let prev = Hash::read(reader)?;
diff --git a/core/src/core/block.rs b/core/src/core/block.rs
index f916c156f..e892e2709 100644
--- a/core/src/core/block.rs
+++ b/core/src/core/block.rs
@@ -132,7 +132,7 @@ pub struct HeaderEntry {
 }
 
 impl Readable for HeaderEntry {
-	fn read(reader: &mut dyn Reader) -> Result<HeaderEntry, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<HeaderEntry, ser::Error> {
 		let hash = Hash::read(reader)?;
 		let timestamp = reader.read_u64()?;
 		let total_difficulty = Difficulty::read(reader)?;
@@ -192,7 +192,7 @@ impl Writeable for HeaderVersion {
 }
 
 impl Readable for HeaderVersion {
-	fn read(reader: &mut dyn Reader) -> Result<HeaderVersion, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<HeaderVersion, ser::Error> {
 		let version = reader.read_u16()?;
 		Ok(HeaderVersion(version))
 	}
@@ -280,7 +280,7 @@ impl Writeable for BlockHeader {
 	}
 }
 
-fn read_block_header(reader: &mut dyn Reader) -> Result<BlockHeader, ser::Error> {
+fn read_block_header<R: Reader>(reader: &mut R) -> Result<BlockHeader, ser::Error> {
 	let version = HeaderVersion::read(reader)?;
 	let (height, timestamp) = ser_multiread!(reader, read_u64, read_i64);
 	let prev_hash = Hash::read(reader)?;
@@ -316,7 +316,7 @@ fn read_block_header(reader: &mut dyn Reader) -> Result<BlockHeader, ser::Error>
 
 /// Deserialization of a block header
 impl Readable for BlockHeader {
-	fn read(reader: &mut dyn Reader) -> Result<BlockHeader, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<BlockHeader, ser::Error> {
 		read_block_header(reader)
 	}
 }
@@ -413,7 +413,7 @@ pub struct UntrustedBlockHeader(BlockHeader);
 
 /// Deserialization of an untrusted block header
 impl Readable for UntrustedBlockHeader {
-	fn read(reader: &mut dyn Reader) -> Result<UntrustedBlockHeader, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<UntrustedBlockHeader, ser::Error> {
 		let header = read_block_header(reader)?;
 		if header.timestamp
 			> Utc::now() + Duration::seconds(12 * (consensus::BLOCK_TIME_SEC as i64))
@@ -490,7 +490,7 @@ impl Writeable for Block {
 /// Implementation of Readable for a block, defines how to read a full block
 /// from a binary stream.
 impl Readable for Block {
-	fn read(reader: &mut dyn Reader) -> Result<Block, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Block, ser::Error> {
 		let header = BlockHeader::read(reader)?;
 		let body = TransactionBody::read(reader)?;
 		Ok(Block { header, body })
@@ -828,7 +828,7 @@ pub struct UntrustedBlock(Block);
 
 /// Deserialization of an untrusted block header
 impl Readable for UntrustedBlock {
-	fn read(reader: &mut dyn Reader) -> Result<UntrustedBlock, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<UntrustedBlock, ser::Error> {
 		// we validate header here before parsing the body
 		let header = UntrustedBlockHeader::read(reader)?;
 		let body = TransactionBody::read(reader)?;
diff --git a/core/src/core/block_sums.rs b/core/src/core/block_sums.rs
index fa50fbd68..b6b05c886 100644
--- a/core/src/core/block_sums.rs
+++ b/core/src/core/block_sums.rs
@@ -41,7 +41,7 @@ impl Writeable for BlockSums {
 }
 
 impl Readable for BlockSums {
-	fn read(reader: &mut dyn Reader) -> Result<BlockSums, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<BlockSums, ser::Error> {
 		Ok(BlockSums {
 			utxo_sum: Commitment::read(reader)?,
 			kernel_sum: Commitment::read(reader)?,
diff --git a/core/src/core/compact_block.rs b/core/src/core/compact_block.rs
index 8a4b088af..128c1e1e7 100644
--- a/core/src/core/compact_block.rs
+++ b/core/src/core/compact_block.rs
@@ -82,7 +82,7 @@ impl CompactBlockBody {
 }
 
 impl Readable for CompactBlockBody {
-	fn read(reader: &mut dyn Reader) -> Result<CompactBlockBody, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<CompactBlockBody, ser::Error> {
 		let (out_full_len, kern_full_len, kern_id_len) =
 			ser_multiread!(reader, read_u64, read_u64, read_u64);
 
@@ -215,7 +215,7 @@ impl Writeable for CompactBlock {
 /// Implementation of Readable for a compact block, defines how to read a
 /// compact block from a binary stream.
 impl Readable for CompactBlock {
-	fn read(reader: &mut dyn Reader) -> Result<CompactBlock, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<CompactBlock, ser::Error> {
 		let header = BlockHeader::read(reader)?;
 		let nonce = reader.read_u64()?;
 		let body = CompactBlockBody::read(reader)?;
@@ -241,7 +241,7 @@ pub struct UntrustedCompactBlock(CompactBlock);
 /// Implementation of Readable for an untrusted compact block, defines how to read a
 /// compact block from a binary stream.
 impl Readable for UntrustedCompactBlock {
-	fn read(reader: &mut dyn Reader) -> Result<UntrustedCompactBlock, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<UntrustedCompactBlock, ser::Error> {
 		let header = UntrustedBlockHeader::read(reader)?;
 		let nonce = reader.read_u64()?;
 		let body = CompactBlockBody::read(reader)?;
diff --git a/core/src/core/hash.rs b/core/src/core/hash.rs
index 34c17fc55..2dc968fa4 100644
--- a/core/src/core/hash.rs
+++ b/core/src/core/hash.rs
@@ -131,7 +131,7 @@ impl AsRef<[u8]> for Hash {
 }
 
 impl Readable for Hash {
-	fn read(reader: &mut dyn Reader) -> Result<Hash, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Hash, ser::Error> {
 		let v = reader.read_fixed_bytes(32)?;
 		let mut a = [0; 32];
 		a.copy_from_slice(&v[..]);
diff --git a/core/src/core/id.rs b/core/src/core/id.rs
index af97709f4..dec4fea2c 100644
--- a/core/src/core/id.rs
+++ b/core/src/core/id.rs
@@ -91,7 +91,7 @@ impl AsRef<[u8]> for ShortId {
 }
 
 impl Readable for ShortId {
-	fn read(reader: &mut dyn Reader) -> Result<ShortId, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<ShortId, ser::Error> {
 		let v = reader.read_fixed_bytes(SHORT_ID_SIZE)?;
 		let mut a = [0; SHORT_ID_SIZE];
 		a.copy_from_slice(&v[..]);
diff --git a/core/src/core/merkle_proof.rs b/core/src/core/merkle_proof.rs
index 813a87d7f..8711ab5fc 100644
--- a/core/src/core/merkle_proof.rs
+++ b/core/src/core/merkle_proof.rs
@@ -47,7 +47,7 @@ impl Writeable for MerkleProof {
 }
 
 impl Readable for MerkleProof {
-	fn read(reader: &mut dyn Reader) -> Result<MerkleProof, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<MerkleProof, ser::Error> {
 		let mmr_size = reader.read_u64()?;
 		let path_len = reader.read_u64()?;
 		let mut path = Vec::with_capacity(path_len as usize);
diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs
index 2da44cb5a..1502323dc 100644
--- a/core/src/core/transaction.rs
+++ b/core/src/core/transaction.rs
@@ -133,7 +133,7 @@ impl KernelFeatures {
 	// Always read feature byte, 8 bytes for fee and 8 bytes for lock height.
 	// Fee and lock height may be unused for some kernel variants but we need
 	// to read these bytes and verify they are 0 if unused.
-	fn read_v1(reader: &mut dyn Reader) -> Result<KernelFeatures, ser::Error> {
+	fn read_v1<R: Reader>(reader: &mut R) -> Result<KernelFeatures, ser::Error> {
 		let feature_byte = reader.read_u8()?;
 		let fee = reader.read_u64()?;
 		let lock_height = reader.read_u64()?;
@@ -164,7 +164,7 @@ impl KernelFeatures {
 
 	// V2 kernels only expect bytes specific to each variant.
 	// Coinbase kernels have no associated fee and we do not serialize a fee for these.
-	fn read_v2(reader: &mut dyn Reader) -> Result<KernelFeatures, ser::Error> {
+	fn read_v2<R: Reader>(reader: &mut R) -> Result<KernelFeatures, ser::Error> {
 		let features = match reader.read_u8()? {
 			KernelFeatures::PLAIN_U8 => {
 				let fee = reader.read_u64()?;
@@ -202,7 +202,7 @@ impl Writeable for KernelFeatures {
 }
 
 impl Readable for KernelFeatures {
-	fn read(reader: &mut dyn Reader) -> Result<KernelFeatures, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<KernelFeatures, ser::Error> {
 		match reader.protocol_version().value() {
 			0..=1 => KernelFeatures::read_v1(reader),
 			2..=ProtocolVersion::MAX => KernelFeatures::read_v2(reader),
@@ -336,7 +336,7 @@ impl Writeable for TxKernel {
 }
 
 impl Readable for TxKernel {
-	fn read(reader: &mut dyn Reader) -> Result<TxKernel, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<TxKernel, ser::Error> {
 		Ok(TxKernel {
 			features: KernelFeatures::read(reader)?,
 			excess: Commitment::read(reader)?,
@@ -534,7 +534,7 @@ impl Writeable for TransactionBody {
 /// Implementation of Readable for a body, defines how to read a
 /// body from a binary stream.
 impl Readable for TransactionBody {
-	fn read(reader: &mut dyn Reader) -> Result<TransactionBody, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<TransactionBody, ser::Error> {
 		let (input_len, output_len, kernel_len) =
 			ser_multiread!(reader, read_u64, read_u64, read_u64);
 
@@ -908,7 +908,7 @@ impl Writeable for Transaction {
 /// Implementation of Readable for a transaction, defines how to read a full
 /// transaction from a binary stream.
 impl Readable for Transaction {
-	fn read(reader: &mut dyn Reader) -> Result<Transaction, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Transaction, ser::Error> {
 		let offset = BlindingFactor::read(reader)?;
 		let body = TransactionBody::read(reader)?;
 		let tx = Transaction { offset, body };
@@ -1294,7 +1294,7 @@ impl Writeable for Input {
 /// Implementation of Readable for a transaction Input, defines how to read
 /// an Input from a binary stream.
 impl Readable for Input {
-	fn read(reader: &mut dyn Reader) -> Result<Input, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Input, ser::Error> {
 		let features = OutputFeatures::read(reader)?;
 		let commit = Commitment::read(reader)?;
 		Ok(Input::new(features, commit))
@@ -1352,7 +1352,7 @@ impl Writeable for OutputFeatures {
 }
 
 impl Readable for OutputFeatures {
-	fn read(reader: &mut dyn Reader) -> Result<OutputFeatures, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<OutputFeatures, ser::Error> {
 		let features =
 			OutputFeatures::from_u8(reader.read_u8()?).ok_or(ser::Error::CorruptedData)?;
 		Ok(features)
@@ -1410,7 +1410,7 @@ impl Writeable for Output {
 /// Implementation of Readable for a transaction Output, defines how to read
 /// an Output from a binary stream.
 impl Readable for Output {
-	fn read(reader: &mut dyn Reader) -> Result<Output, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Output, ser::Error> {
 		Ok(Output {
 			features: OutputFeatures::read(reader)?,
 			commit: Commitment::read(reader)?,
@@ -1545,7 +1545,7 @@ impl Writeable for OutputIdentifier {
 }
 
 impl Readable for OutputIdentifier {
-	fn read(reader: &mut dyn Reader) -> Result<OutputIdentifier, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<OutputIdentifier, ser::Error> {
 		Ok(OutputIdentifier {
 			features: OutputFeatures::read(reader)?,
 			commit: Commitment::read(reader)?,
diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs
index 001f57a8b..c03f3a203 100644
--- a/core/src/pow/types.rs
+++ b/core/src/pow/types.rs
@@ -150,7 +150,7 @@ impl Writeable for Difficulty {
 }
 
 impl Readable for Difficulty {
-	fn read(reader: &mut dyn Reader) -> Result<Difficulty, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Difficulty, ser::Error> {
 		let data = reader.read_u64()?;
 		Ok(Difficulty { num: data })
 	}
@@ -249,7 +249,7 @@ impl Writeable for ProofOfWork {
 }
 
 impl Readable for ProofOfWork {
-	fn read(reader: &mut dyn Reader) -> Result<ProofOfWork, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<ProofOfWork, ser::Error> {
 		let total_difficulty = Difficulty::read(reader)?;
 		let secondary_scaling = reader.read_u32()?;
 		let nonce = reader.read_u64()?;
@@ -425,7 +425,7 @@ fn read_number(bits: &[u8], bit_start: usize, bit_count: usize) -> u64 {
 }
 
 impl Readable for Proof {
-	fn read(reader: &mut dyn Reader) -> Result<Proof, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Proof, ser::Error> {
 		let edge_bits = reader.read_u8()?;
 		if edge_bits == 0 || edge_bits > 63 {
 			return Err(ser::Error::CorruptedData);
diff --git a/core/src/ser.rs b/core/src/ser.rs
index 0dd583347..a99229565 100644
--- a/core/src/ser.rs
+++ b/core/src/ser.rs
@@ -224,17 +224,17 @@ pub trait Writeable {
 }
 
 /// Reader that exposes an Iterator interface.
-pub struct IteratingReader<'a, T> {
+pub struct IteratingReader<'a, T, R: Reader> {
 	count: u64,
 	curr: u64,
-	reader: &'a mut dyn Reader,
+	reader: &'a mut R,
 	_marker: marker::PhantomData<T>,
 }
 
-impl<'a, T> IteratingReader<'a, T> {
+impl<'a, T, R: Reader> IteratingReader<'a, T, R> {
 	/// Constructor to create a new iterating reader for the provided underlying reader.
 	/// Takes a count so we know how many to iterate over.
-	pub fn new(reader: &'a mut dyn Reader, count: u64) -> IteratingReader<'a, T> {
+	pub fn new(reader: &'a mut R, count: u64) -> Self {
 		let curr = 0;
 		IteratingReader {
 			count,
@@ -245,9 +245,10 @@ impl<'a, T> IteratingReader<'a, T> {
 	}
 }
 
-impl<'a, T> Iterator for IteratingReader<'a, T>
+impl<'a, T, R> Iterator for IteratingReader<'a, T, R>
 where
 	T: Readable,
+	R: Reader,
 {
 	type Item = T;
 
@@ -261,9 +262,10 @@ where
 }
 
 /// Reads multiple serialized items into a Vec.
-pub fn read_multi<T>(reader: &mut dyn Reader, count: u64) -> Result<Vec<T>, Error>
+pub fn read_multi<T, R>(reader: &mut R, count: u64) -> Result<Vec<T>, Error>
 where
 	T: Readable,
+	R: Reader,
 {
 	// Very rudimentary check to ensure we do not overflow anything
 	// attempting to read huge amounts of data.
@@ -331,7 +333,7 @@ impl Writeable for ProtocolVersion {
 }
 
 impl Readable for ProtocolVersion {
-	fn read(reader: &mut dyn Reader) -> Result<ProtocolVersion, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<ProtocolVersion, Error> {
 		let version = reader.read_u32()?;
 		Ok(ProtocolVersion(version))
 	}
@@ -345,12 +347,12 @@ where
 	Self: Sized,
 {
 	/// Reads the data necessary to this Readable from the provided reader
-	fn read(reader: &mut dyn Reader) -> Result<Self, Error>;
+	fn read<R: Reader>(reader: &mut R) -> Result<Self, Error>;
 }
 
 /// Deserializes a Readable from any std::io::Read implementation.
-pub fn deserialize<T: Readable>(
-	source: &mut dyn Read,
+pub fn deserialize<T: Readable, R: Read>(
+	source: &mut R,
 	version: ProtocolVersion,
 ) -> Result<T, Error> {
 	let mut reader = BinReader::new(source, version);
@@ -358,7 +360,7 @@ pub fn deserialize<T: Readable>(
 }
 
 /// Deserialize a Readable based on our default "local" protocol version.
-pub fn deserialize_default<T: Readable>(source: &mut dyn Read) -> Result<T, Error> {
+pub fn deserialize_default<T: Readable, R: Read>(source: &mut R) -> Result<T, Error> {
 	deserialize(source, ProtocolVersion::local())
 }
 
@@ -386,14 +388,14 @@ pub fn ser_vec<W: Writeable>(thing: &W, version: ProtocolVersion) -> Result<Vec<
 }
 
 /// Utility to read from a binary source
-pub struct BinReader<'a> {
-	source: &'a mut dyn Read,
+pub struct BinReader<'a, R: Read> {
+	source: &'a mut R,
 	version: ProtocolVersion,
 }
 
-impl<'a> BinReader<'a> {
+impl<'a, R: Read> BinReader<'a, R> {
 	/// Constructor for a new BinReader for the provided source and protocol version.
-	pub fn new(source: &'a mut dyn Read, version: ProtocolVersion) -> BinReader<'a> {
+	pub fn new(source: &'a mut R, version: ProtocolVersion) -> Self {
 		BinReader { source, version }
 	}
 }
@@ -404,7 +406,7 @@ fn map_io_err(err: io::Error) -> Error {
 
 /// Utility wrapper for an underlying byte Reader. Defines higher level methods
 /// to read numbers, byte vectors, hashes, etc.
-impl<'a> Reader for BinReader<'a> {
+impl<'a, R: Read> Reader for BinReader<'a, R> {
 	fn read_u8(&mut self) -> Result<u8, Error> {
 		self.source.read_u8().map_err(map_io_err)
 	}
@@ -544,7 +546,7 @@ impl<'a> Reader for StreamingReader<'a> {
 }
 
 impl Readable for Commitment {
-	fn read(reader: &mut dyn Reader) -> Result<Commitment, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Commitment, Error> {
 		let a = reader.read_fixed_bytes(PEDERSEN_COMMITMENT_SIZE)?;
 		let mut c = [0; PEDERSEN_COMMITMENT_SIZE];
 		c[..PEDERSEN_COMMITMENT_SIZE].clone_from_slice(&a[..PEDERSEN_COMMITMENT_SIZE]);
@@ -565,7 +567,7 @@ impl Writeable for BlindingFactor {
 }
 
 impl Readable for BlindingFactor {
-	fn read(reader: &mut dyn Reader) -> Result<BlindingFactor, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<BlindingFactor, Error> {
 		let bytes = reader.read_fixed_bytes(SECRET_KEY_SIZE)?;
 		Ok(BlindingFactor::from_slice(&bytes))
 	}
@@ -578,7 +580,7 @@ impl Writeable for Identifier {
 }
 
 impl Readable for Identifier {
-	fn read(reader: &mut dyn Reader) -> Result<Identifier, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Identifier, Error> {
 		let bytes = reader.read_fixed_bytes(IDENTIFIER_SIZE)?;
 		Ok(Identifier::from_bytes(&bytes))
 	}
@@ -591,7 +593,7 @@ impl Writeable for RangeProof {
 }
 
 impl Readable for RangeProof {
-	fn read(reader: &mut dyn Reader) -> Result<RangeProof, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<RangeProof, Error> {
 		let len = reader.read_u64()?;
 		let max_len = cmp::min(len as usize, MAX_PROOF_SIZE);
 		let p = reader.read_fixed_bytes(max_len)?;
@@ -618,7 +620,7 @@ impl PMMRable for RangeProof {
 }
 
 impl Readable for Signature {
-	fn read(reader: &mut dyn Reader) -> Result<Signature, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Signature, Error> {
 		let a = reader.read_fixed_bytes(AGG_SIGNATURE_SIZE)?;
 		let mut c = [0; AGG_SIGNATURE_SIZE];
 		c[..AGG_SIGNATURE_SIZE].clone_from_slice(&a[..AGG_SIGNATURE_SIZE]);
@@ -643,7 +645,7 @@ impl Writeable for PublicKey {
 
 impl Readable for PublicKey {
 	// Read the public key in compressed form
-	fn read(reader: &mut dyn Reader) -> Result<Self, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Self, Error> {
 		let buf = reader.read_fixed_bytes(COMPRESSED_PUBLIC_KEY_SIZE)?;
 		let secp = Secp256k1::with_caps(ContextFlag::None);
 		let pk = PublicKey::from_slice(&secp, &buf).map_err(|_| Error::CorruptedData)?;
@@ -715,7 +717,7 @@ macro_rules! impl_int {
 		}
 
 		impl Readable for $int {
-			fn read(reader: &mut dyn Reader) -> Result<$int, Error> {
+			fn read<R: Reader>(reader: &mut R) -> Result<$int, Error> {
 				reader.$r_fn()
 			}
 		}
@@ -733,7 +735,7 @@ impl<T> Readable for Vec<T>
 where
 	T: Readable,
 {
-	fn read(reader: &mut dyn Reader) -> Result<Vec<T>, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Vec<T>, Error> {
 		let mut buf = Vec::new();
 		loop {
 			let elem = T::read(reader);
@@ -775,7 +777,7 @@ impl<A: Writeable, B: Writeable> Writeable for (A, B) {
 }
 
 impl<A: Readable, B: Readable> Readable for (A, B) {
-	fn read(reader: &mut dyn Reader) -> Result<(A, B), Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<(A, B), Error> {
 		Ok((Readable::read(reader)?, Readable::read(reader)?))
 	}
 }
@@ -798,7 +800,7 @@ impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable> Writeable for (A, B
 }
 
 impl<A: Readable, B: Readable, C: Readable> Readable for (A, B, C) {
-	fn read(reader: &mut dyn Reader) -> Result<(A, B, C), Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<(A, B, C), Error> {
 		Ok((
 			Readable::read(reader)?,
 			Readable::read(reader)?,
@@ -808,7 +810,7 @@ impl<A: Readable, B: Readable, C: Readable> Readable for (A, B, C) {
 }
 
 impl<A: Readable, B: Readable, C: Readable, D: Readable> Readable for (A, B, C, D) {
-	fn read(reader: &mut dyn Reader) -> Result<(A, B, C, D), Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<(A, B, C, D), Error> {
 		Ok((
 			Readable::read(reader)?,
 			Readable::read(reader)?,
diff --git a/core/tests/common.rs b/core/tests/common.rs
index d184beea6..758577393 100644
--- a/core/tests/common.rs
+++ b/core/tests/common.rs
@@ -155,7 +155,7 @@ impl Writeable for TestElem {
 }
 
 impl Readable for TestElem {
-	fn read(reader: &mut dyn Reader) -> Result<TestElem, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<TestElem, ser::Error> {
 		Ok(TestElem([
 			reader.read_u32()?,
 			reader.read_u32()?,
diff --git a/p2p/src/conn.rs b/p2p/src/conn.rs
index f848e746a..f1a7c9b35 100644
--- a/p2p/src/conn.rs
+++ b/p2p/src/conn.rs
@@ -48,9 +48,9 @@ const BODY_IO_TIMEOUT: Duration = Duration::from_millis(60000);
 /// A trait to be implemented in order to receive messages from the
 /// connection. Allows providing an optional response.
 pub trait MessageHandler: Send + 'static {
-	fn consume<'a>(
+	fn consume<'a, R: Read>(
 		&self,
-		msg: Message<'a>,
+		msg: Message<'a, R>,
 		stopped: Arc<AtomicBool>,
 		tracker: Arc<Tracker>,
 	) -> Result<Option<Msg>, Error>;
@@ -88,18 +88,14 @@ macro_rules! try_header {
 
 /// A message as received by the connection. Provides access to the message
 /// header lazily consumes the message body, handling its deserialization.
-pub struct Message<'a> {
+pub struct Message<'a, R: Read> {
 	pub header: MsgHeader,
-	stream: &'a mut dyn Read,
+	stream: &'a mut R,
 	version: ProtocolVersion,
 }
 
-impl<'a> Message<'a> {
-	fn from_header(
-		header: MsgHeader,
-		stream: &'a mut dyn Read,
-		version: ProtocolVersion,
-	) -> Message<'a> {
+impl<'a, R: Read> Message<'a, R> {
+	fn from_header(header: MsgHeader, stream: &'a mut R, version: ProtocolVersion) -> Self {
 		Message {
 			header,
 			stream,
diff --git a/p2p/src/msg.rs b/p2p/src/msg.rs
index c41498281..0907bf048 100644
--- a/p2p/src/msg.rs
+++ b/p2p/src/msg.rs
@@ -146,21 +146,21 @@ impl Msg {
 ///
 /// Note: We return a MsgHeaderWrapper here as we may encounter an unknown msg type.
 ///
-pub fn read_header(
-	stream: &mut dyn Read,
+pub fn read_header<R: Read>(
+	stream: &mut R,
 	version: ProtocolVersion,
 ) -> Result<MsgHeaderWrapper, Error> {
 	let mut head = vec![0u8; MsgHeader::LEN];
 	stream.read_exact(&mut head)?;
-	let header = ser::deserialize::<MsgHeaderWrapper>(&mut &head[..], version)?;
+	let header: MsgHeaderWrapper = ser::deserialize(&mut &head[..], version)?;
 	Ok(header)
 }
 
 /// Read a single item from the provided stream, always blocking until we
 /// have a result (or timeout).
 /// Returns the item and the total bytes read.
-pub fn read_item<T: Readable>(
-	stream: &mut dyn Read,
+pub fn read_item<T: Readable, R: Read>(
+	stream: &mut R,
 	version: ProtocolVersion,
 ) -> Result<(T, u64), Error> {
 	let mut reader = StreamingReader::new(stream, version);
@@ -170,9 +170,9 @@ pub fn read_item<T: Readable>(
 
 /// Read a message body from the provided stream, always blocking
 /// until we have a result (or timeout).
-pub fn read_body<T: Readable>(
+pub fn read_body<T: Readable, R: Read>(
 	h: &MsgHeader,
-	stream: &mut dyn Read,
+	stream: &mut R,
 	version: ProtocolVersion,
 ) -> Result<T, Error> {
 	let mut body = vec![0u8; h.msg_len as usize];
@@ -181,15 +181,15 @@ pub fn read_body<T: Readable>(
 }
 
 /// Read (an unknown) message from the provided stream and discard it.
-pub fn read_discard(msg_len: u64, stream: &mut dyn Read) -> Result<(), Error> {
+pub fn read_discard<R: Read>(msg_len: u64, stream: &mut R) -> Result<(), Error> {
 	let mut buffer = vec![0u8; msg_len as usize];
 	stream.read_exact(&mut buffer)?;
 	Ok(())
 }
 
 /// Reads a full message from the underlying stream.
-pub fn read_message<T: Readable>(
-	stream: &mut dyn Read,
+pub fn read_message<T: Readable, R: Read>(
+	stream: &mut R,
 	version: ProtocolVersion,
 	msg_type: Type,
 ) -> Result<T, Error> {
@@ -208,8 +208,8 @@ pub fn read_message<T: Readable>(
 	}
 }
 
-pub fn write_message(
-	stream: &mut dyn Write,
+pub fn write_message<W: Write>(
+	stream: &mut W,
 	msg: &Msg,
 	tracker: Arc<Tracker>,
 ) -> Result<(), Error> {
@@ -285,7 +285,7 @@ impl Writeable for MsgHeader {
 }
 
 impl Readable for MsgHeaderWrapper {
-	fn read(reader: &mut dyn Reader) -> Result<MsgHeaderWrapper, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<MsgHeaderWrapper, ser::Error> {
 		let m = magic();
 		reader.expect_u8(m[0])?;
 		reader.expect_u8(m[1])?;
@@ -371,7 +371,7 @@ impl Writeable for Hand {
 }
 
 impl Readable for Hand {
-	fn read(reader: &mut dyn Reader) -> Result<Hand, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Hand, ser::Error> {
 		let version = ProtocolVersion::read(reader)?;
 		let (capab, nonce) = ser_multiread!(reader, read_u32, read_u64);
 		let capabilities = Capabilities::from_bits_truncate(capab);
@@ -422,7 +422,7 @@ impl Writeable for Shake {
 }
 
 impl Readable for Shake {
-	fn read(reader: &mut dyn Reader) -> Result<Shake, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Shake, ser::Error> {
 		let version = ProtocolVersion::read(reader)?;
 		let capab = reader.read_u32()?;
 		let capabilities = Capabilities::from_bits_truncate(capab);
@@ -453,7 +453,7 @@ impl Writeable for GetPeerAddrs {
 }
 
 impl Readable for GetPeerAddrs {
-	fn read(reader: &mut dyn Reader) -> Result<GetPeerAddrs, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<GetPeerAddrs, ser::Error> {
 		let capab = reader.read_u32()?;
 		let capabilities = Capabilities::from_bits_truncate(capab);
 		Ok(GetPeerAddrs { capabilities })
@@ -478,7 +478,7 @@ impl Writeable for PeerAddrs {
 }
 
 impl Readable for PeerAddrs {
-	fn read(reader: &mut dyn Reader) -> Result<PeerAddrs, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<PeerAddrs, ser::Error> {
 		let peer_count = reader.read_u32()?;
 		if peer_count > MAX_PEER_ADDRS {
 			return Err(ser::Error::TooLargeReadErr);
@@ -510,7 +510,7 @@ impl Writeable for PeerError {
 }
 
 impl Readable for PeerError {
-	fn read(reader: &mut dyn Reader) -> Result<PeerError, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<PeerError, ser::Error> {
 		let code = reader.read_u32()?;
 		let msg = reader.read_bytes_len_prefix()?;
 		let message = String::from_utf8(msg).map_err(|_| ser::Error::CorruptedData)?;
@@ -538,7 +538,7 @@ impl Writeable for Locator {
 }
 
 impl Readable for Locator {
-	fn read(reader: &mut dyn Reader) -> Result<Locator, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Locator, ser::Error> {
 		let len = reader.read_u8()?;
 		if len > (MAX_LOCATORS as u8) {
 			return Err(ser::Error::TooLargeReadErr);
@@ -583,7 +583,7 @@ impl Writeable for Ping {
 }
 
 impl Readable for Ping {
-	fn read(reader: &mut dyn Reader) -> Result<Ping, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Ping, ser::Error> {
 		let total_difficulty = Difficulty::read(reader)?;
 		let height = reader.read_u64()?;
 		Ok(Ping {
@@ -610,7 +610,7 @@ impl Writeable for Pong {
 }
 
 impl Readable for Pong {
-	fn read(reader: &mut dyn Reader) -> Result<Pong, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<Pong, ser::Error> {
 		let total_difficulty = Difficulty::read(reader)?;
 		let height = reader.read_u64()?;
 		Ok(Pong {
@@ -635,7 +635,7 @@ impl Writeable for BanReason {
 }
 
 impl Readable for BanReason {
-	fn read(reader: &mut dyn Reader) -> Result<BanReason, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<BanReason, ser::Error> {
 		let ban_reason_i32 = match reader.read_i32() {
 			Ok(h) => h,
 			Err(_) => 0,
@@ -665,7 +665,7 @@ impl Writeable for TxHashSetRequest {
 }
 
 impl Readable for TxHashSetRequest {
-	fn read(reader: &mut dyn Reader) -> Result<TxHashSetRequest, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<TxHashSetRequest, ser::Error> {
 		Ok(TxHashSetRequest {
 			hash: Hash::read(reader)?,
 			height: reader.read_u64()?,
@@ -693,7 +693,7 @@ impl Writeable for TxHashSetArchive {
 }
 
 impl Readable for TxHashSetArchive {
-	fn read(reader: &mut dyn Reader) -> Result<TxHashSetArchive, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<TxHashSetArchive, ser::Error> {
 		let hash = Hash::read(reader)?;
 		let (height, bytes) = ser_multiread!(reader, read_u64, read_u64);
 
diff --git a/p2p/src/protocol.rs b/p2p/src/protocol.rs
index b8cd75b58..bfdf4b56d 100644
--- a/p2p/src/protocol.rs
+++ b/p2p/src/protocol.rs
@@ -25,7 +25,7 @@ use chrono::prelude::Utc;
 use rand::{thread_rng, Rng};
 use std::cmp;
 use std::fs::{self, File, OpenOptions};
-use std::io::BufWriter;
+use std::io::{BufWriter, Read};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use std::time::Instant;
@@ -51,9 +51,9 @@ impl Protocol {
 }
 
 impl MessageHandler for Protocol {
-	fn consume(
+	fn consume<R: Read>(
 		&self,
-		mut msg: Message,
+		mut msg: Message<R>,
 		stopped: Arc<AtomicBool>,
 		tracker: Arc<Tracker>,
 	) -> Result<Option<Msg>, Error> {
diff --git a/p2p/src/store.rs b/p2p/src/store.rs
index e3627786c..baba718c9 100644
--- a/p2p/src/store.rs
+++ b/p2p/src/store.rs
@@ -75,7 +75,7 @@ impl Writeable for PeerData {
 }
 
 impl Readable for PeerData {
-	fn read(reader: &mut dyn Reader) -> Result<PeerData, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<PeerData, ser::Error> {
 		let addr = PeerAddr::read(reader)?;
 		let capab = reader.read_u32()?;
 		let ua = reader.read_bytes_len_prefix()?;
diff --git a/p2p/src/types.rs b/p2p/src/types.rs
index 8e24d624d..24066f6d3 100644
--- a/p2p/src/types.rs
+++ b/p2p/src/types.rs
@@ -138,7 +138,7 @@ impl Writeable for PeerAddr {
 }
 
 impl Readable for PeerAddr {
-	fn read(reader: &mut dyn Reader) -> Result<PeerAddr, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<PeerAddr, ser::Error> {
 		let v4_or_v6 = reader.read_u8()?;
 		if v4_or_v6 == 0 {
 			let ip = reader.read_fixed_bytes(4)?;
diff --git a/store/src/types.rs b/store/src/types.rs
index c1621cc6e..0b2c7a633 100644
--- a/store/src/types.rs
+++ b/store/src/types.rs
@@ -44,7 +44,7 @@ impl SizeEntry {
 }
 
 impl Readable for SizeEntry {
-	fn read(reader: &mut dyn Reader) -> Result<SizeEntry, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<SizeEntry, ser::Error> {
 		Ok(SizeEntry {
 			offset: reader.read_u64()?,
 			size: reader.read_u16()?,
diff --git a/store/tests/lmdb.rs b/store/tests/lmdb.rs
index 0f37916cb..a8d1e397c 100644
--- a/store/tests/lmdb.rs
+++ b/store/tests/lmdb.rs
@@ -35,7 +35,7 @@ impl PhatChunkStruct {
 }
 
 impl Readable for PhatChunkStruct {
-	fn read(reader: &mut dyn Reader) -> Result<PhatChunkStruct, ser::Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<PhatChunkStruct, ser::Error> {
 		let mut retval = PhatChunkStruct::new();
 		for _ in 0..TEST_ALLOC_SIZE {
 			retval.phatness = reader.read_u64()?;
diff --git a/store/tests/pmmr.rs b/store/tests/pmmr.rs
index 931e7c432..7e4e9bbe0 100644
--- a/store/tests/pmmr.rs
+++ b/store/tests/pmmr.rs
@@ -982,7 +982,7 @@ impl Writeable for TestElem {
 	}
 }
 impl Readable for TestElem {
-	fn read(reader: &mut dyn Reader) -> Result<TestElem, Error> {
+	fn read<R: Reader>(reader: &mut R) -> Result<TestElem, Error> {
 		Ok(TestElem(reader.read_u32()?))
 	}
 }