Implement Readable/Writeable for Events
authorMatt Corallo <git@bluematt.me>
Wed, 12 Feb 2020 22:02:03 +0000 (17:02 -0500)
committerMatt Corallo <git@bluematt.me>
Fri, 28 Feb 2020 19:36:02 +0000 (14:36 -0500)
As noted in the docs, Events don't round-trip fully, but round-trip
in a way that is useful for ChannelManagers, specifically some events
don't make sense anymore after a restart.

lightning/src/chain/keysinterface.rs
lightning/src/util/events.rs
lightning/src/util/ser.rs

index e2e4403b528997e7a4ce56fb2a4591af85631efa..544df015a8f48b404c390a63a909ec7aca3746ed 100644 (file)
@@ -88,6 +88,57 @@ pub enum SpendableOutputDescriptor {
        }
 }
 
+impl Writeable for SpendableOutputDescriptor {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
+               match self {
+                       &SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
+                               0u8.write(writer)?;
+                               outpoint.write(writer)?;
+                               output.write(writer)?;
+                       },
+                       &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref key, ref witness_script, ref to_self_delay, ref output } => {
+                               1u8.write(writer)?;
+                               outpoint.write(writer)?;
+                               key.write(writer)?;
+                               witness_script.write(writer)?;
+                               to_self_delay.write(writer)?;
+                               output.write(writer)?;
+                       },
+                       &SpendableOutputDescriptor::DynamicOutputP2WPKH { ref outpoint, ref key, ref output } => {
+                               2u8.write(writer)?;
+                               outpoint.write(writer)?;
+                               key.write(writer)?;
+                               output.write(writer)?;
+                       },
+               }
+               Ok(())
+       }
+}
+
+impl<R: ::std::io::Read> Readable<R> for SpendableOutputDescriptor {
+       fn read(reader: &mut R) -> Result<Self, DecodeError> {
+               match Readable::read(reader)? {
+                       0u8 => Ok(SpendableOutputDescriptor::StaticOutput {
+                               outpoint: Readable::read(reader)?,
+                               output: Readable::read(reader)?,
+                       }),
+                       1u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WSH {
+                               outpoint: Readable::read(reader)?,
+                               key: Readable::read(reader)?,
+                               witness_script: Readable::read(reader)?,
+                               to_self_delay: Readable::read(reader)?,
+                               output: Readable::read(reader)?,
+                       }),
+                       2u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WPKH {
+                               outpoint: Readable::read(reader)?,
+                               key: Readable::read(reader)?,
+                               output: Readable::read(reader)?,
+                       }),
+                       _ => Err(DecodeError::InvalidValue),
+               }
+       }
+}
+
 /// A trait to describe an object which can get user secrets and key material.
 pub trait KeysInterface: Send + Sync {
        /// A type which implements ChannelKeys which will be returned by get_channel_keys.
index eda6fc7ee4eab89068a0b7f7a5848c5a3d2e2810..a41b7a3bbb95243b21d271359b66c1f58d88a60f 100644 (file)
@@ -16,6 +16,7 @@ use ln::msgs;
 use ln::channelmanager::{PaymentPreimage, PaymentHash};
 use chain::transaction::OutPoint;
 use chain::keysinterface::SpendableOutputDescriptor;
+use util::ser::{Writeable, Writer, MaybeReadable, Readable};
 
 use bitcoin::blockdata::script::Script;
 
@@ -24,6 +25,10 @@ use secp256k1::key::PublicKey;
 use std::time::Duration;
 
 /// An Event which you should probably take some action in response to.
+///
+/// Note that while Writeable and Readable are implemented for Event, you probably shouldn't use
+/// them directly as they don't round-trip exactly (for example FundingGenerationReady is never
+/// written as it makes no sense to respond to it after reconnecting to peers).
 pub enum Event {
        /// Used to indicate that the client should generate a funding transaction with the given
        /// parameters and then call ChannelManager::funding_transaction_generated.
@@ -108,6 +113,91 @@ pub enum Event {
        },
 }
 
+impl Writeable for Event {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
+               match self {
+                       &Event::FundingGenerationReady { .. } => {
+                               0u8.write(writer)?;
+                               // We never write out FundingGenerationReady events as, upon disconnection, peers
+                               // drop any channels which have not yet exchanged funding_signed.
+                       },
+                       &Event::FundingBroadcastSafe { ref funding_txo, ref user_channel_id } => {
+                               1u8.write(writer)?;
+                               funding_txo.write(writer)?;
+                               user_channel_id.write(writer)?;
+                       },
+                       &Event::PaymentReceived { ref payment_hash, ref amt } => {
+                               2u8.write(writer)?;
+                               payment_hash.write(writer)?;
+                               amt.write(writer)?;
+                       },
+                       &Event::PaymentSent { ref payment_preimage } => {
+                               3u8.write(writer)?;
+                               payment_preimage.write(writer)?;
+                       },
+                       &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest,
+                               #[cfg(test)]
+                               ref error_code,
+                       } => {
+                               4u8.write(writer)?;
+                               payment_hash.write(writer)?;
+                               rejected_by_dest.write(writer)?;
+                               #[cfg(test)]
+                               error_code.write(writer)?;
+                       },
+                       &Event::PendingHTLCsForwardable { time_forwardable: _ } => {
+                               5u8.write(writer)?;
+                               // We don't write the time_fordwardable out at all, as we presume when the user
+                               // deserializes us at least that much time has elapsed.
+                       },
+                       &Event::SpendableOutputs { ref outputs } => {
+                               6u8.write(writer)?;
+                               (outputs.len() as u64).write(writer)?;
+                               for output in outputs.iter() {
+                                       output.write(writer)?;
+                               }
+                       },
+               }
+               Ok(())
+       }
+}
+impl<R: ::std::io::Read> MaybeReadable<R> for Event {
+       fn read(reader: &mut R) -> Result<Option<Self>, msgs::DecodeError> {
+               match Readable::read(reader)? {
+                       0u8 => Ok(None),
+                       1u8 => Ok(Some(Event::FundingBroadcastSafe {
+                                       funding_txo: Readable::read(reader)?,
+                                       user_channel_id: Readable::read(reader)?,
+                               })),
+                       2u8 => Ok(Some(Event::PaymentReceived {
+                                       payment_hash: Readable::read(reader)?,
+                                       amt: Readable::read(reader)?,
+                               })),
+                       3u8 => Ok(Some(Event::PaymentSent {
+                                       payment_preimage: Readable::read(reader)?,
+                               })),
+                       4u8 => Ok(Some(Event::PaymentFailed {
+                                       payment_hash: Readable::read(reader)?,
+                                       rejected_by_dest: Readable::read(reader)?,
+                                       #[cfg(test)]
+                                       error_code: Readable::read(reader)?,
+                               })),
+                       5u8 => Ok(Some(Event::PendingHTLCsForwardable {
+                                       time_forwardable: Duration::from_secs(0)
+                               })),
+                       6u8 => {
+                               let outputs_len: u64 = Readable::read(reader)?;
+                               let mut outputs = Vec::new();
+                               for _ in 0..outputs_len {
+                                       outputs.push(Readable::read(reader)?);
+                               }
+                               Ok(Some(Event::SpendableOutputs { outputs }))
+                       },
+                       _ => Err(msgs::DecodeError::InvalidValue)
+               }
+       }
+}
+
 /// An event generated by ChannelManager which indicates a message should be sent to a peer (or
 /// broadcast to most peers).
 /// These events are handled by PeerManager::process_events if you are using a PeerManager.
