diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61e961690fe..d24b2be7a45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,13 +143,13 @@ jobs: id: cache-graph uses: actions/cache@v2 with: - path: lightning/net_graph-2021-05-27.bin - key: ldk-net_graph-45d86ead641d-2021-05-27.bin + path: lightning/net_graph-2021-05-31.bin + key: ldk-net_graph-v0.0.15-2021-05-31.bin - name: Fetch routing graph snapshot if: steps.cache-graph.outputs.cache-hit != 'true' run: | - wget -O lightning/net_graph-2021-05-27.bin https://bitcoin.ninja/ldk-net_graph-45d86ead641d-2021-05-27.bin - if [ "$(sha256sum lightning/net_graph-2021-05-27.bin | awk '{ print $1 }')" != "3d6261187cfa583255d978efb908b51c2f4dc4ad9a7160cd2c5263c9a4830121" ]; then + wget -O lightning/net_graph-2021-05-31.bin https://bitcoin.ninja/ldk-net_graph-v0.0.15-2021-05-31.bin + if [ "$(sha256sum lightning/net_graph-2021-05-31.bin | awk '{ print $1 }')" != "05a5361278f68ee2afd086cc04a1f927a63924be451f3221d380533acfacc303" ]; then echo "Bad hash" exit 1 fi diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 26187ce3416..fe581ac65ad 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -47,7 +47,7 @@ use chain::onchaintx::OnchainTxHandler; use chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput}; use chain::Filter; use util::logger::Logger; -use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48}; +use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48, OptionDeserWrapper}; use util::byte_utils; use util::events::Event; @@ -90,22 +90,26 @@ pub const CLOSED_CHANNEL_UPDATE_ID: u64 = core::u64::MAX; impl Writeable for ChannelMonitorUpdate { fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + write_ver_prefix!(w, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); self.update_id.write(w)?; (self.updates.len() as u64).write(w)?; for update_step in self.updates.iter() { update_step.write(w)?; } + write_tlv_fields!(w, {}, {}); Ok(()) } } impl Readable for ChannelMonitorUpdate { fn read(r: &mut R) -> Result { + let _ver = read_ver_prefix!(r, SERIALIZATION_VERSION); let update_id: u64 = Readable::read(r)?; let len: u64 = Readable::read(r)?; let mut updates = Vec::with_capacity(cmp::min(len as usize, MAX_ALLOC_SIZE / ::core::mem::size_of::())); for _ in 0..len { updates.push(Readable::read(r)?); } + read_tlv_fields!(r, {}, {}); Ok(Self { update_id, updates }) } } @@ -198,7 +202,12 @@ pub struct HTLCUpdate { pub(crate) payment_preimage: Option, pub(crate) source: HTLCSource } -impl_writeable!(HTLCUpdate, 0, { payment_hash, payment_preimage, source }); +impl_writeable_tlv_based!(HTLCUpdate, { + (0, payment_hash), + (2, source), +}, { + (4, payment_preimage) +}, {}); /// If an HTLC expires within this many blocks, don't try to claim it in a shared transaction, /// instead claiming it in its own individual transaction. @@ -264,6 +273,17 @@ struct HolderSignedTx { feerate_per_kw: u32, htlc_outputs: Vec<(HTLCOutputInCommitment, Option, Option)>, } +impl_writeable_tlv_based!(HolderSignedTx, { + (0, txid), + (2, revocation_key), + (4, a_htlc_key), + (6, b_htlc_key), + (8, delayed_payment_key), + (10, per_commitment_point), + (12, feerate_per_kw), +}, {}, { + (14, htlc_outputs) +}); /// We use this to track counterparty commitment transactions and htlcs outputs and /// use it to generate any justice or 2nd-stage preimage/timeout transactions. @@ -277,9 +297,6 @@ struct CounterpartyCommitmentTransaction { impl Writeable for CounterpartyCommitmentTransaction { fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { - self.counterparty_delayed_payment_base_key.write(w)?; - self.counterparty_htlc_base_key.write(w)?; - w.write_all(&byte_utils::be16_to_array(self.on_counterparty_tx_csv))?; w.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?; for (ref txid, ref htlcs) in self.per_htlc.iter() { w.write_all(&txid[..])?; @@ -288,15 +305,17 @@ impl Writeable for CounterpartyCommitmentTransaction { htlc.write(w)?; } } + write_tlv_fields!(w, { + (0, self.counterparty_delayed_payment_base_key), + (2, self.counterparty_htlc_base_key), + (4, self.on_counterparty_tx_csv), + }, {}); Ok(()) } } impl Readable for CounterpartyCommitmentTransaction { fn read(r: &mut R) -> Result { let counterparty_commitment_transaction = { - let counterparty_delayed_payment_base_key = Readable::read(r)?; - let counterparty_htlc_base_key = Readable::read(r)?; - let on_counterparty_tx_csv: u16 = Readable::read(r)?; let per_htlc_len: u64 = Readable::read(r)?; let mut per_htlc = HashMap::with_capacity(cmp::min(per_htlc_len as usize, MAX_ALLOC_SIZE / 64)); for _ in 0..per_htlc_len { @@ -311,9 +330,17 @@ impl Readable for CounterpartyCommitmentTransaction { return Err(DecodeError::InvalidValue); } } + let mut counterparty_delayed_payment_base_key = OptionDeserWrapper(None); + let mut counterparty_htlc_base_key = OptionDeserWrapper(None); + let mut on_counterparty_tx_csv: u16 = 0; + read_tlv_fields!(r, { + (0, counterparty_delayed_payment_base_key), + (2, counterparty_htlc_base_key), + (4, on_counterparty_tx_csv), + }, {}); CounterpartyCommitmentTransaction { - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, + counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(), + counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(), on_counterparty_tx_csv, per_htlc, } @@ -351,13 +378,30 @@ enum OnchainEvent { /// inbound HTLC in backward channel. Note, in case of preimage, we pass info to upstream without delay as we can /// only win from it, so it's never an OnchainEvent HTLCUpdate { - htlc_update: (HTLCSource, PaymentHash), + source: HTLCSource, + payment_hash: PaymentHash, }, MaturingOutput { descriptor: SpendableOutputDescriptor, }, } +impl_writeable_tlv_based!(OnchainEventEntry, { + (0, txid), + (2, height), + (4, event), +}, {}, {}); + +impl_writeable_tlv_based_enum!(OnchainEvent, + (0, HTLCUpdate) => { + (0, source), + (2, payment_hash), + }, {}, {}, + (1, MaturingOutput) => { + (0, descriptor), + }, {}, {}, +;); + #[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))] #[derive(Clone)] pub(crate) enum ChannelMonitorUpdateStep { @@ -387,98 +431,30 @@ pub(crate) enum ChannelMonitorUpdateStep { }, } -impl Writeable for ChannelMonitorUpdateStep { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { - match self { - &ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { ref commitment_tx, ref htlc_outputs } => { - 0u8.write(w)?; - commitment_tx.write(w)?; - (htlc_outputs.len() as u64).write(w)?; - for &(ref output, ref signature, ref source) in htlc_outputs.iter() { - output.write(w)?; - signature.write(w)?; - source.write(w)?; - } - } - &ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, ref htlc_outputs, ref commitment_number, ref their_revocation_point } => { - 1u8.write(w)?; - commitment_txid.write(w)?; - commitment_number.write(w)?; - their_revocation_point.write(w)?; - (htlc_outputs.len() as u64).write(w)?; - for &(ref output, ref source) in htlc_outputs.iter() { - output.write(w)?; - source.as_ref().map(|b| b.as_ref()).write(w)?; - } - }, - &ChannelMonitorUpdateStep::PaymentPreimage { ref payment_preimage } => { - 2u8.write(w)?; - payment_preimage.write(w)?; - }, - &ChannelMonitorUpdateStep::CommitmentSecret { ref idx, ref secret } => { - 3u8.write(w)?; - idx.write(w)?; - secret.write(w)?; - }, - &ChannelMonitorUpdateStep::ChannelForceClosed { ref should_broadcast } => { - 4u8.write(w)?; - should_broadcast.write(w)?; - }, - } - Ok(()) - } -} -impl Readable for ChannelMonitorUpdateStep { - fn read(r: &mut R) -> Result { - match Readable::read(r)? { - 0u8 => { - Ok(ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { - commitment_tx: Readable::read(r)?, - htlc_outputs: { - let len: u64 = Readable::read(r)?; - let mut res = Vec::new(); - for _ in 0..len { - res.push((Readable::read(r)?, Readable::read(r)?, Readable::read(r)?)); - } - res - }, - }) - }, - 1u8 => { - Ok(ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { - commitment_txid: Readable::read(r)?, - commitment_number: Readable::read(r)?, - their_revocation_point: Readable::read(r)?, - htlc_outputs: { - let len: u64 = Readable::read(r)?; - let mut res = Vec::new(); - for _ in 0..len { - res.push((Readable::read(r)?, as Readable>::read(r)?.map(|o| Box::new(o)))); - } - res - }, - }) - }, - 2u8 => { - Ok(ChannelMonitorUpdateStep::PaymentPreimage { - payment_preimage: Readable::read(r)?, - }) - }, - 3u8 => { - Ok(ChannelMonitorUpdateStep::CommitmentSecret { - idx: Readable::read(r)?, - secret: Readable::read(r)?, - }) - }, - 4u8 => { - Ok(ChannelMonitorUpdateStep::ChannelForceClosed { - should_broadcast: Readable::read(r)? - }) - }, - _ => Err(DecodeError::InvalidValue), - } - } -} +impl_writeable_tlv_based_enum!(ChannelMonitorUpdateStep, + (0, LatestHolderCommitmentTXInfo) => { + (0, commitment_tx), + }, {}, { + (2, htlc_outputs), + }, + (1, LatestCounterpartyCommitmentTXInfo) => { + (0, commitment_txid), + (2, commitment_number), + (4, their_revocation_point), + }, {}, { + (6, htlc_outputs), + }, + (2, PaymentPreimage) => { + (0, payment_preimage), + }, {}, {}, + (3, CommitmentSecret) => { + (0, idx), + (2, secret), + }, {}, {}, + (4, ChannelForceClosed) => { + (0, should_broadcast), + }, {}, {}, +;); /// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates /// on-chain transactions to ensure no loss of funds occurs. @@ -662,6 +638,7 @@ impl Writeable for ChannelMonitor { } } +// These are also used for ChannelMonitorUpdate, above. const SERIALIZATION_VERSION: u8 = 1; const MIN_SERIALIZATION_VERSION: u8 = 1; @@ -753,38 +730,14 @@ impl Writeable for ChannelMonitorImpl { writer.write_all(&byte_utils::be48_to_array(*commitment_number))?; } - macro_rules! serialize_holder_tx { - ($holder_tx: expr) => { - $holder_tx.txid.write(writer)?; - writer.write_all(&$holder_tx.revocation_key.serialize())?; - writer.write_all(&$holder_tx.a_htlc_key.serialize())?; - writer.write_all(&$holder_tx.b_htlc_key.serialize())?; - writer.write_all(&$holder_tx.delayed_payment_key.serialize())?; - writer.write_all(&$holder_tx.per_commitment_point.serialize())?; - - writer.write_all(&byte_utils::be32_to_array($holder_tx.feerate_per_kw))?; - writer.write_all(&byte_utils::be64_to_array($holder_tx.htlc_outputs.len() as u64))?; - for &(ref htlc_output, ref sig, ref htlc_source) in $holder_tx.htlc_outputs.iter() { - serialize_htlc_in_commitment!(htlc_output); - if let &Some(ref their_sig) = sig { - 1u8.write(writer)?; - writer.write_all(&their_sig.serialize_compact())?; - } else { - 0u8.write(writer)?; - } - htlc_source.write(writer)?; - } - } - } - if let Some(ref prev_holder_tx) = self.prev_holder_signed_commitment_tx { writer.write_all(&[1; 1])?; - serialize_holder_tx!(prev_holder_tx); + prev_holder_tx.write(writer)?; } else { writer.write_all(&[0; 1])?; } - serialize_holder_tx!(self.current_holder_commitment_tx); + self.current_holder_commitment_tx.write(writer)?; writer.write_all(&byte_utils::be48_to_array(self.current_counterparty_commitment_number))?; writer.write_all(&byte_utils::be48_to_array(self.current_holder_commitment_number))?; @@ -815,19 +768,7 @@ impl Writeable for ChannelMonitorImpl { writer.write_all(&byte_utils::be64_to_array(self.onchain_events_awaiting_threshold_conf.len() as u64))?; for ref entry in self.onchain_events_awaiting_threshold_conf.iter() { - entry.txid.write(writer)?; - writer.write_all(&byte_utils::be32_to_array(entry.height))?; - match entry.event { - OnchainEvent::HTLCUpdate { ref htlc_update } => { - 0u8.write(writer)?; - htlc_update.0.write(writer)?; - htlc_update.1.write(writer)?; - }, - OnchainEvent::MaturingOutput { ref descriptor } => { - 1u8.write(writer)?; - descriptor.write(writer)?; - }, - } + entry.write(writer)?; } (self.outputs_to_watch.len() as u64).write(writer)?; @@ -1609,17 +1550,18 @@ impl ChannelMonitorImpl { self.onchain_events_awaiting_threshold_conf.retain(|ref entry| { if entry.height != height { return true; } match entry.event { - OnchainEvent::HTLCUpdate { ref htlc_update } => { - htlc_update.0 != **source - }, - _ => true, + OnchainEvent::HTLCUpdate { source: ref update_source, .. } => { + *update_source != **source + }, + _ => true, } }); let entry = OnchainEventEntry { txid: *$txid, height, event: OnchainEvent::HTLCUpdate { - htlc_update: ((**source).clone(), htlc.payment_hash.clone()) + source: (**source).clone(), + payment_hash: htlc.payment_hash.clone(), }, }; log_info!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of revoked counterparty commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, entry.confirmation_threshold()); @@ -1675,17 +1617,18 @@ impl ChannelMonitorImpl { self.onchain_events_awaiting_threshold_conf.retain(|ref entry| { if entry.height != height { return true; } match entry.event { - OnchainEvent::HTLCUpdate { ref htlc_update } => { - htlc_update.0 != **source - }, - _ => true, + OnchainEvent::HTLCUpdate { source: ref update_source, .. } => { + *update_source != **source + }, + _ => true, } }); self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid: *$txid, height, event: OnchainEvent::HTLCUpdate { - htlc_update: ((**source).clone(), htlc.payment_hash.clone()) + source: (**source).clone(), + payment_hash: htlc.payment_hash.clone(), }, }); } @@ -1829,16 +1772,16 @@ impl ChannelMonitorImpl { self.onchain_events_awaiting_threshold_conf.retain(|ref entry| { if entry.height != height { return true; } match entry.event { - OnchainEvent::HTLCUpdate { ref htlc_update } => { - htlc_update.0 != $source - }, - _ => true, + OnchainEvent::HTLCUpdate { source: ref update_source, .. } => { + *update_source != $source + }, + _ => true, } }); let entry = OnchainEventEntry { txid: commitment_txid, height, - event: OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash) }, + event: OnchainEvent::HTLCUpdate { source: $source, payment_hash: $payment_hash }, }; log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, entry.confirmation_threshold()); self.onchain_events_awaiting_threshold_conf.push(entry); @@ -2109,7 +2052,7 @@ impl ChannelMonitorImpl { let unmatured_htlcs: Vec<_> = self.onchain_events_awaiting_threshold_conf .iter() .filter_map(|entry| match &entry.event { - OnchainEvent::HTLCUpdate { htlc_update } => Some(htlc_update.0.clone()), + OnchainEvent::HTLCUpdate { source, .. } => Some(source), OnchainEvent::MaturingOutput { .. } => None, }) .collect(); @@ -2119,28 +2062,28 @@ impl ChannelMonitorImpl { // Produce actionable events from on-chain events having reached their threshold. for entry in onchain_events_reaching_threshold_conf.drain(..) { match entry.event { - OnchainEvent::HTLCUpdate { htlc_update } => { + OnchainEvent::HTLCUpdate { ref source, payment_hash } => { // Check for duplicate HTLC resolutions. #[cfg(debug_assertions)] { debug_assert!( - unmatured_htlcs.iter().find(|&htlc| htlc == &htlc_update.0).is_none(), + unmatured_htlcs.iter().find(|&htlc| htlc == &source).is_none(), "An unmature HTLC transaction conflicts with a maturing one; failed to \ call either transaction_unconfirmed for the conflicting transaction \ or block_disconnected for a block containing it."); debug_assert!( - matured_htlcs.iter().find(|&htlc| htlc == &htlc_update.0).is_none(), + matured_htlcs.iter().find(|&htlc| htlc == source).is_none(), "A matured HTLC transaction conflicts with a maturing one; failed to \ call either transaction_unconfirmed for the conflicting transaction \ or block_disconnected for a block containing it."); - matured_htlcs.push(htlc_update.0.clone()); + matured_htlcs.push(source.clone()); } - log_trace!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0)); + log_trace!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!(payment_hash.0)); self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate { - payment_hash: htlc_update.1, + payment_hash: payment_hash, payment_preimage: None, - source: htlc_update.0, + source: source.clone(), })); }, OnchainEvent::MaturingOutput { descriptor } => { @@ -2437,16 +2380,16 @@ impl ChannelMonitorImpl { self.onchain_events_awaiting_threshold_conf.retain(|ref entry| { if entry.height != height { return true; } match entry.event { - OnchainEvent::HTLCUpdate { ref htlc_update } => { - htlc_update.0 != source - }, - _ => true, + OnchainEvent::HTLCUpdate { source: ref htlc_source, .. } => { + *htlc_source != source + }, + _ => true, } }); let entry = OnchainEventEntry { txid: tx.txid(), height, - event: OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash) }, + event: OnchainEvent::HTLCUpdate { source: source, payment_hash: payment_hash }, }; log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height{})", log_bytes!(payment_hash.0), entry.confirmation_threshold()); self.onchain_events_awaiting_threshold_conf.push(entry); @@ -2723,46 +2666,14 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> } } - macro_rules! read_holder_tx { - () => { - { - let txid = Readable::read(reader)?; - let revocation_key = Readable::read(reader)?; - let a_htlc_key = Readable::read(reader)?; - let b_htlc_key = Readable::read(reader)?; - let delayed_payment_key = Readable::read(reader)?; - let per_commitment_point = Readable::read(reader)?; - let feerate_per_kw: u32 = Readable::read(reader)?; - - let htlcs_len: u64 = Readable::read(reader)?; - let mut htlcs = Vec::with_capacity(cmp::min(htlcs_len as usize, MAX_ALLOC_SIZE / 128)); - for _ in 0..htlcs_len { - let htlc = read_htlc_in_commitment!(); - let sigs = match ::read(reader)? { - 0 => None, - 1 => Some(Readable::read(reader)?), - _ => return Err(DecodeError::InvalidValue), - }; - htlcs.push((htlc, sigs, Readable::read(reader)?)); - } - - HolderSignedTx { - txid, - revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, per_commitment_point, feerate_per_kw, - htlc_outputs: htlcs - } - } - } - } - let prev_holder_signed_commitment_tx = match ::read(reader)? { 0 => None, 1 => { - Some(read_holder_tx!()) + Some(Readable::read(reader)?) }, _ => return Err(DecodeError::InvalidValue), }; - let current_holder_commitment_tx = read_holder_tx!(); + let current_holder_commitment_tx = Readable::read(reader)?; let current_counterparty_commitment_number = ::read(reader)?.0; let current_holder_commitment_number = ::read(reader)?.0; @@ -2801,25 +2712,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> let waiting_threshold_conf_len: u64 = Readable::read(reader)?; let mut onchain_events_awaiting_threshold_conf = Vec::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128)); for _ in 0..waiting_threshold_conf_len { - let txid = Readable::read(reader)?; - let height = Readable::read(reader)?; - let event = match ::read(reader)? { - 0 => { - let htlc_source = Readable::read(reader)?; - let hash = Readable::read(reader)?; - OnchainEvent::HTLCUpdate { - htlc_update: (htlc_source, hash) - } - }, - 1 => { - let descriptor = Readable::read(reader)?; - OnchainEvent::MaturingOutput { - descriptor - } - }, - _ => return Err(DecodeError::InvalidValue), - }; - onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid, height, event }); + onchain_events_awaiting_threshold_conf.push(Readable::read(reader)?); } let outputs_to_watch_len: u64 = Readable::read(reader)?; diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 1f118562008..84d83196472 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -73,6 +73,16 @@ impl DelayedPaymentOutputDescriptor { pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1; } +impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, { + (0, outpoint), + (2, per_commitment_point), + (4, to_self_delay), + (6, output), + (8, revocation_pubkey), + (10, channel_keys_id), + (12, channel_value_satoshis), +}, {}, {}); + /// Information about a spendable output to our "payment key". See /// SpendableOutputDescriptor::StaticPaymentOutput for more details on how to spend this. #[derive(Clone, Debug, PartialEq)] @@ -94,6 +104,12 @@ impl StaticPaymentOutputDescriptor { // redeemscript push length. pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34; } +impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, { + (0, outpoint), + (2, output), + (4, channel_keys_id), + (6, channel_value_satoshis), +}, {}, {}); /// When on-chain outputs are created by rust-lightning (which our counterparty is not able to /// claim at any point in the future) an event is generated which you must track and be able to @@ -152,62 +168,15 @@ pub enum SpendableOutputDescriptor { StaticPaymentOutput(StaticPaymentOutputDescriptor), } -impl Writeable for SpendableOutputDescriptor { - fn write(&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::DelayedPaymentOutput(ref descriptor) => { - 1u8.write(writer)?; - descriptor.outpoint.write(writer)?; - descriptor.per_commitment_point.write(writer)?; - descriptor.to_self_delay.write(writer)?; - descriptor.output.write(writer)?; - descriptor.revocation_pubkey.write(writer)?; - descriptor.channel_keys_id.write(writer)?; - descriptor.channel_value_satoshis.write(writer)?; - }, - &SpendableOutputDescriptor::StaticPaymentOutput(ref descriptor) => { - 2u8.write(writer)?; - descriptor.outpoint.write(writer)?; - descriptor.output.write(writer)?; - descriptor.channel_keys_id.write(writer)?; - descriptor.channel_value_satoshis.write(writer)?; - }, - } - Ok(()) - } -} - -impl Readable for SpendableOutputDescriptor { - fn read(reader: &mut R) -> Result { - match Readable::read(reader)? { - 0u8 => Ok(SpendableOutputDescriptor::StaticOutput { - outpoint: Readable::read(reader)?, - output: Readable::read(reader)?, - }), - 1u8 => Ok(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor { - outpoint: Readable::read(reader)?, - per_commitment_point: Readable::read(reader)?, - to_self_delay: Readable::read(reader)?, - output: Readable::read(reader)?, - revocation_pubkey: Readable::read(reader)?, - channel_keys_id: Readable::read(reader)?, - channel_value_satoshis: Readable::read(reader)?, - })), - 2u8 => Ok(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor { - outpoint: Readable::read(reader)?, - output: Readable::read(reader)?, - channel_keys_id: Readable::read(reader)?, - channel_value_satoshis: Readable::read(reader)?, - })), - _ => Err(DecodeError::InvalidValue), - } - } -} +impl_writeable_tlv_based_enum!(SpendableOutputDescriptor, + (0, StaticOutput) => { + (0, outpoint), + (2, output), + }, {}, {}, +; + (1, DelayedPaymentOutput), + (2, StaticPaymentOutput), +); /// A trait to sign lightning channel transactions as described in BOLT 3. /// diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index 1f604422f5c..dd4d1d8f0b3 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -79,6 +79,21 @@ enum OnchainEvent { } } +impl_writeable_tlv_based!(OnchainEventEntry, { + (0, txid), + (2, height), + (4, event), +}, {}, {}); + +impl_writeable_tlv_based_enum!(OnchainEvent, + (0, Claim) => { + (0, claim_request), + }, {}, {}, + (1, ContentiousOutpoint) => { + (0, package), + }, {}, {}, +;); + impl Readable for Option>> { fn read(reader: &mut R) -> Result { match Readable::read(reader)? { @@ -219,18 +234,7 @@ impl OnchainTxHandler { writer.write_all(&byte_utils::be64_to_array(self.onchain_events_awaiting_threshold_conf.len() as u64))?; for ref entry in self.onchain_events_awaiting_threshold_conf.iter() { - entry.txid.write(writer)?; - writer.write_all(&byte_utils::be32_to_array(entry.height))?; - match entry.event { - OnchainEvent::Claim { ref claim_request } => { - writer.write_all(&[0; 1])?; - claim_request.write(writer)?; - }, - OnchainEvent::ContentiousOutpoint { ref package } => { - writer.write_all(&[1; 1])?; - package.write(writer)?; - } - } + entry.write(writer)?; } write_tlv_fields!(writer, {}, {}); @@ -292,24 +296,7 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler { let waiting_threshold_conf_len: u64 = Readable::read(reader)?; let mut onchain_events_awaiting_threshold_conf = Vec::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128)); for _ in 0..waiting_threshold_conf_len { - let txid = Readable::read(reader)?; - let height = Readable::read(reader)?; - let event = match ::read(reader)? { - 0 => { - let claim_request = Readable::read(reader)?; - OnchainEvent::Claim { - claim_request - } - }, - 1 => { - let package = Readable::read(reader)?; - OnchainEvent::ContentiousOutpoint { - package - } - } - _ => return Err(DecodeError::InvalidValue), - }; - onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid, height, event }); + onchain_events_awaiting_threshold_conf.push(Readable::read(reader)?); } read_tlv_fields!(reader, {}, {}); diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index a7339468991..bd983ecd916 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -404,65 +404,14 @@ impl PackageSolvingData { } } -impl Writeable for PackageSolvingData { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - PackageSolvingData::RevokedOutput(ref revoked_outp) => { - 0u8.write(writer)?; - revoked_outp.write(writer)?; - }, - PackageSolvingData::RevokedHTLCOutput(ref revoked_outp) => { - 1u8.write(writer)?; - revoked_outp.write(writer)?; - }, - PackageSolvingData::CounterpartyOfferedHTLCOutput(ref counterparty_outp) => { - 2u8.write(writer)?; - counterparty_outp.write(writer)?; - }, - PackageSolvingData::CounterpartyReceivedHTLCOutput(ref counterparty_outp) => { - 3u8.write(writer)?; - counterparty_outp.write(writer)?; - }, - PackageSolvingData::HolderHTLCOutput(ref holder_outp) => { - 4u8.write(writer)?; - holder_outp.write(writer)?; - }, - PackageSolvingData::HolderFundingOutput(ref funding_outp) => { - 5u8.write(writer)?; - funding_outp.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for PackageSolvingData { - fn read(reader: &mut R) -> Result { - let byte = ::read(reader)?; - let solving_data = match byte { - 0 => { - PackageSolvingData::RevokedOutput(Readable::read(reader)?) - }, - 1 => { - PackageSolvingData::RevokedHTLCOutput(Readable::read(reader)?) - }, - 2 => { - PackageSolvingData::CounterpartyOfferedHTLCOutput(Readable::read(reader)?) - }, - 3 => { - PackageSolvingData::CounterpartyReceivedHTLCOutput(Readable::read(reader)?) - }, - 4 => { - PackageSolvingData::HolderHTLCOutput(Readable::read(reader)?) - }, - 5 => { - PackageSolvingData::HolderFundingOutput(Readable::read(reader)?) - } - _ => return Err(DecodeError::UnknownVersion) - }; - Ok(solving_data) - } -} +impl_writeable_tlv_based_enum!(PackageSolvingData, ; + (0, RevokedOutput), + (1, RevokedHTLCOutput), + (2, CounterpartyOfferedHTLCOutput), + (3, CounterpartyReceivedHTLCOutput), + (4, HolderHTLCOutput), + (5, HolderFundingOutput), +); /// A malleable package might be aggregated with other packages to save on fees. /// A untractable package has been counter-signed and aggregable will break cached counterparty diff --git a/lightning/src/chain/transaction.rs b/lightning/src/chain/transaction.rs index 4db5fbed6a8..502eb895b26 100644 --- a/lightning/src/chain/transaction.rs +++ b/lightning/src/chain/transaction.rs @@ -73,14 +73,6 @@ impl OutPoint { vout: self.index as u32, } } - - /// Creates a dummy BitcoinOutPoint, useful for deserializing into. - pub(crate) fn null() -> Self { - Self { - txid: Default::default(), - index: 0 - } - } } impl_writeable!(OutPoint, 0, { txid, index }); diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 81b0df09607..b698558e1b2 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -22,7 +22,7 @@ use bitcoin::hash_types::{Txid, PubkeyHash}; use ln::{PaymentHash, PaymentPreimage}; use ln::msgs::DecodeError; -use util::ser::{Readable, Writeable, Writer, MAX_BUF_SIZE}; +use util::ser::{Readable, Writeable, Writer}; use util::byte_utils; use bitcoin::hash_types::WPubkeyHash; @@ -36,18 +36,11 @@ use core::cmp; use ln::chan_utils; use util::transaction_utils::sort_outputs; use ln::channel::INITIAL_COMMITMENT_NUMBER; -use std::io::Read; use core::ops::Deref; use chain; -// Maximum size of a serialized HTLCOutputInCommitment -pub(crate) const HTLC_OUTPUT_IN_COMMITMENT_SIZE: usize = 1 + 8 + 4 + 32 + 5; - pub(crate) const MAX_HTLCS: u16 = 483; -// This checks that the buffer size is greater than the maximum possible size for serialized HTLCS -const _EXCESS_BUFFER_SIZE: usize = MAX_BUF_SIZE - MAX_HTLCS as usize * HTLC_OUTPUT_IN_COMMITMENT_SIZE; - pub(super) const HTLC_SUCCESS_TX_WEIGHT: u64 = 703; pub(super) const HTLC_TIMEOUT_TX_WEIGHT: u64 = 663; @@ -866,44 +859,16 @@ impl PartialEq for CommitmentTransaction { } } -/// (C-not exported) as users never need to call this directly -impl Writeable for Vec { - #[inline] - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { - (self.len() as u16).write(w)?; - for e in self.iter() { - e.write(w)?; - } - Ok(()) - } -} - -/// (C-not exported) as users never need to call this directly -impl Readable for Vec { - #[inline] - fn read(r: &mut R) -> Result { - let len: u16 = Readable::read(r)?; - let byte_size = (len as usize) - .checked_mul(HTLC_OUTPUT_IN_COMMITMENT_SIZE) - .ok_or(DecodeError::BadLengthDescriptor)?; - if byte_size > MAX_BUF_SIZE { - return Err(DecodeError::BadLengthDescriptor); - } - let mut ret = Vec::with_capacity(len as usize); - for _ in 0..len { ret.push(HTLCOutputInCommitment::read(r)?); } - Ok(ret) - } -} - impl_writeable_tlv_based!(CommitmentTransaction, { (0, commitment_number), (2, to_broadcaster_value_sat), (4, to_countersignatory_value_sat), (6, feerate_per_kw), - (8, htlcs), - (10, keys), - (12, built), -}, {}, {}); + (8, keys), + (10, built), +}, {}, { + (12, htlcs), +}); impl CommitmentTransaction { /// Construct an object of the class while assigning transaction output indices to HTLCs. diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 40b73c3bd5f..07293a102ea 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -4382,37 +4382,11 @@ fn is_unsupported_shutdown_script(their_features: &InitFeatures, script: &Script const SERIALIZATION_VERSION: u8 = 1; const MIN_SERIALIZATION_VERSION: u8 = 1; -impl Writeable for InboundHTLCRemovalReason { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - &InboundHTLCRemovalReason::FailRelay(ref error_packet) => { - 0u8.write(writer)?; - error_packet.write(writer)?; - }, - &InboundHTLCRemovalReason::FailMalformed((ref onion_hash, ref err_code)) => { - 1u8.write(writer)?; - onion_hash.write(writer)?; - err_code.write(writer)?; - }, - &InboundHTLCRemovalReason::Fulfill(ref payment_preimage) => { - 2u8.write(writer)?; - payment_preimage.write(writer)?; - }, - } - Ok(()) - } -} - -impl Readable for InboundHTLCRemovalReason { - fn read(reader: &mut R) -> Result { - Ok(match ::read(reader)? { - 0 => InboundHTLCRemovalReason::FailRelay(Readable::read(reader)?), - 1 => InboundHTLCRemovalReason::FailMalformed((Readable::read(reader)?, Readable::read(reader)?)), - 2 => InboundHTLCRemovalReason::Fulfill(Readable::read(reader)?), - _ => return Err(DecodeError::InvalidValue), - }) - } -} +impl_writeable_tlv_based_enum!(InboundHTLCRemovalReason,; + (0, FailRelay), + (1, FailMalformed), + (2, Fulfill), +); impl Writeable for ChannelUpdateStatus { fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 9c1f8ecfd97..7ab1d2b438e 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -4317,43 +4317,16 @@ impl PersistenceNotifier { const SERIALIZATION_VERSION: u8 = 1; const MIN_SERIALIZATION_VERSION: u8 = 1; -impl Writeable for PendingHTLCRouting { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match &self { - &PendingHTLCRouting::Forward { ref onion_packet, ref short_channel_id } => { - 0u8.write(writer)?; - onion_packet.write(writer)?; - short_channel_id.write(writer)?; - }, - &PendingHTLCRouting::Receive { ref payment_data, ref incoming_cltv_expiry } => { - 1u8.write(writer)?; - payment_data.payment_secret.write(writer)?; - payment_data.total_msat.write(writer)?; - incoming_cltv_expiry.write(writer)?; - }, - } - Ok(()) - } -} - -impl Readable for PendingHTLCRouting { - fn read(reader: &mut R) -> Result { - match Readable::read(reader)? { - 0u8 => Ok(PendingHTLCRouting::Forward { - onion_packet: Readable::read(reader)?, - short_channel_id: Readable::read(reader)?, - }), - 1u8 => Ok(PendingHTLCRouting::Receive { - payment_data: msgs::FinalOnionHopData { - payment_secret: Readable::read(reader)?, - total_msat: Readable::read(reader)?, - }, - incoming_cltv_expiry: Readable::read(reader)?, - }), - _ => Err(DecodeError::InvalidValue), - } - } -} +impl_writeable_tlv_based_enum!(PendingHTLCRouting, + (0, Forward) => { + (0, onion_packet), + (2, short_channel_id), + }, {}, {}, + (1, Receive) => { + (0, payment_data), + (2, incoming_cltv_expiry), + }, {}, {} +;); impl_writeable_tlv_based!(PendingHTLCInfo, { (0, routing), @@ -4363,57 +4336,14 @@ impl_writeable_tlv_based!(PendingHTLCInfo, { (8, outgoing_cltv_value) }, {}, {}); -impl Writeable for HTLCFailureMsg { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - &HTLCFailureMsg::Relay(ref fail_msg) => { - 0u8.write(writer)?; - fail_msg.write(writer)?; - }, - &HTLCFailureMsg::Malformed(ref fail_msg) => { - 1u8.write(writer)?; - fail_msg.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for HTLCFailureMsg { - fn read(reader: &mut R) -> Result { - match ::read(reader)? { - 0 => Ok(HTLCFailureMsg::Relay(Readable::read(reader)?)), - 1 => Ok(HTLCFailureMsg::Malformed(Readable::read(reader)?)), - _ => Err(DecodeError::InvalidValue), - } - } -} - -impl Writeable for PendingHTLCStatus { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - &PendingHTLCStatus::Forward(ref forward_info) => { - 0u8.write(writer)?; - forward_info.write(writer)?; - }, - &PendingHTLCStatus::Fail(ref fail_msg) => { - 1u8.write(writer)?; - fail_msg.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for PendingHTLCStatus { - fn read(reader: &mut R) -> Result { - match ::read(reader)? { - 0 => Ok(PendingHTLCStatus::Forward(Readable::read(reader)?)), - 1 => Ok(PendingHTLCStatus::Fail(Readable::read(reader)?)), - _ => Err(DecodeError::InvalidValue), - } - } -} +impl_writeable_tlv_based_enum!(HTLCFailureMsg, ; + (0, Relay), + (1, Malformed), +); +impl_writeable_tlv_based_enum!(PendingHTLCStatus, ; + (0, Forward), + (1, Fail), +); impl_writeable_tlv_based!(HTLCPreviousHopData, { (0, short_channel_id), @@ -4422,148 +4352,46 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, { (6, incoming_packet_shared_secret) }, {}, {}); -impl Writeable for ClaimableHTLC { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - write_tlv_fields!(writer, { - (0, self.prev_hop), - (2, self.value), - (4, self.payment_data.payment_secret), - (6, self.payment_data.total_msat), - (8, self.cltv_expiry) - }, {}); - Ok(()) - } -} - -impl Readable for ClaimableHTLC { - fn read(reader: &mut R) -> Result { - let mut prev_hop = HTLCPreviousHopData { - short_channel_id: 0, htlc_id: 0, - incoming_packet_shared_secret: [0; 32], - outpoint: OutPoint::null(), - }; - let mut value = 0; - let mut payment_secret = PaymentSecret([0; 32]); - let mut total_msat = 0; - let mut cltv_expiry = 0; - read_tlv_fields!(reader, { - (0, prev_hop), - (2, value), - (4, payment_secret), - (6, total_msat), - (8, cltv_expiry) - }, {}); - Ok(ClaimableHTLC { - prev_hop, - value, - payment_data: msgs::FinalOnionHopData { - payment_secret, - total_msat, - }, - cltv_expiry, - }) - } -} - -impl Writeable for HTLCSource { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - &HTLCSource::PreviousHopData(ref hop_data) => { - 0u8.write(writer)?; - hop_data.write(writer)?; - }, - &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } => { - 1u8.write(writer)?; - path.write(writer)?; - session_priv.write(writer)?; - first_hop_htlc_msat.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for HTLCSource { - fn read(reader: &mut R) -> Result { - match ::read(reader)? { - 0 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)), - 1 => Ok(HTLCSource::OutboundRoute { - path: Readable::read(reader)?, - session_priv: Readable::read(reader)?, - first_hop_htlc_msat: Readable::read(reader)?, - }), - _ => Err(DecodeError::InvalidValue), - } - } -} - -impl Writeable for HTLCFailReason { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - &HTLCFailReason::LightningError { ref err } => { - 0u8.write(writer)?; - err.write(writer)?; - }, - &HTLCFailReason::Reason { ref failure_code, ref data } => { - 1u8.write(writer)?; - failure_code.write(writer)?; - data.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for HTLCFailReason { - fn read(reader: &mut R) -> Result { - match ::read(reader)? { - 0 => Ok(HTLCFailReason::LightningError { err: Readable::read(reader)? }), - 1 => Ok(HTLCFailReason::Reason { - failure_code: Readable::read(reader)?, - data: Readable::read(reader)?, - }), - _ => Err(DecodeError::InvalidValue), - } - } -} - -impl Writeable for HTLCForwardInfo { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - match self { - &HTLCForwardInfo::AddHTLC { ref prev_short_channel_id, ref prev_funding_outpoint, ref prev_htlc_id, ref forward_info } => { - 0u8.write(writer)?; - prev_short_channel_id.write(writer)?; - prev_funding_outpoint.write(writer)?; - prev_htlc_id.write(writer)?; - forward_info.write(writer)?; - }, - &HTLCForwardInfo::FailHTLC { ref htlc_id, ref err_packet } => { - 1u8.write(writer)?; - htlc_id.write(writer)?; - err_packet.write(writer)?; - }, - } - Ok(()) - } -} +impl_writeable_tlv_based!(ClaimableHTLC, { + (0, prev_hop), + (2, value), + (4, payment_data), + (6, cltv_expiry), +}, {}, {}); -impl Readable for HTLCForwardInfo { - fn read(reader: &mut R) -> Result { - match ::read(reader)? { - 0 => Ok(HTLCForwardInfo::AddHTLC { - prev_short_channel_id: Readable::read(reader)?, - prev_funding_outpoint: Readable::read(reader)?, - prev_htlc_id: Readable::read(reader)?, - forward_info: Readable::read(reader)?, - }), - 1 => Ok(HTLCForwardInfo::FailHTLC { - htlc_id: Readable::read(reader)?, - err_packet: Readable::read(reader)?, - }), - _ => Err(DecodeError::InvalidValue), - } - } -} +impl_writeable_tlv_based_enum!(HTLCSource, + (0, OutboundRoute) => { + (0, session_priv), + (2, first_hop_htlc_msat), + }, {}, { + (4, path), + }; + (1, PreviousHopData) +); + +impl_writeable_tlv_based_enum!(HTLCFailReason, + (0, LightningError) => { + (0, err), + }, {}, {}, + (1, Reason) => { + (0, failure_code), + }, {}, { + (2, data), + }, +;); + +impl_writeable_tlv_based_enum!(HTLCForwardInfo, + (0, AddHTLC) => { + (0, forward_info), + (2, prev_short_channel_id), + (4, prev_htlc_id), + (6, prev_funding_outpoint), + }, {}, {}, + (1, FailHTLC) => { + (0, htlc_id), + (2, err_packet), + }, {}, {}, +;); impl_writeable_tlv_based!(PendingInboundPayment, { (0, payment_secret), diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index eec1a053802..f02a478357b 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -46,7 +46,7 @@ use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; pub(crate) const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000; /// An error in decoding a message or struct. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum DecodeError { /// A version byte specified something we don't know how to handle. /// Includes unknown realm byte in an OnionHopData packet diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 3bda19fd58f..52ac9b77578 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -49,40 +49,14 @@ pub struct RouteHop { pub cltv_expiry_delta: u32, } -/// (C-not exported) -impl Writeable for Vec { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - (self.len() as u8).write(writer)?; - for hop in self.iter() { - hop.pubkey.write(writer)?; - hop.node_features.write(writer)?; - hop.short_channel_id.write(writer)?; - hop.channel_features.write(writer)?; - hop.fee_msat.write(writer)?; - hop.cltv_expiry_delta.write(writer)?; - } - Ok(()) - } -} - -/// (C-not exported) -impl Readable for Vec { - fn read(reader: &mut R) -> Result, DecodeError> { - let hops_count: u8 = Readable::read(reader)?; - let mut hops = Vec::with_capacity(hops_count as usize); - for _ in 0..hops_count { - hops.push(RouteHop { - pubkey: Readable::read(reader)?, - node_features: Readable::read(reader)?, - short_channel_id: Readable::read(reader)?, - channel_features: Readable::read(reader)?, - fee_msat: Readable::read(reader)?, - cltv_expiry_delta: Readable::read(reader)?, - }); - } - Ok(hops) - } -} +impl_writeable_tlv_based!(RouteHop, { + (0, pubkey), + (2, node_features), + (4, short_channel_id), + (6, channel_features), + (8, fee_msat), + (10, cltv_expiry_delta), +}, {}, {}); /// A route directs a payment from the sender (us) to the recipient. If the recipient supports MPP, /// it can take multiple paths. Each path is composed of one or more hops through the network. @@ -105,7 +79,10 @@ impl Writeable for Route { write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); (self.paths.len() as u64).write(writer)?; for hops in self.paths.iter() { - hops.write(writer)?; + (hops.len() as u8).write(writer)?; + for hop in hops.iter() { + hop.write(writer)?; + } } write_tlv_fields!(writer, {}, {}); Ok(()) @@ -118,7 +95,12 @@ impl Readable for Route { let path_count: u64 = Readable::read(reader)?; let mut paths = Vec::with_capacity(cmp::min(path_count, 128) as usize); for _ in 0..path_count { - paths.push(Readable::read(reader)?); + let hop_count: u8 = Readable::read(reader)?; + let mut hops = Vec::with_capacity(hop_count as usize); + for _ in 0..hop_count { + hops.push(Readable::read(reader)?); + } + paths.push(hops); } read_tlv_fields!(reader, {}, {}); Ok(Route { paths }) @@ -3925,8 +3907,8 @@ pub(crate) mod test_utils { use std::fs::File; /// Tries to open a network graph file, or panics with a URL to fetch it. pub(crate) fn get_route_file() -> Result { - let res = File::open("net_graph-2021-05-27.bin") // By default we're run in RL/lightning - .or_else(|_| File::open("lightning/net_graph-2021-05-27.bin")) // We may be run manually in RL/ + let res = File::open("net_graph-2021-05-31.bin") // By default we're run in RL/lightning + .or_else(|_| File::open("lightning/net_graph-2021-05-31.bin")) // We may be run manually in RL/ .or_else(|_| { // Fall back to guessing based on the binary location // path is likely something like .../rust-lightning/target/debug/deps/lightning-... let mut path = std::env::current_exe().unwrap(); @@ -3935,11 +3917,11 @@ pub(crate) mod test_utils { path.pop(); // debug path.pop(); // target path.push("lightning"); - path.push("net_graph-2021-05-27.bin"); + path.push("net_graph-2021-05-31.bin"); eprintln!("{}", path.to_str().unwrap()); File::open(path) }) - .map_err(|_| "Please fetch https://bitcoin.ninja/ldk-net_graph-45d86ead641d-2021-05-27.bin and place it at lightning/net_graph-2021-05-27.bin"); + .map_err(|_| "Please fetch https://bitcoin.ninja/ldk-net_graph-v0.0.15-2021-05-31.bin and place it at lightning/net_graph-2021-05-31.bin"); #[cfg(require_route_graph_test)] return Ok(res.unwrap()); #[cfg(not(require_route_graph_test))] diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index 7ce73889875..a5d223c338d 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -17,7 +17,7 @@ use ln::msgs; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; use chain::keysinterface::SpendableOutputDescriptor; -use util::ser::{Writeable, Writer, MaybeReadable, Readable}; +use util::ser::{Writeable, Writer, MaybeReadable, Readable, VecReadWrapper, VecWriteWrapper}; use bitcoin::blockdata::script::Script; @@ -146,14 +146,20 @@ impl Writeable for Event { }, &Event::PaymentReceived { ref payment_hash, ref payment_preimage, ref payment_secret, ref amt, ref user_payment_id } => { 1u8.write(writer)?; - payment_hash.write(writer)?; - payment_preimage.write(writer)?; - payment_secret.write(writer)?; - amt.write(writer)?; - user_payment_id.write(writer)?; + write_tlv_fields!(writer, { + (0, payment_hash), + (2, payment_secret), + (4, amt), + (6, user_payment_id), + }, { + (8, payment_preimage), + }); }, &Event::PaymentSent { ref payment_preimage } => { 2u8.write(writer)?; + write_tlv_fields!(writer, { + (0, payment_preimage), + }, {}); payment_preimage.write(writer)?; }, &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, @@ -163,24 +169,26 @@ impl Writeable for Event { ref error_data, } => { 3u8.write(writer)?; - payment_hash.write(writer)?; - rejected_by_dest.write(writer)?; #[cfg(test)] error_code.write(writer)?; #[cfg(test)] error_data.write(writer)?; + write_tlv_fields!(writer, { + (0, payment_hash), + (2, rejected_by_dest), + }, {}); }, &Event::PendingHTLCsForwardable { time_forwardable: _ } => { 4u8.write(writer)?; + write_tlv_fields!(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 } => { 5u8.write(writer)?; - (outputs.len() as u64).write(writer)?; - for output in outputs.iter() { - output.write(writer)?; - } + write_tlv_fields!(writer, { + (0, VecWriteWrapper(outputs)), + }, {}); }, } Ok(()) @@ -190,34 +198,84 @@ impl MaybeReadable for Event { fn read(reader: &mut R) -> Result, msgs::DecodeError> { match Readable::read(reader)? { 0u8 => Ok(None), - 1u8 => Ok(Some(Event::PaymentReceived { - payment_hash: Readable::read(reader)?, - payment_preimage: Readable::read(reader)?, - payment_secret: Readable::read(reader)?, - amt: Readable::read(reader)?, - user_payment_id: Readable::read(reader)?, - })), - 2u8 => Ok(Some(Event::PaymentSent { - payment_preimage: Readable::read(reader)?, - })), - 3u8 => Ok(Some(Event::PaymentFailed { - payment_hash: Readable::read(reader)?, - rejected_by_dest: Readable::read(reader)?, + 1u8 => { + let f = || { + let mut payment_hash = PaymentHash([0; 32]); + let mut payment_preimage = None; + let mut payment_secret = PaymentSecret([0; 32]); + let mut amt = 0; + let mut user_payment_id = 0; + read_tlv_fields!(reader, { + (0, payment_hash), + (2, payment_secret), + (4, amt), + (6, user_payment_id), + }, { + (8, payment_preimage), + }); + Ok(Some(Event::PaymentReceived { + payment_hash, + payment_preimage, + payment_secret, + amt, + user_payment_id, + })) + }; + f() + }, + 2u8 => { + let f = || { + let mut payment_preimage = PaymentPreimage([0; 32]); + read_tlv_fields!(reader, { + (0, payment_preimage), + }, {}); + Ok(Some(Event::PaymentSent { + payment_preimage, + })) + }; + f() + }, + 3u8 => { + let f = || { #[cfg(test)] - error_code: Readable::read(reader)?, + let error_code = Readable::read(reader)?; #[cfg(test)] - error_data: Readable::read(reader)?, - })), - 4u8 => Ok(Some(Event::PendingHTLCsForwardable { - time_forwardable: Duration::from_secs(0) - })), + let error_data = Readable::read(reader)?; + let mut payment_hash = PaymentHash([0; 32]); + let mut rejected_by_dest = false; + read_tlv_fields!(reader, { + (0, payment_hash), + (2, rejected_by_dest), + }, {}); + Ok(Some(Event::PaymentFailed { + payment_hash, + rejected_by_dest, + #[cfg(test)] + error_code, + #[cfg(test)] + error_data, + })) + }; + f() + }, + 4u8 => { + let f = || { + read_tlv_fields!(reader, {}, {}); + Ok(Some(Event::PendingHTLCsForwardable { + time_forwardable: Duration::from_secs(0) + })) + }; + f() + }, 5u8 => { - 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 })) + let f = || { + let mut outputs = VecReadWrapper(Vec::new()); + read_tlv_fields!(reader, { + (0, outputs), + }, {}); + Ok(Some(Event::SpendableOutputs { outputs: outputs.0 })) + }; + f() }, _ => Err(msgs::DecodeError::InvalidValue) } diff --git a/lightning/src/util/mod.rs b/lightning/src/util/mod.rs index e8118a9a1a9..cc0c3192a85 100644 --- a/lightning/src/util/mod.rs +++ b/lightning/src/util/mod.rs @@ -12,6 +12,9 @@ #[macro_use] pub(crate) mod fuzz_wrappers; +#[macro_use] +pub(crate) mod ser_macros; + pub mod events; pub mod errors; pub mod ser; @@ -29,9 +32,6 @@ pub(crate) mod chacha20poly1305rfc; pub(crate) mod transaction_utils; pub(crate) mod scid_utils; -#[macro_use] -pub(crate) mod ser_macros; - /// Logging macro utilities. #[macro_use] pub(crate) mod macro_logger; diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 7bc09e7f8da..4e69a37d20e 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -249,28 +249,31 @@ impl Readable for OptionDeserWrapper { } } -const MAX_ALLOC_SIZE: u64 = 64*1024; - +/// Wrapper to write each element of a Vec with no length prefix pub(crate) struct VecWriteWrapper<'a, T: Writeable>(pub &'a Vec); impl<'a, T: Writeable> Writeable for VecWriteWrapper<'a, T> { #[inline] fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - (self.0.len() as u64).write(writer)?; for ref v in self.0.iter() { v.write(writer)?; } Ok(()) } } + +/// Wrapper to read elements from a given stream until it reaches the end of the stream. pub(crate) struct VecReadWrapper(pub Vec); impl Readable for VecReadWrapper { #[inline] - fn read(reader: &mut R) -> Result { - let count: u64 = Readable::read(reader)?; - let mut values = Vec::with_capacity(cmp::min(count, MAX_ALLOC_SIZE / (core::mem::size_of::() as u64)) as usize); - for _ in 0..count { - match Readable::read(reader) { + fn read(mut reader: &mut R) -> Result { + let mut values = Vec::new(); + loop { + let mut track_read = ReadTrackingReader::new(&mut reader); + match Readable::read(&mut track_read) { Ok(v) => { values.push(v); }, + // If we failed to read any bytes at all, we reached the end of our TLV + // stream and have simply exhausted all entries. + Err(ref e) if e == &DecodeError::ShortRead && !track_read.have_read => break, Err(e) => return Err(e), } } @@ -695,6 +698,18 @@ impl Readable for PaymentSecret { } } +impl Writeable for Box { + fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + T::write(&**self, w) + } +} + +impl Readable for Box { + fn read(r: &mut R) -> Result { + Ok(Box::new(Readable::read(r)?)) + } +} + impl Writeable for Option { fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { match *self { @@ -821,3 +836,19 @@ impl Writeable for (A, B) { self.1.write(w) } } + +impl Readable for (A, B, C) { + fn read(r: &mut R) -> Result { + let a: A = Readable::read(r)?; + let b: B = Readable::read(r)?; + let c: C = Readable::read(r)?; + Ok((a, b, c)) + } +} +impl Writeable for (A, B, C) { + fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + self.0.write(w)?; + self.1.write(w)?; + self.2.write(w) + } +} diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs index 45435ecdd64..cf780ef06b2 100644 --- a/lightning/src/util/ser_macros.rs +++ b/lightning/src/util/ser_macros.rs @@ -317,7 +317,7 @@ macro_rules! read_tlv_fields { let tlv_len = ::util::ser::BigSize::read($stream)?; let mut rd = ::util::ser::FixedLengthReader::new($stream, tlv_len.0); decode_tlv!(&mut rd, {$(($reqtype, $reqfield)),*}, {$(($type, $field)),*}); - rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?; + rd.eat_remaining().map_err(|_| ::ln::msgs::DecodeError::ShortRead)?; } } } @@ -325,26 +325,26 @@ macro_rules! read_tlv_fields { // `Self { ,,vecfield: vecfield }` which is obviously incorrect. Instead, we have to match here to // detect at least one empty field set and skip the potentially-extra comma. macro_rules! _init_tlv_based_struct { - ({}, {$($field: ident),*}, {$($vecfield: ident),*}) => { - Ok(Self { + ($($type: ident)::*, {}, {$($field: ident),*}, {$($vecfield: ident),*}) => { + Ok($($type)::* { $($field),*, $($vecfield: $vecfield.unwrap().0),* }) }; - ({$($reqfield: ident),*}, {}, {$($vecfield: ident),*}) => { - Ok(Self { + ($($type: ident)::*, {$($reqfield: ident),*}, {}, {$($vecfield: ident),*}) => { + Ok($($type)::* { $($reqfield: $reqfield.0.unwrap()),*, $($vecfield: $vecfield.unwrap().0),* }) }; - ({$($reqfield: ident),*}, {$($field: ident),*}, {}) => { - Ok(Self { + ($($type: ident)::*, {$($reqfield: ident),*}, {$($field: ident),*}, {}) => { + Ok($($type)::* { $($reqfield: $reqfield.0.unwrap()),*, $($field),* }) }; - ({$($reqfield: ident),*}, {$($field: ident),*}, {$($vecfield: ident),*}) => { - Ok(Self { + ($($type: ident)::*, {$($reqfield: ident),*}, {$($field: ident),*}, {$($vecfield: ident),*}) => { + Ok($($type)::* { $($reqfield: $reqfield.0.unwrap()),*, $($field),*, $($vecfield: $vecfield.unwrap().0),* @@ -433,7 +433,88 @@ macro_rules! impl_writeable_tlv_based { }, { $(($vectype, $vecfield)),* }); - _init_tlv_based_struct!({$($reqfield),*}, {$($field),*}, {$($vecfield),*}) + _init_tlv_based_struct!($st, {$($reqfield),*}, {$($field),*}, {$($vecfield),*}) + } + } + } +} + +/// Implement Readable and Writeable for an enum, with struct variants stored as TLVs and tuple +/// variants stored directly. +/// The format is, for example +/// impl_writeable_tlv_based_enum!(EnumName, +/// (0, StructVariantA) => {(0, variant_field)}, {(1, variant_optional_field)}, {}, +/// (1, StructVariantB) => {(0, variant_field_a), (1, variant_field_b)}, {}, {(2, variant_vec_field)}; +/// (2, TupleVariantA), (3, TupleVariantB), +/// ); +/// The type is written as a single byte, followed by any variant data. +/// Attempts to read an unknown type byte result in DecodeError::UnknownRequiredFeature. +macro_rules! impl_writeable_tlv_based_enum { + ($st: ident, $(($variant_id: expr, $variant_name: ident) => + {$(($reqtype: expr, $reqfield: ident)),* $(,)*}, + {$(($type: expr, $field: ident)),* $(,)*}, + {$(($vectype: expr, $vecfield: ident)),* $(,)*} + ),* $(,)*; + $(($tuple_variant_id: expr, $tuple_variant_name: ident)),* $(,)*) => { + impl ::util::ser::Writeable for $st { + fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + match self { + $($st::$variant_name { $(ref $reqfield),* $(ref $field),*, $(ref $vecfield),* } => { + let id: u8 = $variant_id; + id.write(writer)?; + _write_tlv_fields!(writer, { + $(($reqtype, $reqfield)),* + }, { + $(($type, $field)),* + }, { + $(($vectype, Some(::util::ser::VecWriteWrapper(&$vecfield)))),* + }); + }),* + $($st::$tuple_variant_name (ref field) => { + let id: u8 = $tuple_variant_id; + id.write(writer)?; + field.write(writer)?; + }),* + } + Ok(()) + } + } + + impl ::util::ser::Readable for $st { + fn read(reader: &mut R) -> Result { + let id: u8 = ::util::ser::Readable::read(reader)?; + match id { + $($variant_id => { + // Because read_tlv_fields creates a labeled loop, we cannot call it twice + // in the same function body. Instead, we define a closure and call it. + let f = || { + $( + let mut $reqfield = ::util::ser::OptionDeserWrapper(None); + )* + $( + let mut $field = None; + )* + $( + let mut $vecfield = Some(::util::ser::VecReadWrapper(Vec::new())); + )* + _read_tlv_fields!(reader, { + $(($reqtype, $reqfield)),* + }, { + $(($type, $field)),* + }, { + $(($vectype, $vecfield)),* + }); + _init_tlv_based_struct!($st::$variant_name, {$($reqfield),*}, {$($field),*}, {$($vecfield),*}) + }; + f() + }),* + $($tuple_variant_id => { + Ok($st::$tuple_variant_name(Readable::read(reader)?)) + }),* + _ => { + Err(DecodeError::UnknownRequiredFeature)? + }, + } } } } @@ -442,7 +523,7 @@ macro_rules! impl_writeable_tlv_based { #[cfg(test)] mod tests { use prelude::*; - use std::io::{Cursor, Read}; + use std::io::Cursor; use ln::msgs::DecodeError; use util::ser::{Readable, Writeable, HighZeroBytesDroppedVarInt, VecWriter}; use bitcoin::secp256k1::PublicKey; @@ -512,13 +593,6 @@ mod tests { (0xdeadbeef1badbeef, 0x1bad1dea, Some(0x01020304))); } - impl Readable for (PublicKey, u64, u64) { - #[inline] - fn read(reader: &mut R) -> Result<(PublicKey, u64, u64), DecodeError> { - Ok((Readable::read(reader)?, Readable::read(reader)?, Readable::read(reader)?)) - } - } - // BOLT TLV test cases fn tlv_reader_n1(s: &[u8]) -> Result<(Option>, Option, Option<(PublicKey, u64, u64)>, Option), DecodeError> { let mut s = Cursor::new(s);