X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;ds=sidebyside;f=lightning%2Fsrc%2Futil%2Fser.rs;h=cbdb5485e7bd984312c5a139e220835769f8f73b;hb=6aca7e1c4db17f43b79504fd44b942b4bc08db9d;hp=e276e72719e4853d7094af615caed56e1415fd8d;hpb=e94647ca4ebc63be4a8164804d08cf37e4655d6c;p=rust-lightning diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index e276e727..cbdb5485 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -31,7 +31,7 @@ use bitcoin::secp256k1::schnorr; use bitcoin::blockdata::constants::ChainHash; use bitcoin::blockdata::script::{self, Script}; use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut}; -use bitcoin::consensus; +use bitcoin::{consensus, Witness}; use bitcoin::consensus::Encodable; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hash_types::{Txid, BlockHash}; @@ -512,6 +512,10 @@ impl_writeable_primitive!(u128, 16); impl_writeable_primitive!(u64, 8); impl_writeable_primitive!(u32, 4); impl_writeable_primitive!(u16, 2); +impl_writeable_primitive!(i64, 8); +impl_writeable_primitive!(i32, 4); +impl_writeable_primitive!(i16, 2); +impl_writeable_primitive!(i8, 1); impl Writeable for u8 { #[inline] @@ -832,6 +836,40 @@ impl_for_vec!((A, B), A, B); impl_writeable_for_vec!(&crate::routing::router::BlindedTail); impl_readable_for_vec!(crate::routing::router::BlindedTail); +impl Writeable for Vec { + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + (self.len() as u16).write(w)?; + for witness in self { + (witness.serialized_len() as u16).write(w)?; + witness.write(w)?; + } + Ok(()) + } +} + +impl Readable for Vec { + #[inline] + fn read(r: &mut R) -> Result { + let num_witnesses = ::read(r)? as usize; + let mut witnesses = Vec::with_capacity(num_witnesses); + for _ in 0..num_witnesses { + // Even though the length of each witness can be inferred in its consensus-encoded form, + // the spec includes a length prefix so that implementations don't have to deserialize + // each initially. We do that here anyway as in general we'll need to be able to make + // assertions on some properties of the witnesses when receiving a message providing a list + // of witnesses. We'll just do a sanity check for the lengths and error if there is a mismatch. + let witness_len = ::read(r)? as usize; + let witness = ::read(r)?; + if witness.serialized_len() != witness_len { + return Err(DecodeError::BadLengthDescriptor); + } + witnesses.push(witness); + } + Ok(witnesses) + } +} + impl Writeable for Script { fn write(&self, w: &mut W) -> Result<(), io::Error> { (self.len() as u16).write(w)?; @@ -1135,6 +1173,7 @@ macro_rules! impl_consensus_ser { } impl_consensus_ser!(Transaction); impl_consensus_ser!(TxOut); +impl_consensus_ser!(Witness); impl Readable for Mutex { fn read(r: &mut R) -> Result { @@ -1310,6 +1349,50 @@ impl Readable for Duration { } } +/// A wrapper for a `Transaction` which can only be constructed with [`TransactionU16LenLimited::new`] +/// if the `Transaction`'s consensus-serialized length is <= u16::MAX. +/// +/// Use [`TransactionU16LenLimited::into_transaction`] to convert into the contained `Transaction`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TransactionU16LenLimited(Transaction); + +impl TransactionU16LenLimited { + /// Constructs a new `TransactionU16LenLimited` from a `Transaction` only if it's consensus- + /// serialized length is <= u16::MAX. + pub fn new(transaction: Transaction) -> Result { + if transaction.serialized_length() > (u16::MAX as usize) { + Err(()) + } else { + Ok(Self(transaction)) + } + } + + /// Consumes this `TransactionU16LenLimited` and returns its contained `Transaction`. + pub fn into_transaction(self) -> Transaction { + self.0 + } +} + +impl Writeable for TransactionU16LenLimited { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + (self.0.serialized_length() as u16).write(w)?; + self.0.write(w) + } +} + +impl Readable for TransactionU16LenLimited { + fn read(r: &mut R) -> Result { + let len = ::read(r)?; + let mut tx_reader = FixedLengthReader::new(r, len as u64); + let tx: Transaction = Readable::read(&mut tx_reader)?; + if tx_reader.bytes_remain() { + Err(DecodeError::BadLengthDescriptor) + } else { + Ok(Self(tx)) + } + } +} + #[cfg(test)] mod tests { use core::convert::TryFrom;