//! For a normal node you probably don't need to use anything here, however, if you wish to split a
//! node into an internet-facing route/message socket handling daemon and a separate daemon (or
//! server entirely) which handles only channel-related messages you may wish to implement
-//! ChannelMessageHandler yourself and use it to re-serialize messages and pass them across
+//! [`ChannelMessageHandler`] yourself and use it to re-serialize messages and pass them across
//! daemons/servers.
//!
//! Note that if you go with such an architecture (instead of passing raw socket events to a
//! non-internet-facing system) you trust the frontend internet-facing system to not lie about the
-//! source node_id of the message, however this does allow you to significantly reduce bandwidth
+//! source `node_id` of the message, however this does allow you to significantly reduce bandwidth
//! between the systems as routing messages can represent a significant chunk of bandwidth usage
//! (especially for non-channel-publicly-announcing nodes). As an alternate design which avoids
//! this issue, if you have sufficient bidirectional bandwidth between your systems, you may send
//! raw socket events into your non-internet-facing system and then send routing events back to
//! track the network on the less-secure system.
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::secp256k1::PublicKey;
use bitcoin::secp256k1::ecdsa::Signature;
-use bitcoin::secp256k1;
+use bitcoin::{secp256k1, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hash_types::{Txid, BlockHash};
-use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
-use ln::onion_utils;
-use onion_message;
+use crate::blinded_path::payment::ReceiveTlvs;
+use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
+use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
+use crate::ln::onion_utils;
+use crate::onion_message;
+use crate::sign::{NodeSigner, Recipient};
-use prelude::*;
+use crate::prelude::*;
+#[cfg(feature = "std")]
+use core::convert::TryFrom;
use core::fmt;
use core::fmt::Debug;
-use io::{self, Read};
-use io_extras::read_to_end;
+use core::ops::Deref;
+#[cfg(feature = "std")]
+use core::str::FromStr;
+use crate::io::{self, Cursor, Read};
+use crate::io_extras::read_to_end;
-use util::events::{MessageSendEventsProvider, OnionMessageProvider};
-use util::logger;
-use util::ser::{BigSize, LengthReadable, Readable, ReadableArgs, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname};
+use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
+use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
+use crate::util::logger;
+use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
+use crate::util::base32;
-use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
+use crate::routing::gossip::{NodeAlias, NodeId};
/// 21 million * 10^8 * 1000
pub(crate) const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
+#[cfg(taproot)]
+/// A partial signature that also contains the Musig2 nonce its signer used
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PartialSignatureWithNonce(pub musig2::types::PartialSignature, pub musig2::types::PublicNonce);
+
/// An error in decoding a message or struct.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DecodeError {
/// A version byte specified something we don't know how to handle.
- /// Includes unknown realm byte in an OnionHopData packet
+ ///
+ /// Includes unknown realm byte in an onion hop data packet.
UnknownVersion,
- /// Unknown feature mandating we fail to parse message (eg TLV with an even, unknown type)
+ /// Unknown feature mandating we fail to parse message (e.g., TLV with an even, unknown type)
UnknownRequiredFeature,
- /// Value was invalid, eg a byte which was supposed to be a bool was something other than a 0
+ /// Value was invalid.
+ ///
+ /// For example, a byte which was supposed to be a bool was something other than a 0
/// or 1, a public key/private key/signature was invalid, text wasn't UTF-8, TLV was
- /// syntactically incorrect, etc
+ /// syntactically incorrect, etc.
InvalidValue,
- /// Buffer too short
+ /// The buffer to be read was too short.
ShortRead,
- /// A length descriptor in the packet didn't describe the later data correctly
+ /// A length descriptor in the packet didn't describe the later data correctly.
BadLengthDescriptor,
- /// Error from std::io
- Io(/// (C-not exported) as ErrorKind doesn't have a reasonable mapping
- io::ErrorKind),
+ /// Error from [`std::io`].
+ Io(io::ErrorKind),
/// The message included zlib-compressed values, which we don't support.
UnsupportedCompression,
}
-/// An init message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`init`] message to be sent to or received from a peer.
+///
+/// [`init`]: https://github.com/lightning/bolts/blob/master/01-messaging.md#the-init-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Init {
- /// The relevant features which the sender supports
+ /// The relevant features which the sender supports.
pub features: InitFeatures,
- /// The receipient's network address. This adds the option to report a remote IP address
- /// back to a connecting peer using the init message. A node can decide to use that information
- /// to discover a potential update to its public IPv4 address (NAT) and use
- /// that for a node_announcement update message containing the new address.
- pub remote_network_address: Option<NetAddress>,
+ /// Indicates chains the sender is interested in.
+ ///
+ /// If there are no common chains, the connection will be closed.
+ pub networks: Option<Vec<ChainHash>>,
+ /// The receipient's network address.
+ ///
+ /// This adds the option to report a remote IP address back to a connecting peer using the init
+ /// message. A node can decide to use that information to discover a potential update to its
+ /// public IPv4 address (NAT) and use that for a [`NodeAnnouncement`] update message containing
+ /// the new address.
+ pub remote_network_address: Option<SocketAddress>,
}
-/// An error message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`error`] message to be sent to or received from a peer.
+///
+/// [`error`]: https://github.com/lightning/bolts/blob/master/01-messaging.md#the-error-and-warning-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ErrorMessage {
/// The channel ID involved in the error.
///
/// All-0s indicates a general error unrelated to a specific channel, after which all channels
/// with the sending peer should be closed.
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// A possibly human-readable error description.
- /// The string should be sanitized before it is used (e.g. emitted to logs or printed to
- /// stdout). Otherwise, a well crafted error message may trigger a security vulnerability in
+ ///
+ /// The string should be sanitized before it is used (e.g., emitted to logs or printed to
+ /// `stdout`). Otherwise, a well crafted error message may trigger a security vulnerability in
/// the terminal emulator or the logging subsystem.
pub data: String,
}
-/// A warning message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`warning`] message to be sent to or received from a peer.
+///
+/// [`warning`]: https://github.com/lightning/bolts/blob/master/01-messaging.md#the-error-and-warning-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WarningMessage {
/// The channel ID involved in the warning.
///
/// All-0s indicates a warning unrelated to a specific channel.
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// A possibly human-readable warning description.
+ ///
/// The string should be sanitized before it is used (e.g. emitted to logs or printed to
/// stdout). Otherwise, a well crafted error message may trigger a security vulnerability in
/// the terminal emulator or the logging subsystem.
pub data: String,
}
-/// A ping message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`ping`] message to be sent to or received from a peer.
+///
+/// [`ping`]: https://github.com/lightning/bolts/blob/master/01-messaging.md#the-ping-and-pong-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ping {
- /// The desired response length
+ /// The desired response length.
pub ponglen: u16,
/// The ping packet size.
+ ///
/// This field is not sent on the wire. byteslen zeros are sent.
pub byteslen: u16,
}
-/// A pong message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`pong`] message to be sent to or received from a peer.
+///
+/// [`pong`]: https://github.com/lightning/bolts/blob/master/01-messaging.md#the-ping-and-pong-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Pong {
/// The pong packet size.
+ ///
/// This field is not sent on the wire. byteslen zeros are sent.
pub byteslen: u16,
}
-/// An open_channel message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`open_channel`] message to be sent to or received from a peer.
+///
+/// Used in V1 channel establishment
+///
+/// [`open_channel`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenChannel {
/// The genesis hash of the blockchain where the channel is to be opened
pub chain_hash: BlockHash,
/// A temporary channel ID, until the funding outpoint is announced
- pub temporary_channel_id: [u8; 32],
+ pub temporary_channel_id: ChannelId,
/// The channel value
pub funding_satoshis: u64,
/// The amount to push to the counterparty as part of the open, in milli-satoshi
pub channel_reserve_satoshis: u64,
/// The minimum HTLC size incoming to sender, in milli-satoshi
pub htlc_minimum_msat: u64,
- /// The feerate per 1000-weight of sender generated transactions, until updated by update_fee
+ /// The feerate per 1000-weight of sender generated transactions, until updated by
+ /// [`UpdateFee`]
pub feerate_per_kw: u32,
- /// The number of blocks which the counterparty will have to wait to claim on-chain funds if they broadcast a commitment transaction
+ /// The number of blocks which the counterparty will have to wait to claim on-chain funds if
+ /// they broadcast a commitment transaction
pub to_self_delay: u16,
/// The maximum number of inbound HTLCs towards sender
pub max_accepted_htlcs: u16,
pub htlc_basepoint: PublicKey,
/// The first to-be-broadcast-by-sender transaction's per commitment point
pub first_per_commitment_point: PublicKey,
+ /// The channel flags to be used
+ pub channel_flags: u8,
+ /// A request to pre-set the to-sender output's `scriptPubkey` for when we collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
+ /// The channel type that this channel will represent
+ ///
+ /// If this is `None`, we derive the channel type from the intersection of our
+ /// feature bits with our counterparty's feature bits from the [`Init`] message.
+ pub channel_type: Option<ChannelTypeFeatures>,
+}
+
+/// An open_channel2 message to be sent by or received from the channel initiator.
+///
+/// Used in V2 channel establishment
+///
+// TODO(dual_funding): Add spec link for `open_channel2`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct OpenChannelV2 {
+ /// The genesis hash of the blockchain where the channel is to be opened
+ pub chain_hash: BlockHash,
+ /// A temporary channel ID derived using a zeroed out value for the channel acceptor's revocation basepoint
+ pub temporary_channel_id: ChannelId,
+ /// The feerate for the funding transaction set by the channel initiator
+ pub funding_feerate_sat_per_1000_weight: u32,
+ /// The feerate for the commitment transaction set by the channel initiator
+ pub commitment_feerate_sat_per_1000_weight: u32,
+ /// Part of the channel value contributed by the channel initiator
+ pub funding_satoshis: u64,
+ /// The threshold below which outputs on transactions broadcast by the channel initiator will be
+ /// omitted
+ pub dust_limit_satoshis: u64,
+ /// The maximum inbound HTLC value in flight towards channel initiator, in milli-satoshi
+ pub max_htlc_value_in_flight_msat: u64,
+ /// The minimum HTLC size incoming to channel initiator, in milli-satoshi
+ pub htlc_minimum_msat: u64,
+ /// The number of blocks which the counterparty will have to wait to claim on-chain funds if they
+ /// broadcast a commitment transaction
+ pub to_self_delay: u16,
+ /// The maximum number of inbound HTLCs towards channel initiator
+ pub max_accepted_htlcs: u16,
+ /// The locktime for the funding transaction
+ pub locktime: u32,
+ /// The channel initiator's key controlling the funding transaction
+ pub funding_pubkey: PublicKey,
+ /// Used to derive a revocation key for transactions broadcast by counterparty
+ pub revocation_basepoint: PublicKey,
+ /// A payment key to channel initiator for transactions broadcast by counterparty
+ pub payment_basepoint: PublicKey,
+ /// Used to derive a payment key to channel initiator for transactions broadcast by channel
+ /// initiator
+ pub delayed_payment_basepoint: PublicKey,
+ /// Used to derive an HTLC payment key to channel initiator
+ pub htlc_basepoint: PublicKey,
+ /// The first to-be-broadcast-by-channel-initiator transaction's per commitment point
+ pub first_per_commitment_point: PublicKey,
+ /// The second to-be-broadcast-by-channel-initiator transaction's per commitment point
+ pub second_per_commitment_point: PublicKey,
/// Channel flags
pub channel_flags: u8,
- /// Optionally, a request to pre-set the to-sender output's scriptPubkey for when we collaboratively close
- pub shutdown_scriptpubkey: OptionalField<Script>,
+ /// Optionally, a request to pre-set the to-channel-initiator output's scriptPubkey for when we
+ /// collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
/// The channel type that this channel will represent. If none is set, we derive the channel
/// type from the intersection of our feature bits with our counterparty's feature bits from
/// the Init message.
pub channel_type: Option<ChannelTypeFeatures>,
+ /// Optionally, a requirement that only confirmed inputs can be added
+ pub require_confirmed_inputs: Option<()>,
}
-/// An accept_channel message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`accept_channel`] message to be sent to or received from a peer.
+///
+/// Used in V1 channel establishment
+///
+/// [`accept_channel`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-accept_channel-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AcceptChannel {
/// A temporary channel ID, until the funding outpoint is announced
- pub temporary_channel_id: [u8; 32],
+ pub temporary_channel_id: ChannelId,
/// The threshold below which outputs on transactions broadcast by sender will be omitted
pub dust_limit_satoshis: u64,
/// The maximum inbound HTLC value in flight towards sender, in milli-satoshi
pub htlc_basepoint: PublicKey,
/// The first to-be-broadcast-by-sender transaction's per commitment point
pub first_per_commitment_point: PublicKey,
- /// Optionally, a request to pre-set the to-sender output's scriptPubkey for when we collaboratively close
- pub shutdown_scriptpubkey: OptionalField<Script>,
+ /// A request to pre-set the to-sender output's scriptPubkey for when we collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
+ /// The channel type that this channel will represent.
+ ///
+ /// If this is `None`, we derive the channel type from the intersection of
+ /// our feature bits with our counterparty's feature bits from the [`Init`] message.
+ /// This is required to match the equivalent field in [`OpenChannel::channel_type`].
+ pub channel_type: Option<ChannelTypeFeatures>,
+ #[cfg(taproot)]
+ /// Next nonce the channel initiator should use to create a funding output signature against
+ pub next_local_nonce: Option<musig2::types::PublicNonce>,
+}
+
+/// An accept_channel2 message to be sent by or received from the channel accepter.
+///
+/// Used in V2 channel establishment
+///
+// TODO(dual_funding): Add spec link for `accept_channel2`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct AcceptChannelV2 {
+ /// The same `temporary_channel_id` received from the initiator's `open_channel2` message.
+ pub temporary_channel_id: ChannelId,
+ /// Part of the channel value contributed by the channel acceptor
+ pub funding_satoshis: u64,
+ /// The threshold below which outputs on transactions broadcast by the channel acceptor will be
+ /// omitted
+ pub dust_limit_satoshis: u64,
+ /// The maximum inbound HTLC value in flight towards channel acceptor, in milli-satoshi
+ pub max_htlc_value_in_flight_msat: u64,
+ /// The minimum HTLC size incoming to channel acceptor, in milli-satoshi
+ pub htlc_minimum_msat: u64,
+ /// Minimum depth of the funding transaction before the channel is considered open
+ pub minimum_depth: u32,
+ /// The number of blocks which the counterparty will have to wait to claim on-chain funds if they
+ /// broadcast a commitment transaction
+ pub to_self_delay: u16,
+ /// The maximum number of inbound HTLCs towards channel acceptor
+ pub max_accepted_htlcs: u16,
+ /// The channel acceptor's key controlling the funding transaction
+ pub funding_pubkey: PublicKey,
+ /// Used to derive a revocation key for transactions broadcast by counterparty
+ pub revocation_basepoint: PublicKey,
+ /// A payment key to channel acceptor for transactions broadcast by counterparty
+ pub payment_basepoint: PublicKey,
+ /// Used to derive a payment key to channel acceptor for transactions broadcast by channel
+ /// acceptor
+ pub delayed_payment_basepoint: PublicKey,
+ /// Used to derive an HTLC payment key to channel acceptor for transactions broadcast by counterparty
+ pub htlc_basepoint: PublicKey,
+ /// The first to-be-broadcast-by-channel-acceptor transaction's per commitment point
+ pub first_per_commitment_point: PublicKey,
+ /// The second to-be-broadcast-by-channel-acceptor transaction's per commitment point
+ pub second_per_commitment_point: PublicKey,
+ /// Optionally, a request to pre-set the to-channel-acceptor output's scriptPubkey for when we
+ /// collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
/// The channel type that this channel will represent. If none is set, we derive the channel
/// type from the intersection of our feature bits with our counterparty's feature bits from
/// the Init message.
///
- /// This is required to match the equivalent field in [`OpenChannel::channel_type`].
+ /// This is required to match the equivalent field in [`OpenChannelV2::channel_type`].
pub channel_type: Option<ChannelTypeFeatures>,
+ /// Optionally, a requirement that only confirmed inputs can be added
+ pub require_confirmed_inputs: Option<()>,
}
-/// A funding_created message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`funding_created`] message to be sent to or received from a peer.
+///
+/// Used in V1 channel establishment
+///
+/// [`funding_created`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-funding_created-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingCreated {
/// A temporary channel ID, until the funding is established
- pub temporary_channel_id: [u8; 32],
+ pub temporary_channel_id: ChannelId,
/// The funding transaction ID
pub funding_txid: Txid,
/// The specific output index funding this channel
pub funding_output_index: u16,
/// The signature of the channel initiator (funder) on the initial commitment transaction
pub signature: Signature,
+ #[cfg(taproot)]
+ /// The partial signature of the channel initiator (funder)
+ pub partial_signature_with_nonce: Option<PartialSignatureWithNonce>,
+ #[cfg(taproot)]
+ /// Next nonce the channel acceptor should use to finalize the funding output signature
+ pub next_local_nonce: Option<musig2::types::PublicNonce>
}
-/// A funding_signed message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`funding_signed`] message to be sent to or received from a peer.
+///
+/// Used in V1 channel establishment
+///
+/// [`funding_signed`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-funding_signed-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingSigned {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The signature of the channel acceptor (fundee) on the initial commitment transaction
pub signature: Signature,
+ #[cfg(taproot)]
+ /// The partial signature of the channel acceptor (fundee)
+ pub partial_signature_with_nonce: Option<PartialSignatureWithNonce>,
}
-/// A channel_ready message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`channel_ready`] message to be sent to or received from a peer.
+///
+/// [`channel_ready`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-channel_ready-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelReady {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The per-commitment point of the second commitment transaction
pub next_per_commitment_point: PublicKey,
- /// If set, provides a short_channel_id alias for this channel. The sender will accept payments
- /// to be forwarded over this SCID and forward them to this messages' recipient.
+ /// If set, provides a `short_channel_id` alias for this channel.
+ ///
+ /// The sender will accept payments to be forwarded over this SCID and forward them to this
+ /// messages' recipient.
pub short_channel_id_alias: Option<u64>,
}
-/// A shutdown message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A tx_add_input message for adding an input during interactive transaction construction
+///
+// TODO(dual_funding): Add spec link for `tx_add_input`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAddInput {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// A randomly chosen unique identifier for this input, which is even for initiators and odd for
+ /// non-initiators.
+ pub serial_id: u64,
+ /// Serialized transaction that contains the output this input spends to verify that it is non
+ /// malleable.
+ pub prevtx: TransactionU16LenLimited,
+ /// The index of the output being spent
+ pub prevtx_out: u32,
+ /// The sequence number of this input
+ pub sequence: u32,
+}
+
+/// A tx_add_output message for adding an output during interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_add_output`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAddOutput {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// A randomly chosen unique identifier for this output, which is even for initiators and odd for
+ /// non-initiators.
+ pub serial_id: u64,
+ /// The satoshi value of the output
+ pub sats: u64,
+ /// The scriptPubKey for the output
+ pub script: Script,
+}
+
+/// A tx_remove_input message for removing an input during interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_remove_input`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxRemoveInput {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// The serial ID of the input to be removed
+ pub serial_id: u64,
+}
+
+/// A tx_remove_output message for removing an output during interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_remove_output`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxRemoveOutput {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// The serial ID of the output to be removed
+ pub serial_id: u64,
+}
+
+/// A tx_complete message signalling the conclusion of a peer's transaction contributions during
+/// interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_complete`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxComplete {
+ /// The channel ID
+ pub channel_id: ChannelId,
+}
+
+/// A tx_signatures message containing the sender's signatures for a transaction constructed with
+/// interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_signatures`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxSignatures {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// The TXID
+ pub tx_hash: Txid,
+ /// The list of witnesses
+ pub witnesses: Vec<Witness>,
+}
+
+/// A tx_init_rbf message which initiates a replacement of the transaction after it's been
+/// completed.
+///
+// TODO(dual_funding): Add spec link for `tx_init_rbf`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxInitRbf {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// The locktime of the transaction
+ pub locktime: u32,
+ /// The feerate of the transaction
+ pub feerate_sat_per_1000_weight: u32,
+ /// The number of satoshis the sender will contribute to or, if negative, remove from
+ /// (e.g. splice-out) the funding output of the transaction
+ pub funding_output_contribution: Option<i64>,
+}
+
+/// A tx_ack_rbf message which acknowledges replacement of the transaction after it's been
+/// completed.
+///
+// TODO(dual_funding): Add spec link for `tx_ack_rbf`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAckRbf {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// The number of satoshis the sender will contribute to or, if negative, remove from
+ /// (e.g. splice-out) the funding output of the transaction
+ pub funding_output_contribution: Option<i64>,
+}
+
+/// A tx_abort message which signals the cancellation of an in-progress transaction negotiation.
+///
+// TODO(dual_funding): Add spec link for `tx_abort`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAbort {
+ /// The channel ID
+ pub channel_id: ChannelId,
+ /// Message data
+ pub data: Vec<u8>,
+}
+
+/// A [`shutdown`] message to be sent to or received from a peer.
+///
+/// [`shutdown`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#closing-initiation-shutdown
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Shutdown {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The destination of this peer's funds on closing.
- /// Must be in one of these forms: p2pkh, p2sh, p2wpkh, p2wsh.
+ ///
+ /// Must be in one of these forms: P2PKH, P2SH, P2WPKH, P2WSH, P2TR.
pub scriptpubkey: Script,
}
/// The minimum and maximum fees which the sender is willing to place on the closing transaction.
+///
/// This is provided in [`ClosingSigned`] by both sides to indicate the fee range they are willing
/// to use.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClosingSignedFeeRange {
/// The minimum absolute fee, in satoshis, which the sender is willing to place on the closing
/// transaction.
pub max_fee_satoshis: u64,
}
-/// A closing_signed message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`closing_signed`] message to be sent to or received from a peer.
+///
+/// [`closing_signed`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#closing-negotiation-closing_signed
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClosingSigned {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The proposed total fee for the closing transaction
pub fee_satoshis: u64,
/// A signature on the closing transaction
pub fee_range: Option<ClosingSignedFeeRange>,
}
-/// An update_add_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`update_add_htlc`] message to be sent to or received from a peer.
+///
+/// [`update_add_htlc`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#adding-an-htlc-update_add_htlc
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateAddHTLC {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The HTLC ID
pub htlc_id: u64,
/// The HTLC value in milli-satoshi
pub payment_hash: PaymentHash,
/// The expiry height of the HTLC
pub cltv_expiry: u32,
+ /// The extra fee skimmed by the sender of this message. See
+ /// [`ChannelConfig::accept_underpaying_htlcs`].
+ ///
+ /// [`ChannelConfig::accept_underpaying_htlcs`]: crate::util::config::ChannelConfig::accept_underpaying_htlcs
+ pub skimmed_fee_msat: Option<u64>,
pub(crate) onion_routing_packet: OnionPacket,
}
- /// An onion message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+ /// An onion message to be sent to or received from a peer.
+ ///
+ // TODO: update with link to OM when they are merged into the BOLTs
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OnionMessage {
/// Used in decrypting the onion packet's payload.
pub blinding_point: PublicKey,
- pub(crate) onion_routing_packet: onion_message::Packet,
+ /// The full onion packet including hop data, pubkey, and hmac
+ pub onion_routing_packet: onion_message::Packet,
}
-/// An update_fulfill_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`update_fulfill_htlc`] message to be sent to or received from a peer.
+///
+/// [`update_fulfill_htlc`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFulfillHTLC {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The HTLC ID
pub htlc_id: u64,
/// The pre-image of the payment hash, allowing HTLC redemption
pub payment_preimage: PaymentPreimage,
}
-/// An update_fail_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`update_fail_htlc`] message to be sent to or received from a peer.
+///
+/// [`update_fail_htlc`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFailHTLC {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The HTLC ID
pub htlc_id: u64,
pub(crate) reason: OnionErrorPacket,
}
-/// An update_fail_malformed_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`update_fail_malformed_htlc`] message to be sent to or received from a peer.
+///
+/// [`update_fail_malformed_htlc`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFailMalformedHTLC {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The HTLC ID
pub htlc_id: u64,
pub(crate) sha256_of_onion: [u8; 32],
pub failure_code: u16,
}
-/// A commitment_signed message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// 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
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommitmentSigned {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// A signature on the commitment transaction
pub signature: Signature,
/// Signatures on the HTLC transactions
pub htlc_signatures: Vec<Signature>,
+ #[cfg(taproot)]
+ /// The partial Taproot signature on the commitment transaction
+ pub partial_signature_with_nonce: Option<PartialSignatureWithNonce>,
}
-/// A revoke_and_ack message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`revoke_and_ack`] message to be sent to or received from a peer.
+///
+/// [`revoke_and_ack`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#completing-the-transition-to-the-updated-state-revoke_and_ack
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RevokeAndACK {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The secret corresponding to the per-commitment point
pub per_commitment_secret: [u8; 32],
/// The next sender-broadcast commitment transaction's per-commitment point
pub next_per_commitment_point: PublicKey,
+ #[cfg(taproot)]
+ /// Musig nonce the recipient should use in their next commitment signature message
+ pub next_local_nonce: Option<musig2::types::PublicNonce>
}
-/// An update_fee message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`update_fee`] message to be sent to or received from a peer
+///
+/// [`update_fee`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#updating-fees-update_fee
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFee {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// Fee rate per 1000-weight of the transaction
pub feerate_per_kw: u32,
}
-#[derive(Clone, Debug, PartialEq)]
-/// Proof that the sender knows the per-commitment secret of the previous commitment transaction.
-/// This is used to convince the recipient that the channel is at a certain commitment
-/// number even if they lost that data due to a local failure. Of course, the peer may lie
-/// and even later commitments may have been revoked.
-pub struct DataLossProtect {
- /// Proof that the sender knows the per-commitment secret of a specific commitment transaction
- /// belonging to the recipient
- pub your_last_per_commitment_secret: [u8; 32],
- /// The sender's per-commitment point for their current commitment transaction
- pub my_current_per_commitment_point: PublicKey,
-}
-
-/// A channel_reestablish message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`channel_reestablish`] message to be sent to or received from a peer.
+///
+/// [`channel_reestablish`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#message-retransmission
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelReestablish {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The next commitment number for the sender
pub next_local_commitment_number: u64,
/// The next commitment number for the recipient
pub next_remote_commitment_number: u64,
- /// Optionally, a field proving that next_remote_commitment_number-1 has been revoked
- pub data_loss_protect: OptionalField<DataLossProtect>,
+ /// Proof that the sender knows the per-commitment secret of a specific commitment transaction
+ /// belonging to the recipient
+ pub your_last_per_commitment_secret: [u8; 32],
+ /// The sender's per-commitment point for their current commitment transaction
+ pub my_current_per_commitment_point: PublicKey,
+ /// The next funding transaction ID
+ pub next_funding_txid: Option<Txid>,
}
-/// An announcement_signatures message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// An [`announcement_signatures`] message to be sent to or received from a peer.
+///
+/// [`announcement_signatures`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-announcement_signatures-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AnnouncementSignatures {
/// The channel ID
- pub channel_id: [u8; 32],
+ pub channel_id: ChannelId,
/// The short channel ID
pub short_channel_id: u64,
/// A signature by the node key
pub bitcoin_signature: Signature,
}
-/// An address which can be used to connect to a remote peer
-#[derive(Clone, Debug, PartialEq)]
-pub enum NetAddress {
- /// An IPv4 address/port on which the peer is listening.
- IPv4 {
+/// An address which can be used to connect to a remote peer.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum SocketAddress {
+ /// An IPv4 address and port on which the peer is listening.
+ TcpIpV4 {
/// The 4-byte IPv4 address
addr: [u8; 4],
/// The port on which the node is listening
port: u16,
},
- /// An IPv6 address/port on which the peer is listening.
- IPv6 {
+ /// An IPv6 address and port on which the peer is listening.
+ TcpIpV6 {
/// The 16-byte IPv6 address
addr: [u8; 16],
/// The port on which the node is listening
/// addresses. Thus, the details are not parsed here.
OnionV2([u8; 12]),
/// A new-style Tor onion address/port on which the peer is listening.
- /// To create the human-readable "hostname", concatenate ed25519_pubkey, checksum, and version,
+ ///
+ /// To create the human-readable "hostname", concatenate the ED25519 pubkey, checksum, and version,
/// wrap as base32 and append ".onion".
OnionV3 {
/// The ed25519 long-term public key of the peer
port: u16,
},
}
-impl NetAddress {
- /// Gets the ID of this address type. Addresses in node_announcement messages should be sorted
+impl SocketAddress {
+ /// Gets the ID of this address type. Addresses in [`NodeAnnouncement`] messages should be sorted
/// by this.
pub(crate) fn get_id(&self) -> u8 {
match self {
- &NetAddress::IPv4 {..} => { 1 },
- &NetAddress::IPv6 {..} => { 2 },
- &NetAddress::OnionV2(_) => { 3 },
- &NetAddress::OnionV3 {..} => { 4 },
- &NetAddress::Hostname {..} => { 5 },
+ &SocketAddress::TcpIpV4 {..} => { 1 },
+ &SocketAddress::TcpIpV6 {..} => { 2 },
+ &SocketAddress::OnionV2(_) => { 3 },
+ &SocketAddress::OnionV3 {..} => { 4 },
+ &SocketAddress::Hostname {..} => { 5 },
}
}
/// Strict byte-length of address descriptor, 1-byte type not recorded
fn len(&self) -> u16 {
match self {
- &NetAddress::IPv4 { .. } => { 6 },
- &NetAddress::IPv6 { .. } => { 18 },
- &NetAddress::OnionV2(_) => { 12 },
- &NetAddress::OnionV3 { .. } => { 37 },
+ &SocketAddress::TcpIpV4 { .. } => { 6 },
+ &SocketAddress::TcpIpV6 { .. } => { 18 },
+ &SocketAddress::OnionV2(_) => { 12 },
+ &SocketAddress::OnionV3 { .. } => { 37 },
// Consists of 1-byte hostname length, hostname bytes, and 2-byte port.
- &NetAddress::Hostname { ref hostname, .. } => { u16::from(hostname.len()) + 3 },
+ &SocketAddress::Hostname { ref hostname, .. } => { u16::from(hostname.len()) + 3 },
}
}
pub(crate) const MAX_LEN: u16 = 258;
}
-impl Writeable for NetAddress {
+impl Writeable for SocketAddress {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
match self {
- &NetAddress::IPv4 { ref addr, ref port } => {
+ &SocketAddress::TcpIpV4 { ref addr, ref port } => {
1u8.write(writer)?;
addr.write(writer)?;
port.write(writer)?;
},
- &NetAddress::IPv6 { ref addr, ref port } => {
+ &SocketAddress::TcpIpV6 { ref addr, ref port } => {
2u8.write(writer)?;
addr.write(writer)?;
port.write(writer)?;
},
- &NetAddress::OnionV2(bytes) => {
+ &SocketAddress::OnionV2(bytes) => {
3u8.write(writer)?;
bytes.write(writer)?;
},
- &NetAddress::OnionV3 { ref ed25519_pubkey, ref checksum, ref version, ref port } => {
+ &SocketAddress::OnionV3 { ref ed25519_pubkey, ref checksum, ref version, ref port } => {
4u8.write(writer)?;
ed25519_pubkey.write(writer)?;
checksum.write(writer)?;
version.write(writer)?;
port.write(writer)?;
},
- &NetAddress::Hostname { ref hostname, ref port } => {
+ &SocketAddress::Hostname { ref hostname, ref port } => {
5u8.write(writer)?;
hostname.write(writer)?;
port.write(writer)?;
}
}
-impl Readable for Result<NetAddress, u8> {
- fn read<R: Read>(reader: &mut R) -> Result<Result<NetAddress, u8>, DecodeError> {
+impl Readable for Result<SocketAddress, u8> {
+ fn read<R: Read>(reader: &mut R) -> Result<Result<SocketAddress, u8>, DecodeError> {
let byte = <u8 as Readable>::read(reader)?;
match byte {
1 => {
- Ok(Ok(NetAddress::IPv4 {
+ Ok(Ok(SocketAddress::TcpIpV4 {
addr: Readable::read(reader)?,
port: Readable::read(reader)?,
}))
},
2 => {
- Ok(Ok(NetAddress::IPv6 {
+ Ok(Ok(SocketAddress::TcpIpV6 {
addr: Readable::read(reader)?,
port: Readable::read(reader)?,
}))
},
- 3 => Ok(Ok(NetAddress::OnionV2(Readable::read(reader)?))),
+ 3 => Ok(Ok(SocketAddress::OnionV2(Readable::read(reader)?))),
4 => {
- Ok(Ok(NetAddress::OnionV3 {
+ Ok(Ok(SocketAddress::OnionV3 {
ed25519_pubkey: Readable::read(reader)?,
checksum: Readable::read(reader)?,
version: Readable::read(reader)?,
}))
},
5 => {
- Ok(Ok(NetAddress::Hostname {
+ Ok(Ok(SocketAddress::Hostname {
hostname: Readable::read(reader)?,
port: Readable::read(reader)?,
}))
}
}
-impl Readable for NetAddress {
- fn read<R: Read>(reader: &mut R) -> Result<NetAddress, DecodeError> {
+impl Readable for SocketAddress {
+ fn read<R: Read>(reader: &mut R) -> Result<SocketAddress, DecodeError> {
match Readable::read(reader) {
Ok(Ok(res)) => Ok(res),
Ok(Err(_)) => Err(DecodeError::UnknownVersion),
}
}
+/// [`SocketAddress`] error variants
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum SocketAddressParseError {
+ /// Socket address (IPv4/IPv6) parsing error
+ SocketAddrParse,
+ /// Invalid input format
+ InvalidInput,
+ /// Invalid port
+ InvalidPort,
+ /// Invalid onion v3 address
+ InvalidOnionV3,
+}
-/// The unsigned part of a node_announcement
-#[derive(Clone, Debug, PartialEq)]
+impl fmt::Display for SocketAddressParseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ SocketAddressParseError::SocketAddrParse => write!(f, "Socket address (IPv4/IPv6) parsing error"),
+ SocketAddressParseError::InvalidInput => write!(f, "Invalid input format. \
+ Expected: \"<ipv4>:<port>\", \"[<ipv6>]:<port>\", \"<onion address>.onion:<port>\" or \"<hostname>:<port>\""),
+ SocketAddressParseError::InvalidPort => write!(f, "Invalid port"),
+ SocketAddressParseError::InvalidOnionV3 => write!(f, "Invalid onion v3 address"),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<std::net::SocketAddrV4> for SocketAddress {
+ fn from(addr: std::net::SocketAddrV4) -> Self {
+ SocketAddress::TcpIpV4 { addr: addr.ip().octets(), port: addr.port() }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<std::net::SocketAddrV6> for SocketAddress {
+ fn from(addr: std::net::SocketAddrV6) -> Self {
+ SocketAddress::TcpIpV6 { addr: addr.ip().octets(), port: addr.port() }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<std::net::SocketAddr> for SocketAddress {
+ fn from(addr: std::net::SocketAddr) -> Self {
+ match addr {
+ std::net::SocketAddr::V4(addr) => addr.into(),
+ std::net::SocketAddr::V6(addr) => addr.into(),
+ }
+ }
+}
+
+/// Parses an OnionV3 host and port into a [`SocketAddress::OnionV3`].
+///
+/// The host part must end with ".onion".
+pub fn parse_onion_address(host: &str, port: u16) -> Result<SocketAddress, SocketAddressParseError> {
+ if host.ends_with(".onion") {
+ let domain = &host[..host.len() - ".onion".len()];
+ if domain.len() != 56 {
+ return Err(SocketAddressParseError::InvalidOnionV3);
+ }
+ let onion = base32::Alphabet::RFC4648 { padding: false }.decode(&domain).map_err(|_| SocketAddressParseError::InvalidOnionV3)?;
+ if onion.len() != 35 {
+ return Err(SocketAddressParseError::InvalidOnionV3);
+ }
+ let version = onion[0];
+ let first_checksum_flag = onion[1];
+ let second_checksum_flag = onion[2];
+ let mut ed25519_pubkey = [0; 32];
+ ed25519_pubkey.copy_from_slice(&onion[3..35]);
+ let checksum = u16::from_be_bytes([first_checksum_flag, second_checksum_flag]);
+ return Ok(SocketAddress::OnionV3 { ed25519_pubkey, checksum, version, port });
+
+ } else {
+ return Err(SocketAddressParseError::InvalidInput);
+ }
+}
+
+#[cfg(feature = "std")]
+impl FromStr for SocketAddress {
+ type Err = SocketAddressParseError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match std::net::SocketAddr::from_str(s) {
+ Ok(addr) => Ok(addr.into()),
+ Err(_) => {
+ let trimmed_input = match s.rfind(":") {
+ Some(pos) => pos,
+ None => return Err(SocketAddressParseError::InvalidInput),
+ };
+ let host = &s[..trimmed_input];
+ let port: u16 = s[trimmed_input + 1..].parse().map_err(|_| SocketAddressParseError::InvalidPort)?;
+ if host.ends_with(".onion") {
+ return parse_onion_address(host, port);
+ };
+ if let Ok(hostname) = Hostname::try_from(s[..trimmed_input].to_string()) {
+ return Ok(SocketAddress::Hostname { hostname, port });
+ };
+ return Err(SocketAddressParseError::SocketAddrParse)
+ },
+ }
+ }
+}
+
+/// Represents the set of gossip messages that require a signature from a node's identity key.
+pub enum UnsignedGossipMessage<'a> {
+ /// An unsigned channel announcement.
+ ChannelAnnouncement(&'a UnsignedChannelAnnouncement),
+ /// An unsigned channel update.
+ ChannelUpdate(&'a UnsignedChannelUpdate),
+ /// An unsigned node announcement.
+ NodeAnnouncement(&'a UnsignedNodeAnnouncement)
+}
+
+impl<'a> Writeable for UnsignedGossipMessage<'a> {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ match self {
+ UnsignedGossipMessage::ChannelAnnouncement(ref msg) => msg.write(writer),
+ UnsignedGossipMessage::ChannelUpdate(ref msg) => msg.write(writer),
+ UnsignedGossipMessage::NodeAnnouncement(ref msg) => msg.write(writer),
+ }
+ }
+}
+
+/// The unsigned part of a [`node_announcement`] message.
+///
+/// [`node_announcement`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-node_announcement-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnsignedNodeAnnouncement {
/// The advertised features
pub features: NodeFeatures,
/// A strictly monotonic announcement counter, with gaps allowed
pub timestamp: u32,
- /// The node_id this announcement originated from (don't rebroadcast the node_announcement back
+ /// The `node_id` this announcement originated from (don't rebroadcast the `node_announcement` back
/// to this node).
- pub node_id: PublicKey,
+ pub node_id: NodeId,
/// An RGB color for UI purposes
pub rgb: [u8; 3],
- /// An alias, for UI purposes. This should be sanitized before use. There is no guarantee
- /// of uniqueness.
- pub alias: [u8; 32],
+ /// An alias, for UI purposes.
+ ///
+ /// This should be sanitized before use. There is no guarantee of uniqueness.
+ pub alias: NodeAlias,
/// List of addresses on which this node is reachable
- pub addresses: Vec<NetAddress>,
+ pub addresses: Vec<SocketAddress>,
pub(crate) excess_address_data: Vec<u8>,
pub(crate) excess_data: Vec<u8>,
}
-#[derive(Clone, Debug, PartialEq)]
-/// A node_announcement message to be sent or received from a peer
+#[derive(Clone, Debug, PartialEq, Eq)]
+/// A [`node_announcement`] message to be sent to or received from a peer.
+///
+/// [`node_announcement`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-node_announcement-message
pub struct NodeAnnouncement {
/// The signature by the node key
pub signature: Signature,
pub contents: UnsignedNodeAnnouncement,
}
-/// The unsigned part of a channel_announcement
-#[derive(Clone, Debug, PartialEq)]
+/// The unsigned part of a [`channel_announcement`] message.
+///
+/// [`channel_announcement`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_announcement-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnsignedChannelAnnouncement {
/// The advertised channel features
pub features: ChannelFeatures,
pub chain_hash: BlockHash,
/// The short channel ID
pub short_channel_id: u64,
- /// One of the two node_ids which are endpoints of this channel
- pub node_id_1: PublicKey,
- /// The other of the two node_ids which are endpoints of this channel
- pub node_id_2: PublicKey,
+ /// One of the two `node_id`s which are endpoints of this channel
+ pub node_id_1: NodeId,
+ /// The other of the two `node_id`s which are endpoints of this channel
+ pub node_id_2: NodeId,
/// The funding key for the first node
- pub bitcoin_key_1: PublicKey,
+ pub bitcoin_key_1: NodeId,
/// The funding key for the second node
- pub bitcoin_key_2: PublicKey,
- pub(crate) excess_data: Vec<u8>,
+ pub bitcoin_key_2: NodeId,
+ /// Excess data which was signed as a part of the message which we do not (yet) understand how
+ /// to decode.
+ ///
+ /// This is stored to ensure forward-compatibility as new fields are added to the lightning gossip protocol.
+ pub excess_data: Vec<u8>,
}
-/// A channel_announcement message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`channel_announcement`] message to be sent to or received from a peer.
+///
+/// [`channel_announcement`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_announcement-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelAnnouncement {
/// Authentication of the announcement by the first public node
pub node_signature_1: Signature,
pub contents: UnsignedChannelAnnouncement,
}
-/// The unsigned part of a channel_update
-#[derive(Clone, Debug, PartialEq)]
+/// The unsigned part of a [`channel_update`] message.
+///
+/// [`channel_update`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_update-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnsignedChannelUpdate {
/// The genesis hash of the blockchain where the channel is to be opened
pub chain_hash: BlockHash,
pub 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
- /// the outgoing HTLC's minimum cltv_expiry value -- so, if an incoming HTLC comes in with a
- /// cltv_expiry of 100000, and the node we're forwarding to has a cltv_expiry_delta value of 10,
- /// then we'll check that the outgoing HTLC's cltv_expiry value is at least 100010 before
+ /// then we need to fail the HTLC backwards. When forwarding an HTLC, `cltv_expiry_delta` determines
+ /// the outgoing HTLC's minimum `cltv_expiry` value -- so, if an incoming HTLC comes in with a
+ /// `cltv_expiry` of 100000, and the node we're forwarding to has a `cltv_expiry_delta` value of 10,
+ /// then we'll check that the outgoing HTLC's `cltv_expiry` value is at least 100010 before
/// forwarding. Note that the HTLC sender is the one who originally sets this value when
/// constructing the route.
pub cltv_expiry_delta: u16,
/// The minimum HTLC size incoming to sender, in milli-satoshi
pub htlc_minimum_msat: u64,
- /// The maximum HTLC value incoming to sender, in milli-satoshi. Used to be optional.
+ /// The maximum HTLC value incoming to sender, in milli-satoshi.
+ ///
+ /// This used to be optional.
pub htlc_maximum_msat: u64,
/// The base HTLC fee charged by sender, in milli-satoshi
pub fee_base_msat: u32,
/// The amount to fee multiplier, in micro-satoshi
pub fee_proportional_millionths: u32,
/// Excess data which was signed as a part of the message which we do not (yet) understand how
- /// to decode. This is stored to ensure forward-compatibility as new fields are added to the
- /// lightning gossip
+ /// to decode.
+ ///
+ /// This is stored to ensure forward-compatibility as new fields are added to the lightning gossip protocol.
pub excess_data: Vec<u8>,
}
-/// A channel_update message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+/// A [`channel_update`] message to be sent to or received from a peer.
+///
+/// [`channel_update`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_update-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelUpdate {
/// A signature of the channel update
pub signature: Signature,
pub contents: UnsignedChannelUpdate,
}
-/// A query_channel_range message is used to query a peer for channel
+/// A [`query_channel_range`] message is used to query a peer for channel
/// UTXOs in a range of blocks. The recipient of a query makes a best
-/// effort to reply to the query using one or more reply_channel_range
+/// effort to reply to the query using one or more [`ReplyChannelRange`]
/// messages.
-#[derive(Clone, Debug, PartialEq)]
+///
+/// [`query_channel_range`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-query_channel_range-and-reply_channel_range-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryChannelRange {
/// The genesis hash of the blockchain being queried
pub chain_hash: BlockHash,
pub number_of_blocks: u32,
}
-/// A reply_channel_range message is a reply to a query_channel_range
-/// message. Multiple reply_channel_range messages can be sent in reply
-/// to a single query_channel_range message. The query recipient makes a
+/// A [`reply_channel_range`] message is a reply to a [`QueryChannelRange`]
+/// message.
+///
+/// Multiple `reply_channel_range` messages can be sent in reply
+/// to a single [`QueryChannelRange`] message. The query recipient makes a
/// best effort to respond based on their local network view which may
-/// not be a perfect view of the network. The short_channel_ids in the
-/// reply are encoded. We only support encoding_type=0 uncompressed
-/// serialization and do not support encoding_type=1 zlib serialization.
-#[derive(Clone, Debug, PartialEq)]
+/// not be a perfect view of the network. The `short_channel_id`s in the
+/// reply are encoded. We only support `encoding_type=0` uncompressed
+/// serialization and do not support `encoding_type=1` zlib serialization.
+///
+/// [`reply_channel_range`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-query_channel_range-and-reply_channel_range-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReplyChannelRange {
/// The genesis hash of the blockchain being queried
pub chain_hash: BlockHash,
pub number_of_blocks: u32,
/// True when this is the final reply for a query
pub sync_complete: bool,
- /// The short_channel_ids in the channel range
+ /// The `short_channel_id`s in the channel range
pub short_channel_ids: Vec<u64>,
}
-/// A query_short_channel_ids message is used to query a peer for
-/// routing gossip messages related to one or more short_channel_ids.
+/// A [`query_short_channel_ids`] message is used to query a peer for
+/// routing gossip messages related to one or more `short_channel_id`s.
+///
/// The query recipient will reply with the latest, if available,
-/// channel_announcement, channel_update and node_announcement messages
-/// it maintains for the requested short_channel_ids followed by a
-/// reply_short_channel_ids_end message. The short_channel_ids sent in
-/// this query are encoded. We only support encoding_type=0 uncompressed
-/// serialization and do not support encoding_type=1 zlib serialization.
-#[derive(Clone, Debug, PartialEq)]
+/// [`ChannelAnnouncement`], [`ChannelUpdate`] and [`NodeAnnouncement`] messages
+/// it maintains for the requested `short_channel_id`s followed by a
+/// [`ReplyShortChannelIdsEnd`] message. The `short_channel_id`s sent in
+/// this query are encoded. We only support `encoding_type=0` uncompressed
+/// serialization and do not support `encoding_type=1` zlib serialization.
+///
+/// [`query_short_channel_ids`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-query_short_channel_idsreply_short_channel_ids_end-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryShortChannelIds {
/// The genesis hash of the blockchain being queried
pub chain_hash: BlockHash,
pub short_channel_ids: Vec<u64>,
}
-/// A reply_short_channel_ids_end message is sent as a reply to a
-/// query_short_channel_ids message. The query recipient makes a best
+/// A [`reply_short_channel_ids_end`] message is sent as a reply to a
+/// message. The query recipient makes a best
/// effort to respond based on their local network view which may not be
/// a perfect view of the network.
-#[derive(Clone, Debug, PartialEq)]
+///
+/// [`reply_short_channel_ids_end`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-query_short_channel_idsreply_short_channel_ids_end-messages
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReplyShortChannelIdsEnd {
/// The genesis hash of the blockchain that was queried
pub chain_hash: BlockHash,
/// Indicates if the query recipient maintains up-to-date channel
- /// information for the chain_hash
+ /// information for the `chain_hash`
pub full_information: bool,
}
-/// A gossip_timestamp_filter message is used by a node to request
+/// A [`gossip_timestamp_filter`] message is used by a node to request
/// gossip relay for messages in the requested time range when the
-/// gossip_queries feature has been negotiated.
-#[derive(Clone, Debug, PartialEq)]
+/// `gossip_queries` feature has been negotiated.
+///
+/// [`gossip_timestamp_filter`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-gossip_timestamp_filter-message
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GossipTimestampFilter {
/// The genesis hash of the blockchain for channel and node information
pub chain_hash: BlockHash,
}
/// Encoding type for data compression of collections in gossip queries.
-/// We do not support encoding_type=1 zlib serialization defined in BOLT #7.
+///
+/// We do not support `encoding_type=1` zlib serialization [defined in BOLT
+/// #7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#query-messages).
enum EncodingType {
Uncompressed = 0x00,
}
-/// Used to put an error message in a LightningError
-#[derive(Clone, Debug)]
+/// Used to put an error message in a [`LightningError`].
+#[derive(Clone, Debug, PartialEq)]
pub enum ErrorAction {
/// The peer took some action which made us think they were useless. Disconnect them.
DisconnectPeer {
/// An error message which we should make an effort to send before we disconnect.
msg: Option<ErrorMessage>
},
+ /// The peer did something incorrect. Tell them without closing any channels and disconnect them.
+ DisconnectPeerWithWarning {
+ /// A warning message which we should make an effort to send before we disconnect.
+ msg: WarningMessage,
+ },
/// The peer did something harmless that we weren't able to process, just log and ignore
// New code should *not* use this. New code must use IgnoreAndLog, below!
IgnoreError,
pub action: ErrorAction,
}
-/// Struct used to return values from revoke_and_ack messages, containing a bunch of commitment
+/// Struct used to return values from [`RevokeAndACK`] messages, containing a bunch of commitment
/// transaction updates if they were pending.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommitmentUpdate {
- /// update_add_htlc messages which should be sent
+ /// `update_add_htlc` messages which should be sent
pub update_add_htlcs: Vec<UpdateAddHTLC>,
- /// update_fulfill_htlc messages which should be sent
+ /// `update_fulfill_htlc` messages which should be sent
pub update_fulfill_htlcs: Vec<UpdateFulfillHTLC>,
- /// update_fail_htlc messages which should be sent
+ /// `update_fail_htlc` messages which should be sent
pub update_fail_htlcs: Vec<UpdateFailHTLC>,
- /// update_fail_malformed_htlc messages which should be sent
+ /// `update_fail_malformed_htlc` messages which should be sent
pub update_fail_malformed_htlcs: Vec<UpdateFailMalformedHTLC>,
- /// An update_fee message which should be sent
+ /// An `update_fee` message which should be sent
pub update_fee: Option<UpdateFee>,
- /// Finally, the commitment_signed message which should be sent
+ /// A `commitment_signed` message which should be sent
pub commitment_signed: CommitmentSigned,
}
-/// Messages could have optional fields to use with extended features
-/// As we wish to serialize these differently from Option<T>s (Options get a tag byte, but
-/// OptionalFeild simply gets Present if there are enough bytes to read into it), we have a
-/// separate enum type for them.
-/// (C-not exported) due to a free generic in T
-#[derive(Clone, Debug, PartialEq)]
-pub enum OptionalField<T> {
- /// Optional field is included in message
- Present(T),
- /// Optional field is absent in message
- Absent
-}
-
/// A trait to describe an object which can receive channel messages.
///
-/// Messages MAY be called in parallel when they originate from different their_node_ids, however
-/// they MUST NOT be called in parallel when the two calls have the same their_node_id.
+/// Messages MAY be called in parallel when they originate from different `their_node_ids`, however
+/// they MUST NOT be called in parallel when the two calls have the same `their_node_id`.
pub trait ChannelMessageHandler : MessageSendEventsProvider {
- //Channel init:
- /// Handle an incoming open_channel message from the given peer.
- fn handle_open_channel(&self, their_node_id: &PublicKey, their_features: InitFeatures, msg: &OpenChannel);
- /// Handle an incoming accept_channel message from the given peer.
- fn handle_accept_channel(&self, their_node_id: &PublicKey, their_features: InitFeatures, msg: &AcceptChannel);
- /// Handle an incoming funding_created message from the given peer.
+ // Channel init:
+ /// Handle an incoming `open_channel` message from the given peer.
+ fn handle_open_channel(&self, their_node_id: &PublicKey, msg: &OpenChannel);
+ /// Handle an incoming `open_channel2` message from the given peer.
+ fn handle_open_channel_v2(&self, their_node_id: &PublicKey, msg: &OpenChannelV2);
+ /// Handle an incoming `accept_channel` message from the given peer.
+ fn handle_accept_channel(&self, their_node_id: &PublicKey, msg: &AcceptChannel);
+ /// Handle an incoming `accept_channel2` message from the given peer.
+ fn handle_accept_channel_v2(&self, their_node_id: &PublicKey, msg: &AcceptChannelV2);
+ /// Handle an incoming `funding_created` message from the given peer.
fn handle_funding_created(&self, their_node_id: &PublicKey, msg: &FundingCreated);
- /// Handle an incoming funding_signed message from the given peer.
+ /// Handle an incoming `funding_signed` message from the given peer.
fn handle_funding_signed(&self, their_node_id: &PublicKey, msg: &FundingSigned);
- /// Handle an incoming channel_ready message from the given peer.
+ /// Handle an incoming `channel_ready` message from the given peer.
fn handle_channel_ready(&self, their_node_id: &PublicKey, msg: &ChannelReady);
- // Channl close:
- /// Handle an incoming shutdown message from the given peer.
- fn handle_shutdown(&self, their_node_id: &PublicKey, their_features: &InitFeatures, msg: &Shutdown);
- /// Handle an incoming closing_signed message from the given peer.
+ // Channel close:
+ /// Handle an incoming `shutdown` message from the given peer.
+ fn handle_shutdown(&self, their_node_id: &PublicKey, msg: &Shutdown);
+ /// Handle an incoming `closing_signed` message from the given peer.
fn handle_closing_signed(&self, their_node_id: &PublicKey, msg: &ClosingSigned);
+ // Interactive channel construction
+ /// Handle an incoming `tx_add_input message` from the given peer.
+ fn handle_tx_add_input(&self, their_node_id: &PublicKey, msg: &TxAddInput);
+ /// Handle an incoming `tx_add_output` message from the given peer.
+ fn handle_tx_add_output(&self, their_node_id: &PublicKey, msg: &TxAddOutput);
+ /// Handle an incoming `tx_remove_input` message from the given peer.
+ fn handle_tx_remove_input(&self, their_node_id: &PublicKey, msg: &TxRemoveInput);
+ /// Handle an incoming `tx_remove_output` message from the given peer.
+ fn handle_tx_remove_output(&self, their_node_id: &PublicKey, msg: &TxRemoveOutput);
+ /// Handle an incoming `tx_complete message` from the given peer.
+ fn handle_tx_complete(&self, their_node_id: &PublicKey, msg: &TxComplete);
+ /// Handle an incoming `tx_signatures` message from the given peer.
+ fn handle_tx_signatures(&self, their_node_id: &PublicKey, msg: &TxSignatures);
+ /// Handle an incoming `tx_init_rbf` message from the given peer.
+ fn handle_tx_init_rbf(&self, their_node_id: &PublicKey, msg: &TxInitRbf);
+ /// Handle an incoming `tx_ack_rbf` message from the given peer.
+ fn handle_tx_ack_rbf(&self, their_node_id: &PublicKey, msg: &TxAckRbf);
+ /// Handle an incoming `tx_abort message` from the given peer.
+ fn handle_tx_abort(&self, their_node_id: &PublicKey, msg: &TxAbort);
+
// HTLC handling:
- /// Handle an incoming update_add_htlc message from the given peer.
+ /// Handle an incoming `update_add_htlc` message from the given peer.
fn handle_update_add_htlc(&self, their_node_id: &PublicKey, msg: &UpdateAddHTLC);
- /// Handle an incoming update_fulfill_htlc message from the given peer.
+ /// Handle an incoming `update_fulfill_htlc` message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC);
- /// Handle an incoming update_fail_htlc message from the given peer.
+ /// Handle an incoming `update_fail_htlc` message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC);
- /// Handle an incoming update_fail_malformed_htlc message from the given peer.
+ /// Handle an incoming `update_fail_malformed_htlc` message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC);
- /// Handle an incoming commitment_signed message from the given peer.
+ /// Handle an incoming `commitment_signed` message from the given peer.
fn handle_commitment_signed(&self, their_node_id: &PublicKey, msg: &CommitmentSigned);
- /// Handle an incoming revoke_and_ack message from the given peer.
+ /// Handle an incoming `revoke_and_ack` message from the given peer.
fn handle_revoke_and_ack(&self, their_node_id: &PublicKey, msg: &RevokeAndACK);
- /// Handle an incoming update_fee message from the given peer.
+ /// Handle an incoming `update_fee` message from the given peer.
fn handle_update_fee(&self, their_node_id: &PublicKey, msg: &UpdateFee);
// Channel-to-announce:
- /// Handle an incoming announcement_signatures message from the given peer.
+ /// Handle an incoming `announcement_signatures` message from the given peer.
fn handle_announcement_signatures(&self, their_node_id: &PublicKey, msg: &AnnouncementSignatures);
// Connection loss/reestablish:
- /// Indicates a connection to the peer failed/an existing connection was lost. If no connection
- /// is believed to be possible in the future (eg they're sending us messages we don't
- /// understand or indicate they require unknown feature bits), no_connection_possible is set
- /// and any outstanding channels should be failed.
- ///
- /// Note that in some rare cases this may be called without a corresponding
- /// [`Self::peer_connected`].
- fn peer_disconnected(&self, their_node_id: &PublicKey, no_connection_possible: bool);
+ /// Indicates a connection to the peer failed/an existing connection was lost.
+ fn peer_disconnected(&self, their_node_id: &PublicKey);
- /// Handle a peer reconnecting, possibly generating channel_reestablish message(s).
+ /// Handle a peer reconnecting, possibly generating `channel_reestablish` message(s).
///
/// May return an `Err(())` if the features the peer supports are not sufficient to communicate
/// with us. Implementors should be somewhat conservative about doing so, however, as other
/// message handlers may still wish to communicate with this peer.
- fn peer_connected(&self, their_node_id: &PublicKey, msg: &Init) -> Result<(), ()>;
- /// Handle an incoming channel_reestablish message from the given peer.
+ fn peer_connected(&self, their_node_id: &PublicKey, msg: &Init, inbound: bool) -> Result<(), ()>;
+ /// Handle an incoming `channel_reestablish` message from the given peer.
fn handle_channel_reestablish(&self, their_node_id: &PublicKey, msg: &ChannelReestablish);
- /// Handle an incoming channel update from the given peer.
+ /// Handle an incoming `channel_update` message from the given peer.
fn handle_channel_update(&self, their_node_id: &PublicKey, msg: &ChannelUpdate);
// Error:
- /// Handle an incoming error message from the given peer.
+ /// Handle an incoming `error` message from the given peer.
fn handle_error(&self, their_node_id: &PublicKey, msg: &ErrorMessage);
// Handler information:
///
/// Note that this method is called before [`Self::peer_connected`].
fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures;
+
+ /// Gets the genesis hashes for this `ChannelMessageHandler` indicating which chains it supports.
+ ///
+ /// If it's `None`, then no particular network chain hash compatibility will be enforced when
+ /// connecting to peers.
+ fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>>;
}
/// A trait to describe an object which can receive routing messages.
///
/// # Implementor DoS Warnings
///
-/// For `gossip_queries` messages there are potential DoS vectors when handling
-/// inbound queries. Implementors using an on-disk network graph should be aware of
+/// For messages enabled with the `gossip_queries` feature there are potential DoS vectors when
+/// handling inbound queries. Implementors using an on-disk network graph should be aware of
/// repeated disk I/O for queries accessing different parts of the network graph.
pub trait RoutingMessageHandler : MessageSendEventsProvider {
- /// Handle an incoming node_announcement message, returning true if it should be forwarded on,
- /// false or returning an Err otherwise.
+ /// Handle an incoming `node_announcement` message, returning `true` if it should be forwarded on,
+ /// `false` or returning an `Err` otherwise.
fn handle_node_announcement(&self, msg: &NodeAnnouncement) -> Result<bool, LightningError>;
- /// Handle a channel_announcement message, returning true if it should be forwarded on, false
- /// or returning an Err otherwise.
+ /// Handle a `channel_announcement` message, returning `true` if it should be forwarded on, `false`
+ /// or returning an `Err` otherwise.
fn handle_channel_announcement(&self, msg: &ChannelAnnouncement) -> Result<bool, LightningError>;
- /// Handle an incoming channel_update message, returning true if it should be forwarded on,
- /// false or returning an Err otherwise.
+ /// Handle an incoming `channel_update` message, returning true if it should be forwarded on,
+ /// `false` or returning an `Err` otherwise.
fn handle_channel_update(&self, msg: &ChannelUpdate) -> Result<bool, LightningError>;
/// Gets channel announcements and updates required to dump our routing table to a remote node,
- /// starting at the short_channel_id indicated by starting_point and including announcements
+ /// starting at the `short_channel_id` indicated by `starting_point` and including announcements
/// for a single channel.
fn get_next_channel_announcement(&self, starting_point: u64) -> Option<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)>;
/// Gets a node announcement required to dump our routing table to a remote node, starting at
/// the node *after* the provided pubkey and including up to one announcement immediately
- /// higher (as defined by <PublicKey as Ord>::cmp) than starting_point.
- /// If None is provided for starting_point, we start at the first node.
- fn get_next_node_announcement(&self, starting_point: Option<&PublicKey>) -> Option<NodeAnnouncement>;
+ /// higher (as defined by `<PublicKey as Ord>::cmp`) than `starting_point`.
+ /// If `None` is provided for `starting_point`, we start at the first node.
+ fn get_next_node_announcement(&self, starting_point: Option<&NodeId>) -> Option<NodeAnnouncement>;
/// Called when a connection is established with a peer. This can be used to
/// perform routing table synchronization using a strategy defined by the
/// implementor.
/// May return an `Err(())` if the features the peer supports are not sufficient to communicate
/// with us. Implementors should be somewhat conservative about doing so, however, as other
/// message handlers may still wish to communicate with this peer.
- fn peer_connected(&self, their_node_id: &PublicKey, init: &Init) -> Result<(), ()>;
+ fn peer_connected(&self, their_node_id: &PublicKey, init: &Init, inbound: bool) -> Result<(), ()>;
/// Handles the reply of a query we initiated to learn about channels
/// for a given range of blocks. We can expect to receive one or more
/// replies to a single query.
/// a node has completed its best effort to send us the pertaining routing
/// gossip messages.
fn handle_reply_short_channel_ids_end(&self, their_node_id: &PublicKey, msg: ReplyShortChannelIdsEnd) -> Result<(), LightningError>;
- /// Handles when a peer asks us to send a list of short_channel_ids
+ /// Handles when a peer asks us to send a list of `short_channel_id`s
/// for the requested range of blocks.
fn handle_query_channel_range(&self, their_node_id: &PublicKey, msg: QueryChannelRange) -> Result<(), LightningError>;
/// Handles when a peer asks us to send routing gossip messages for a
- /// list of short_channel_ids.
+ /// list of `short_channel_id`s.
fn handle_query_short_channel_ids(&self, their_node_id: &PublicKey, msg: QueryShortChannelIds) -> Result<(), LightningError>;
+ // Handler queueing status:
+ /// Indicates that there are a large number of [`ChannelAnnouncement`] (or other) messages
+ /// pending some async action. While there is no guarantee of the rate of future messages, the
+ /// caller should seek to reduce the rate of new gossip messages handled, especially
+ /// [`ChannelAnnouncement`]s.
+ fn processing_queue_high(&self) -> bool;
+
// Handler information:
/// Gets the node feature flags which this handler itself supports. All available handlers are
/// queried similarly and their feature flags are OR'd together to form the [`NodeFeatures`]
/// A trait to describe an object that can receive onion messages.
pub trait OnionMessageHandler : OnionMessageProvider {
- /// Handle an incoming onion_message message from the given peer.
+ /// Handle an incoming `onion_message` message from the given peer.
fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage);
/// Called when a connection is established with a peer. Can be used to track which peers
/// advertise onion message support and are online.
/// May return an `Err(())` if the features the peer supports are not sufficient to communicate
/// with us. Implementors should be somewhat conservative about doing so, however, as other
/// message handlers may still wish to communicate with this peer.
- fn peer_connected(&self, their_node_id: &PublicKey, init: &Init) -> Result<(), ()>;
+ fn peer_connected(&self, their_node_id: &PublicKey, init: &Init, inbound: bool) -> Result<(), ()>;
/// Indicates a connection to the peer failed/an existing connection was lost. Allows handlers to
/// drop and refuse to forward onion messages to this peer.
- ///
- /// Note that in some rare cases this may be called without a corresponding
- /// [`Self::peer_connected`].
- fn peer_disconnected(&self, their_node_id: &PublicKey, no_connection_possible: bool);
+ fn peer_disconnected(&self, their_node_id: &PublicKey);
// Handler information:
/// Gets the node feature flags which this handler itself supports. All available handlers are
}
mod fuzzy_internal_msgs {
- use prelude::*;
- use ln::{PaymentPreimage, PaymentSecret};
+ use bitcoin::secp256k1::PublicKey;
+ use crate::blinded_path::payment::PaymentConstraints;
+ use crate::prelude::*;
+ use crate::ln::{PaymentPreimage, PaymentSecret};
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
// them from untrusted input):
#[derive(Clone)]
- pub(crate) struct FinalOnionHopData {
- pub(crate) payment_secret: PaymentSecret,
+ pub struct FinalOnionHopData {
+ pub payment_secret: PaymentSecret,
/// The total value, in msat, of the payment as received by the ultimate recipient.
/// Message serialization may panic if this value is more than 21 million Bitcoin.
- pub(crate) total_msat: u64,
+ pub total_msat: u64,
}
- pub(crate) enum OnionHopDataFormat {
- Legacy { // aka Realm-0
+ pub enum InboundOnionPayload {
+ Forward {
short_channel_id: u64,
+ /// The value, in msat, of the payment after this hop's fee is deducted.
+ amt_to_forward: u64,
+ outgoing_cltv_value: u32,
},
- NonFinalNode {
- short_channel_id: u64,
- },
- FinalNode {
+ Receive {
payment_data: Option<FinalOnionHopData>,
+ payment_metadata: Option<Vec<u8>>,
keysend_preimage: Option<PaymentPreimage>,
+ custom_tlvs: Vec<(u64, Vec<u8>)>,
+ amt_msat: u64,
+ outgoing_cltv_value: u32,
},
+ BlindedReceive {
+ amt_msat: u64,
+ total_msat: u64,
+ outgoing_cltv_value: u32,
+ payment_secret: PaymentSecret,
+ payment_constraints: PaymentConstraints,
+ intro_node_blinding_point: PublicKey,
+ }
}
- pub struct OnionHopData {
- pub(crate) format: OnionHopDataFormat,
- /// The value, in msat, of the payment after this hop's fee is deducted.
- /// Message serialization may panic if this value is more than 21 million Bitcoin.
- pub(crate) amt_to_forward: u64,
- pub(crate) outgoing_cltv_value: u32,
- // 12 bytes of 0-padding for Legacy format
+ pub(crate) enum OutboundOnionPayload {
+ Forward {
+ short_channel_id: u64,
+ /// The value, in msat, of the payment after this hop's fee is deducted.
+ amt_to_forward: u64,
+ outgoing_cltv_value: u32,
+ },
+ Receive {
+ payment_data: Option<FinalOnionHopData>,
+ payment_metadata: Option<Vec<u8>>,
+ keysend_preimage: Option<PaymentPreimage>,
+ custom_tlvs: Vec<(u64, Vec<u8>)>,
+ amt_msat: u64,
+ outgoing_cltv_value: u32,
+ },
+ BlindedForward {
+ encrypted_tlvs: Vec<u8>,
+ intro_node_blinding_point: Option<PublicKey>,
+ },
+ BlindedReceive {
+ amt_msat: u64,
+ total_msat: u64,
+ outgoing_cltv_value: u32,
+ encrypted_tlvs: Vec<u8>,
+ intro_node_blinding_point: Option<PublicKey>, // Set if the introduction node of the blinded path is the final node
+ }
}
pub struct DecodedOnionErrorPacket {
#[derive(Clone)]
pub(crate) struct OnionPacket {
pub(crate) version: u8,
- /// In order to ensure we always return an error on Onion decode in compliance with BOLT 4, we
- /// have to deserialize OnionPackets contained in UpdateAddHTLCs even if the ephemeral public
- /// key (here) is bogus, so we hold a Result instead of a PublicKey as we'd like.
+ /// In order to ensure we always return an error on onion decode in compliance with [BOLT
+ /// #4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md), we have to
+ /// deserialize `OnionPacket`s contained in [`UpdateAddHTLC`] messages even if the ephemeral
+ /// public key (here) is bogus, so we hold a [`Result`] instead of a [`PublicKey`] as we'd
+ /// like.
pub(crate) public_key: Result<PublicKey, secp256k1::Error>,
pub(crate) hop_data: [u8; 20*65],
pub(crate) hmac: [u8; 32],
}
}
+impl Eq for OnionPacket { }
impl PartialEq for OnionPacket {
fn eq(&self, other: &OnionPacket) -> bool {
for (i, j) in self.hop_data.iter().zip(other.hop_data.iter()) {
}
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct OnionErrorPacket {
// This really should be a constant size slice, but the spec lets these things be up to 128KB?
// (TODO) We limit it in decode to much lower...
}
}
-impl Writeable for OptionalField<Script> {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- match *self {
- OptionalField::Present(ref script) => {
- // Note that Writeable for script includes the 16-bit length tag for us
- script.write(w)?;
- },
- OptionalField::Absent => {}
- }
- Ok(())
- }
-}
-
-impl Readable for OptionalField<Script> {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- match <u16 as Readable>::read(r) {
- Ok(len) => {
- let mut buf = vec![0; len as usize];
- r.read_exact(&mut buf)?;
- Ok(OptionalField::Present(Script::from(buf)))
- },
- Err(DecodeError::ShortRead) => Ok(OptionalField::Absent),
- Err(e) => Err(e)
- }
- }
-}
-
-impl Writeable for OptionalField<u64> {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- match *self {
- OptionalField::Present(ref value) => {
- value.write(w)?;
- },
- OptionalField::Absent => {}
- }
- Ok(())
- }
-}
-
-impl Readable for OptionalField<u64> {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let value: u64 = Readable::read(r)?;
- Ok(OptionalField::Present(value))
- }
-}
-
+#[cfg(not(taproot))]
+impl_writeable_msg!(AcceptChannel, {
+ temporary_channel_id,
+ dust_limit_satoshis,
+ max_htlc_value_in_flight_msat,
+ channel_reserve_satoshis,
+ htlc_minimum_msat,
+ minimum_depth,
+ to_self_delay,
+ max_accepted_htlcs,
+ funding_pubkey,
+ revocation_basepoint,
+ payment_point,
+ delayed_payment_basepoint,
+ htlc_basepoint,
+ first_per_commitment_point,
+}, {
+ (0, shutdown_scriptpubkey, (option, encoding: (Script, WithoutLength))), // Don't encode length twice.
+ (1, channel_type, option),
+});
+#[cfg(taproot)]
impl_writeable_msg!(AcceptChannel, {
temporary_channel_id,
dust_limit_satoshis,
delayed_payment_basepoint,
htlc_basepoint,
first_per_commitment_point,
- shutdown_scriptpubkey
}, {
+ (0, shutdown_scriptpubkey, (option, encoding: (Script, WithoutLength))), // Don't encode length twice.
(1, channel_type, option),
+ (4, next_local_nonce, option),
+});
+
+impl_writeable_msg!(AcceptChannelV2, {
+ temporary_channel_id,
+ funding_satoshis,
+ dust_limit_satoshis,
+ max_htlc_value_in_flight_msat,
+ htlc_minimum_msat,
+ minimum_depth,
+ to_self_delay,
+ max_accepted_htlcs,
+ funding_pubkey,
+ revocation_basepoint,
+ payment_basepoint,
+ delayed_payment_basepoint,
+ htlc_basepoint,
+ first_per_commitment_point,
+ second_per_commitment_point,
+}, {
+ (0, shutdown_scriptpubkey, option),
+ (1, channel_type, option),
+ (2, require_confirmed_inputs, option),
+});
+
+impl_writeable_msg!(TxAddInput, {
+ channel_id,
+ serial_id,
+ prevtx,
+ prevtx_out,
+ sequence,
+}, {});
+
+impl_writeable_msg!(TxAddOutput, {
+ channel_id,
+ serial_id,
+ sats,
+ script,
+}, {});
+
+impl_writeable_msg!(TxRemoveInput, {
+ channel_id,
+ serial_id,
+}, {});
+
+impl_writeable_msg!(TxRemoveOutput, {
+ channel_id,
+ serial_id,
+}, {});
+
+impl_writeable_msg!(TxComplete, {
+ channel_id,
+}, {});
+
+impl_writeable_msg!(TxSignatures, {
+ channel_id,
+ tx_hash,
+ witnesses,
+}, {});
+
+impl_writeable_msg!(TxInitRbf, {
+ channel_id,
+ locktime,
+ feerate_sat_per_1000_weight,
+}, {
+ (0, funding_output_contribution, option),
+});
+
+impl_writeable_msg!(TxAckRbf, {
+ channel_id,
+}, {
+ (0, funding_output_contribution, option),
});
+impl_writeable_msg!(TxAbort, {
+ channel_id,
+ data,
+}, {});
+
impl_writeable_msg!(AnnouncementSignatures, {
channel_id,
short_channel_id,
bitcoin_signature
}, {});
-impl Writeable for ChannelReestablish {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- self.channel_id.write(w)?;
- self.next_local_commitment_number.write(w)?;
- self.next_remote_commitment_number.write(w)?;
- match self.data_loss_protect {
- OptionalField::Present(ref data_loss_protect) => {
- (*data_loss_protect).your_last_per_commitment_secret.write(w)?;
- (*data_loss_protect).my_current_per_commitment_point.write(w)?;
- },
- OptionalField::Absent => {}
- }
- Ok(())
- }
-}
-
-impl Readable for ChannelReestablish{
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- Ok(Self {
- channel_id: Readable::read(r)?,
- next_local_commitment_number: Readable::read(r)?,
- next_remote_commitment_number: Readable::read(r)?,
- data_loss_protect: {
- match <[u8; 32] as Readable>::read(r) {
- Ok(your_last_per_commitment_secret) =>
- OptionalField::Present(DataLossProtect {
- your_last_per_commitment_secret,
- my_current_per_commitment_point: Readable::read(r)?,
- }),
- Err(DecodeError::ShortRead) => OptionalField::Absent,
- Err(e) => return Err(e)
- }
- }
- })
- }
-}
+impl_writeable_msg!(ChannelReestablish, {
+ channel_id,
+ next_local_commitment_number,
+ next_remote_commitment_number,
+ your_last_per_commitment_secret,
+ my_current_per_commitment_point,
+}, {
+ (0, next_funding_txid, option),
+});
impl_writeable_msg!(ClosingSigned,
{ channel_id, fee_satoshis, signature },
max_fee_satoshis
});
+#[cfg(not(taproot))]
impl_writeable_msg!(CommitmentSigned, {
channel_id,
signature,
htlc_signatures
}, {});
+#[cfg(taproot)]
+impl_writeable_msg!(CommitmentSigned, {
+ channel_id,
+ signature,
+ htlc_signatures
+}, {
+ (2, partial_signature_with_nonce, option)
+});
+
impl_writeable!(DecodedOnionErrorPacket, {
hmac,
failuremsg,
pad
});
+#[cfg(not(taproot))]
impl_writeable_msg!(FundingCreated, {
temporary_channel_id,
funding_txid,
funding_output_index,
signature
}, {});
+#[cfg(taproot)]
+impl_writeable_msg!(FundingCreated, {
+ temporary_channel_id,
+ funding_txid,
+ funding_output_index,
+ signature
+}, {
+ (2, partial_signature_with_nonce, option),
+ (4, next_local_nonce, option)
+});
+
+#[cfg(not(taproot))]
+impl_writeable_msg!(FundingSigned, {
+ channel_id,
+ signature
+}, {});
+#[cfg(taproot)]
impl_writeable_msg!(FundingSigned, {
channel_id,
signature
-}, {});
+}, {
+ (2, partial_signature_with_nonce, option)
+});
impl_writeable_msg!(ChannelReady, {
channel_id,
self.features.write_up_to_13(w)?;
self.features.write(w)?;
encode_tlv_stream!(w, {
- (3, self.remote_network_address, option)
+ (1, self.networks.as_ref().map(|n| WithoutLength(n)), option),
+ (3, self.remote_network_address, option),
});
Ok(())
}
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let global_features: InitFeatures = Readable::read(r)?;
let features: InitFeatures = Readable::read(r)?;
- let mut remote_network_address: Option<NetAddress> = None;
+ let mut remote_network_address: Option<SocketAddress> = None;
+ let mut networks: Option<WithoutLength<Vec<ChainHash>>> = None;
decode_tlv_stream!(r, {
+ (1, networks, option),
(3, remote_network_address, option)
});
Ok(Init {
- features: features.or(global_features),
+ features: features | global_features,
+ networks: networks.map(|n| n.0),
remote_network_address,
})
}
htlc_basepoint,
first_per_commitment_point,
channel_flags,
- shutdown_scriptpubkey
}, {
+ (0, shutdown_scriptpubkey, (option, encoding: (Script, WithoutLength))), // Don't encode length twice.
+ (1, channel_type, option),
+});
+
+impl_writeable_msg!(OpenChannelV2, {
+ chain_hash,
+ temporary_channel_id,
+ funding_feerate_sat_per_1000_weight,
+ commitment_feerate_sat_per_1000_weight,
+ funding_satoshis,
+ dust_limit_satoshis,
+ max_htlc_value_in_flight_msat,
+ htlc_minimum_msat,
+ to_self_delay,
+ max_accepted_htlcs,
+ locktime,
+ funding_pubkey,
+ revocation_basepoint,
+ payment_basepoint,
+ delayed_payment_basepoint,
+ htlc_basepoint,
+ first_per_commitment_point,
+ second_per_commitment_point,
+ channel_flags,
+}, {
+ (0, shutdown_scriptpubkey, option),
(1, channel_type, option),
+ (2, require_confirmed_inputs, option),
});
+#[cfg(not(taproot))]
impl_writeable_msg!(RevokeAndACK, {
channel_id,
per_commitment_secret,
next_per_commitment_point
}, {});
+#[cfg(taproot)]
+impl_writeable_msg!(RevokeAndACK, {
+ channel_id,
+ per_commitment_secret,
+ next_per_commitment_point
+}, {
+ (4, next_local_nonce, option)
+});
+
impl_writeable_msg!(Shutdown, {
channel_id,
scriptpubkey
amount_msat,
payment_hash,
cltv_expiry,
- onion_routing_packet
-}, {});
+ onion_routing_packet,
+}, {
+ (65537, skimmed_fee_msat, option)
+});
impl Readable for OnionMessage {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
}
}
-impl Writeable for OnionHopData {
+impl Writeable for OutboundOnionPayload {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- match self.format {
- OnionHopDataFormat::Legacy { short_channel_id } => {
- 0u8.write(w)?;
- short_channel_id.write(w)?;
- self.amt_to_forward.write(w)?;
- self.outgoing_cltv_value.write(w)?;
- w.write_all(&[0;12])?;
- },
- OnionHopDataFormat::NonFinalNode { short_channel_id } => {
- encode_varint_length_prefixed_tlv!(w, {
- (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
- (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
+ match self {
+ Self::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } => {
+ _encode_varint_length_prefixed_tlv!(w, {
+ (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
+ (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
(6, short_channel_id, required)
});
},
- OnionHopDataFormat::FinalNode { ref payment_data, ref keysend_preimage } => {
- encode_varint_length_prefixed_tlv!(w, {
- (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
- (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
+ Self::Receive {
+ ref payment_data, ref payment_metadata, ref keysend_preimage, amt_msat,
+ outgoing_cltv_value, ref custom_tlvs,
+ } => {
+ // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
+ // to reject any reserved types in the experimental range if new ones are ever
+ // standardized.
+ let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode()));
+ let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect();
+ custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
+ _encode_varint_length_prefixed_tlv!(w, {
+ (2, HighZeroBytesDroppedBigSize(*amt_msat), required),
+ (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
(8, payment_data, option),
- (5482373484, keysend_preimage, option)
+ (16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
+ }, custom_tlvs.iter());
+ },
+ Self::BlindedForward { encrypted_tlvs, intro_node_blinding_point } => {
+ _encode_varint_length_prefixed_tlv!(w, {
+ (10, *encrypted_tlvs, required_vec),
+ (12, intro_node_blinding_point, option)
+ });
+ },
+ Self::BlindedReceive {
+ amt_msat, total_msat, outgoing_cltv_value, encrypted_tlvs,
+ intro_node_blinding_point,
+ } => {
+ _encode_varint_length_prefixed_tlv!(w, {
+ (2, HighZeroBytesDroppedBigSize(*amt_msat), required),
+ (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
+ (10, *encrypted_tlvs, required_vec),
+ (12, intro_node_blinding_point, option),
+ (18, HighZeroBytesDroppedBigSize(*total_msat), required)
});
},
}
}
}
-impl Readable for OnionHopData {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let b: BigSize = Readable::read(r)?;
- const LEGACY_ONION_HOP_FLAG: u64 = 0;
- let (format, amt, cltv_value) = if b.0 != LEGACY_ONION_HOP_FLAG {
- let mut rd = FixedLengthReader::new(r, b.0);
- let mut amt = HighZeroBytesDroppedBigSize(0u64);
- let mut cltv_value = HighZeroBytesDroppedBigSize(0u32);
- let mut short_id: Option<u64> = None;
- let mut payment_data: Option<FinalOnionHopData> = None;
- let mut keysend_preimage: Option<PaymentPreimage> = None;
- decode_tlv_stream!(&mut rd, {
- (2, amt, required),
- (4, cltv_value, required),
- (6, short_id, option),
- (8, payment_data, option),
- // See https://github.com/lightning/blips/blob/master/blip-0003.md
- (5482373484, keysend_preimage, option)
- });
- rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
- let format = if let Some(short_channel_id) = short_id {
- if payment_data.is_some() { return Err(DecodeError::InvalidValue); }
- OnionHopDataFormat::NonFinalNode {
- short_channel_id,
- }
- } else {
- if let &Some(ref data) = &payment_data {
- if data.total_msat > MAX_VALUE_MSAT {
- return Err(DecodeError::InvalidValue);
- }
- }
- OnionHopDataFormat::FinalNode {
- payment_data,
- keysend_preimage,
- }
- };
- (format, amt.0, cltv_value.0)
- } else {
- let format = OnionHopDataFormat::Legacy {
- short_channel_id: Readable::read(r)?,
- };
- let amt: u64 = Readable::read(r)?;
- let cltv_value: u32 = Readable::read(r)?;
- r.read_exact(&mut [0; 12])?;
- (format, amt, cltv_value)
- };
+impl<NS: Deref> ReadableArgs<&NS> for InboundOnionPayload where NS::Target: NodeSigner {
+ fn read<R: Read>(r: &mut R, node_signer: &NS) -> Result<Self, DecodeError> {
+ let mut amt = None;
+ let mut cltv_value = None;
+ let mut short_id: Option<u64> = None;
+ let mut payment_data: Option<FinalOnionHopData> = None;
+ let mut encrypted_tlvs_opt: Option<WithoutLength<Vec<u8>>> = None;
+ let mut intro_node_blinding_point = None;
+ let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
+ let mut total_msat = None;
+ let mut keysend_preimage: Option<PaymentPreimage> = None;
+ let mut custom_tlvs = Vec::new();
+
+ let tlv_len = BigSize::read(r)?;
+ let rd = FixedLengthReader::new(r, tlv_len.0);
+ decode_tlv_stream_with_custom_tlv_decode!(rd, {
+ (2, amt, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
+ (4, cltv_value, (option, encoding: (u32, HighZeroBytesDroppedBigSize))),
+ (6, short_id, option),
+ (8, payment_data, option),
+ (10, encrypted_tlvs_opt, option),
+ (12, intro_node_blinding_point, option),
+ (16, payment_metadata, option),
+ (18, total_msat, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
+ // See https://github.com/lightning/blips/blob/master/blip-0003.md
+ (5482373484, keysend_preimage, option)
+ }, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
+ if msg_type < 1 << 16 { return Ok(false) }
+ let mut value = Vec::new();
+ msg_reader.read_to_end(&mut value)?;
+ custom_tlvs.push((msg_type, value));
+ Ok(true)
+ });
- if amt > MAX_VALUE_MSAT {
- return Err(DecodeError::InvalidValue);
- }
- Ok(OnionHopData {
- format,
- amt_to_forward: amt,
- outgoing_cltv_value: cltv_value,
- })
- }
-}
+ if amt.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
-// ReadableArgs because we need onion_utils::decode_next_hop to accommodate payment packets and
-// onion message packets.
-impl ReadableArgs<()> for OnionHopData {
- fn read<R: Read>(r: &mut R, _arg: ()) -> Result<Self, DecodeError> {
- <Self as Readable>::read(r)
+ if let Some(blinding_point) = intro_node_blinding_point {
+ if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() {
+ return Err(DecodeError::InvalidValue)
+ }
+ let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0;
+ let enc_tlvs_ss = node_signer.ecdh(Recipient::Node, &blinding_point, None)
+ .map_err(|_| DecodeError::InvalidValue)?;
+ let rho = onion_utils::gen_rho_from_shared_secret(&enc_tlvs_ss.secret_bytes());
+ let mut s = Cursor::new(&enc_tlvs);
+ let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64);
+ match ChaChaPolyReadAdapter::read(&mut reader, rho)? {
+ ChaChaPolyReadAdapter { readable: ReceiveTlvs { payment_secret, payment_constraints }} => {
+ if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
+ Ok(Self::BlindedReceive {
+ amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
+ total_msat: total_msat.ok_or(DecodeError::InvalidValue)?,
+ outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
+ payment_secret,
+ payment_constraints,
+ intro_node_blinding_point: blinding_point,
+ })
+ },
+ }
+ } else if let Some(short_channel_id) = short_id {
+ if payment_data.is_some() || payment_metadata.is_some() || encrypted_tlvs_opt.is_some() ||
+ total_msat.is_some()
+ { return Err(DecodeError::InvalidValue) }
+ Ok(Self::Forward {
+ short_channel_id,
+ amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?,
+ outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
+ })
+ } else {
+ if encrypted_tlvs_opt.is_some() || total_msat.is_some() {
+ return Err(DecodeError::InvalidValue)
+ }
+ if let Some(data) = &payment_data {
+ if data.total_msat > MAX_VALUE_MSAT {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
+ Ok(Self::Receive {
+ payment_data,
+ payment_metadata: payment_metadata.map(|w| w.0),
+ keysend_preimage,
+ amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
+ outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
+ custom_tlvs,
+ })
+ }
}
}
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let features: NodeFeatures = Readable::read(r)?;
let timestamp: u32 = Readable::read(r)?;
- let node_id: PublicKey = Readable::read(r)?;
+ let node_id: NodeId = Readable::read(r)?;
let mut rgb = [0; 3];
r.read_exact(&mut rgb)?;
- let alias: [u8; 32] = Readable::read(r)?;
+ let alias: NodeAlias = Readable::read(r)?;
let addr_len: u16 = Readable::read(r)?;
- let mut addresses: Vec<NetAddress> = Vec::new();
+ let mut addresses: Vec<SocketAddress> = Vec::new();
let mut addr_readpos = 0;
let mut excess = false;
let mut excess_byte = 0;
}, {});
impl QueryChannelRange {
- /**
- * Calculates the overflow safe ending block height for the query.
- * Overflow returns `0xffffffff`, otherwise returns `first_blocknum + number_of_blocks`
- */
+ /// Calculates the overflow safe ending block height for the query.
+ ///
+ /// Overflow returns `0xffffffff`, otherwise returns `first_blocknum + number_of_blocks`.
pub fn end_blocknum(&self) -> u32 {
match self.first_blocknum.checked_add(self.number_of_blocks) {
Some(block) => block,
#[cfg(test)]
mod tests {
+ use std::convert::TryFrom;
+ use bitcoin::blockdata::constants::ChainHash;
+ use bitcoin::{Transaction, PackedLockTime, TxIn, Script, Sequence, Witness, TxOut};
use hex;
- use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
- use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
- use ln::msgs;
- use ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat};
- use util::ser::{Writeable, Readable, Hostname};
+ use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
+ use crate::ln::ChannelId;
+ use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
+ use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket};
+ use crate::ln::msgs::SocketAddress;
+ use crate::routing::gossip::{NodeAlias, NodeId};
+ use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited};
+ use crate::util::test_utils;
use bitcoin::hashes::hex::FromHex;
use bitcoin::util::address::Address;
use bitcoin::secp256k1::{PublicKey,SecretKey};
use bitcoin::secp256k1::{Secp256k1, Message};
- use io::{self, Cursor};
- use prelude::*;
- use core::convert::TryFrom;
+ use crate::io::{self, Cursor};
+ use crate::prelude::*;
+ use core::str::FromStr;
+ use crate::chain::transaction::OutPoint;
+
+ #[cfg(feature = "std")]
+ use std::net::{Ipv4Addr, Ipv6Addr};
+ use crate::ln::msgs::SocketAddressParseError;
#[test]
- fn encoding_channel_reestablish_no_secret() {
+ fn encoding_channel_reestablish() {
+ let public_key = {
+ let secp_ctx = Secp256k1::new();
+ PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap())
+ };
+
let cr = msgs::ChannelReestablish {
- channel_id: [4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0],
+ channel_id: ChannelId::from_bytes([4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0]),
next_local_commitment_number: 3,
next_remote_commitment_number: 4,
- data_loss_protect: OptionalField::Absent,
+ your_last_per_commitment_secret: [9;32],
+ my_current_per_commitment_point: public_key,
+ next_funding_txid: None,
};
let encoded_value = cr.encode();
assert_eq!(
encoded_value,
- vec![4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4]
+ vec![
+ 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, // channel_id
+ 0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number
+ 0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // your_last_per_commitment_secret
+ 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, // my_current_per_commitment_point
+ ]
);
}
#[test]
- fn encoding_channel_reestablish_with_secret() {
+ fn encoding_channel_reestablish_with_next_funding_txid() {
let public_key = {
let secp_ctx = Secp256k1::new();
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap())
};
let cr = msgs::ChannelReestablish {
- channel_id: [4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0],
+ channel_id: ChannelId::from_bytes([4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0]),
next_local_commitment_number: 3,
next_remote_commitment_number: 4,
- data_loss_protect: OptionalField::Present(msgs::DataLossProtect { your_last_per_commitment_secret: [9;32], my_current_per_commitment_point: public_key}),
+ your_last_per_commitment_secret: [9;32],
+ my_current_per_commitment_point: public_key,
+ next_funding_txid: Some(Txid::from_hash(bitcoin::hashes::Hash::from_slice(&[
+ 48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124,
+ ]).unwrap())),
};
let encoded_value = cr.encode();
assert_eq!(
encoded_value,
- vec![4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143]
+ vec![
+ 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, // channel_id
+ 0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number
+ 0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // your_last_per_commitment_secret
+ 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, // my_current_per_commitment_point
+ 0, // Type (next_funding_txid)
+ 32, // Length
+ 48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, // Value
+ ]
);
}
let sig_1 = get_sig_on!(privkey, secp_ctx, String::from("01010101010101010101010101010101"));
let sig_2 = get_sig_on!(privkey, secp_ctx, String::from("02020202020202020202020202020202"));
let announcement_signatures = msgs::AnnouncementSignatures {
- channel_id: [4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0],
+ channel_id: ChannelId::from_bytes([4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0]),
short_channel_id: 2316138423780173,
node_signature: sig_1,
bitcoin_signature: sig_2,
features,
chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
short_channel_id: 2316138423780173,
- node_id_1: pubkey_1,
- node_id_2: pubkey_2,
- bitcoin_key_1: pubkey_3,
- bitcoin_key_2: pubkey_4,
+ node_id_1: NodeId::from_pubkey(&pubkey_1),
+ node_id_2: NodeId::from_pubkey(&pubkey_2),
+ bitcoin_key_1: NodeId::from_pubkey(&pubkey_3),
+ bitcoin_key_2: NodeId::from_pubkey(&pubkey_4),
excess_data: if excess_data { vec![10, 0, 0, 20, 0, 0, 30, 0, 0, 40] } else { Vec::new() },
};
let channel_announcement = msgs::ChannelAnnouncement {
};
let mut addresses = Vec::new();
if ipv4 {
- addresses.push(msgs::NetAddress::IPv4 {
+ addresses.push(SocketAddress::TcpIpV4 {
addr: [255, 254, 253, 252],
port: 9735
});
}
if ipv6 {
- addresses.push(msgs::NetAddress::IPv6 {
+ addresses.push(SocketAddress::TcpIpV6 {
addr: [255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240],
port: 9735
});
}
if onionv2 {
- addresses.push(msgs::NetAddress::OnionV2(
+ addresses.push(msgs::SocketAddress::OnionV2(
[255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 38, 7]
));
}
if onionv3 {
- addresses.push(msgs::NetAddress::OnionV3 {
+ addresses.push(msgs::SocketAddress::OnionV3 {
ed25519_pubkey: [255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224],
checksum: 32,
version: 16,
});
}
if hostname {
- addresses.push(msgs::NetAddress::Hostname {
+ addresses.push(SocketAddress::Hostname {
hostname: Hostname::try_from(String::from("host")).unwrap(),
port: 9735,
});
let unsigned_node_announcement = msgs::UnsignedNodeAnnouncement {
features,
timestamp: 20190119,
- node_id: pubkey_1,
+ node_id: NodeId::from_pubkey(&pubkey_1),
rgb: [32; 3],
- alias: [16;32],
+ alias: NodeAlias([16;32]),
addresses,
excess_address_data: if excess_address_data { vec![33, 108, 40, 11, 83, 149, 162, 84, 110, 126, 75, 38, 99, 224, 79, 129, 22, 34, 241, 90, 79, 146, 232, 58, 162, 233, 43, 162, 165, 115, 193, 57, 20, 44, 84, 174, 99, 7, 42, 30, 193, 238, 125, 192, 192, 75, 222, 92, 132, 120, 6, 23, 42, 160, 92, 146, 194, 42, 232, 227, 8, 209, 210, 105] } else { Vec::new() },
excess_data: if excess_data { vec![59, 18, 204, 25, 92, 224, 162, 209, 189, 166, 168, 139, 239, 161, 159, 160, 127, 81, 202, 167, 92, 232, 56, 55, 242, 137, 101, 96, 11, 138, 172, 171, 8, 85, 255, 176, 231, 65, 236, 95, 124, 65, 66, 30, 152, 41, 169, 212, 134, 17, 200, 200, 49, 247, 27, 229, 234, 115, 230, 101, 148, 151, 127, 253] } else { Vec::new() },
let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
let open_channel = msgs::OpenChannel {
chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
- temporary_channel_id: [2; 32],
+ temporary_channel_id: ChannelId::from_bytes([2; 32]),
funding_satoshis: 1311768467284833366,
push_msat: 2536655962884945560,
dust_limit_satoshis: 3608586615801332854,
htlc_basepoint: pubkey_5,
first_per_commitment_point: pubkey_6,
channel_flags: if random_bit { 1 << 5 } else { 0 },
- shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent },
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
channel_type: if incl_chan_type { Some(ChannelTypeFeatures::empty()) } else { None },
};
let encoded_value = open_channel.encode();
do_encoding_open_channel(true, true, true);
}
+ fn do_encoding_open_channelv2(random_bit: bool, shutdown: bool, incl_chan_type: bool, require_confirmed_inputs: bool) {
+ let secp_ctx = Secp256k1::new();
+ let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
+ let (_, pubkey_2) = get_keys_from!("0202020202020202020202020202020202020202020202020202020202020202", secp_ctx);
+ let (_, pubkey_3) = get_keys_from!("0303030303030303030303030303030303030303030303030303030303030303", secp_ctx);
+ let (_, pubkey_4) = get_keys_from!("0404040404040404040404040404040404040404040404040404040404040404", secp_ctx);
+ let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
+ let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
+ let (_, pubkey_7) = get_keys_from!("0707070707070707070707070707070707070707070707070707070707070707", secp_ctx);
+ let open_channelv2 = msgs::OpenChannelV2 {
+ chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
+ temporary_channel_id: ChannelId::from_bytes([2; 32]),
+ funding_feerate_sat_per_1000_weight: 821716,
+ commitment_feerate_sat_per_1000_weight: 821716,
+ funding_satoshis: 1311768467284833366,
+ dust_limit_satoshis: 3608586615801332854,
+ max_htlc_value_in_flight_msat: 8517154655701053848,
+ htlc_minimum_msat: 2316138423780173,
+ to_self_delay: 49340,
+ max_accepted_htlcs: 49340,
+ locktime: 305419896,
+ funding_pubkey: pubkey_1,
+ revocation_basepoint: pubkey_2,
+ payment_basepoint: pubkey_3,
+ delayed_payment_basepoint: pubkey_4,
+ htlc_basepoint: pubkey_5,
+ first_per_commitment_point: pubkey_6,
+ second_per_commitment_point: pubkey_7,
+ channel_flags: if random_bit { 1 << 5 } else { 0 },
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
+ channel_type: if incl_chan_type { Some(ChannelTypeFeatures::empty()) } else { None },
+ require_confirmed_inputs: if require_confirmed_inputs { Some(()) } else { None },
+ };
+ let encoded_value = open_channelv2.encode();
+ let mut target_value = Vec::new();
+ target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
+ target_value.append(&mut hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap());
+ target_value.append(&mut hex::decode("000c89d4").unwrap());
+ target_value.append(&mut hex::decode("000c89d4").unwrap());
+ target_value.append(&mut hex::decode("1234567890123456").unwrap());
+ target_value.append(&mut hex::decode("3214466870114476").unwrap());
+ target_value.append(&mut hex::decode("7633030896203198").unwrap());
+ target_value.append(&mut hex::decode("00083a840000034d").unwrap());
+ target_value.append(&mut hex::decode("c0bc").unwrap());
+ target_value.append(&mut hex::decode("c0bc").unwrap());
+ target_value.append(&mut hex::decode("12345678").unwrap());
+ target_value.append(&mut hex::decode("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap());
+ target_value.append(&mut hex::decode("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766").unwrap());
+ target_value.append(&mut hex::decode("02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337").unwrap());
+ target_value.append(&mut hex::decode("03462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap());
+ target_value.append(&mut hex::decode("0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f7").unwrap());
+ target_value.append(&mut hex::decode("03f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap());
+ target_value.append(&mut hex::decode("02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f").unwrap());
+
+ if random_bit {
+ target_value.append(&mut hex::decode("20").unwrap());
+ } else {
+ target_value.append(&mut hex::decode("00").unwrap());
+ }
+ if shutdown {
+ target_value.append(&mut hex::decode("001b").unwrap()); // Type 0 + Length 27
+ target_value.append(&mut hex::decode("001976a91479b000887626b294a914501a4cd226b58b23598388ac").unwrap());
+ }
+ if incl_chan_type {
+ target_value.append(&mut hex::decode("0100").unwrap());
+ }
+ if require_confirmed_inputs {
+ target_value.append(&mut hex::decode("0200").unwrap());
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_open_channelv2() {
+ do_encoding_open_channelv2(false, false, false, false);
+ do_encoding_open_channelv2(false, false, false, true);
+ do_encoding_open_channelv2(false, false, true, false);
+ do_encoding_open_channelv2(false, false, true, true);
+ do_encoding_open_channelv2(false, true, false, false);
+ do_encoding_open_channelv2(false, true, false, true);
+ do_encoding_open_channelv2(false, true, true, false);
+ do_encoding_open_channelv2(false, true, true, true);
+ do_encoding_open_channelv2(true, false, false, false);
+ do_encoding_open_channelv2(true, false, false, true);
+ do_encoding_open_channelv2(true, false, true, false);
+ do_encoding_open_channelv2(true, false, true, true);
+ do_encoding_open_channelv2(true, true, false, false);
+ do_encoding_open_channelv2(true, true, false, true);
+ do_encoding_open_channelv2(true, true, true, false);
+ do_encoding_open_channelv2(true, true, true, true);
+ }
+
fn do_encoding_accept_channel(shutdown: bool) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
let accept_channel = msgs::AcceptChannel {
- temporary_channel_id: [2; 32],
+ temporary_channel_id: ChannelId::from_bytes([2; 32]),
dust_limit_satoshis: 1311768467284833366,
max_htlc_value_in_flight_msat: 2536655962884945560,
channel_reserve_satoshis: 3608586615801332854,
delayed_payment_basepoint: pubkey_4,
htlc_basepoint: pubkey_5,
first_per_commitment_point: pubkey_6,
- shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent },
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
channel_type: None,
+ #[cfg(taproot)]
+ next_local_nonce: None,
};
let encoded_value = accept_channel.encode();
let mut target_value = hex::decode("020202020202020202020202020202020202020202020202020202020202020212345678901234562334032891223698321446687011447600083a840000034d000c89d4c0bcc0bc031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d076602531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33703462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f703f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap();
do_encoding_accept_channel(true);
}
+ fn do_encoding_accept_channelv2(shutdown: bool) {
+ let secp_ctx = Secp256k1::new();
+ let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
+ let (_, pubkey_2) = get_keys_from!("0202020202020202020202020202020202020202020202020202020202020202", secp_ctx);
+ let (_, pubkey_3) = get_keys_from!("0303030303030303030303030303030303030303030303030303030303030303", secp_ctx);
+ let (_, pubkey_4) = get_keys_from!("0404040404040404040404040404040404040404040404040404040404040404", secp_ctx);
+ let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
+ let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
+ let (_, pubkey_7) = get_keys_from!("0707070707070707070707070707070707070707070707070707070707070707", secp_ctx);
+ let accept_channelv2 = msgs::AcceptChannelV2 {
+ temporary_channel_id: ChannelId::from_bytes([2; 32]),
+ funding_satoshis: 1311768467284833366,
+ dust_limit_satoshis: 1311768467284833366,
+ max_htlc_value_in_flight_msat: 2536655962884945560,
+ htlc_minimum_msat: 2316138423780173,
+ minimum_depth: 821716,
+ to_self_delay: 49340,
+ max_accepted_htlcs: 49340,
+ funding_pubkey: pubkey_1,
+ revocation_basepoint: pubkey_2,
+ payment_basepoint: pubkey_3,
+ delayed_payment_basepoint: pubkey_4,
+ htlc_basepoint: pubkey_5,
+ first_per_commitment_point: pubkey_6,
+ second_per_commitment_point: pubkey_7,
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
+ channel_type: None,
+ require_confirmed_inputs: None,
+ };
+ let encoded_value = accept_channelv2.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // temporary_channel_id
+ target_value.append(&mut hex::decode("1234567890123456").unwrap()); // funding_satoshis
+ target_value.append(&mut hex::decode("1234567890123456").unwrap()); // dust_limit_satoshis
+ target_value.append(&mut hex::decode("2334032891223698").unwrap()); // max_htlc_value_in_flight_msat
+ target_value.append(&mut hex::decode("00083a840000034d").unwrap()); // htlc_minimum_msat
+ target_value.append(&mut hex::decode("000c89d4").unwrap()); // minimum_depth
+ target_value.append(&mut hex::decode("c0bc").unwrap()); // to_self_delay
+ target_value.append(&mut hex::decode("c0bc").unwrap()); // max_accepted_htlcs
+ target_value.append(&mut hex::decode("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap()); // funding_pubkey
+ target_value.append(&mut hex::decode("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766").unwrap()); // revocation_basepoint
+ target_value.append(&mut hex::decode("02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337").unwrap()); // payment_basepoint
+ target_value.append(&mut hex::decode("03462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap()); // delayed_payment_basepoint
+ target_value.append(&mut hex::decode("0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f7").unwrap()); // htlc_basepoint
+ target_value.append(&mut hex::decode("03f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap()); // first_per_commitment_point
+ target_value.append(&mut hex::decode("02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f").unwrap()); // second_per_commitment_point
+ if shutdown {
+ target_value.append(&mut hex::decode("001b").unwrap()); // Type 0 + Length 27
+ target_value.append(&mut hex::decode("001976a91479b000887626b294a914501a4cd226b58b23598388ac").unwrap());
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_accept_channelv2() {
+ do_encoding_accept_channelv2(false);
+ do_encoding_accept_channelv2(true);
+ }
+
#[test]
fn encoding_funding_created() {
let secp_ctx = Secp256k1::new();
let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
let funding_created = msgs::FundingCreated {
- temporary_channel_id: [2; 32],
+ temporary_channel_id: ChannelId::from_bytes([2; 32]),
funding_txid: Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
funding_output_index: 255,
signature: sig_1,
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
+ #[cfg(taproot)]
+ next_local_nonce: None,
};
let encoded_value = funding_created.encode();
let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202026e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c200ffd977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap();
let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
let funding_signed = msgs::FundingSigned {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
signature: sig_1,
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
};
let encoded_value = funding_signed.encode();
let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap();
let secp_ctx = Secp256k1::new();
let (_, pubkey_1,) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let channel_ready = msgs::ChannelReady {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
next_per_commitment_point: pubkey_1,
short_channel_id_alias: None,
};
assert_eq!(encoded_value, target_value);
}
+ #[test]
+ fn encoding_tx_add_input() {
+ let tx_add_input = msgs::TxAddInput {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ serial_id: 4886718345,
+ prevtx: TransactionU16LenLimited::new(Transaction {
+ version: 2,
+ lock_time: PackedLockTime(0),
+ input: vec![TxIn {
+ previous_output: OutPoint { txid: Txid::from_hex("305bab643ee297b8b6b76b320792c8223d55082122cb606bf89382146ced9c77").unwrap(), index: 2 }.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: Sequence(0xfffffffd),
+ witness: Witness::from_vec(vec![
+ hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap(),
+ hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap()]),
+ }],
+ output: vec![
+ TxOut {
+ value: 12704566,
+ script_pubkey: Address::from_str("bc1qzlffunw52jav8vwdu5x3jfk6sr8u22rmq3xzw2").unwrap().script_pubkey(),
+ },
+ TxOut {
+ value: 245148,
+ script_pubkey: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().script_pubkey(),
+ },
+ ],
+ }).unwrap(),
+ prevtx_out: 305419896,
+ sequence: 305419896,
+ };
+ let encoded_value = tx_add_input.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202000000012345678900de02000000000101779ced6c148293f86b60cb222108553d22c89207326bb7b6b897e23e64ab5b300200000000fdffffff0236dbc1000000000016001417d29e4dd454bac3b1cde50d1926da80cfc5287b9cbd03000000000016001436ec78d514df462da95e6a00c24daa8915362d420247304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701210301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944000000001234567812345678").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_add_output() {
+ let tx_add_output = msgs::TxAddOutput {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ serial_id: 4886718345,
+ sats: 4886718345,
+ script: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().script_pubkey(),
+ };
+ let encoded_value = tx_add_output.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202000000012345678900000001234567890016001436ec78d514df462da95e6a00c24daa8915362d42").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_remove_input() {
+ let tx_remove_input = msgs::TxRemoveInput {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ serial_id: 4886718345,
+ };
+ let encoded_value = tx_remove_input.encode();
+ let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020000000123456789").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_remove_output() {
+ let tx_remove_output = msgs::TxRemoveOutput {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ serial_id: 4886718345,
+ };
+ let encoded_value = tx_remove_output.encode();
+ let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020000000123456789").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_complete() {
+ let tx_complete = msgs::TxComplete {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ };
+ let encoded_value = tx_complete.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_signatures() {
+ let tx_signatures = msgs::TxSignatures {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ tx_hash: Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
+ witnesses: vec![
+ Witness::from_vec(vec![
+ hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap(),
+ hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap()]),
+ Witness::from_vec(vec![
+ hex::decode("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap(),
+ hex::decode("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap()]),
+ ],
+ };
+ let encoded_value = tx_signatures.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
+ target_value.append(&mut hex::decode("6e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2").unwrap()); // tx_hash (sha256) (big endian byte order)
+ target_value.append(&mut hex::decode("0002").unwrap()); // num_witnesses (u16)
+ // Witness 1
+ target_value.append(&mut hex::decode("006b").unwrap()); // len of witness_data
+ target_value.append(&mut hex::decode("02").unwrap()); // num_witness_elements (VarInt)
+ target_value.append(&mut hex::decode("47").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap());
+ target_value.append(&mut hex::decode("21").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap());
+ // Witness 2
+ target_value.append(&mut hex::decode("006c").unwrap()); // len of witness_data
+ target_value.append(&mut hex::decode("02").unwrap()); // num_witness_elements (VarInt)
+ target_value.append(&mut hex::decode("48").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap());
+ target_value.append(&mut hex::decode("21").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap());
+ assert_eq!(encoded_value, target_value);
+ }
+
+ fn do_encoding_tx_init_rbf(funding_value_with_hex_target: Option<(i64, &str)>) {
+ let tx_init_rbf = msgs::TxInitRbf {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ locktime: 305419896,
+ feerate_sat_per_1000_weight: 20190119,
+ funding_output_contribution: if let Some((value, _)) = funding_value_with_hex_target { Some(value) } else { None },
+ };
+ let encoded_value = tx_init_rbf.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
+ target_value.append(&mut hex::decode("12345678").unwrap()); // locktime
+ target_value.append(&mut hex::decode("013413a7").unwrap()); // feerate_sat_per_1000_weight
+ if let Some((_, target)) = funding_value_with_hex_target {
+ target_value.push(0x00); // Type
+ target_value.push(target.len() as u8 / 2); // Length
+ target_value.append(&mut hex::decode(target).unwrap()); // Value (i64)
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_init_rbf() {
+ do_encoding_tx_init_rbf(Some((1311768467284833366, "1234567890123456")));
+ do_encoding_tx_init_rbf(Some((13117684672, "000000030DDFFBC0")));
+ do_encoding_tx_init_rbf(None);
+ }
+
+ fn do_encoding_tx_ack_rbf(funding_value_with_hex_target: Option<(i64, &str)>) {
+ let tx_ack_rbf = msgs::TxAckRbf {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ funding_output_contribution: if let Some((value, _)) = funding_value_with_hex_target { Some(value) } else { None },
+ };
+ let encoded_value = tx_ack_rbf.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap();
+ if let Some((_, target)) = funding_value_with_hex_target {
+ target_value.push(0x00); // Type
+ target_value.push(target.len() as u8 / 2); // Length
+ target_value.append(&mut hex::decode(target).unwrap()); // Value (i64)
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_ack_rbf() {
+ do_encoding_tx_ack_rbf(Some((1311768467284833366, "1234567890123456")));
+ do_encoding_tx_ack_rbf(Some((13117684672, "000000030DDFFBC0")));
+ do_encoding_tx_ack_rbf(None);
+ }
+
+ #[test]
+ fn encoding_tx_abort() {
+ let tx_abort = msgs::TxAbort {
+ channel_id: ChannelId::from_bytes([2; 32]),
+ data: hex::decode("54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E").unwrap(),
+ };
+ let encoded_value = tx_abort.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202002C54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
fn do_encoding_shutdown(script_type: u8) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let script = Builder::new().push_opcode(opcodes::OP_TRUE).into_script();
let shutdown = msgs::Shutdown {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
scriptpubkey:
- if script_type == 1 { Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey() }
+ if script_type == 1 { Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey() }
else if script_type == 2 { Address::p2sh(&script, Network::Testnet).unwrap().script_pubkey() }
else if script_type == 3 { Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).unwrap().script_pubkey() }
- else { Address::p2wsh(&script, Network::Testnet).script_pubkey() },
+ else { Address::p2wsh(&script, Network::Testnet).script_pubkey() },
};
let encoded_value = shutdown.encode();
let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap();
let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
let closing_signed = msgs::ClosingSigned {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
fee_satoshis: 2316138423780173,
signature: sig_1,
fee_range: None,
assert_eq!(msgs::ClosingSigned::read(&mut Cursor::new(&target_value)).unwrap(), closing_signed);
let closing_signed_with_range = msgs::ClosingSigned {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
fee_satoshis: 2316138423780173,
signature: sig_1,
fee_range: Some(msgs::ClosingSignedFeeRange {
hmac: [2; 32]
};
let update_add_htlc = msgs::UpdateAddHTLC {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
htlc_id: 2316138423780173,
amount_msat: 3608586615801332854,
payment_hash: PaymentHash([1; 32]),
cltv_expiry: 821716,
- onion_routing_packet
+ onion_routing_packet,
+ skimmed_fee_msat: None,
};
let encoded_value = update_add_htlc.encode();
let target_value = hex::decode("020202020202020202020202020202020202020202020202020202020202020200083a840000034d32144668701144760101010101010101010101010101010101010101010101010101010101010101000c89d4ff031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202").unwrap();
#[test]
fn encoding_update_fulfill_htlc() {
let update_fulfill_htlc = msgs::UpdateFulfillHTLC {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
htlc_id: 2316138423780173,
payment_preimage: PaymentPreimage([1; 32]),
};
data: [1; 32].to_vec(),
};
let update_fail_htlc = msgs::UpdateFailHTLC {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
htlc_id: 2316138423780173,
reason
};
#[test]
fn encoding_update_fail_malformed_htlc() {
let update_fail_malformed_htlc = msgs::UpdateFailMalformedHTLC {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
htlc_id: 2316138423780173,
sha256_of_onion: [1; 32],
failure_code: 255
let sig_3 = get_sig_on!(privkey_3, secp_ctx, String::from("01010101010101010101010101010101"));
let sig_4 = get_sig_on!(privkey_4, secp_ctx, String::from("01010101010101010101010101010101"));
let commitment_signed = msgs::CommitmentSigned {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
signature: sig_1,
htlc_signatures: if htlcs { vec![sig_2, sig_3, sig_4] } else { Vec::new() },
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
};
let encoded_value = commitment_signed.encode();
let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap();
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let raa = msgs::RevokeAndACK {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
per_commitment_secret: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
next_per_commitment_point: pubkey_1,
+ #[cfg(taproot)]
+ next_local_nonce: None,
};
let encoded_value = raa.encode();
let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020101010101010101010101010101010101010101010101010101010101010101031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap();
#[test]
fn encoding_update_fee() {
let update_fee = msgs::UpdateFee {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
feerate_per_kw: 20190119,
};
let encoded_value = update_fee.encode();
#[test]
fn encoding_init() {
+ let mainnet_hash = ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap();
assert_eq!(msgs::Init {
features: InitFeatures::from_le_bytes(vec![0xFF, 0xFF, 0xFF]),
+ networks: Some(vec![mainnet_hash]),
remote_network_address: None,
- }.encode(), hex::decode("00023fff0003ffffff").unwrap());
+ }.encode(), hex::decode("00023fff0003ffffff01206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap());
assert_eq!(msgs::Init {
features: InitFeatures::from_le_bytes(vec![0xFF]),
+ networks: None,
remote_network_address: None,
}.encode(), hex::decode("0001ff0001ff").unwrap());
assert_eq!(msgs::Init {
features: InitFeatures::from_le_bytes(vec![]),
+ networks: Some(vec![mainnet_hash]),
remote_network_address: None,
- }.encode(), hex::decode("00000000").unwrap());
-
+ }.encode(), hex::decode("0000000001206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap());
+ assert_eq!(msgs::Init {
+ features: InitFeatures::from_le_bytes(vec![]),
+ networks: Some(vec![ChainHash::from(&[1; 32][..]), ChainHash::from(&[2; 32][..])]),
+ remote_network_address: None,
+ }.encode(), hex::decode("00000000014001010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202").unwrap());
let init_msg = msgs::Init { features: InitFeatures::from_le_bytes(vec![]),
- remote_network_address: Some(msgs::NetAddress::IPv4 {
+ networks: Some(vec![mainnet_hash]),
+ remote_network_address: Some(SocketAddress::TcpIpV4 {
addr: [127, 0, 0, 1],
port: 1000,
}),
};
let encoded_value = init_msg.encode();
- let target_value = hex::decode("000000000307017f00000103e8").unwrap();
+ let target_value = hex::decode("0000000001206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000307017f00000103e8").unwrap();
assert_eq!(encoded_value, target_value);
assert_eq!(msgs::Init::read(&mut Cursor::new(&target_value)).unwrap(), init_msg);
}
#[test]
fn encoding_error() {
let error = msgs::ErrorMessage {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
data: String::from("rust-lightning"),
};
let encoded_value = error.encode();
#[test]
fn encoding_warning() {
let error = msgs::WarningMessage {
- channel_id: [2; 32],
+ channel_id: ChannelId::from_bytes([2; 32]),
data: String::from("rust-lightning"),
};
let encoded_value = error.encode();
assert_eq!(encoded_value, target_value);
}
- #[test]
- fn encoding_legacy_onion_hop_data() {
- let msg = msgs::OnionHopData {
- format: OnionHopDataFormat::Legacy {
- short_channel_id: 0xdeadbeef1bad1dea,
- },
- amt_to_forward: 0x0badf00d01020304,
- outgoing_cltv_value: 0xffffffff,
- };
- let encoded_value = msg.encode();
- let target_value = hex::decode("00deadbeef1bad1dea0badf00d01020304ffffffff000000000000000000000000").unwrap();
- assert_eq!(encoded_value, target_value);
- }
-
#[test]
fn encoding_nonfinal_onion_hop_data() {
- let mut msg = msgs::OnionHopData {
- format: OnionHopDataFormat::NonFinalNode {
- short_channel_id: 0xdeadbeef1bad1dea,
- },
+ let outbound_msg = msgs::OutboundOnionPayload::Forward {
+ short_channel_id: 0xdeadbeef1bad1dea,
amt_to_forward: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
};
- let encoded_value = msg.encode();
+ let encoded_value = outbound_msg.encode();
let target_value = hex::decode("1a02080badf00d010203040404ffffffff0608deadbeef1bad1dea").unwrap();
assert_eq!(encoded_value, target_value);
- msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
- if let OnionHopDataFormat::NonFinalNode { short_channel_id } = msg.format {
+
+ let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
+ let inbound_msg = ReadableArgs::read(&mut Cursor::new(&target_value[..]), &&node_signer).unwrap();
+ if let msgs::InboundOnionPayload::Forward {
+ short_channel_id, amt_to_forward, outgoing_cltv_value
+ } = inbound_msg {
assert_eq!(short_channel_id, 0xdeadbeef1bad1dea);
+ assert_eq!(amt_to_forward, 0x0badf00d01020304);
+ assert_eq!(outgoing_cltv_value, 0xffffffff);
} else { panic!(); }
- assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
- assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
}
#[test]
fn encoding_final_onion_hop_data() {
- let mut msg = msgs::OnionHopData {
- format: OnionHopDataFormat::FinalNode {
- payment_data: None,
- keysend_preimage: None,
- },
- amt_to_forward: 0x0badf00d01020304,
+ let outbound_msg = msgs::OutboundOnionPayload::Receive {
+ payment_data: None,
+ payment_metadata: None,
+ keysend_preimage: None,
+ amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
+ custom_tlvs: vec![],
};
- let encoded_value = msg.encode();
+ let encoded_value = outbound_msg.encode();
let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
assert_eq!(encoded_value, target_value);
- msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
- if let OnionHopDataFormat::FinalNode { payment_data: None, .. } = msg.format { } else { panic!(); }
- assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
- assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
+
+ let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
+ let inbound_msg = ReadableArgs::read(&mut Cursor::new(&target_value[..]), &&node_signer).unwrap();
+ if let msgs::InboundOnionPayload::Receive {
+ payment_data: None, amt_msat, outgoing_cltv_value, ..
+ } = inbound_msg {
+ assert_eq!(amt_msat, 0x0badf00d01020304);
+ assert_eq!(outgoing_cltv_value, 0xffffffff);
+ } else { panic!(); }
}
#[test]
fn encoding_final_onion_hop_data_with_secret() {
let expected_payment_secret = PaymentSecret([0x42u8; 32]);
- let mut msg = msgs::OnionHopData {
- format: OnionHopDataFormat::FinalNode {
- payment_data: Some(FinalOnionHopData {
- payment_secret: expected_payment_secret,
- total_msat: 0x1badca1f
- }),
- keysend_preimage: None,
- },
- amt_to_forward: 0x0badf00d01020304,
+ let outbound_msg = msgs::OutboundOnionPayload::Receive {
+ payment_data: Some(FinalOnionHopData {
+ payment_secret: expected_payment_secret,
+ total_msat: 0x1badca1f
+ }),
+ payment_metadata: None,
+ keysend_preimage: None,
+ amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
+ custom_tlvs: vec![],
};
- let encoded_value = msg.encode();
+ let encoded_value = outbound_msg.encode();
let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
assert_eq!(encoded_value, target_value);
- msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
- if let OnionHopDataFormat::FinalNode {
+
+ let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
+ let inbound_msg = ReadableArgs::read(&mut Cursor::new(&target_value[..]), &&node_signer).unwrap();
+ if let msgs::InboundOnionPayload::Receive {
payment_data: Some(FinalOnionHopData {
payment_secret,
total_msat: 0x1badca1f
}),
+ amt_msat, outgoing_cltv_value,
+ payment_metadata: None,
keysend_preimage: None,
- } = msg.format {
+ custom_tlvs,
+ } = inbound_msg {
assert_eq!(payment_secret, expected_payment_secret);
+ assert_eq!(amt_msat, 0x0badf00d01020304);
+ assert_eq!(outgoing_cltv_value, 0xffffffff);
+ assert_eq!(custom_tlvs, vec![]);
+ } else { panic!(); }
+ }
+
+ #[test]
+ fn encoding_final_onion_hop_data_with_bad_custom_tlvs() {
+ // If custom TLVs have type number within the range reserved for protocol, treat them as if
+ // they're unknown
+ let bad_type_range_tlvs = vec![
+ ((1 << 16) - 4, vec![42]),
+ ((1 << 16) - 2, vec![42; 32]),
+ ];
+ let mut msg = msgs::OutboundOnionPayload::Receive {
+ payment_data: None,
+ payment_metadata: None,
+ keysend_preimage: None,
+ custom_tlvs: bad_type_range_tlvs,
+ amt_msat: 0x0badf00d01020304,
+ outgoing_cltv_value: 0xffffffff,
+ };
+ let encoded_value = msg.encode();
+ let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
+ assert!(msgs::InboundOnionPayload::read(&mut Cursor::new(&encoded_value[..]), &&node_signer).is_err());
+ let good_type_range_tlvs = vec![
+ ((1 << 16) - 3, vec![42]),
+ ((1 << 16) - 1, vec![42; 32]),
+ ];
+ if let msgs::OutboundOnionPayload::Receive { ref mut custom_tlvs, .. } = msg {
+ *custom_tlvs = good_type_range_tlvs.clone();
+ }
+ let encoded_value = msg.encode();
+ let inbound_msg = ReadableArgs::read(&mut Cursor::new(&encoded_value[..]), &&node_signer).unwrap();
+ match inbound_msg {
+ msgs::InboundOnionPayload::Receive { custom_tlvs, .. } => assert!(custom_tlvs.is_empty()),
+ _ => panic!(),
+ }
+ }
+
+ #[test]
+ fn encoding_final_onion_hop_data_with_custom_tlvs() {
+ let expected_custom_tlvs = vec![
+ (5482373483, vec![0x12, 0x34]),
+ (5482373487, vec![0x42u8; 8]),
+ ];
+ let msg = msgs::OutboundOnionPayload::Receive {
+ payment_data: None,
+ payment_metadata: None,
+ keysend_preimage: None,
+ custom_tlvs: expected_custom_tlvs.clone(),
+ amt_msat: 0x0badf00d01020304,
+ outgoing_cltv_value: 0xffffffff,
+ };
+ let encoded_value = msg.encode();
+ let target_value = hex::decode("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
+ assert_eq!(encoded_value, target_value);
+ let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
+ let inbound_msg: msgs::InboundOnionPayload = ReadableArgs::read(&mut Cursor::new(&target_value[..]), &&node_signer).unwrap();
+ if let msgs::InboundOnionPayload::Receive {
+ payment_data: None,
+ payment_metadata: None,
+ keysend_preimage: None,
+ custom_tlvs,
+ amt_msat,
+ outgoing_cltv_value,
+ ..
+ } = inbound_msg {
+ assert_eq!(custom_tlvs, expected_custom_tlvs);
+ assert_eq!(amt_msat, 0x0badf00d01020304);
+ assert_eq!(outgoing_cltv_value, 0xffffffff);
} else { panic!(); }
- assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
- assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
}
#[test]
// payload length to be encoded over multiple bytes rather than a single u8.
let big_payload = encode_big_payload().unwrap();
let mut rd = Cursor::new(&big_payload[..]);
- <msgs::OnionHopData as Readable>::read(&mut rd).unwrap();
+
+ let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
+ <msgs::InboundOnionPayload as ReadableArgs<&&test_utils::TestKeysInterface>>
+ ::read(&mut rd, &&node_signer).unwrap();
}
// see above test, needs to be a separate method for use of the serialization macros.
fn encode_big_payload() -> Result<Vec<u8>, io::Error> {
- use util::ser::HighZeroBytesDroppedBigSize;
- let payload = msgs::OnionHopData {
- format: OnionHopDataFormat::NonFinalNode {
- short_channel_id: 0xdeadbeef1bad1dea,
- },
+ use crate::util::ser::HighZeroBytesDroppedBigSize;
+ let payload = msgs::OutboundOnionPayload::Forward {
+ short_channel_id: 0xdeadbeef1bad1dea,
amt_to_forward: 1000,
outgoing_cltv_value: 0xffffffff,
};
let mut encoded_payload = Vec::new();
let test_bytes = vec![42u8; 1000];
- if let OnionHopDataFormat::NonFinalNode { short_channel_id } = payload.format {
- encode_varint_length_prefixed_tlv!(&mut encoded_payload, {
- (1, test_bytes, vec_type),
- (2, HighZeroBytesDroppedBigSize(payload.amt_to_forward), required),
- (4, HighZeroBytesDroppedBigSize(payload.outgoing_cltv_value), required),
+ if let msgs::OutboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } = payload {
+ _encode_varint_length_prefixed_tlv!(&mut encoded_payload, {
+ (1, test_bytes, required_vec),
+ (2, HighZeroBytesDroppedBigSize(amt_to_forward), required),
+ (4, HighZeroBytesDroppedBigSize(outgoing_cltv_value), required),
(6, short_channel_id, required)
});
}
Ok(encoded_payload)
}
+
+ #[test]
+ #[cfg(feature = "std")]
+ fn test_socket_address_from_str() {
+ assert_eq!(SocketAddress::TcpIpV4 {
+ addr: Ipv4Addr::new(127, 0, 0, 1).octets(),
+ port: 1234,
+ }, SocketAddress::from_str("127.0.0.1:1234").unwrap());
+
+ assert_eq!(SocketAddress::TcpIpV6 {
+ addr: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).octets(),
+ port: 1234,
+ }, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap());
+ assert_eq!(
+ SocketAddress::Hostname {
+ hostname: Hostname::try_from("lightning-node.mydomain.com".to_string()).unwrap(),
+ port: 1234,
+ }, SocketAddress::from_str("lightning-node.mydomain.com:1234").unwrap());
+ assert_eq!(
+ SocketAddress::Hostname {
+ hostname: Hostname::try_from("example.com".to_string()).unwrap(),
+ port: 1234,
+ }, SocketAddress::from_str("example.com:1234").unwrap());
+ assert_eq!(SocketAddress::OnionV3 {
+ ed25519_pubkey: [37, 24, 75, 5, 25, 73, 117, 194, 139, 102, 182, 107, 4, 105, 247, 246, 85,
+ 111, 177, 172, 49, 137, 167, 155, 64, 221, 163, 47, 31, 33, 71, 3],
+ checksum: 48326,
+ version: 121,
+ port: 1234
+ }, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap());
+ assert_eq!(Err(SocketAddressParseError::InvalidOnionV3), SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234"));
+ assert_eq!(Err(SocketAddressParseError::InvalidInput), SocketAddress::from_str("127.0.0.1@1234"));
+ assert_eq!(Err(SocketAddressParseError::InvalidInput), "".parse::<SocketAddress>());
+ assert!(SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:9735:94").is_err());
+ assert!(SocketAddress::from_str("wrong$%#.com:1234").is_err());
+ assert_eq!(Err(SocketAddressParseError::InvalidPort), SocketAddress::from_str("example.com:wrong"));
+ assert!("localhost".parse::<SocketAddress>().is_err());
+ assert!("localhost:invalid-port".parse::<SocketAddress>().is_err());
+ assert!( "invalid-onion-v3-hostname.onion:8080".parse::<SocketAddress>().is_err());
+ assert!("b32.example.onion:invalid-port".parse::<SocketAddress>().is_err());
+ assert!("invalid-address".parse::<SocketAddress>().is_err());
+ assert!(SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:1234").is_err());
+ }
}