From 6080ce800499b1847d19f409f18d484958b6fa56 Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 28 Mar 2023 14:37:01 -0700 Subject: [PATCH 1/7] Introduce MuSig2-related types for Taproot channels. --- lightning/Cargo.toml | 3 +++ lightning/src/ln/msgs.rs | 5 ++++ lightning/src/util/ser.rs | 52 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) 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()); From 64a800720e63c17910a524ab72dd9dedc05d3345 Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 28 Mar 2023 14:59:20 -0700 Subject: [PATCH 2/7] Update the `AcceptChannel` message for Taproot support. --- lightning/src/ln/channel.rs | 2 ++ lightning/src/ln/msgs.rs | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index f1c5cae25..efb7da415 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -5332,6 +5332,8 @@ impl Channel { None => Builder::new().into_script(), }), channel_type: Some(self.channel_type.clone()), + #[cfg(taproot)] + next_local_nonce: None, } } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index e7d6c27f5..38c832564 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -249,6 +249,9 @@ pub struct AcceptChannel { /// our feature bits with our counterparty's feature bits from the [`Init`] message. /// This is required to match the equivalent field in [`OpenChannel::channel_type`]. pub channel_type: Option, + #[cfg(taproot)] + /// Next nonce the channel initiator should use to create a funding output signature against + pub next_local_nonce: Option, } /// A [`funding_created`] message to be sent to or received from a peer. @@ -1293,7 +1296,7 @@ impl Readable for OptionalField { } } - +#[cfg(not(taproot))] impl_writeable_msg!(AcceptChannel, { temporary_channel_id, dust_limit_satoshis, @@ -1314,6 +1317,28 @@ impl_writeable_msg!(AcceptChannel, { (1, channel_type, option), }); +#[cfg(taproot)] +impl_writeable_msg!(AcceptChannel, { + temporary_channel_id, + dust_limit_satoshis, + max_htlc_value_in_flight_msat, + channel_reserve_satoshis, + htlc_minimum_msat, + minimum_depth, + to_self_delay, + max_accepted_htlcs, + funding_pubkey, + revocation_basepoint, + payment_point, + delayed_payment_basepoint, + htlc_basepoint, + first_per_commitment_point, + shutdown_scriptpubkey +}, { + (1, channel_type, option), + (4, next_local_nonce, option), +}); + impl_writeable_msg!(AnnouncementSignatures, { channel_id, short_channel_id, @@ -2449,6 +2474,8 @@ mod tests { first_per_commitment_point: pubkey_6, shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent }, channel_type: None, + #[cfg(taproot)] + next_local_nonce: None, }; let encoded_value = accept_channel.encode(); let mut target_value = hex::decode("020202020202020202020202020202020202020202020202020202020202020212345678901234562334032891223698321446687011447600083a840000034d000c89d4c0bcc0bc031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d076602531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33703462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f703f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap(); From 0219a8def0ff422b77f7fe4024aa5862435fe23a Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 28 Mar 2023 17:33:45 -0700 Subject: [PATCH 3/7] Update the `FundingCreated` message for Taproot support. --- lightning/src/ln/channel.rs | 6 +++++- lightning/src/ln/msgs.rs | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index efb7da415..e413ab641 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -5398,7 +5398,11 @@ impl Channel { temporary_channel_id, funding_txid: funding_txo.txid, funding_output_index: funding_txo.index, - signature + signature, + #[cfg(taproot)] + partial_signature_with_nonce: None, + #[cfg(taproot)] + next_local_nonce: None, }) } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 38c832564..fa6c84f18 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -267,6 +267,12 @@ pub struct FundingCreated { pub funding_output_index: u16, /// The signature of the channel initiator (funder) on the initial commitment transaction pub signature: Signature, + #[cfg(taproot)] + /// The partial signature of the channel initiator (funder) + pub partial_signature_with_nonce: Option, + #[cfg(taproot)] + /// Next nonce the channel acceptor should use to finalize the funding output signature + pub next_local_nonce: Option } /// A [`funding_signed`] message to be sent to or received from a peer. @@ -1405,12 +1411,23 @@ impl_writeable!(DecodedOnionErrorPacket, { pad }); +#[cfg(not(taproot))] impl_writeable_msg!(FundingCreated, { temporary_channel_id, funding_txid, funding_output_index, signature }, {}); +#[cfg(taproot)] +impl_writeable_msg!(FundingCreated, { + temporary_channel_id, + funding_txid, + funding_output_index, + signature +}, { + (2, partial_signature_with_nonce, option), + (4, next_local_nonce, option) +}); impl_writeable_msg!(FundingSigned, { channel_id, @@ -2501,6 +2518,10 @@ mod tests { funding_txid: Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(), funding_output_index: 255, signature: sig_1, + #[cfg(taproot)] + partial_signature_with_nonce: None, + #[cfg(taproot)] + next_local_nonce: None, }; let encoded_value = funding_created.encode(); let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202026e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c200ffd977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap(); From 5c79c8e2b83c2b24e08142f739a779acd0b3f57e Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Wed, 29 Mar 2023 15:02:34 -0700 Subject: [PATCH 4/7] Update the `FundingSigned` message for Taproot support. --- lightning/src/ln/channel.rs | 4 +++- lightning/src/ln/msgs.rs | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index e413ab641..43604c191 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -2352,7 +2352,9 @@ impl Channel { Ok((msgs::FundingSigned { channel_id: self.channel_id, - signature + signature, + #[cfg(taproot)] + partial_signature_with_nonce: None, }, channel_monitor)) } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index fa6c84f18..5e51fc0a9 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -284,6 +284,9 @@ pub struct FundingSigned { pub channel_id: [u8; 32], /// The signature of the channel acceptor (fundee) on the initial commitment transaction pub signature: Signature, + #[cfg(taproot)] + /// The partial signature of the channel acceptor (fundee) + pub partial_signature_with_nonce: Option, } /// A [`channel_ready`] message to be sent to or received from a peer. @@ -1429,11 +1432,20 @@ impl_writeable_msg!(FundingCreated, { (4, next_local_nonce, option) }); +#[cfg(not(taproot))] impl_writeable_msg!(FundingSigned, { channel_id, signature }, {}); +#[cfg(taproot)] +impl_writeable_msg!(FundingSigned, { + channel_id, + signature +}, { + (2, partial_signature_with_nonce, option) +}); + impl_writeable_msg!(ChannelReady, { channel_id, next_per_commitment_point, @@ -2536,6 +2548,8 @@ mod tests { let funding_signed = msgs::FundingSigned { channel_id: [2; 32], signature: sig_1, + #[cfg(taproot)] + partial_signature_with_nonce: None, }; let encoded_value = funding_signed.encode(); let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap(); From fe25bbb44ea879e6b84e0cd6f62fd53058d77417 Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Wed, 29 Mar 2023 15:21:30 -0700 Subject: [PATCH 5/7] Update the `CommitmentSigned` message for Taproot support. --- lightning/src/ln/channel.rs | 2 ++ lightning/src/ln/functional_tests.rs | 8 ++++++-- lightning/src/ln/msgs.rs | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 43604c191..e2b3fb066 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -5923,6 +5923,8 @@ impl Channel { channel_id: self.channel_id, signature, htlc_signatures, + #[cfg(taproot)] + partial_signature_with_nonce: None, }, (counterparty_commitment_txid, commitment_stats.htlcs_included))) } diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 56586b0a2..af547b3af 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -735,7 +735,9 @@ fn test_update_fee_that_funder_cannot_afford() { let commit_signed_msg = msgs::CommitmentSigned { channel_id: chan.2, signature: res.0, - htlc_signatures: res.1 + htlc_signatures: res.1, + #[cfg(taproot)] + partial_signature_with_nonce: None, }; let update_fee = msgs::UpdateFee { @@ -1455,7 +1457,9 @@ fn test_fee_spike_violation_fails_htlc() { let commit_signed_msg = msgs::CommitmentSigned { channel_id: chan.2, signature: res.0, - htlc_signatures: res.1 + htlc_signatures: res.1, + #[cfg(taproot)] + partial_signature_with_nonce: None, }; // Send the commitment_signed message to the nodes[1]. diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 5e51fc0a9..61e18648a 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -426,6 +426,9 @@ pub struct CommitmentSigned { pub signature: Signature, /// Signatures on the HTLC transactions pub htlc_signatures: Vec, + #[cfg(taproot)] + /// The partial Taproot signature on the commitment transaction + pub partial_signature_with_nonce: Option, } /// A [`revoke_and_ack`] message to be sent to or received from a peer. @@ -1402,12 +1405,22 @@ impl_writeable!(ClosingSignedFeeRange, { max_fee_satoshis }); +#[cfg(not(taproot))] impl_writeable_msg!(CommitmentSigned, { channel_id, signature, htlc_signatures }, {}); +#[cfg(taproot)] +impl_writeable_msg!(CommitmentSigned, { + channel_id, + signature, + htlc_signatures +}, { + (2, partial_signature_with_nonce, option) +}); + impl_writeable!(DecodedOnionErrorPacket, { hmac, failuremsg, @@ -2713,6 +2726,8 @@ mod tests { channel_id: [2; 32], signature: sig_1, htlc_signatures: if htlcs { vec![sig_2, sig_3, sig_4] } else { Vec::new() }, + #[cfg(taproot)] + partial_signature_with_nonce: None, }; let encoded_value = commitment_signed.encode(); let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap(); From 15dbe55e676e858ddfde774c9e24a2ede4421ec5 Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Wed, 29 Mar 2023 16:35:05 -0700 Subject: [PATCH 6/7] Update the `RevokeAndACK` message for Taproot support. --- lightning/src/ln/channel.rs | 2 ++ lightning/src/ln/functional_tests.rs | 12 ++++++++++-- lightning/src/ln/msgs.rs | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index e2b3fb066..39e07bed8 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -3910,6 +3910,8 @@ impl Channel { channel_id: self.channel_id, per_commitment_secret, next_per_commitment_point, + #[cfg(taproot)] + next_local_nonce: None, } } diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index af547b3af..c77195cfa 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -1470,7 +1470,9 @@ fn test_fee_spike_violation_fails_htlc() { let raa_msg = msgs::RevokeAndACK { channel_id: chan.2, per_commitment_secret: local_secret, - next_per_commitment_point: next_local_point + next_per_commitment_point: next_local_point, + #[cfg(taproot)] + next_local_nonce: None, }; nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &raa_msg); @@ -7498,7 +7500,13 @@ fn test_counterparty_raa_skip_no_crash() { } nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), - &msgs::RevokeAndACK { channel_id, per_commitment_secret, next_per_commitment_point }); + &msgs::RevokeAndACK { + channel_id, + per_commitment_secret, + next_per_commitment_point, + #[cfg(taproot)] + next_local_nonce: None, + }); assert_eq!(check_closed_broadcast!(nodes[1], true).unwrap().data, "Received an unexpected revoke_and_ack"); check_added_monitors!(nodes[1], 1); check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "Received an unexpected revoke_and_ack".to_string() }); diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 61e18648a..2557454b4 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -442,6 +442,9 @@ pub struct RevokeAndACK { pub per_commitment_secret: [u8; 32], /// The next sender-broadcast commitment transaction's per-commitment point pub next_per_commitment_point: PublicKey, + #[cfg(taproot)] + /// Musig nonce the recipient should use in their next commitment signature message + pub next_local_nonce: Option } /// An [`update_fee`] message to be sent to or received from a peer @@ -1518,12 +1521,22 @@ impl_writeable_msg!(OpenChannel, { (1, channel_type, option), }); +#[cfg(not(taproot))] impl_writeable_msg!(RevokeAndACK, { channel_id, per_commitment_secret, next_per_commitment_point }, {}); +#[cfg(taproot)] +impl_writeable_msg!(RevokeAndACK, { + channel_id, + per_commitment_secret, + next_per_commitment_point +}, { + (4, next_local_nonce, option) +}); + impl_writeable_msg!(Shutdown, { channel_id, scriptpubkey @@ -2753,6 +2766,8 @@ mod tests { channel_id: [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)] + next_local_nonce: None, }; let encoded_value = raa.encode(); let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020101010101010101010101010101010101010101010101010101010101010101031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap(); From bc97b82de4a6162485519c7dbb776c12e5a1174e Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Thu, 30 Mar 2023 22:59:21 -0700 Subject: [PATCH 7/7] Add Taproot CI test. --- .github/workflows/build.yml | 9 +++++++++ ci/ci-tests.sh | 2 ++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 72220d831..a08549c0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -180,6 +180,15 @@ jobs: cargo check --no-default-features --features=futures --release cargo doc --release RUSTDOCFLAGS="--cfg=anchors" cargo doc --release + - name: Run cargo check for Taproot build. + run: | + cargo check --release + cargo check --no-default-features --features=no-std --release + cargo check --no-default-features --features=futures --release + cargo doc --release + env: + RUSTFLAGS: '--cfg=anchors --cfg=taproot' + RUSTDOCFLAGS: '--cfg=anchors --cfg=taproot' fuzz: runs-on: ubuntu-latest diff --git a/ci/ci-tests.sh b/ci/ci-tests.sh index b2de8bc4a..01ef631f7 100755 --- a/ci/ci-tests.sh +++ b/ci/ci-tests.sh @@ -94,4 +94,6 @@ fi echo -e "\n\nTest anchors builds" pushd lightning RUSTFLAGS="$RUSTFLAGS --cfg=anchors" cargo test --verbose --color always -p lightning +echo -e "\n\nTest Taproot builds" +RUSTFLAGS="$RUSTFLAGS --cfg=anchors --cfg=taproot" cargo test --verbose --color always -p lightning popd