index 44fdd1c0cf4d3097d27e7abebb83e0a397f0ac86..96936fe95416fd06a1061e323dbbe1952cb09c09 100644 (file)
@@ -11,7 +11,7 @@ use std::cmp;
 use secp256k1::Signature;
 use secp256k1::key::{PublicKey, SecretKey};
 use bitcoin::blockdata::script::Script;
-use bitcoin::blockdata::transaction::{OutPoint, Transaction};
+use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
 use bitcoin::consensus;
 use bitcoin::consensus::Encodable;
 use bitcoin_hashes::sha256d::Hash as Sha256dHash;
@@ -191,6 +191,15 @@ pub trait ReadableArgs<R, P>
        fn read(reader: &mut R, params: P) -> Result<Self, DecodeError>;
 }
 
+/// A trait that various rust-lightning types implement allowing them to (maybe) be read in from a Read
+pub trait MaybeReadable<R>
+       where Self: Sized,
+             R: Read
+{
+       /// Reads a Self in from the given Read
+       fn read(reader: &mut R) -> Result<Option<Self>, DecodeError>;
+}
+
 pub(crate) struct U48(pub u64);
 impl Writeable for U48 {
        #[inline]
@@ -627,26 +636,32 @@ impl<R: Read> Readable<R> for OutPoint {
        }
 }
 
-impl Writeable for Transaction {
-       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
-               match self.consensus_encode(WriterWriteAdaptor(writer)) {
-                       Ok(_) => Ok(()),
-                       Err(consensus::encode::Error::Io(e)) => Err(e),
-                       Err(_) => panic!("We shouldn't get a consensus::encode::Error unless our Write generated an std::io::Error"),
+macro_rules! impl_consensus_ser {
+       ($bitcoin_type: ty) => {
+               impl Writeable for $bitcoin_type {
+                       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
+                               match self.consensus_encode(WriterWriteAdaptor(writer)) {
+                                       Ok(_) => Ok(()),
+                                       Err(consensus::encode::Error::Io(e)) => Err(e),
+                                       Err(_) => panic!("We shouldn't get a consensus::encode::Error unless our Write generated an std::io::Error"),
+                               }
+                       }
                }
-       }
-}
 
-impl<R: Read> Readable<R> for Transaction {
-       fn read(r: &mut R) -> Result<Self, DecodeError> {
-               match consensus::encode::Decodable::consensus_decode(r) {
-                       Ok(t) => Ok(t),
-                       Err(consensus::encode::Error::Io(ref e)) if e.kind() == ::std::io::ErrorKind::UnexpectedEof => Err(DecodeError::ShortRead),
-                       Err(consensus::encode::Error::Io(e)) => Err(DecodeError::Io(e)),
-                       Err(_) => Err(DecodeError::InvalidValue),
+               impl<R: Read> Readable<R> for $bitcoin_type {
+                       fn read(r: &mut R) -> Result<Self, DecodeError> {
+                               match consensus::encode::Decodable::consensus_decode(r) {
+                                       Ok(t) => Ok(t),
+                                       Err(consensus::encode::Error::Io(ref e)) if e.kind() == ::std::io::ErrorKind::UnexpectedEof => Err(DecodeError::ShortRead),
+                                       Err(consensus::encode::Error::Io(e)) => Err(DecodeError::Io(e)),
+                                       Err(_) => Err(DecodeError::InvalidValue),
+                               }
+                       }
                }
        }
 }
+impl_consensus_ser!(Transaction);
+impl_consensus_ser!(TxOut);
 
 impl<R: Read, T: Readable<R>> Readable<R> for Mutex<T> {
        fn read(r: &mut R) -> Result<Self, DecodeError> {