lightning/ln/
types.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Various wrapper types (most around 32-byte arrays) for use in lightning.
11
12use crate::chain::transaction::OutPoint;
13use crate::io;
14use crate::ln::msgs::DecodeError;
15use crate::sign::EntropySource;
16use crate::util::ser::{Readable, Writeable, Writer};
17use super::channel_keys::RevocationBasepoint;
18
19#[allow(unused_imports)]
20use crate::prelude::*;
21
22use bitcoin::hashes::{
23	Hash as _,
24	HashEngine as _,
25	sha256::Hash as Sha256,
26};
27use bitcoin::hex::display::impl_fmt_traits;
28use core::borrow::Borrow;
29use core::ops::Deref;
30
31/// A unique 32-byte identifier for a channel.
32/// Depending on how the ID is generated, several varieties are distinguished
33/// (but all are stored as 32 bytes):
34///   _v1_ and _temporary_.
35/// A _v1_ channel ID is generated based on funding tx outpoint (txid & index).
36/// A _temporary_ ID is generated randomly.
37/// (Later revocation-point-based _v2_ is a possibility.)
38/// The variety (context) is not stored, it is relevant only at creation.
39#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
40pub struct ChannelId(pub [u8; 32]);
41
42impl ChannelId {
43	/// Create _v1_ channel ID based on a funding TX ID and output index
44	pub fn v1_from_funding_txid(txid: &[u8; 32], output_index: u16) -> Self {
45		let mut res = [0; 32];
46		res[..].copy_from_slice(&txid[..]);
47		res[30] ^= ((output_index >> 8) & 0xff) as u8;
48		res[31] ^= ((output_index >> 0) & 0xff) as u8;
49		Self(res)
50	}
51
52	/// Create _v1_ channel ID from a funding tx outpoint
53	pub fn v1_from_funding_outpoint(outpoint: OutPoint) -> Self {
54		Self::v1_from_funding_txid(outpoint.txid.as_byte_array(), outpoint.index)
55	}
56
57	/// Create a _temporary_ channel ID randomly, based on an entropy source.
58	pub fn temporary_from_entropy_source<ES: Deref>(entropy_source: &ES) -> Self
59	where ES::Target: EntropySource {
60		Self(entropy_source.get_secure_random_bytes())
61	}
62
63	/// Generic constructor; create a new channel ID from the provided data.
64	/// Use a more specific `*_from_*` constructor when possible.
65	pub fn from_bytes(data: [u8; 32]) -> Self {
66		Self(data)
67	}
68
69	/// Create a channel ID consisting of all-zeros data (e.g. when uninitialized or a placeholder).
70	pub fn new_zero() -> Self {
71		Self([0; 32])
72	}
73
74	/// Check whether ID is consisting of all zeros (uninitialized)
75	pub fn is_zero(&self) -> bool {
76		self.0[..] == [0; 32]
77	}
78
79	/// Create _v2_ channel ID by concatenating the holder revocation basepoint with the counterparty
80	/// revocation basepoint and hashing the result. The basepoints will be concatenated in increasing
81	/// sorted order.
82	pub fn v2_from_revocation_basepoints(
83		ours: &RevocationBasepoint,
84		theirs: &RevocationBasepoint,
85	) -> Self {
86		let ours = ours.0.serialize();
87		let theirs = theirs.0.serialize();
88		let (lesser, greater) = if ours < theirs {
89			(ours, theirs)
90		} else {
91			(theirs, ours)
92		};
93		let mut engine = Sha256::engine();
94		engine.input(&lesser[..]);
95		engine.input(&greater[..]);
96		Self(Sha256::from_engine(engine).to_byte_array())
97	}
98
99	/// Create temporary _v2_ channel ID by concatenating a zeroed out basepoint with the holder
100	/// revocation basepoint and hashing the result.
101	pub fn temporary_v2_from_revocation_basepoint(our_revocation_basepoint: &RevocationBasepoint) -> Self {
102		Self(Sha256::hash(&[[0u8; 33], our_revocation_basepoint.0.serialize()].concat()).to_byte_array())
103	}
104}
105
106impl Writeable for ChannelId {
107	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
108		self.0.write(w)
109	}
110}
111
112impl Readable for ChannelId {
113	fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
114		let buf: [u8; 32] = Readable::read(r)?;
115		Ok(ChannelId(buf))
116	}
117}
118
119impl Borrow<[u8]> for ChannelId {
120	fn borrow(&self) -> &[u8] {
121		&self.0[..]
122	}
123}
124
125impl_fmt_traits! {
126	impl fmt_traits for ChannelId {
127		const LENGTH: usize = 32;
128	}
129}
130
131#[cfg(test)]
132mod tests {
133	use bitcoin::hashes::{
134		Hash as _,
135		HashEngine as _,
136		hex::FromHex as _,
137		sha256::Hash as Sha256,
138	};
139	use bitcoin::secp256k1::PublicKey;
140	use bitcoin::hex::DisplayHex;
141
142	use super::ChannelId;
143	use crate::ln::channel_keys::RevocationBasepoint;
144	use crate::util::ser::{Readable, Writeable};
145	use crate::util::test_utils;
146	use crate::prelude::*;
147	use crate::io;
148
149	#[test]
150	fn test_channel_id_v1_from_funding_txid() {
151		let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
152		assert_eq!(channel_id.0.as_hex().to_string(), "0202020202020202020202020202020202020202020202020202020202020203");
153	}
154
155	#[test]
156	fn test_channel_id_new_from_data() {
157		let data: [u8; 32] = [2; 32];
158		let channel_id = ChannelId::from_bytes(data.clone());
159		assert_eq!(channel_id.0, data);
160	}
161
162	#[test]
163	fn test_channel_id_equals() {
164		let channel_id11 = ChannelId::v1_from_funding_txid(&[2; 32], 2);
165		let channel_id12 = ChannelId::v1_from_funding_txid(&[2; 32], 2);
166		let channel_id21 = ChannelId::v1_from_funding_txid(&[2; 32], 42);
167		assert_eq!(channel_id11, channel_id12);
168		assert_ne!(channel_id11, channel_id21);
169	}
170
171	#[test]
172	fn test_channel_id_write_read() {
173		let data: [u8; 32] = [2; 32];
174		let channel_id = ChannelId::from_bytes(data.clone());
175
176		let mut w = test_utils::TestVecWriter(Vec::new());
177		channel_id.write(&mut w).unwrap();
178
179		let channel_id_2 = ChannelId::read(&mut io::Cursor::new(&w.0)).unwrap();
180		assert_eq!(channel_id_2, channel_id);
181		assert_eq!(channel_id_2.0, data);
182	}
183
184	#[test]
185	fn test_channel_id_display() {
186		let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
187		assert_eq!(format!("{}", &channel_id), "0202020202020202020202020202020202020202020202020202020202020203");
188	}
189
190	#[test]
191	fn test_channel_id_v2_from_basepoints() {
192		// Ours greater than theirs
193		let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap());
194		let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
195
196		let mut engine = Sha256::engine();
197		engine.input(&theirs.0.serialize());
198		engine.input(&ours.0.serialize());
199		let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
200
201		assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
202
203		// Theirs greater than ours
204		let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap());
205		let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
206
207		let mut engine = Sha256::engine();
208		engine.input(&ours.0.serialize());
209		engine.input(&theirs.0.serialize());
210		let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
211
212		assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
213	}
214}