]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Merge pull request #3144 from TheBlueMatt/2024-06-message-flags
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Tue, 2 Jul 2024 22:16:59 +0000 (15:16 -0700)
committerGitHub <noreply@github.com>
Tue, 2 Jul 2024 22:16:59 +0000 (15:16 -0700)
(Re-)add handling for `ChannelUpdate::message_flags`

1  2 
fuzz/src/msg_targets/gen_target.sh
fuzz/src/msg_targets/mod.rs
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/msgs.rs
lightning/src/util/test_utils.rs

index 62b1130ae9effc9194979cd89a95e754bd87bb03,71c3d895ba2fabfcea029abc1175ee8e42eec974..c8cc45926a1a885322220ff2669cff4d15666706
@@@ -39,14 -39,17 +39,17 @@@ GEN_TEST lightning::ln::msgs::ChannelRe
  
  GEN_TEST lightning::ln::msgs::DecodedOnionErrorPacket test_msg ""
  
+ # Gossip messages need to use `test_msg_exact` to ensure that messages
+ # round-trip exactly when doing signature validation.
  GEN_TEST lightning::ln::msgs::ChannelAnnouncement test_msg_exact ""
  GEN_TEST lightning::ln::msgs::NodeAnnouncement test_msg_exact ""
+ GEN_TEST lightning::ln::msgs::ChannelUpdate test_msg_exact ""
  GEN_TEST lightning::ln::msgs::QueryShortChannelIds test_msg ""
  GEN_TEST lightning::ln::msgs::ReplyChannelRange test_msg ""
  
  GEN_TEST lightning::ln::msgs::ErrorMessage test_msg_hole ", 32, 2"
  GEN_TEST lightning::ln::msgs::WarningMessage test_msg_hole ", 32, 2"
- GEN_TEST lightning::ln::msgs::ChannelUpdate test_msg_hole ", 108, 1"
  
  GEN_TEST lightning::ln::channel_state::ChannelDetails test_msg_simple ""
  
@@@ -64,6 -67,6 +67,6 @@@ GEN_TEST lightning::ln::msgs::TxAbort t
  
  GEN_TEST lightning::ln::msgs::Stfu test_msg_simple ""
  
 -GEN_TEST lightning::ln::msgs::Splice test_msg_simple ""
 +GEN_TEST lightning::ln::msgs::SpliceInit test_msg_simple ""
  GEN_TEST lightning::ln::msgs::SpliceAck test_msg_simple ""
  GEN_TEST lightning::ln::msgs::SpliceLocked test_msg_simple ""
index 527cb530d000a14fb16ccf8a6882c52a57dcf158,16721fa627a763683a279b031da36c52e397c937..6f5f8120ca0210cc6622ef2f0459fc7c3594d428
@@@ -25,11 -25,11 +25,11 @@@ pub mod msg_channel_reestablish
  pub mod msg_decoded_onion_error_packet;
  pub mod msg_channel_announcement;
  pub mod msg_node_announcement;
+ pub mod msg_channel_update;
  pub mod msg_query_short_channel_ids;
  pub mod msg_reply_channel_range;
  pub mod msg_error_message;
  pub mod msg_warning_message;
- pub mod msg_channel_update;
  pub mod msg_channel_details;
  pub mod msg_open_channel_v2;
  pub mod msg_accept_channel_v2;
@@@ -43,6 -43,6 +43,6 @@@ pub mod msg_tx_init_rbf
  pub mod msg_tx_ack_rbf;
  pub mod msg_tx_abort;
  pub mod msg_stfu;
 -pub mod msg_splice;
 +pub mod msg_splice_init;
  pub mod msg_splice_ack;
  pub mod msg_splice_locked;
index 4d166cd60f5c84ed6312592b3e59d5783d501914,5db076931ab83e8abc208bea708fee59ae4b70e5..4de551b72bfdf331d4ee64b15bbe8b69ee95d8bf
@@@ -2768,7 -2768,7 +2768,7 @@@ impl<SP: Deref> ChannelContext<SP> wher
                        feerate_per_kw = cmp::max(feerate_per_kw, feerate);
                }
                let feerate_plus_quarter = feerate_per_kw.checked_mul(1250).map(|v| v / 1000);
 -              cmp::max(feerate_per_kw + 2530, feerate_plus_quarter.unwrap_or(u32::max_value()))
 +              cmp::max(feerate_per_kw.saturating_add(2530), feerate_plus_quarter.unwrap_or(u32::MAX))
        }
  
        /// Get forwarding information for the counterparty.
