Implement Readable/Writeable for Events

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.
This commit is contained in:
Matt Corallo 2020-02-12 17:02:03 -05:00
parent 8829d1b80f
commit bfd4ac4995
3 changed files with 172 additions and 16 deletions

View 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.

View 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.

View 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> {