Merge pull request #2935 from valentinewallace/2024-03-keysend-to-blinded
[rust-lightning] / lightning / src / ln / channel_id.rs
index 621ebf0c460333446e22d2a28bd7bdc4b724dae8..90efe3c94be6ba6c40fe9970b56737ed7a3ba93e 100644 (file)
@@ -9,19 +9,25 @@
 
 //! ChannelId definition.
 
+use crate::chain::transaction::OutPoint;
+use crate::io;
 use crate::ln::msgs::DecodeError;
 use crate::sign::EntropySource;
 use crate::util::ser::{Readable, Writeable, Writer};
+use super::channel_keys::RevocationBasepoint;
 
-use bitcoin::hashes::hex::ToHex;
-
-use crate::io;
-use crate::prelude::*;
+use bitcoin::hashes::{
+       Hash as _,
+       HashEngine as _,
+       sha256::Hash as Sha256,
+};
 use core::fmt;
 use core::ops::Deref;
 
 /// A unique 32-byte identifier for a channel.
-/// Depending on how the ID is generated, several varieties are distinguished (but all are stored as 32 bytes): _v1_ and _temporary_.
+/// Depending on how the ID is generated, several varieties are distinguished
+/// (but all are stored as 32 bytes):
+///   _v1_ and _temporary_.
 /// A _v1_ channel ID is generated based on funding tx outpoint (txid & index).
 /// A _temporary_ ID is generated randomly.
 /// (Later revocation-point-based _v2_ is a possibility.)
@@ -41,6 +47,11 @@ impl ChannelId {
                Self(res)
        }
 
+       /// Create _v1_ channel ID from a funding tx outpoint
+       pub fn v1_from_funding_outpoint(outpoint: OutPoint) -> Self {
+               Self::v1_from_funding_txid(outpoint.txid.as_byte_array(), outpoint.index)
+       }
+
        /// Create a _temporary_ channel ID randomly, based on an entropy source.
        pub fn temporary_from_entropy_source<ES: Deref>(entropy_source: &ES) -> Self
        where ES::Target: EntropySource {
@@ -62,6 +73,32 @@ impl ChannelId {
        pub fn is_zero(&self) -> bool {
                self.0[..] == [0; 32]
        }
+
+       /// Create _v2_ channel ID by concatenating the holder revocation basepoint with the counterparty
+       /// revocation basepoint and hashing the result. The basepoints will be concatenated in increasing
+       /// sorted order.
+       pub fn v2_from_revocation_basepoints(
+               ours: &RevocationBasepoint,
+               theirs: &RevocationBasepoint,
+       ) -> Self {
+               let ours = ours.0.serialize();
+               let theirs = theirs.0.serialize();
+               let (lesser, greater) = if ours < theirs {
+                       (ours, theirs)
+               } else {
+                       (theirs, ours)
+               };
+               let mut engine = Sha256::engine();
+               engine.input(&lesser[..]);
+               engine.input(&greater[..]);
+               Self(Sha256::from_engine(engine).to_byte_array())
+       }
+
+       /// Create temporary _v2_ channel ID by concatenating a zeroed out basepoint with the holder
+       /// revocation basepoint and hashing the result.
+       pub fn temporary_v2_from_revocation_basepoint(our_revocation_basepoint: &RevocationBasepoint) -> Self {
+               Self(Sha256::hash(&[[0u8; 33], our_revocation_basepoint.0.serialize()].concat()).to_byte_array())
+       }
 }
 
 impl Writeable for ChannelId {
@@ -77,12 +114,6 @@ impl Readable for ChannelId {
        }
 }
 
-impl ToHex for ChannelId {
-       fn to_hex(&self) -> String {
-               self.0.to_hex()
-       }
-}
-
 impl fmt::Display for ChannelId {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                crate::util::logger::DebugBytes(&self.0).fmt(f)
@@ -91,17 +122,26 @@ impl fmt::Display for ChannelId {
 
 #[cfg(test)]
 mod tests {
+       use bitcoin::hashes::{
+               Hash as _,
+               HashEngine as _,
+               hex::FromHex as _,
+               sha256::Hash as Sha256,
+       };
+       use bitcoin::secp256k1::PublicKey;
+       use hex::DisplayHex;
+
        use crate::ln::ChannelId;
+       use crate::ln::channel_keys::RevocationBasepoint;
        use crate::util::ser::{Readable, Writeable};
        use crate::util::test_utils;
-       use bitcoin::hashes::hex::ToHex;
        use crate::prelude::*;
        use crate::io;
 
        #[test]
        fn test_channel_id_v1_from_funding_txid() {
                let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
-               assert_eq!(channel_id.to_hex(), "0202020202020202020202020202020202020202020202020202020202020203");
+               assert_eq!(channel_id.0.as_hex().to_string(), "0202020202020202020202020202020202020202020202020202020202020203");
        }
 
        #[test]
@@ -138,4 +178,29 @@ mod tests {
                let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
                assert_eq!(format!("{}", &channel_id), "0202020202020202020202020202020202020202020202020202020202020203");
        }
+
+       #[test]
+       fn test_channel_id_v2_from_basepoints() {
+               // Ours greater than theirs
+               let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap());
+               let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
+
+               let mut engine = Sha256::engine();
+               engine.input(&theirs.0.serialize());
+               engine.input(&ours.0.serialize());
+               let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
+
+               assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
+
+               // Theirs greater than ours
+               let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap());
+               let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
+
+               let mut engine = Sha256::engine();
+               engine.input(&ours.0.serialize());
+               engine.input(&theirs.0.serialize());
+               let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
+
+               assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
+       }
 }