Merge pull request #2935 from valentinewallace/2024-03-keysend-to-blinded
[rust-lightning] / lightning / src / ln / channel_id.rs
index 8df6d75ef5e80b4d4acb2cb39b2358be9c638131..90efe3c94be6ba6c40fe9970b56737ed7a3ba93e 100644 (file)
@@ -9,11 +9,18 @@
 
 //! 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 crate::io;
+use bitcoin::hashes::{
+       Hash as _,
+       HashEngine as _,
+       sha256::Hash as Sha256,
+};
 use core::fmt;
 use core::ops::Deref;
 
@@ -40,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 {
@@ -61,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 {
@@ -84,9 +122,17 @@ 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 crate::prelude::*;
@@ -132,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);
+       }
 }