X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fmsgs.rs;h=9d872a6f53d999ac817cfaf9b6e4aa2a444814d2;hb=870a0f14bab32d21c7652773b5d9a37f8e59cd5e;hp=6bd5ec3f7293fcac5c043bf3437e47eb90127d9d;hpb=448b191fec4c6d1e9638c82aade7385b1516aa5d;p=rust-lightning diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 6bd5ec3f..9d872a6f 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -29,24 +29,33 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::secp256k1::ecdsa::Signature; use bitcoin::{secp256k1, Witness}; use bitcoin::blockdata::script::Script; -use bitcoin::hash_types::{Txid, BlockHash}; +use bitcoin::hash_types::Txid; +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 crate::prelude::*; +#[cfg(feature = "std")] use core::convert::TryFrom; use core::fmt; use core::fmt::Debug; +use core::ops::Deref; +#[cfg(feature = "std")] use core::str::FromStr; -use crate::io::{self, Read}; +#[cfg(feature = "std")] +use std::net::SocketAddr; +use core::fmt::Display; +use crate::io::{self, Cursor, Read}; use crate::io_extras::read_to_end; -use crate::events::{MessageSendEventsProvider, OnionMessageProvider}; +use crate::events::MessageSendEventsProvider; +use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter; use crate::util::logger; -use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize}; +use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize}; use crate::util::base32; use crate::routing::gossip::{NodeAlias, NodeId}; @@ -56,11 +65,11 @@ 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)] +#[derive(Clone, Debug, Hash, 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, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum DecodeError { /// A version byte specified something we don't know how to handle. /// @@ -87,7 +96,7 @@ pub enum DecodeError { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Init { /// The relevant features which the sender supports. pub features: InitFeatures, @@ -107,7 +116,7 @@ pub struct Init { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ErrorMessage { /// The channel ID involved in the error. /// @@ -125,7 +134,7 @@ pub struct ErrorMessage { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct WarningMessage { /// The channel ID involved in the warning. /// @@ -142,7 +151,7 @@ pub struct WarningMessage { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Ping { /// The desired response length. pub ponglen: u16, @@ -155,7 +164,7 @@ pub struct Ping { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Pong { /// The pong packet size. /// @@ -168,10 +177,10 @@ pub struct Pong { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct OpenChannel { /// The genesis hash of the blockchain where the channel is to be opened - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// A temporary channel ID, until the funding outpoint is announced pub temporary_channel_id: ChannelId, /// The channel value @@ -222,10 +231,10 @@ pub struct OpenChannel { /// Used in V2 channel establishment /// // TODO(dual_funding): Add spec link for `open_channel2`. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct OpenChannelV2 { /// The genesis hash of the blockchain where the channel is to be opened - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// 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 @@ -281,7 +290,7 @@ pub struct OpenChannelV2 { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct AcceptChannel { /// A temporary channel ID, until the funding outpoint is announced pub temporary_channel_id: ChannelId, @@ -329,7 +338,7 @@ pub struct AcceptChannel { /// Used in V2 channel establishment /// // TODO(dual_funding): Add spec link for `accept_channel2`. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct AcceptChannelV2 { /// The same `temporary_channel_id` received from the initiator's `open_channel2` message. pub temporary_channel_id: ChannelId, @@ -382,7 +391,7 @@ pub struct AcceptChannelV2 { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct FundingCreated { /// A temporary channel ID, until the funding is established pub temporary_channel_id: ChannelId, @@ -405,7 +414,7 @@ pub struct FundingCreated { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct FundingSigned { /// The channel ID pub channel_id: ChannelId, @@ -419,7 +428,7 @@ pub struct FundingSigned { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ChannelReady { /// The channel ID pub channel_id: ChannelId, @@ -432,10 +441,64 @@ pub struct ChannelReady { pub short_channel_id_alias: Option, } +/// An stfu (quiescence) message to be sent by or received from the stfu initiator. +// TODO(splicing): Add spec link for `stfu`; still in draft, using from https://github.com/lightning/bolts/pull/863 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Stfu { + /// The channel ID where quiescence is intended + pub channel_id: ChannelId, + /// Initiator flag, 1 if initiating, 0 if replying to an stfu. + pub initiator: u8, +} + +/// A splice message to be sent by or received from the stfu initiator (splice initiator). +// TODO(splicing): Add spec link for `splice`; still in draft, using from https://github.com/lightning/bolts/pull/863 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Splice { + /// The channel ID where splicing is intended + pub channel_id: ChannelId, + /// The genesis hash of the blockchain where the channel is intended to be spliced + pub chain_hash: ChainHash, + /// The intended change in channel capacity: the amount to be added (positive value) + /// or removed (negative value) by the sender (splice initiator) by splicing into/from the channel. + pub relative_satoshis: i64, + /// The feerate for the new funding transaction, set by the splice initiator + pub funding_feerate_perkw: u32, + /// The locktime for the new funding transaction + pub locktime: u32, + /// The key of the sender (splice initiator) controlling the new funding transaction + pub funding_pubkey: PublicKey, +} + +/// A splice_ack message to be received by or sent to the splice initiator. +/// +// TODO(splicing): Add spec link for `splice_ack`; still in draft, using from https://github.com/lightning/bolts/pull/863 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SpliceAck { + /// The channel ID where splicing is intended + pub channel_id: ChannelId, + /// The genesis hash of the blockchain where the channel is intended to be spliced + pub chain_hash: ChainHash, + /// The intended change in channel capacity: the amount to be added (positive value) + /// or removed (negative value) by the sender (splice acceptor) by splicing into/from the channel. + pub relative_satoshis: i64, + /// The key of the sender (splice acceptor) controlling the new funding transaction + pub funding_pubkey: PublicKey, +} + +/// A splice_locked message to be sent to or received from a peer. +/// +// TODO(splicing): Add spec link for `splice_locked`; still in draft, using from https://github.com/lightning/bolts/pull/863 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SpliceLocked { + /// The channel ID + pub channel_id: ChannelId, +} + /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxAddInput { /// The channel ID pub channel_id: ChannelId, @@ -454,7 +517,7 @@ pub struct TxAddInput { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxAddOutput { /// The channel ID pub channel_id: ChannelId, @@ -470,7 +533,7 @@ pub struct TxAddOutput { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxRemoveInput { /// The channel ID pub channel_id: ChannelId, @@ -481,7 +544,7 @@ pub struct TxRemoveInput { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxRemoveOutput { /// The channel ID pub channel_id: ChannelId, @@ -493,7 +556,7 @@ pub struct TxRemoveOutput { /// interactive transaction construction. /// // TODO(dual_funding): Add spec link for `tx_complete`. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxComplete { /// The channel ID pub channel_id: ChannelId, @@ -503,7 +566,7 @@ pub struct TxComplete { /// interactive transaction construction. /// // TODO(dual_funding): Add spec link for `tx_signatures`. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxSignatures { /// The channel ID pub channel_id: ChannelId, @@ -517,7 +580,7 @@ pub struct TxSignatures { /// completed. /// // TODO(dual_funding): Add spec link for `tx_init_rbf`. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxInitRbf { /// The channel ID pub channel_id: ChannelId, @@ -534,7 +597,7 @@ pub struct TxInitRbf { /// completed. /// // TODO(dual_funding): Add spec link for `tx_ack_rbf`. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxAckRbf { /// The channel ID pub channel_id: ChannelId, @@ -546,7 +609,7 @@ pub struct TxAckRbf { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxAbort { /// The channel ID pub channel_id: ChannelId, @@ -557,7 +620,7 @@ pub struct TxAbort { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Shutdown { /// The channel ID pub channel_id: ChannelId, @@ -571,7 +634,7 @@ pub struct Shutdown { /// /// This is provided in [`ClosingSigned`] by both sides to indicate the fee range they are willing /// to use. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ClosingSignedFeeRange { /// The minimum absolute fee, in satoshis, which the sender is willing to place on the closing /// transaction. @@ -584,7 +647,7 @@ pub struct ClosingSignedFeeRange { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ClosingSigned { /// The channel ID pub channel_id: ChannelId, @@ -600,7 +663,7 @@ pub struct ClosingSigned { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UpdateAddHTLC { /// The channel ID pub channel_id: ChannelId, @@ -623,17 +686,18 @@ pub struct UpdateAddHTLC { /// 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)] +#[derive(Clone, Debug, Hash, 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 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UpdateFulfillHTLC { /// The channel ID pub channel_id: ChannelId, @@ -646,7 +710,7 @@ pub struct UpdateFulfillHTLC { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UpdateFailHTLC { /// The channel ID pub channel_id: ChannelId, @@ -658,7 +722,7 @@ pub struct UpdateFailHTLC { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UpdateFailMalformedHTLC { /// The channel ID pub channel_id: ChannelId, @@ -672,7 +736,7 @@ pub struct UpdateFailMalformedHTLC { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct CommitmentSigned { /// The channel ID pub channel_id: ChannelId, @@ -688,7 +752,7 @@ pub struct CommitmentSigned { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct RevokeAndACK { /// The channel ID pub channel_id: ChannelId, @@ -704,7 +768,7 @@ pub struct RevokeAndACK { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UpdateFee { /// The channel ID pub channel_id: ChannelId, @@ -715,7 +779,7 @@ pub struct UpdateFee { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ChannelReestablish { /// The channel ID pub channel_id: ChannelId, @@ -735,7 +799,7 @@ pub struct ChannelReestablish { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct AnnouncementSignatures { /// The channel ID pub channel_id: ChannelId, @@ -748,7 +812,7 @@ pub struct AnnouncementSignatures { } /// An address which can be used to connect to a remote peer. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum SocketAddress { /// An IPv4 address and port on which the peer is listening. TcpIpV4 { @@ -903,7 +967,7 @@ impl Readable for SocketAddress { } /// [`SocketAddress`] error variants -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum SocketAddressParseError { /// Socket address (IPv4/IPv6) parsing error SocketAddrParse, @@ -951,7 +1015,41 @@ impl From for SocketAddress { } } -fn parse_onion_address(host: &str, port: u16) -> Result { +#[cfg(feature = "std")] +impl std::net::ToSocketAddrs for SocketAddress { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> std::io::Result { + match self { + SocketAddress::TcpIpV4 { addr, port } => { + let ip_addr = std::net::Ipv4Addr::from(*addr); + let socket_addr = SocketAddr::new(ip_addr.into(), *port); + Ok(vec![socket_addr].into_iter()) + } + SocketAddress::TcpIpV6 { addr, port } => { + let ip_addr = std::net::Ipv6Addr::from(*addr); + let socket_addr = SocketAddr::new(ip_addr.into(), *port); + Ok(vec![socket_addr].into_iter()) + } + SocketAddress::Hostname { ref hostname, port } => { + (hostname.as_str(), *port).to_socket_addrs() + } + SocketAddress::OnionV2(..) => { + Err(std::io::Error::new(std::io::ErrorKind::Other, "Resolution of OnionV2 \ + addresses is currently unsupported.")) + } + SocketAddress::OnionV3 { .. } => { + Err(std::io::Error::new(std::io::ErrorKind::Other, "Resolution of OnionV3 \ + addresses is currently unsupported.")) + } + } + } +} + +/// 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 { if host.ends_with(".onion") { let domain = &host[..host.len() - ".onion".len()]; if domain.len() != 56 { @@ -974,6 +1072,35 @@ fn parse_onion_address(host: &str, port: u16) -> Result) -> core::fmt::Result { + match self { + SocketAddress::TcpIpV4{addr, port} => write!( + f, "{}.{}.{}.{}:{}", addr[0], addr[1], addr[2], addr[3], port)?, + SocketAddress::TcpIpV6{addr, port} => write!( + f, + "[{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}]:{}", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15], port + )?, + SocketAddress::OnionV2(bytes) => write!(f, "OnionV2({:?})", bytes)?, + SocketAddress::OnionV3 { + ed25519_pubkey, + checksum, + version, + port, + } => { + let [first_checksum_flag, second_checksum_flag] = checksum.to_be_bytes(); + let mut addr = vec![*version, first_checksum_flag, second_checksum_flag]; + addr.extend_from_slice(ed25519_pubkey); + let onion = base32::Alphabet::RFC4648 { padding: false }.encode(&addr); + write!(f, "{}.onion:{}", onion, port)? + }, + SocketAddress::Hostname { hostname, port } => write!(f, "{}:{}", hostname, port)?, + } + Ok(()) + } +} + #[cfg(feature = "std")] impl FromStr for SocketAddress { type Err = SocketAddressParseError; @@ -1023,7 +1150,7 @@ impl<'a> Writeable for UnsignedGossipMessage<'a> { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UnsignedNodeAnnouncement { /// The advertised features pub features: NodeFeatures, @@ -1043,7 +1170,7 @@ pub struct UnsignedNodeAnnouncement { pub(crate) excess_address_data: Vec, pub(crate) excess_data: Vec, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, 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 @@ -1057,12 +1184,12 @@ pub struct NodeAnnouncement { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UnsignedChannelAnnouncement { /// The advertised channel features pub features: ChannelFeatures, /// The genesis hash of the blockchain where the channel is to be opened - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// The short channel ID pub short_channel_id: u64, /// One of the two `node_id`s which are endpoints of this channel @@ -1082,7 +1209,7 @@ pub struct UnsignedChannelAnnouncement { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ChannelAnnouncement { /// Authentication of the announcement by the first public node pub node_signature_1: Signature, @@ -1099,10 +1226,10 @@ pub struct ChannelAnnouncement { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct UnsignedChannelUpdate { /// The genesis hash of the blockchain where the channel is to be opened - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// The short channel ID pub short_channel_id: u64, /// A strictly monotonic announcement counter, with gaps allowed, specific to this channel @@ -1137,7 +1264,7 @@ pub struct UnsignedChannelUpdate { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ChannelUpdate { /// A signature of the channel update pub signature: Signature, @@ -1151,10 +1278,10 @@ pub struct ChannelUpdate { /// messages. /// /// [`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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct QueryChannelRange { /// The genesis hash of the blockchain being queried - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// The height of the first block for the channel UTXOs being queried pub first_blocknum: u32, /// The number of blocks to include in the query results @@ -1172,10 +1299,10 @@ pub struct QueryChannelRange { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ReplyChannelRange { /// The genesis hash of the blockchain being queried - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// The height of the first block in the range of the reply pub first_blocknum: u32, /// The number of blocks included in the range of the reply @@ -1197,10 +1324,10 @@ pub struct ReplyChannelRange { /// 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct QueryShortChannelIds { /// The genesis hash of the blockchain being queried - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// The short_channel_ids that are being queried pub short_channel_ids: Vec, } @@ -1211,10 +1338,10 @@ pub struct QueryShortChannelIds { /// a perfect view of the network. /// /// [`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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ReplyShortChannelIdsEnd { /// The genesis hash of the blockchain that was queried - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// Indicates if the query recipient maintains up-to-date channel /// information for the `chain_hash` pub full_information: bool, @@ -1225,10 +1352,10 @@ pub struct ReplyShortChannelIdsEnd { /// `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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct GossipTimestampFilter { /// The genesis hash of the blockchain for channel and node information - pub chain_hash: BlockHash, + pub chain_hash: ChainHash, /// The starting unix timestamp pub first_timestamp: u32, /// The range of information in seconds @@ -1244,7 +1371,7 @@ enum EncodingType { } /// Used to put an error message in a [`LightningError`]. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Hash, PartialEq)] pub enum ErrorAction { /// The peer took some action which made us think they were useless. Disconnect them. DisconnectPeer { @@ -1293,7 +1420,7 @@ pub struct LightningError { /// Struct used to return values from [`RevokeAndACK`] messages, containing a bunch of commitment /// transaction updates if they were pending. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct CommitmentUpdate { /// `update_add_htlc` messages which should be sent pub update_add_htlcs: Vec, @@ -1336,6 +1463,18 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider { /// Handle an incoming `closing_signed` message from the given peer. fn handle_closing_signed(&self, their_node_id: &PublicKey, msg: &ClosingSigned); + // Quiescence + /// Handle an incoming `stfu` message from the given peer. + fn handle_stfu(&self, their_node_id: &PublicKey, msg: &Stfu); + + // Splicing + /// Handle an incoming `splice` message from the given peer. + fn handle_splice(&self, their_node_id: &PublicKey, msg: &Splice); + /// Handle an incoming `splice_ack` message from the given peer. + fn handle_splice_ack(&self, their_node_id: &PublicKey, msg: &SpliceAck); + /// Handle an incoming `splice_locked` message from the given peer. + fn handle_splice_locked(&self, their_node_id: &PublicKey, msg: &SpliceLocked); + // 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); @@ -1410,11 +1549,11 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider { /// 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. + /// Gets the chain 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>; + fn get_chain_hashes(&self) -> Option>; } /// A trait to describe an object which can receive routing messages. @@ -1487,10 +1626,14 @@ pub trait RoutingMessageHandler : MessageSendEventsProvider { fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures; } -/// A trait to describe an object that can receive onion messages. -pub trait OnionMessageHandler : OnionMessageProvider { +/// A handler for received [`OnionMessage`]s and for providing generated ones to send. +pub trait OnionMessageHandler { /// Handle an incoming `onion_message` message from the given peer. fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage); + + /// Returns the next pending onion message for the peer with the given node id. + fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option; + /// Called when a connection is established with a peer. Can be used to track which peers /// advertise onion message support and are online. /// @@ -1498,6 +1641,7 @@ pub trait OnionMessageHandler : OnionMessageProvider { /// 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, 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. fn peer_disconnected(&self, their_node_id: &PublicKey); @@ -1517,6 +1661,8 @@ pub trait OnionMessageHandler : OnionMessageProvider { } mod fuzzy_internal_msgs { + use bitcoin::secp256k1::PublicKey; + use crate::blinded_path::payment::PaymentConstraints; use crate::prelude::*; use crate::ln::{PaymentPreimage, PaymentSecret}; @@ -1545,6 +1691,14 @@ mod fuzzy_internal_msgs { 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(crate) enum OutboundOnionPayload { @@ -1562,6 +1716,17 @@ mod fuzzy_internal_msgs { amt_msat: u64, outgoing_cltv_value: u32, }, + BlindedForward { + encrypted_tlvs: Vec, + intro_node_blinding_point: Option, + }, + BlindedReceive { + amt_msat: u64, + total_msat: u64, + outgoing_cltv_value: u32, + encrypted_tlvs: Vec, + intro_node_blinding_point: Option, // Set if the introduction node of the blinded path is the final node + } } pub struct DecodedOnionErrorPacket { @@ -1575,17 +1740,21 @@ pub use self::fuzzy_internal_msgs::*; #[cfg(not(fuzzing))] pub(crate) use self::fuzzy_internal_msgs::*; -#[derive(Clone)] -pub(crate) struct OnionPacket { - pub(crate) version: u8, +/// BOLT 4 onion packet including hop data for the next peer. +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct OnionPacket { + /// BOLT 4 version number. + pub version: u8, /// 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, - pub(crate) hop_data: [u8; 20*65], - pub(crate) hmac: [u8; 32], + pub public_key: Result, + /// 1300 bytes encrypted payload for the next hop. + pub hop_data: [u8; 20*65], + /// HMAC to verify the integrity of hop_data. + pub hmac: [u8; 32], } impl onion_utils::Packet for OnionPacket { @@ -1600,25 +1769,13 @@ impl onion_utils::Packet for OnionPacket { } } -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()) { - if i != j { return false; } - } - self.version == other.version && - self.public_key == other.public_key && - self.hmac == other.hmac - } -} - impl fmt::Debug for OnionPacket { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_fmt(format_args!("OnionPacket version {} with hmac {:?}", self.version, &self.hmac[..])) } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, 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... @@ -1714,6 +1871,31 @@ impl_writeable_msg!(AcceptChannelV2, { (2, require_confirmed_inputs, option), }); +impl_writeable_msg!(Stfu, { + channel_id, + initiator, +}, {}); + +impl_writeable_msg!(Splice, { + channel_id, + chain_hash, + relative_satoshis, + funding_feerate_perkw, + locktime, + funding_pubkey, +}, {}); + +impl_writeable_msg!(SpliceAck, { + channel_id, + chain_hash, + relative_satoshis, + funding_pubkey, +}, {}); + +impl_writeable_msg!(SpliceLocked, { + channel_id, +}, {}); + impl_writeable_msg!(TxAddInput, { channel_id, serial_id, @@ -2097,29 +2279,53 @@ impl Writeable for OutboundOnionPayload { (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) + }); + }, } Ok(()) } } -impl Readable for InboundOnionPayload { - fn read(r: &mut R) -> Result { - let mut amt = HighZeroBytesDroppedBigSize(0u64); - let mut cltv_value = HighZeroBytesDroppedBigSize(0u32); +impl ReadableArgs<&NS> for InboundOnionPayload where NS::Target: NodeSigner { + fn read(r: &mut R, node_signer: &NS) -> Result { + let mut amt = None; + let mut cltv_value = None; let mut short_id: Option = None; let mut payment_data: Option = None; + let mut encrypted_tlvs_opt: Option>> = None; + let mut intro_node_blinding_point = None; let mut payment_metadata: Option>> = None; + let mut total_msat = None; let mut keysend_preimage: Option = 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, required), - (4, cltv_value, required), + (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 { @@ -2130,16 +2336,44 @@ impl Readable for InboundOnionPayload { Ok(true) }); - if amt.0 > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } - if let Some(short_channel_id) = short_id { - if payment_data.is_some() { return Err(DecodeError::InvalidValue) } - if payment_metadata.is_some() { return Err(DecodeError::InvalidValue); } + if amt.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } + + 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.0, - outgoing_cltv_value: cltv_value.0, + 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); @@ -2149,22 +2383,14 @@ impl Readable for InboundOnionPayload { payment_data, payment_metadata: payment_metadata.map(|w| w.0), keysend_preimage, - amt_msat: amt.0, - outgoing_cltv_value: cltv_value.0, + amt_msat: amt.ok_or(DecodeError::InvalidValue)?, + outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?, custom_tlvs, }) } } } -// ReadableArgs because we need onion_utils::decode_next_hop to accommodate payment packets and -// onion message packets. -impl ReadableArgs<()> for InboundOnionPayload { - fn read(r: &mut R, _arg: ()) -> Result { - ::read(r) - } -} - impl Writeable for Ping { fn write(&self, w: &mut W) -> Result<(), io::Error> { self.ponglen.write(w)?; @@ -2432,7 +2658,7 @@ impl_writeable!(NodeAnnouncement, { impl Readable for QueryShortChannelIds { fn read(r: &mut R) -> Result { - let chain_hash: BlockHash = Readable::read(r)?; + let chain_hash: ChainHash = Readable::read(r)?; let encoding_len: u16 = Readable::read(r)?; let encoding_type: u8 = Readable::read(r)?; @@ -2508,7 +2734,7 @@ impl_writeable_msg!(QueryChannelRange, { impl Readable for ReplyChannelRange { fn read(r: &mut R) -> Result { - let chain_hash: BlockHash = Readable::read(r)?; + let chain_hash: ChainHash = Readable::read(r)?; let first_blocknum: u32 = Readable::read(r)?; let number_of_blocks: u32 = Readable::read(r)?; let sync_complete: bool = Readable::read(r)?; @@ -2573,7 +2799,6 @@ impl_writeable_msg!(GossipTimestampFilter, { #[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 crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret}; @@ -2582,14 +2807,16 @@ mod tests { use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket}; use crate::ln::msgs::SocketAddress; use crate::routing::gossip::{NodeAlias, NodeId}; - use crate::util::ser::{Writeable, Readable, Hostname, TransactionU16LenLimited}; + 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::network::constants::Network; + use bitcoin::blockdata::constants::ChainHash; use bitcoin::blockdata::script::Builder; use bitcoin::blockdata::opcodes; - use bitcoin::hash_types::{Txid, BlockHash}; + use bitcoin::hash_types::Txid; use bitcoin::secp256k1::{PublicKey,SecretKey}; use bitcoin::secp256k1::{Secp256k1, Message}; @@ -2600,7 +2827,8 @@ mod tests { use crate::chain::transaction::OutPoint; #[cfg(feature = "std")] - use std::net::{Ipv4Addr, Ipv6Addr}; + use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; + #[cfg(feature = "std")] use crate::ln::msgs::SocketAddressParseError; #[test] @@ -2718,7 +2946,7 @@ mod tests { } let unsigned_channel_announcement = msgs::UnsignedChannelAnnouncement { features, - chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), + chain_hash: ChainHash::using_genesis_block(Network::Bitcoin), short_channel_id: 2316138423780173, node_id_1: NodeId::from_pubkey(&pubkey_1), node_id_2: NodeId::from_pubkey(&pubkey_2), @@ -2740,7 +2968,7 @@ mod tests { } else { target_value.append(&mut hex::decode("0000").unwrap()); } - target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap()); + target_value.append(&mut hex::decode("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap()); target_value.append(&mut hex::decode("00083a840000034d031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d076602531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33703462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap()); if excess_data { target_value.append(&mut hex::decode("0a00001400001e000028").unwrap()); @@ -2869,7 +3097,7 @@ mod tests { let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx); let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101")); let unsigned_channel_update = msgs::UnsignedChannelUpdate { - chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), + chain_hash: ChainHash::using_genesis_block(Network::Bitcoin), short_channel_id: 2316138423780173, timestamp: 20190119, flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 }, @@ -2886,7 +3114,7 @@ mod tests { }; let encoded_value = channel_update.encode(); let mut target_value = hex::decode("d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap(); - target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap()); + target_value.append(&mut hex::decode("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap()); target_value.append(&mut hex::decode("00083a840000034d013413a7").unwrap()); target_value.append(&mut hex::decode("01").unwrap()); target_value.append(&mut hex::decode("00").unwrap()); @@ -2927,7 +3155,7 @@ mod tests { let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx); let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx); let open_channel = msgs::OpenChannel { - chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), + chain_hash: ChainHash::using_genesis_block(Network::Bitcoin), temporary_channel_id: ChannelId::from_bytes([2; 32]), funding_satoshis: 1311768467284833366, push_msat: 2536655962884945560, @@ -2950,7 +3178,7 @@ mod tests { }; let encoded_value = open_channel.encode(); let mut target_value = Vec::new(); - target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap()); + target_value.append(&mut hex::decode("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap()); target_value.append(&mut hex::decode("02020202020202020202020202020202020202020202020202020202020202021234567890123456233403289122369832144668701144767633030896203198784335490624111800083a840000034d000c89d4c0bcc0bc031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d076602531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33703462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f703f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap()); if random_bit { target_value.append(&mut hex::decode("20").unwrap()); @@ -2988,7 +3216,7 @@ mod tests { 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(), + chain_hash: ChainHash::using_genesis_block(Network::Bitcoin), temporary_channel_id: ChannelId::from_bytes([2; 32]), funding_feerate_sat_per_1000_weight: 821716, commitment_feerate_sat_per_1000_weight: 821716, @@ -3013,7 +3241,7 @@ mod tests { }; 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("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").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()); @@ -3220,6 +3448,55 @@ mod tests { assert_eq!(encoded_value, target_value); } + #[test] + fn encoding_splice() { + let secp_ctx = Secp256k1::new(); + let (_, pubkey_1,) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx); + let splice = msgs::Splice { + chain_hash: ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), + channel_id: ChannelId::from_bytes([2; 32]), + relative_satoshis: 123456, + funding_feerate_perkw: 2000, + locktime: 0, + funding_pubkey: pubkey_1, + }; + let encoded_value = splice.encode(); + assert_eq!(hex::encode(encoded_value), "02020202020202020202020202020202020202020202020202020202020202026fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000001e240000007d000000000031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"); + } + + #[test] + fn encoding_stfu() { + let stfu = msgs::Stfu { + channel_id: ChannelId::from_bytes([2; 32]), + initiator: 1, + }; + let encoded_value = stfu.encode(); + assert_eq!(hex::encode(encoded_value), "020202020202020202020202020202020202020202020202020202020202020201"); + } + + #[test] + fn encoding_splice_ack() { + let secp_ctx = Secp256k1::new(); + let (_, pubkey_1,) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx); + let splice = msgs::SpliceAck { + chain_hash: ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), + channel_id: ChannelId::from_bytes([2; 32]), + relative_satoshis: 123456, + funding_pubkey: pubkey_1, + }; + let encoded_value = splice.encode(); + assert_eq!(hex::encode(encoded_value), "02020202020202020202020202020202020202020202020202020202020202026fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000001e240031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"); + } + + #[test] + fn encoding_splice_locked() { + let splice = msgs::SpliceLocked { + channel_id: ChannelId::from_bytes([2; 32]), + }; + let encoded_value = splice.encode(); + assert_eq!(hex::encode(encoded_value), "0202020202020202020202020202020202020202020202020202020202020202"); + } + #[test] fn encoding_tx_add_input() { let tx_add_input = msgs::TxAddInput { @@ -3586,7 +3863,7 @@ mod tests { #[test] fn encoding_init() { - let mainnet_hash = ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(); + let mainnet_hash = ChainHash::using_genesis_block(Network::Bitcoin); assert_eq!(msgs::Init { features: InitFeatures::from_le_bytes(vec![0xFF, 0xFF, 0xFF]), networks: Some(vec![mainnet_hash]), @@ -3674,8 +3951,11 @@ mod tests { let target_value = hex::decode("1a02080badf00d010203040404ffffffff0608deadbeef1bad1dea").unwrap(); assert_eq!(encoded_value, target_value); - let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); - if let msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } = inbound_msg { + 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); @@ -3696,8 +3976,11 @@ mod tests { let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap(); assert_eq!(encoded_value, target_value); - let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); - if let msgs::InboundOnionPayload::Receive { payment_data: None, amt_msat, outgoing_cltv_value, .. } = inbound_msg { + 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!(); } @@ -3721,7 +4004,8 @@ mod tests { let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap(); assert_eq!(encoded_value, target_value); - let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); + 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, @@ -3756,7 +4040,8 @@ mod tests { outgoing_cltv_value: 0xffffffff, }; let encoded_value = msg.encode(); - assert!(msgs::InboundOnionPayload::read(&mut Cursor::new(&encoded_value[..])).is_err()); + 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]), @@ -3765,7 +4050,7 @@ mod tests { *custom_tlvs = good_type_range_tlvs.clone(); } let encoded_value = msg.encode(); - let inbound_msg = Readable::read(&mut Cursor::new(&encoded_value[..])).unwrap(); + 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!(), @@ -3789,7 +4074,8 @@ mod tests { let encoded_value = msg.encode(); let target_value = hex::decode("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap(); assert_eq!(encoded_value, target_value); - let inbound_msg: msgs::InboundOnionPayload = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); + 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, @@ -3815,7 +4101,7 @@ mod tests { for (first_blocknum, number_of_blocks, expected) in tests.into_iter() { let sut = msgs::QueryChannelRange { - chain_hash: BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(), + chain_hash: ChainHash::using_genesis_block(Network::Regtest), first_blocknum, number_of_blocks, }; @@ -3826,12 +4112,12 @@ mod tests { #[test] fn encoding_query_channel_range() { let mut query_channel_range = msgs::QueryChannelRange { - chain_hash: BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(), + chain_hash: ChainHash::using_genesis_block(Network::Regtest), first_blocknum: 100000, number_of_blocks: 1500, }; let encoded_value = query_channel_range.encode(); - let target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000186a0000005dc").unwrap(); + let target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f000186a0000005dc").unwrap(); assert_eq!(encoded_value, target_value); query_channel_range = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); @@ -3846,8 +4132,8 @@ mod tests { } fn do_encoding_reply_channel_range(encoding_type: u8) { - let mut target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000b8a06000005dc01").unwrap(); - let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(); + let mut target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f000b8a06000005dc01").unwrap(); + let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest); let mut reply_channel_range = msgs::ReplyChannelRange { chain_hash: expected_chain_hash, first_blocknum: 756230, @@ -3883,8 +4169,8 @@ mod tests { } fn do_encoding_query_short_channel_ids(encoding_type: u8) { - let mut target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206").unwrap(); - let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(); + let mut target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(); + let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest); let mut query_short_channel_ids = msgs::QueryShortChannelIds { chain_hash: expected_chain_hash, short_channel_ids: vec![0x0000000000008e, 0x0000000000003c69, 0x000000000045a6c4], @@ -3909,13 +4195,13 @@ mod tests { #[test] fn encoding_reply_short_channel_ids_end() { - let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(); + let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest); let mut reply_short_channel_ids_end = msgs::ReplyShortChannelIdsEnd { chain_hash: expected_chain_hash, full_information: true, }; let encoded_value = reply_short_channel_ids_end.encode(); - let target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e220601").unwrap(); + let target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f01").unwrap(); assert_eq!(encoded_value, target_value); reply_short_channel_ids_end = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); @@ -3925,14 +4211,14 @@ mod tests { #[test] fn encoding_gossip_timestamp_filter(){ - let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(); + let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest); let mut gossip_timestamp_filter = msgs::GossipTimestampFilter { chain_hash: expected_chain_hash, first_timestamp: 1590000000, timestamp_range: 0xffff_ffff, }; let encoded_value = gossip_timestamp_filter.encode(); - let target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e22065ec57980ffffffff").unwrap(); + let target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f5ec57980ffffffff").unwrap(); assert_eq!(encoded_value, target_value); gossip_timestamp_filter = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); @@ -3952,7 +4238,10 @@ mod tests { // 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[..]); - ::read(&mut rd).unwrap(); + + let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet); + > + ::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, io::Error> { @@ -3978,32 +4267,41 @@ mod tests { #[test] #[cfg(feature = "std")] fn test_socket_address_from_str() { - assert_eq!(SocketAddress::TcpIpV4 { + let tcpip_v4 = SocketAddress::TcpIpV4 { addr: Ipv4Addr::new(127, 0, 0, 1).octets(), port: 1234, - }, SocketAddress::from_str("127.0.0.1:1234").unwrap()); + }; + assert_eq!(tcpip_v4, SocketAddress::from_str("127.0.0.1:1234").unwrap()); + assert_eq!(tcpip_v4, SocketAddress::from_str(&tcpip_v4.to_string()).unwrap()); - assert_eq!(SocketAddress::TcpIpV6 { + let tcpip_v6 = 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 { + }; + assert_eq!(tcpip_v6, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap()); + assert_eq!(tcpip_v6, SocketAddress::from_str(&tcpip_v6.to_string()).unwrap()); + + let hostname = 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 { + }; + assert_eq!(hostname, SocketAddress::from_str("lightning-node.mydomain.com:1234").unwrap()); + assert_eq!(hostname, SocketAddress::from_str(&hostname.to_string()).unwrap()); + + let onion_v2 = SocketAddress::OnionV2 ([40, 4, 64, 185, 202, 19, 162, 75, 90, 200, 38, 7],); + assert_eq!("OnionV2([40, 4, 64, 185, 202, 19, 162, 75, 90, 200, 38, 7])", &onion_v2.to_string()); + assert_eq!(Err(SocketAddressParseError::InvalidOnionV3), SocketAddress::from_str("FACEBOOKCOREWWWI.onion:9735")); + + let onion_v3 = 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!(onion_v3, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap()); + assert_eq!(onion_v3, SocketAddress::from_str(&onion_v3.to_string()).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::()); @@ -4017,4 +4315,22 @@ mod tests { assert!("invalid-address".parse::().is_err()); assert!(SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:1234").is_err()); } + + #[test] + #[cfg(feature = "std")] + fn test_socket_address_to_socket_addrs() { + assert_eq!(SocketAddress::TcpIpV4 {addr:[0u8; 4], port: 1337,}.to_socket_addrs().unwrap().next().unwrap(), + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0,0,0,0), 1337))); + assert_eq!(SocketAddress::TcpIpV6 {addr:[0u8; 16], port: 1337,}.to_socket_addrs().unwrap().next().unwrap(), + SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from([0u8; 16]), 1337, 0, 0))); + assert_eq!(SocketAddress::Hostname { hostname: Hostname::try_from("0.0.0.0".to_string()).unwrap(), port: 0 } + .to_socket_addrs().unwrap().next().unwrap(), SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from([0u8; 4]),0))); + assert!(SocketAddress::OnionV2([0u8; 12]).to_socket_addrs().is_err()); + assert!(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 }.to_socket_addrs().is_err()); + } }