From: Arik Sosman Date: Tue, 28 Mar 2023 21:37:01 +0000 (-0700) Subject: Introduce MuSig2-related types for Taproot channels. X-Git-Tag: v0.0.115~40^2~6 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=6080ce800499b1847d19f409f18d484958b6fa56;p=rust-lightning Introduce MuSig2-related types for Taproot channels. --- diff --git a/lightning/Cargo.toml b/lightning/Cargo.toml index 3f4f5d435..17896ecb3 100644 --- a/lightning/Cargo.toml +++ b/lightning/Cargo.toml @@ -56,3 +56,6 @@ regex = "1.5.6" version = "0.29.0" default-features = false features = ["bitcoinconsensus", "secp-recovery"] + +[target.'cfg(taproot)'.dependencies] +musig2 = { git = "https://github.com/arik-so/rust-musig2", rev = "27797d7" } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index cb857ed5e..e7d6c27f5 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -51,6 +51,11 @@ use crate::routing::gossip::NodeId; /// 21 million * 10^8 * 1000 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)] +pub struct PartialSignatureWithNonce(pub musig2::types::PartialSignature, pub musig2::types::PublicNonce); + /// An error in decoding a message or struct. #[derive(Clone, Debug, PartialEq, Eq)] pub enum DecodeError { diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 349105266..366e6c8cb 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -38,6 +38,8 @@ use bitcoin::hash_types::{Txid, BlockHash}; use core::marker::Sized; use core::time::Duration; use crate::ln::msgs::DecodeError; +#[cfg(taproot)] +use crate::ln::msgs::PartialSignatureWithNonce; use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret}; use crate::util::byte_utils::{be48_to_array, slice_to_be48}; @@ -574,6 +576,7 @@ impl_array!(16); // for IPv6 impl_array!(32); // for channel id & hmac impl_array!(PUBLIC_KEY_SIZE); // for PublicKey impl_array!(64); // for ecdsa::Signature and schnorr::Signature +impl_array!(66); // for MuSig2 nonces impl_array!(1300); // for OnionPacket.hop_data impl Writeable for [u16; 8] { @@ -861,6 +864,39 @@ impl Readable for SecretKey { } } +#[cfg(taproot)] +impl Writeable for musig2::types::PublicNonce { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.serialize().write(w) + } +} + +#[cfg(taproot)] +impl Readable for musig2::types::PublicNonce { + fn read(r: &mut R) -> Result { + let buf: [u8; PUBLIC_KEY_SIZE * 2] = Readable::read(r)?; + musig2::types::PublicNonce::from_slice(&buf).map_err(|_| DecodeError::InvalidValue) + } +} + +#[cfg(taproot)] +impl Writeable for PartialSignatureWithNonce { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.serialize().write(w)?; + self.1.write(w) + } +} + +#[cfg(taproot)] +impl Readable for PartialSignatureWithNonce { + fn read(r: &mut R) -> Result { + let partial_signature_buf: [u8; SECRET_KEY_SIZE] = Readable::read(r)?; + let partial_signature = musig2::types::PartialSignature::from_slice(&partial_signature_buf).map_err(|_| DecodeError::InvalidValue)?; + let public_nonce: musig2::types::PublicNonce = Readable::read(r)?; + Ok(PartialSignatureWithNonce(partial_signature, public_nonce)) + } +} + impl Writeable for Sha256dHash { fn write(&self, w: &mut W) -> Result<(), io::Error> { w.write_all(&self[..]) @@ -1251,6 +1287,7 @@ impl Readable for Duration { #[cfg(test)] mod tests { use core::convert::TryFrom; + use bitcoin::secp256k1::ecdsa; use crate::util::ser::{Readable, Hostname, Writeable}; #[test] @@ -1273,11 +1310,22 @@ mod tests { assert_eq!(Hostname::read(&mut buf.as_slice()).unwrap().as_str(), "test"); } + #[test] + /// Taproot will likely fill legacy signature fields with all 0s. + /// This test ensures that doing so won't break serialization. + fn null_signature_codec() { + let buffer = vec![0u8; 64]; + let mut cursor = crate::io::Cursor::new(buffer.clone()); + let signature = ecdsa::Signature::read(&mut cursor).unwrap(); + let serialization = signature.serialize_compact(); + assert_eq!(buffer, serialization.to_vec()) + } + #[test] fn bigsize_encoding_decoding() { let values = vec![0, 252, 253, 65535, 65536, 4294967295, 4294967296, 18446744073709551615]; let bytes = vec![ - "00", + "00", "fc", "fd00fd", "fdffff", @@ -1286,7 +1334,7 @@ mod tests { "ff0000000100000000", "ffffffffffffffffff" ]; - for i in 0..=7 { + for i in 0..=7 { let mut stream = crate::io::Cursor::new(::hex::decode(bytes[i]).unwrap()); assert_eq!(super::BigSize::read(&mut stream).unwrap().0, values[i]); let mut stream = super::VecWriter(Vec::new());