@@@ -7272,7 -7272,6 +7272,7 @@@ impl<SP: Deref> Channel<SP> wher
                                        channel_id: self.context.channel_id,
                                        signature,
                                        htlc_signatures,
 +                                      batch: None,
                                        #[cfg(taproot)]
                                        partial_signature_with_nonce: None,
                                }, (counterparty_commitment_txid, commitment_stats.htlcs_included)))
@@@ -9895,7 -9894,8 +9895,8 @@@ mod tests 
                                chain_hash,
                                short_channel_id: 0,
                                timestamp: 0,
-                               flags: 0,
+                               message_flags: 1, // Only must_be_one
+                               channel_flags: 0,
                                cltv_expiry_delta: 100,
                                htlc_minimum_msat: 5,
                                htlc_maximum_msat: MAX_VALUE_MSAT,
index d3550d10b50b0cc7cdb86f2d6d46fc074b9c5300,86cf4dbb81554a56419bda56d5d98c857cdcfb8b..807dce7cd4536c2307f74df5190445549a0ebdac
@@@ -66,7 -66,6 +66,7 @@@ use crate::offers::invoice_request::{De
  use crate::offers::offer::{Offer, OfferBuilder};
  use crate::offers::parse::Bolt12SemanticError;
  use crate::offers::refund::{Refund, RefundBuilder};
 +use crate::onion_message::async_payments::{AsyncPaymentsMessage, HeldHtlcAvailable, ReleaseHeldHtlc, AsyncPaymentsMessageHandler};
  use crate::onion_message::messenger::{new_pending_onion_message, Destination, MessageRouter, PendingOnionMessage, Responder, ResponseInstruction};
  use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
  use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider};
@@@ -3506,7 -3505,7 +3506,7 @@@ wher
                        // peer has been disabled for some time), return `channel_disabled`,
                        // otherwise return `temporary_channel_failure`.
                        let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
-                       if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
+                       if chan_update_opt.as_ref().map(|u| u.contents.channel_flags & 2 == 2).unwrap_or(false) {
                                return Err(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
                        } else {
                                return Err(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
                        chain_hash: self.chain_hash,
                        short_channel_id,
                        timestamp: chan.context.get_update_time_counter(),
-                       flags: (!were_node_one) as u8 | ((!enabled as u8) << 1),
+                       message_flags: 1, // Only must_be_one
+                       channel_flags: (!were_node_one) as u8 | ((!enabled as u8) << 1),
                        cltv_expiry_delta: chan.context.get_cltv_expiry_delta(),
                        htlc_minimum_msat: chan.context.get_counterparty_htlc_minimum_msat(),
                        htlc_maximum_msat: chan.context.get_announced_htlc_max_msat(),
                                                return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a channel_update for a channel from the wrong node - it shouldn't know about our private channels!".to_owned(), chan_id));
                                        }
                                        let were_node_one = self.get_our_node_id().serialize()[..] < chan.context.get_counterparty_node_id().serialize()[..];
-                                       let msg_from_node_one = msg.contents.flags & 1 == 0;
+                                       let msg_from_node_one = msg.contents.channel_flags & 1 == 0;
                                        if were_node_one == msg_from_node_one {
                                                return Ok(NotifyOption::SkipPersistNoEvents);
                                        } else {
@@@ -9693,7 -9693,7 +9694,7 @@@ wher
        }
  
        #[cfg(splicing)]
 -      fn handle_splice(&self, counterparty_node_id: &PublicKey, msg: &msgs::Splice) {
 +      fn handle_splice_init(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceInit) {
                let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
                        "Splicing not supported".to_owned(),
                         msg.channel_id.clone())), *counterparty_node_id);
                                                // Quiescence
                                                &events::MessageSendEvent::SendStfu { .. } => false,
                                                // Splicing
 -                                              &events::MessageSendEvent::SendSplice { .. } => false,
 +                                              &events::MessageSendEvent::SendSpliceInit { .. } => false,
                                                &events::MessageSendEvent::SendSpliceAck { .. } => false,
                                                &events::MessageSendEvent::SendSpliceLocked { .. } => false,
                                                // Interactive Transaction Construction
        }
  }
  
 +impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
 +AsyncPaymentsMessageHandler for ChannelManager<M, T, ES, NS, SP, F, R, L>
 +where
 +      M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
 +      T::Target: BroadcasterInterface,
 +      ES::Target: EntropySource,
 +      NS::Target: NodeSigner,
 +      SP::Target: SignerProvider,
 +      F::Target: FeeEstimator,
 +      R::Target: Router,
 +      L::Target: Logger,
 +{
 +      fn held_htlc_available(
 +              &self, _message: HeldHtlcAvailable, _responder: Option<Responder>
 +      ) -> ResponseInstruction<ReleaseHeldHtlc> {
 +              ResponseInstruction::NoResponse
 +      }
 +
 +      fn release_held_htlc(&self, _message: ReleaseHeldHtlc) {}
 +
 +      fn release_pending_messages(&self) -> Vec<PendingOnionMessage<AsyncPaymentsMessage>> {
 +              Vec::new()
 +      }
 +}
 +
  impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
  NodeIdLookUp for ChannelManager<M, T, ES, NS, SP, F, R, L>
  where
