X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fmsgs.rs;h=9d872a6f53d999ac817cfaf9b6e4aa2a444814d2;hb=fb670c8faae8c1e990496b869e62dfbde10a64f8;hp=b6ec022dc14fed1b62281e3b4358677245f78e33;hpb=131560e08fa4f66b8ce9302cde637f87602c86b0;p=rust-lightning diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index b6ec022d..9d872a6f 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -29,23 +29,34 @@ 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 crate::io::{self, Read}; +use core::ops::Deref; +#[cfg(feature = "std")] +use core::str::FromStr; +#[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::ln::{PaymentPreimage, PaymentHash, PaymentSecret}; +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}; @@ -54,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. /// @@ -85,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, @@ -99,19 +110,19 @@ pub struct 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, + pub remote_network_address: Option, } /// 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. /// /// 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 @@ -123,12 +134,12 @@ 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. /// /// 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 @@ -140,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, @@ -153,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. /// @@ -166,12 +177,12 @@ 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: [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 @@ -220,12 +231,12 @@ 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: [u8; 32], + 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 @@ -279,10 +290,10 @@ 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: [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 @@ -327,10 +338,10 @@ 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: [u8; 32], + 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 @@ -380,10 +391,10 @@ 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: [u8; 32], + pub temporary_channel_id: ChannelId, /// The funding transaction ID pub funding_txid: Txid, /// The specific output index funding this channel @@ -403,10 +414,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The signature of the channel acceptor (fundee) on the initial commitment transaction pub signature: Signature, #[cfg(taproot)] @@ -417,10 +428,10 @@ 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: [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. @@ -430,13 +441,67 @@ 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: [u8; 32], + 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, @@ -452,10 +517,10 @@ 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: [u8; 32], + 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, @@ -468,10 +533,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The serial ID of the input to be removed pub serial_id: u64, } @@ -479,10 +544,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The serial ID of the output to be removed pub serial_id: u64, } @@ -491,20 +556,20 @@ 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: [u8; 32], + 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)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct TxSignatures { /// The channel ID - pub channel_id: [u8; 32], + pub channel_id: ChannelId, /// The TXID pub tx_hash: Txid, /// The list of witnesses @@ -515,10 +580,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The locktime of the transaction pub locktime: u32, /// The feerate of the transaction @@ -532,10 +597,10 @@ 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: [u8; 32], + 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, @@ -544,10 +609,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// Message data pub data: Vec, } @@ -555,10 +620,10 @@ 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: [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, P2TR. @@ -569,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. @@ -582,10 +647,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The proposed total fee for the closing transaction pub fee_satoshis: u64, /// A signature on the closing transaction @@ -598,10 +663,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The HTLC ID pub htlc_id: u64, /// The HTLC value in milli-satoshi @@ -621,20 +686,21 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The HTLC ID pub htlc_id: u64, /// The pre-image of the payment hash, allowing HTLC redemption @@ -644,10 +710,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The HTLC ID pub htlc_id: u64, pub(crate) reason: OnionErrorPacket, @@ -656,10 +722,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The HTLC ID pub htlc_id: u64, pub(crate) sha256_of_onion: [u8; 32], @@ -670,10 +736,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// A signature on the commitment transaction pub signature: Signature, /// Signatures on the HTLC transactions @@ -686,10 +752,10 @@ 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: [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 @@ -702,10 +768,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// Fee rate per 1000-weight of the transaction pub feerate_per_kw: u32, } @@ -713,10 +779,10 @@ 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: [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 @@ -733,10 +799,10 @@ 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: [u8; 32], + pub channel_id: ChannelId, /// The short channel ID pub short_channel_id: u64, /// A signature by the node key @@ -746,17 +812,17 @@ pub struct AnnouncementSignatures { } /// An address which can be used to connect to a remote peer. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum NetAddress { - /// An IPv4 address/port on which the peer is listening. - IPv4 { +#[derive(Clone, Debug, Hash, 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 @@ -789,28 +855,28 @@ pub enum NetAddress { port: u16, }, } -impl NetAddress { +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 }, } } @@ -820,31 +886,31 @@ impl NetAddress { pub(crate) const MAX_LEN: u16 = 258; } -impl Writeable for NetAddress { +impl Writeable for SocketAddress { fn write(&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)?; @@ -854,25 +920,25 @@ impl Writeable for NetAddress { } } -impl Readable for Result { - fn read(reader: &mut R) -> Result, DecodeError> { +impl Readable for Result { + fn read(reader: &mut R) -> Result, DecodeError> { let byte = ::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)?, @@ -880,7 +946,7 @@ impl Readable for Result { })) }, 5 => { - Ok(Ok(NetAddress::Hostname { + Ok(Ok(SocketAddress::Hostname { hostname: Readable::read(reader)?, port: Readable::read(reader)?, })) @@ -890,8 +956,8 @@ impl Readable for Result { } } -impl Readable for NetAddress { - fn read(reader: &mut R) -> Result { +impl Readable for SocketAddress { + fn read(reader: &mut R) -> Result { match Readable::read(reader) { Ok(Ok(res)) => Ok(res), Ok(Err(_)) => Err(DecodeError::UnknownVersion), @@ -900,6 +966,167 @@ impl Readable for NetAddress { } } +/// [`SocketAddress`] error variants +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum SocketAddressParseError { + /// Socket address (IPv4/IPv6) parsing error + SocketAddrParse, + /// Invalid input format + InvalidInput, + /// Invalid port + InvalidPort, + /// Invalid onion v3 address + InvalidOnionV3, +} + +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: \":\", \"[]:\", \".onion:\" or \":\""), + SocketAddressParseError::InvalidPort => write!(f, "Invalid port"), + SocketAddressParseError::InvalidOnionV3 => write!(f, "Invalid onion v3 address"), + } + } +} + +#[cfg(feature = "std")] +impl From for SocketAddress { + fn from(addr: std::net::SocketAddrV4) -> Self { + SocketAddress::TcpIpV4 { addr: addr.ip().octets(), port: addr.port() } + } +} + +#[cfg(feature = "std")] +impl From for SocketAddress { + fn from(addr: std::net::SocketAddrV6) -> Self { + SocketAddress::TcpIpV6 { addr: addr.ip().octets(), port: addr.port() } + } +} + +#[cfg(feature = "std")] +impl From 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(), + } + } +} + +#[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 { + 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); + } +} + +impl Display for SocketAddress { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> 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; + + fn from_str(s: &str) -> Result { + 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. @@ -923,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, @@ -939,11 +1166,11 @@ pub struct UnsignedNodeAnnouncement { /// 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, + pub addresses: Vec, 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 @@ -957,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 @@ -982,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, @@ -999,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 @@ -1037,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, @@ -1051,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 @@ -1072,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 @@ -1097,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, } @@ -1111,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, @@ -1125,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 @@ -1144,7 +1371,7 @@ enum EncodingType { } /// Used to put an error message in a [`LightningError`]. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Hash, PartialEq)] pub enum ErrorAction { /// The peer took some action which made us think they were useless. Disconnect them. DisconnectPeer { @@ -1193,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, @@ -1230,12 +1457,24 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider { /// Handle an incoming `channel_ready` message from the given peer. fn handle_channel_ready(&self, their_node_id: &PublicKey, msg: &ChannelReady); - // Channl close: + // 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); + // 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); @@ -1310,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. @@ -1387,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. /// @@ -1398,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); @@ -1417,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}; @@ -1445,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 { @@ -1462,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 { @@ -1475,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 { @@ -1500,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... @@ -1614,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, @@ -1774,7 +2056,7 @@ impl Readable for Init { fn read(r: &mut R) -> Result { let global_features: InitFeatures = Readable::read(r)?; let features: InitFeatures = Readable::read(r)?; - let mut remote_network_address: Option = None; + let mut remote_network_address: Option = None; let mut networks: Option>> = None; decode_tlv_stream!(r, { (1, networks, option), @@ -1987,10 +2269,8 @@ impl Writeable for OutboundOnionPayload { // 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 preimage = if let Some(ref preimage) = keysend_preimage { - Some((5482373484, preimage.encode())) - } else { None }; - let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter().chain(preimage.iter()).collect(); + let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); + let mut custom_tlvs: Vec<&(u64, Vec)> = 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), @@ -1999,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 { @@ -2032,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); @@ -2051,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)?; @@ -2275,7 +2599,7 @@ impl Readable for UnsignedNodeAnnouncement { let alias: NodeAlias = Readable::read(r)?; let addr_len: u16 = Readable::read(r)?; - let mut addresses: Vec = Vec::new(); + let mut addresses: Vec = Vec::new(); let mut addr_readpos = 0; let mut excess = false; let mut excess_byte = 0; @@ -2334,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)?; @@ -2410,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)?; @@ -2474,32 +2798,39 @@ impl_writeable_msg!(GossipTimestampFilter, { #[cfg(test)] mod tests { - use bitcoin::blockdata::constants::ChainHash; + use std::convert::TryFrom; use bitcoin::{Transaction, PackedLockTime, TxIn, Script, Sequence, Witness, TxOut}; use hex; 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, 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}; use crate::io::{self, Cursor}; use crate::prelude::*; - use core::convert::TryFrom; use core::str::FromStr; - use crate::chain::transaction::OutPoint; + #[cfg(feature = "std")] + use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; + #[cfg(feature = "std")] + use crate::ln::msgs::SocketAddressParseError; + #[test] fn encoding_channel_reestablish() { let public_key = { @@ -2508,7 +2839,7 @@ mod tests { }; 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, your_last_per_commitment_secret: [9;32], @@ -2537,7 +2868,7 @@ mod tests { }; 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, your_last_per_commitment_secret: [9;32], @@ -2589,7 +2920,7 @@ mod tests { 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, @@ -2615,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), @@ -2637,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()); @@ -2665,24 +2996,24 @@ mod tests { }; 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, @@ -2690,7 +3021,7 @@ mod tests { }); } if hostname { - addresses.push(msgs::NetAddress::Hostname { + addresses.push(SocketAddress::Hostname { hostname: Hostname::try_from(String::from("host")).unwrap(), port: 9735, }); @@ -2766,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 }, @@ -2783,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()); @@ -2824,8 +3155,8 @@ 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(), - temporary_channel_id: [2; 32], + chain_hash: ChainHash::using_genesis_block(Network::Bitcoin), + temporary_channel_id: ChannelId::from_bytes([2; 32]), funding_satoshis: 1311768467284833366, push_msat: 2536655962884945560, dust_limit_satoshis: 3608586615801332854, @@ -2847,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()); @@ -2885,8 +3216,8 @@ 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(), - temporary_channel_id: [2; 32], + 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, funding_satoshis: 1311768467284833366, @@ -2910,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()); @@ -2976,7 +3307,7 @@ mod tests { 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, @@ -3019,7 +3350,7 @@ mod tests { 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: [2; 32], + temporary_channel_id: ChannelId::from_bytes([2; 32]), funding_satoshis: 1311768467284833366, dust_limit_satoshis: 1311768467284833366, max_htlc_value_in_flight_msat: 2536655962884945560, @@ -3073,7 +3404,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 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, @@ -3093,7 +3424,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 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, @@ -3108,7 +3439,7 @@ mod tests { 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, }; @@ -3117,10 +3448,59 @@ 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 { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), serial_id: 4886718345, prevtx: TransactionU16LenLimited::new(Transaction { version: 2, @@ -3155,7 +3535,7 @@ mod tests { #[test] fn encoding_tx_add_output() { let tx_add_output = msgs::TxAddOutput { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), serial_id: 4886718345, sats: 4886718345, script: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().script_pubkey(), @@ -3168,7 +3548,7 @@ mod tests { #[test] fn encoding_tx_remove_input() { let tx_remove_input = msgs::TxRemoveInput { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), serial_id: 4886718345, }; let encoded_value = tx_remove_input.encode(); @@ -3179,7 +3559,7 @@ mod tests { #[test] fn encoding_tx_remove_output() { let tx_remove_output = msgs::TxRemoveOutput { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), serial_id: 4886718345, }; let encoded_value = tx_remove_output.encode(); @@ -3190,7 +3570,7 @@ mod tests { #[test] fn encoding_tx_complete() { let tx_complete = msgs::TxComplete { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), }; let encoded_value = tx_complete.encode(); let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); @@ -3200,7 +3580,7 @@ mod tests { #[test] fn encoding_tx_signatures() { let tx_signatures = msgs::TxSignatures { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), tx_hash: Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(), witnesses: vec![ Witness::from_vec(vec![ @@ -3234,7 +3614,7 @@ mod tests { fn do_encoding_tx_init_rbf(funding_value_with_hex_target: Option<(i64, &str)>) { let tx_init_rbf = msgs::TxInitRbf { - channel_id: [2; 32], + 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 }, @@ -3260,7 +3640,7 @@ mod tests { fn do_encoding_tx_ack_rbf(funding_value_with_hex_target: Option<(i64, &str)>) { let tx_ack_rbf = msgs::TxAckRbf { - channel_id: [2; 32], + 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(); @@ -3283,7 +3663,7 @@ mod tests { #[test] fn encoding_tx_abort() { let tx_abort = msgs::TxAbort { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), data: hex::decode("54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E").unwrap(), }; let encoded_value = tx_abort.encode(); @@ -3296,12 +3676,12 @@ mod tests { 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(); @@ -3331,7 +3711,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 closing_signed = msgs::ClosingSigned { - channel_id: [2; 32], + channel_id: ChannelId::from_bytes([2; 32]), fee_satoshis: 2316138423780173, signature: sig_1, fee_range: None, @@ -3342,7 +3722,7 @@ mod tests { 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 { @@ -3368,7 +3748,7 @@ mod tests { 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]), @@ -3384,7 +3764,7 @@ mod tests { #[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]), }; @@ -3399,7 +3779,7 @@ mod tests { 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 }; @@ -3411,7 +3791,7 @@ mod tests { #[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 @@ -3432,7 +3812,7 @@ mod tests { 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)] @@ -3459,7 +3839,7 @@ mod tests { 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)] @@ -3473,7 +3853,7 @@ mod tests { #[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(); @@ -3483,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]), @@ -3506,7 +3886,7 @@ mod tests { }.encode(), hex::decode("00000000014001010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202").unwrap()); let init_msg = msgs::Init { features: InitFeatures::from_le_bytes(vec![]), networks: Some(vec![mainnet_hash]), - remote_network_address: Some(msgs::NetAddress::IPv4 { + remote_network_address: Some(SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 1000, }), @@ -3520,7 +3900,7 @@ mod tests { #[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(); @@ -3531,7 +3911,7 @@ mod tests { #[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(); @@ -3571,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); @@ -3593,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!(); } @@ -3618,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, @@ -3653,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]), @@ -3662,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!(), @@ -3686,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, @@ -3712,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, }; @@ -3723,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(); @@ -3743,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, @@ -3780,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], @@ -3806,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(); @@ -3822,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(); @@ -3849,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> { @@ -3871,4 +4263,74 @@ mod tests { } Ok(encoded_payload) } + + #[test] + #[cfg(feature = "std")] + fn test_socket_address_from_str() { + let tcpip_v4 = SocketAddress::TcpIpV4 { + addr: Ipv4Addr::new(127, 0, 0, 1).octets(), + port: 1234, + }; + 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()); + + let tcpip_v6 = SocketAddress::TcpIpV6 { + addr: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).octets(), + port: 1234, + }; + 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, + }; + 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 + }; + 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::()); + 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::().is_err()); + assert!("localhost:invalid-port".parse::().is_err()); + assert!( "invalid-onion-v3-hostname.onion:8080".parse::().is_err()); + assert!("b32.example.onion:invalid-port".parse::().is_err()); + 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()); + } }