@@@ -12262,8 -12237,8 +12263,8 @@@ mod tests 
                // update message and would always update the local fee info, even if our peer was
                // (spuriously) forwarding us our own channel_update.
                let as_node_one = nodes[0].node.get_our_node_id().serialize()[..] < nodes[1].node.get_our_node_id().serialize()[..];
-               let as_update = if as_node_one == (chan.0.contents.flags & 1 == 0 /* chan.0 is from node one */) { &chan.0 } else { &chan.1 };
-               let bs_update = if as_node_one == (chan.0.contents.flags & 1 == 0 /* chan.0 is from node one */) { &chan.1 } else { &chan.0 };
+               let as_update = if as_node_one == (chan.0.contents.channel_flags & 1 == 0 /* chan.0 is from node one */) { &chan.0 } else { &chan.1 };
+               let bs_update = if as_node_one == (chan.0.contents.channel_flags & 1 == 0 /* chan.0 is from node one */) { &chan.1 } else { &chan.0 };
  
                // First deliver each peers' own message, checking that the node doesn't need to be
                // persisted and that its channel info remains the same.
index 7f1ac4226ead4f438eea2c220ddf9662741aaec5,aad61817712b006141f6486adb84e322fab18cfb..c7d2edf51f85e3c83bbcbf804e28e83b135720d3
@@@ -422,7 -422,7 +422,7 @@@ type TestOnionMessenger<'chan_man, 'nod
        &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
        &'node_cfg test_utils::TestMessageRouter<'chan_mon_cfg>,
        &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
 -      IgnoringMessageHandler,
 +      &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
        IgnoringMessageHandler,
  >;
  
@@@ -915,7 -915,7 +915,7 @@@ pub fn remove_first_msg_event_to_node(m
                MessageSendEvent::SendStfu { node_id, .. } => {
                        node_id == msg_node_id
                },
 -              MessageSendEvent::SendSplice { node_id, .. } => {
 +              MessageSendEvent::SendSpliceInit { node_id, .. } => {
                        node_id == msg_node_id
                },
                MessageSendEvent::SendSpliceAck { node_id, .. } => {
@@@ -1554,7 -1554,7 +1554,7 @@@ macro_rules! get_closing_signed_broadca
                        assert!(events.len() == 1 || events.len() == 2);
                        (match events[events.len() - 1] {
                                MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
-                                       assert_eq!(msg.contents.flags & 2, 2);
+                                       assert_eq!(msg.contents.channel_flags & 2, 2);
                                        msg.clone()
                                },
                                _ => panic!("Unexpected event"),
@@@ -1613,7 -1613,7 +1613,7 @@@ pub fn check_closed_broadcast(node: &No
        msg_events.into_iter().filter_map(|msg_event| {
                match msg_event {
                        MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
-                               assert_eq!(msg.contents.flags & 2, 2);
+                               assert_eq!(msg.contents.channel_flags & 2, 2);
                                None
                        },
                        MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { msg }, node_id: _ } => {
@@@ -3259,7 -3259,7 +3259,7 @@@ pub fn create_network<'a, 'b: 'a, 'c: '
                let dedicated_entropy = DedicatedEntropy(RandomBytes::new([i as u8; 32]));
                let onion_messenger = OnionMessenger::new(
                        dedicated_entropy, cfgs[i].keys_manager, cfgs[i].logger, &chan_mgrs[i],
 -                      &cfgs[i].message_router, &chan_mgrs[i], IgnoringMessageHandler {}, IgnoringMessageHandler {},
 +                      &cfgs[i].message_router, &chan_mgrs[i], &chan_mgrs[i], IgnoringMessageHandler {},
                );
                let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
                let wallet_source = Arc::new(test_utils::TestWalletSource::new(SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap()));
index 72415a6bae0ef2332e470cbb18cd4b433d7c988f,6271f73000aa840f8c051929cdf6b3c268c473d5..a01388f20158a65317027bce11f47b51c9108478
@@@ -783,7 -783,6 +783,7 @@@ fn test_update_fee_that_funder_cannot_a
                channel_id: chan.2,
                signature: res.0,
                htlc_signatures: res.1,
 +              batch: None,
                #[cfg(taproot)]
                partial_signature_with_nonce: None,
        };
@@@ -1533,7 -1532,6 +1533,7 @@@ fn test_fee_spike_violation_fails_htlc(
                channel_id: chan.2,
                signature: res.0,
                htlc_signatures: res.1,
 +              batch: None,
                #[cfg(taproot)]
                partial_signature_with_nonce: None,
        };
@@@ -7405,7 -7403,7 +7405,7 @@@ fn test_announce_disable_channels() 
        for e in msg_events {
                match e {
                        MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
-                               assert_eq!(msg.contents.flags & (1<<1), 1<<1); // The "channel disabled" bit should be set
+                               assert_eq!(msg.contents.channel_flags & (1<<1), 1<<1); // The "channel disabled" bit should be set
                                // Check that each channel gets updated exactly once
                                if chans_disabled.insert(msg.contents.short_channel_id, msg.contents.timestamp).is_some() {
                                        panic!("Generated ChannelUpdate for wrong chan!");
        for e in msg_events {
                match e {
                        MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
-                               assert_eq!(msg.contents.flags & (1<<1), 0); // The "channel disabled" bit should be off
+                               assert_eq!(msg.contents.channel_flags & (1<<1), 0); // The "channel disabled" bit should be off
                                match chans_disabled.remove(&msg.contents.short_channel_id) {
                                        // Each update should have a higher timestamp than the previous one, replacing
                                        // the old one.
@@@ -9398,13 -9396,13 +9398,13 @@@ fn test_error_chans_closed() 
        assert_eq!(events.len(), 2);
        match events[0] {
                MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
-                       assert_eq!(msg.contents.flags & 2, 2);
+                       assert_eq!(msg.contents.channel_flags & 2, 2);
                },
                _ => panic!("Unexpected event"),
        }
        match events[1] {
                MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
-                       assert_eq!(msg.contents.flags & 2, 2);
+                       assert_eq!(msg.contents.channel_flags & 2, 2);
                },
                _ => panic!("Unexpected event"),
        }
diff --combined lightning/src/ln/msgs.rs
index aa69abb712b3a0c744ff7cd7c79ad6fe0b976180,0cc8f13236d5cbf5d498002dcd8dee7fd78bdff9..8f389d07cefc9b59cb314b58a597ac75d23aa199
@@@ -410,9 -410,8 +410,9 @@@ pub struct ChannelReady 
  /// construction.
  pub type SerialId = u64;
  
 -/// An stfu (quiescence) message to be sent by or received from the stfu initiator.
 -// TODO(splicing): Add spec link for `stfu`; still in draft, using from https://github.com/lightning/bolts/pull/863
 +/// An `stfu` (quiescence) message to be sent by or received from the stfu initiator.
 +///
 +// TODO(splicing): Add spec link for `stfu`; still in draft, using from https://github.com/lightning/bolts/pull/1160
  #[derive(Clone, Debug, PartialEq, Eq)]
  pub struct Stfu {
        /// The channel ID where quiescence is intended
        pub initiator: u8,
  }
  
 -/// A splice message to be sent by or received from the stfu initiator (splice initiator).
 -// TODO(splicing): Add spec link for `splice`; still in draft, using from https://github.com/lightning/bolts/pull/863
 +/// A `splice_init` message to be sent by or received from the stfu initiator (splice initiator).
 +///
 +// TODO(splicing): Add spec link for `splice_init`; still in draft, using from https://github.com/lightning/bolts/pull/1160
  #[derive(Clone, Debug, PartialEq, Eq)]
 -pub struct Splice {
 +pub struct SpliceInit {
        /// The channel ID where splicing is intended
        pub channel_id: ChannelId,
 -      /// The genesis hash of the blockchain where the channel is intended to be spliced
 -      pub chain_hash: ChainHash,
 -      /// The intended change in channel capacity: the amount to be added (positive value)
 -      /// or removed (negative value) by the sender (splice initiator) by splicing into/from the channel.
 -      pub relative_satoshis: i64,
 +      /// The amount the splice initiator is intending to add to its channel balance (splice-in)
 +      /// or remove from its channel balance (splice-out).
 +      pub funding_contribution_satoshis: i64,
        /// The feerate for the new funding transaction, set by the splice initiator
        pub funding_feerate_perkw: u32,
        /// The locktime for the new funding transaction
        pub locktime: u32,
        /// The key of the sender (splice initiator) controlling the new funding transaction
        pub funding_pubkey: PublicKey,
 +      /// If set, only confirmed inputs added (by the splice acceptor) will be accepted
 +      pub require_confirmed_inputs: Option<()>,
  }
  
 -/// A splice_ack message to be received by or sent to the splice initiator.
 +/// A `splice_ack` message to be received by or sent to the splice initiator.
  ///
 -// TODO(splicing): Add spec link for `splice_ack`; still in draft, using from https://github.com/lightning/bolts/pull/863
 +// TODO(splicing): Add spec link for `splice_ack`; still in draft, using from https://github.com/lightning/bolts/pull/1160
  #[derive(Clone, Debug, PartialEq, Eq)]
  pub struct SpliceAck {
        /// The channel ID where splicing is intended
        pub channel_id: ChannelId,
 -      /// The genesis hash of the blockchain where the channel is intended to be spliced
 -      pub chain_hash: ChainHash,
 -      /// The intended change in channel capacity: the amount to be added (positive value)
 -      /// or removed (negative value) by the sender (splice acceptor) by splicing into/from the channel.
 -      pub relative_satoshis: i64,
 +      /// The amount the splice acceptor is intending to add to its channel balance (splice-in)
 +      /// or remove from its channel balance (splice-out).
 +      pub funding_contribution_satoshis: i64,
        /// The key of the sender (splice acceptor) controlling the new funding transaction
        pub funding_pubkey: PublicKey,
 +      /// If set, only confirmed inputs added (by the splice initiator) will be accepted
 +      pub require_confirmed_inputs: Option<()>,
  }
  
 -/// A splice_locked message to be sent to or received from a peer.
 +/// A `splice_locked` message to be sent to or received from a peer.
  ///
 -// TODO(splicing): Add spec link for `splice_locked`; still in draft, using from https://github.com/lightning/bolts/pull/863
 +// TODO(splicing): Add spec link for `splice_locked`; still in draft, using from https://github.com/lightning/bolts/pull/1160
  #[derive(Clone, Debug, PartialEq, Eq)]
  pub struct SpliceLocked {
        /// The channel ID
        pub channel_id: ChannelId,
 +      /// The ID of the new funding transaction that has been locked
 +      pub splice_txid: Txid,
  }
  
  /// A tx_add_input message for adding an input during interactive transaction construction
@@@ -485,8 -481,6 +485,8 @@@ pub struct TxAddInput 
        pub prevtx_out: u32,
        /// The sequence number of this input
        pub sequence: u32,
 +      /// The ID of the previous funding transaction, when it is being added as an input during splicing
 +      pub shared_input_txid: Option<Txid>,
  }
  
  /// A tx_add_output message for adding an output during interactive transaction construction.
@@@ -550,7 -544,7 +550,7 @@@ pub struct TxSignatures 
        /// The list of witnesses
        pub witnesses: Vec<Witness>,
        /// Optional signature for the shared input -- the previous funding outpoint -- signed by both peers
 -      pub funding_outpoint_sig: Option<Signature>,
 +      pub shared_input_signature: Option<Signature>,
  }
  
  /// A tx_init_rbf message which initiates a replacement of the transaction after it's been
@@@ -714,15 -708,6 +714,15 @@@ pub struct UpdateFailMalformedHTLC 
        pub failure_code: u16,
  }
  
 +/// Optional batch parameters for `commitment_signed` message.
 +#[derive(Clone, Debug, Hash, PartialEq, Eq)]
 +pub struct CommitmentSignedBatch {
 +      /// Batch size N: all N `commitment_signed` messages must be received before being processed
 +      pub batch_size: u16,
 +      /// The funding transaction, to discriminate among multiple pending funding transactions (e.g. in case of splicing)
 +      pub funding_txid: Txid,
 +}
 +
  /// A [`commitment_signed`] message to be sent to or received from a peer.
  ///
  /// [`commitment_signed`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#committing-updates-so-far-commitment_signed
@@@ -734,8 -719,6 +734,8 @@@ pub struct CommitmentSigned 
        pub signature: Signature,
        /// Signatures on the HTLC transactions
        pub htlc_signatures: Vec<Signature>,
 +      /// Optional batch size and other parameters
 +      pub batch: Option<CommitmentSignedBatch>,
        #[cfg(taproot)]
        /// The partial Taproot signature on the commitment transaction
        pub partial_signature_with_nonce: Option<PartialSignatureWithNonce>,
@@@ -1244,8 -1227,11 +1244,11 @@@ pub struct UnsignedChannelUpdate 
        pub short_channel_id: u64,
        /// A strictly monotonic announcement counter, with gaps allowed, specific to this channel
        pub timestamp: u32,
-       /// Channel flags
-       pub flags: u8,
+       /// Flags pertaining to this message.
+       pub message_flags: u8,
+       /// Flags pertaining to the channel, including to which direction in the channel this update
+       /// applies and whether the direction is currently able to forward HTLCs.
+       pub channel_flags: u8,
        /// The number of blocks such that if:
        /// `incoming_htlc.cltv_expiry < outgoing_htlc.cltv_expiry + cltv_expiry_delta`
        /// then we need to fail the HTLC backwards. When forwarding an HTLC, `cltv_expiry_delta` determines
@@@ -1478,9 -1464,9 +1481,9 @@@ pub trait ChannelMessageHandler : Messa
        fn handle_stfu(&self, their_node_id: &PublicKey, msg: &Stfu);
  
        // Splicing
 -      /// Handle an incoming `splice` message from the given peer.
 +      /// Handle an incoming `splice_init` message from the given peer.
        #[cfg(splicing)]
 -      fn handle_splice(&self, their_node_id: &PublicKey, msg: &Splice);
 +      fn handle_splice_init(&self, their_node_id: &PublicKey, msg: &SpliceInit);
        /// Handle an incoming `splice_ack` message from the given peer.
        #[cfg(splicing)]
        fn handle_splice_ack(&self, their_node_id: &PublicKey, msg: &SpliceAck);
@@@ -2105,27 -2091,24 +2108,27 @@@ impl_writeable_msg!(Stfu, 
        initiator,
  }, {});
  
 -impl_writeable_msg!(Splice, {
 +impl_writeable_msg!(SpliceInit, {
        channel_id,
 -      chain_hash,
 -      relative_satoshis,
 +      funding_contribution_satoshis,
        funding_feerate_perkw,
        locktime,
        funding_pubkey,
 -}, {});
 +}, {
 +      (2, require_confirmed_inputs, option), // `splice_init_tlvs`
 +});
  
  impl_writeable_msg!(SpliceAck, {
        channel_id,
 -      chain_hash,
 -      relative_satoshis,
 +      funding_contribution_satoshis,
        funding_pubkey,
 -}, {});
 +}, {
 +      (2, require_confirmed_inputs, option), // `splice_ack_tlvs`
 +});
  
  impl_writeable_msg!(SpliceLocked, {
        channel_id,
 +      splice_txid,
  }, {});
  
  impl_writeable_msg!(TxAddInput, {
        prevtx,
        prevtx_out,
        sequence,
 -}, {});
 +}, {
 +      (0, shared_input_txid, option), // `funding_txid`
 +});
  
  impl_writeable_msg!(TxAddOutput, {
        channel_id,
@@@ -2164,7 -2145,7 +2167,7 @@@ impl_writeable_msg!(TxSignatures, 
        tx_hash,
        witnesses,
  }, {
 -      (0, funding_outpoint_sig, option),
 +      (0, shared_input_signature, option), // `signature`
  });
  
  impl_writeable_msg!(TxInitRbf, {
@@@ -2213,19 -2194,12 +2216,19 @@@ impl_writeable!(ClosingSignedFeeRange, 
        max_fee_satoshis
  });
  
 +impl_writeable_msg!(CommitmentSignedBatch, {
 +      batch_size,
 +      funding_txid,
 +}, {});
 +
  #[cfg(not(taproot))]
  impl_writeable_msg!(CommitmentSigned, {
        channel_id,
        signature,
        htlc_signatures
 -}, {});
 +}, {
 +      (0, batch, option),
 +});
  
  #[cfg(taproot)]
  impl_writeable_msg!(CommitmentSigned, {
        signature,
        htlc_signatures
  }, {
 -      (2, partial_signature_with_nonce, option)
 +      (0, batch, option),
 +      (2, partial_signature_with_nonce, option),
  });
  
  impl_writeable!(DecodedOnionErrorPacket, {
@@@ -2896,13 -2869,13 +2899,13 @@@ impl_writeable!(ChannelAnnouncement, 
  
  impl Writeable for UnsignedChannelUpdate {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               // `message_flags` used to indicate presence of `htlc_maximum_msat`, but was deprecated in the spec.
-               const MESSAGE_FLAGS: u8 = 1;
                self.chain_hash.write(w)?;
                self.short_channel_id.write(w)?;
                self.timestamp.write(w)?;
-               let all_flags = self.flags as u16 | ((MESSAGE_FLAGS as u16) << 8);
-               all_flags.write(w)?;
+               // Thw low bit of message_flags used to indicate the presence of `htlc_maximum_msat`, and
+               // now must be set
+               (self.message_flags | 1).write(w)?;
+               self.channel_flags.write(w)?;
                self.cltv_expiry_delta.write(w)?;
                self.htlc_minimum_msat.write(w)?;
                self.fee_base_msat.write(w)?;
  
  impl Readable for UnsignedChannelUpdate {
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
-               Ok(Self {
+               let res = Self {
                        chain_hash: Readable::read(r)?,
                        short_channel_id: Readable::read(r)?,
                        timestamp: Readable::read(r)?,
-                       flags: {
-                               let flags: u16 = Readable::read(r)?;
-                               // Note: we ignore the `message_flags` for now, since it was deprecated by the spec.
-                               flags as u8
-                       },
+                       message_flags: Readable::read(r)?,
+                       channel_flags: Readable::read(r)?,
                        cltv_expiry_delta: Readable::read(r)?,
                        htlc_minimum_msat: Readable::read(r)?,
                        fee_base_msat: Readable::read(r)?,
                        fee_proportional_millionths: Readable::read(r)?,
                        htlc_maximum_msat: Readable::read(r)?,
                        excess_data: read_to_end(r)?,
-               })
+               };
+               if res.message_flags & 1 != 1 {
+                       // The `must_be_one` flag should be set (historically it indicated the presence of the
+                       // `htlc_maximum_msat` field, which is now required).
+                       Err(DecodeError::InvalidValue)
+               } else {
+                       Ok(res)
+               }
        }
  }
  
@@@ -3526,7 -3503,8 +3533,8 @@@ mod tests 
                        chain_hash: ChainHash::using_genesis_block(Network::Bitcoin),
                        short_channel_id: 2316138423780173,
                        timestamp: 20190119,
-                       flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 },
+                       message_flags: 1, // Only must_be_one
+                       channel_flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 },
                        cltv_expiry_delta: 144,
                        htlc_minimum_msat: 1000000,
                        htlc_maximum_msat: 131355275467161,
        }
  
        #[test]
 -      fn encoding_splice() {
 +      fn encoding_splice_init() {
                let secp_ctx = Secp256k1::new();
                let (_, pubkey_1,) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
 -              let splice = msgs::Splice {
 -                      chain_hash: ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
 +              let splice_init = msgs::SpliceInit {
                        channel_id: ChannelId::from_bytes([2; 32]),
 -                      relative_satoshis: 123456,
 +                      funding_contribution_satoshis: -123456,
                        funding_feerate_perkw: 2000,
                        locktime: 0,
                        funding_pubkey: pubkey_1,
 +                      require_confirmed_inputs: Some(()),
                };
 -              let encoded_value = splice.encode();
 -              assert_eq!(encoded_value.as_hex().to_string(), "02020202020202020202020202020202020202020202020202020202020202026fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000001e240000007d000000000031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f");
 +              let encoded_value = splice_init.encode();
 +              assert_eq!(encoded_value.as_hex().to_string(), "0202020202020202020202020202020202020202020202020202020202020202fffffffffffe1dc0000007d000000000031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0200");
        }
  
        #[test]
        fn encoding_splice_ack() {
                let secp_ctx = Secp256k1::new();
                let (_, pubkey_1,) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
 -              let splice = msgs::SpliceAck {
 -                      chain_hash: ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
 +              let splice_ack = msgs::SpliceAck {
                        channel_id: ChannelId::from_bytes([2; 32]),
 -                      relative_satoshis: 123456,
 +                      funding_contribution_satoshis: -123456,
                        funding_pubkey: pubkey_1,
 +                      require_confirmed_inputs: Some(()),
                };
 -              let encoded_value = splice.encode();
 -              assert_eq!(encoded_value.as_hex().to_string(), "02020202020202020202020202020202020202020202020202020202020202026fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000001e240031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f");
 +              let encoded_value = splice_ack.encode();
 +              assert_eq!(encoded_value.as_hex().to_string(), "0202020202020202020202020202020202020202020202020202020202020202fffffffffffe1dc0031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0200");
        }
  
        #[test]
        fn encoding_splice_locked() {
 -              let splice = msgs::SpliceLocked {
 +              let splice_locked = msgs::SpliceLocked {
                        channel_id: ChannelId::from_bytes([2; 32]),
 +                      splice_txid: Txid::from_str("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
                };
 -              let encoded_value = splice.encode();
 -              assert_eq!(encoded_value.as_hex().to_string(), "0202020202020202020202020202020202020202020202020202020202020202");
 +              let encoded_value = splice_locked.encode();
 +              assert_eq!(encoded_value.as_hex().to_string(), "02020202020202020202020202020202020202020202020202020202020202026e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2");
        }
  
        #[test]
                        }).unwrap(),
                        prevtx_out: 305419896,
                        sequence: 305419896,
 +                      shared_input_txid: Some(Txid::from_str("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap()),
                };
                let encoded_value = tx_add_input.encode();
 -              let target_value = <Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202000000012345678900de02000000000101779ced6c148293f86b60cb222108553d22c89207326bb7b6b897e23e64ab5b300200000000fdffffff0236dbc1000000000016001417d29e4dd454bac3b1cde50d1926da80cfc5287b9cbd03000000000016001436ec78d514df462da95e6a00c24daa8915362d420247304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701210301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944000000001234567812345678").unwrap();
 -              assert_eq!(encoded_value, target_value);
 +              let target_value = "0202020202020202020202020202020202020202020202020202020202020202000000012345678900de02000000000101779ced6c148293f86b60cb222108553d22c89207326bb7b6b897e23e64ab5b300200000000fdffffff0236dbc1000000000016001417d29e4dd454bac3b1cde50d1926da80cfc5287b9cbd03000000000016001436ec78d514df462da95e6a00c24daa8915362d420247304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701210301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd694400000000123456781234567800206e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2";
 +              assert_eq!(encoded_value.as_hex().to_string(), target_value);
        }
  
        #[test]
                                        <Vec<u8>>::from_hex("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap(),
                                        <Vec<u8>>::from_hex("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap()]),
                        ],
 -                      funding_outpoint_sig: Some(sig_1),
 +                      shared_input_signature: Some(sig_1),
                };
                let encoded_value = tx_signatures.encode();
                let mut target_value = <Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
                        channel_id: ChannelId::from_bytes([2; 32]),
                        signature: sig_1,
                        htlc_signatures: if htlcs { vec![sig_2, sig_3, sig_4] } else { Vec::new() },
 +                      batch: Some(msgs::CommitmentSignedBatch { batch_size: 3, funding_txid: Txid::from_str("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap() }),
                        #[cfg(taproot)]
                        partial_signature_with_nonce: None,
                };
                let encoded_value = commitment_signed.encode();
 -              let mut target_value = <Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap();
 +              let mut target_value = "0202020202020202020202020202020202020202020202020202020202020202d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a".to_string();
                if htlcs {
 -                      target_value.append(&mut <Vec<u8>>::from_hex("00031735b6a427e80d5fe7cd90a2f4ee08dc9c27cda7c35a4172e5d85b12c49d4232537e98f9b1f3c5e6989a8b9644e90e8918127680dbd0d4043510840fc0f1e11a216c280b5395a2546e7e4b2663e04f811622f15a4f91e83aa2e92ba2a573c139142c54ae63072a1ec1ee7dc0c04bde5c847806172aa05c92c22ae8e308d1d2692b12cc195ce0a2d1bda6a88befa19fa07f51caa75ce83837f28965600b8aacab0855ffb0e741ec5f7c41421e9829a9d48611c8c831f71be5ea73e66594977ffd").unwrap());
 +                      target_value += "00031735b6a427e80d5fe7cd90a2f4ee08dc9c27cda7c35a4172e5d85b12c49d4232537e98f9b1f3c5e6989a8b9644e90e8918127680dbd0d4043510840fc0f1e11a216c280b5395a2546e7e4b2663e04f811622f15a4f91e83aa2e92ba2a573c139142c54ae63072a1ec1ee7dc0c04bde5c847806172aa05c92c22ae8e308d1d2692b12cc195ce0a2d1bda6a88befa19fa07f51caa75ce83837f28965600b8aacab0855ffb0e741ec5f7c41421e9829a9d48611c8c831f71be5ea73e66594977ffd";
                } else {
 -                      target_value.append(&mut <Vec<u8>>::from_hex("0000").unwrap());
 +                      target_value += "0000";
                }
 -              assert_eq!(encoded_value, target_value);
 +              target_value += "002200036e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2"; // batch
 +              assert_eq!(encoded_value.as_hex().to_string(), target_value);
        }
  
        #[test]
index 4b2c3c2e3742b747e56430451f37d2ea706f29dc,22df8ec31c6f1cee924641e6dff51c9fb6f149c7..1006113f7a31a7a293ef2563110548d6494d5bda
@@@ -809,8 -809,8 +809,8 @@@ impl msgs::ChannelMessageHandler for Te
                self.received_msg(wire::Message::Stfu(msg.clone()));
        }
        #[cfg(splicing)]
 -      fn handle_splice(&self, _their_node_id: &PublicKey, msg: &msgs::Splice) {
 -              self.received_msg(wire::Message::Splice(msg.clone()));
 +      fn handle_splice_init(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceInit) {
 +              self.received_msg(wire::Message::SpliceInit(msg.clone()));
        }
        #[cfg(splicing)]
        fn handle_splice_ack(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceAck) {
@@@ -967,7 -967,8 +967,8 @@@ fn get_dummy_channel_update(short_chan_
                        chain_hash: ChainHash::using_genesis_block(network),
                        short_channel_id: short_chan_id,
                        timestamp: 0,
-                       flags: 0,
+                       message_flags: 1, // Only must_be_one
+                       channel_flags: 0,
                        cltv_expiry_delta: 0,
                        htlc_minimum_msat: 0,
                        htlc_maximum_msat: msgs::MAX_VALUE_MSAT,