diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8cfe44dfe4..2190dd55c4b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -287,3 +287,5 @@ jobs: rustup component add rustfmt - name: Run rustfmt checks run: cargo fmt --check + - name: Run rustfmt checks on lightning-tests + run: cd lightning-tests && cargo fmt --check diff --git a/ext-functional-test-demo/src/main.rs b/ext-functional-test-demo/src/main.rs index 943bacf85d4..654cf91e01c 100644 --- a/ext-functional-test-demo/src/main.rs +++ b/ext-functional-test-demo/src/main.rs @@ -16,7 +16,7 @@ mod tests { impl TestSignerFactory for BrokenSignerFactory { fn make_signer( - &self, _seed: &[u8; 32], _now: Duration, + &self, _seed: &[u8; 32], _now: Duration, _v2_remote_key_derivation: bool, ) -> Box> { panic!() } diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 40a840eb164..ca03139a433 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -382,14 +382,14 @@ impl SignerProvider for KeyProvider { } fn derive_channel_signer(&self, channel_keys_id: [u8; 32]) -> Self::EcdsaSigner { - let secp_ctx = Secp256k1::signing_only(); let id = channel_keys_id[0]; #[rustfmt::skip] let keys = InMemorySigner::new( - &secp_ctx, SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, self.node_secret[31]]).unwrap(), SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, self.node_secret[31]]).unwrap(), SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, self.node_secret[31]]).unwrap(), + SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, self.node_secret[31]]).unwrap(), + true, SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, self.node_secret[31]]).unwrap(), SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, self.node_secret[31]]).unwrap(), [id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, self.node_secret[31]], diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index ee5f4572eb2..9f961bc814c 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -458,7 +458,6 @@ impl SignerProvider for KeyProvider { } fn derive_channel_signer(&self, keys_id: [u8; 32]) -> Self::EcdsaSigner { - let secp_ctx = Secp256k1::signing_only(); let ctr = keys_id[0]; let (inbound, state) = self.signer_state.borrow().get(&ctr).unwrap().clone(); @@ -477,7 +476,7 @@ impl SignerProvider for KeyProvider { e = SecretKey::from_slice(&key).unwrap(); key[30] = 6 + if inbound { 0 } else { 6 }; f = key; - let signer = InMemorySigner::new(&secp_ctx, a, b, c, d, e, f, keys_id, keys_id); + let signer = InMemorySigner::new(a, b, c, c, true, d, e, f, keys_id, keys_id); TestChannelSigner::new_with_revoked(DynSigner::new(signer), state, false, false) } diff --git a/fuzz/src/lsps_message.rs b/fuzz/src/lsps_message.rs index 2bc83c3fcd6..8dfa92221be 100644 --- a/fuzz/src/lsps_message.rs +++ b/fuzz/src/lsps_message.rs @@ -39,7 +39,7 @@ pub fn do_test(data: &[u8]) { let scorer = Arc::new(LockingWrapper::new(TestScorer::new())); let now = Duration::from_secs(genesis_block.header.time as u64); let seed = sha256::Hash::hash(b"lsps-message-seed").to_byte_array(); - let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos())); + let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos(), true)); let router = Arc::new(DefaultRouter::new( Arc::clone(&network_graph), Arc::clone(&logger), diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 44ce52b8291..dc6fbc7f320 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -2313,7 +2313,8 @@ mod tests { let scorer = Arc::new(LockingWrapper::new(TestScorer::new())); let now = Duration::from_secs(genesis_block.header.time as u64); let seed = [i as u8; 32]; - let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos())); + let keys_manager = + Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos(), true)); let router = Arc::new(DefaultRouter::new( Arc::clone(&network_graph), Arc::clone(&logger), @@ -2329,7 +2330,8 @@ mod tests { let kv_store = Arc::new(Persister::new(format!("{}_persister_{}", &persist_dir, i).into())); let now = Duration::from_secs(genesis_block.header.time as u64); - let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos())); + let keys_manager = + Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos(), true)); let chain_monitor = Arc::new(chainmonitor::ChainMonitor::new( Some(Arc::clone(&chain_source)), Arc::clone(&tx_broadcaster), diff --git a/lightning-dns-resolver/src/lib.rs b/lightning-dns-resolver/src/lib.rs index 75fe06fcc9a..f5b1d53fc8a 100644 --- a/lightning-dns-resolver/src/lib.rs +++ b/lightning-dns-resolver/src/lib.rs @@ -231,7 +231,7 @@ mod test { &self, recipient: PublicKey, local_node_receive_key: ReceiveAuthKey, context: MessageContext, _peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - let keys = KeysManager::new(&[0; 32], 42, 43); + let keys = KeysManager::new(&[0; 32], 42, 43, true); Ok(vec![BlindedMessagePath::one_hop( recipient, local_node_receive_key, @@ -274,7 +274,7 @@ mod test { } fn create_resolver() -> (impl AOnionMessenger, PublicKey) { - let resolver_keys = Arc::new(KeysManager::new(&[99; 32], 42, 43)); + let resolver_keys = Arc::new(KeysManager::new(&[99; 32], 42, 43, true)); let resolver_logger = TestLogger { node: "resolver" }; let resolver = OMDomainResolver::ignoring_incoming_proofs("8.8.8.8:53".parse().unwrap()); let resolver = Arc::new(resolver); @@ -313,7 +313,7 @@ mod test { let payment_id = PaymentId([42; 32]); let name = HumanReadableName::from_encoded("matt@mattcorallo.com").unwrap(); - let payer_keys = Arc::new(KeysManager::new(&[2; 32], 42, 43)); + let payer_keys = Arc::new(KeysManager::new(&[2; 32], 42, 43, true)); let payer_logger = TestLogger { node: "payer" }; let payer_id = payer_keys.get_node_id(Recipient::Node).unwrap(); let payer = Arc::new(URIResolver { diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index 2b57cd23a9a..9e60ae847d7 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -10,6 +10,7 @@ //! Tests which test upgrading from previous versions of LDK or downgrading to previous versions of //! LDK. +use lightning_0_1::events::ClosureReason as ClosureReason_0_1; use lightning_0_1::get_monitor as get_monitor_0_1; use lightning_0_1::ln::functional_test_utils as lightning_0_1_utils; use lightning_0_1::util::ser::Writeable as _; @@ -28,10 +29,19 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _; use lightning_0_0_125::routing::router as router_0_0_125; use lightning_0_0_125::util::ser::Writeable as _; +use lightning::chain::channelmonitor::ANTI_REORG_DELAY; +use lightning::events::{ClosureReason, Event}; use lightning::ln::functional_test_utils::*; +use lightning::sign::OutputSpender; use lightning_types::payment::PaymentPreimage; +use bitcoin::opcodes; +use bitcoin::script::Builder; +use bitcoin::secp256k1::Secp256k1; + +use std::sync::Arc; + #[test] fn simple_upgrade() { // Tests a simple case of upgrading from LDK 0.1 with a pending payment @@ -213,3 +223,85 @@ fn test_125_dangling_post_update_actions() { let config = test_default_channel_config(); reload_node!(nodes[3], config, &node_d_ser, &[&mon_ser], persister, chain_mon, node); } + +#[test] +fn test_0_1_legacy_remote_key_derivation() { + // Test that a channel opened with a v1/legacy `remote_key` derivation will be properly spent + // even after upgrading to 0.2 and opting into the new v2 derivation for new channels. + let (node_a_ser, node_b_ser, mon_a_ser, mon_b_ser, commitment_tx, channel_id); + let node_a_blocks; + { + let chanmon_cfgs = lightning_0_1_utils::create_chanmon_cfgs(2); + let node_cfgs = lightning_0_1_utils::create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = lightning_0_1_utils::create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = lightning_0_1_utils::create_network(2, &node_cfgs, &node_chanmgrs); + + let node_a_id = nodes[0].node.get_our_node_id(); + + let chan_id = lightning_0_1_utils::create_announced_chan_between_nodes(&nodes, 0, 1).2; + channel_id = chan_id.0; + + let err = "".to_owned(); + nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &node_a_id, err).unwrap(); + commitment_tx = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0); + assert_eq!(commitment_tx.len(), 1); + + lightning_0_1_utils::check_added_monitors(&nodes[1], 1); + let reason = ClosureReason_0_1::HolderForceClosed { broadcasted_latest_txn: Some(true) }; + lightning_0_1_utils::check_closed_event(&nodes[1], 1, reason, false, &[node_a_id], 100000); + lightning_0_1_utils::check_closed_broadcast(&nodes[1], 1, true); + + node_a_ser = nodes[0].node.encode(); + node_b_ser = nodes[1].node.encode(); + mon_a_ser = get_monitor_0_1!(nodes[0], chan_id).encode(); + mon_b_ser = get_monitor_0_1!(nodes[1], chan_id).encode(); + + node_a_blocks = Arc::clone(&nodes[0].blocks); + } + + // Create a dummy node to reload over with the 0.1 state + + let mut chanmon_cfgs = create_chanmon_cfgs(2); + + // Our TestChannelSigner will fail as we're jumping ahead, so disable its state-based checks + chanmon_cfgs[0].keys_manager.disable_all_state_policy_checks = true; + chanmon_cfgs[1].keys_manager.disable_all_state_policy_checks = true; + + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let (persister_a, persister_b, chain_mon_a, chain_mon_b); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let (node_a, node_b); + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let config = test_default_channel_config(); + let a_mons = &[&mon_a_ser[..]]; + reload_node!(nodes[0], config.clone(), &node_a_ser, a_mons, persister_a, chain_mon_a, node_a); + reload_node!(nodes[1], config, &node_b_ser, &[&mon_b_ser], persister_b, chain_mon_b, node_b); + + nodes[0].blocks = node_a_blocks; + + let node_b_id = nodes[1].node.get_our_node_id(); + + mine_transaction(&nodes[0], &commitment_tx[0]); + let reason = ClosureReason::CommitmentTxConfirmed; + check_closed_event(&nodes[0], 1, reason, false, &[node_b_id], 100_000); + check_added_monitors(&nodes[0], 1); + check_closed_broadcast(&nodes[0], 1, false); + + connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1); + let mut spendable_event = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(spendable_event.len(), 1); + if let Event::SpendableOutputs { outputs, channel_id: ev_id } = spendable_event.pop().unwrap() { + assert_eq!(ev_id.unwrap().0, channel_id); + assert_eq!(outputs.len(), 1); + let spk = Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(); + let spend_tx = nodes[0] + .keys_manager + .backing + .spend_spendable_outputs(&[&outputs[0]], Vec::new(), spk, 253, None, &Secp256k1::new()) + .unwrap(); + check_spends!(spend_tx, commitment_tx[0]); + } else { + panic!("Wrong event"); + } +} diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 0f36cf14e60..82446809342 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -6699,7 +6699,7 @@ mod tests { use crate::ln::functional_test_utils::*; use crate::ln::script::ShutdownScript; use crate::ln::types::ChannelId; - use crate::sign::InMemorySigner; + use crate::sign::{ChannelSigner, InMemorySigner}; use crate::sync::Arc; use crate::types::features::ChannelTypeFeatures; use crate::types::payment::{PaymentHash, PaymentPreimage}; @@ -6872,11 +6872,12 @@ mod tests { } let keys = InMemorySigner::new( - &secp_ctx, SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), + true, + SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), [41; 32], [0; 32], @@ -6893,7 +6894,7 @@ mod tests { let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::MAX }; let channel_id = ChannelId::v1_from_funding_outpoint(funding_outpoint); let channel_parameters = ChannelTransactionParameters { - holder_pubkeys: keys.holder_channel_pubkeys.clone(), + holder_pubkeys: keys.new_pubkeys(None, &secp_ctx), holder_selected_contest_delay: 66, is_outbound_from_holder: true, counterparty_parameters: Some(CounterpartyChannelTransactionParameters { @@ -7134,11 +7135,12 @@ mod tests { let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let keys = InMemorySigner::new( - &secp_ctx, SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), + true, + SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), [41; 32], [0; 32], @@ -7155,7 +7157,7 @@ mod tests { let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::MAX }; let channel_id = ChannelId::v1_from_funding_outpoint(funding_outpoint); let channel_parameters = ChannelTransactionParameters { - holder_pubkeys: keys.holder_channel_pubkeys.clone(), + holder_pubkeys: keys.new_pubkeys(None, &secp_ctx), holder_selected_contest_delay: 66, is_outbound_from_holder: true, counterparty_parameters: Some(CounterpartyChannelTransactionParameters { diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index 0d70f9d1201..48bd40a8347 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -1287,7 +1287,7 @@ mod tests { }; use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint}; use crate::ln::functional_test_utils::create_dummy_block; - use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, InMemorySigner}; + use crate::sign::{ChannelDerivationParameters, ChannelSigner, HTLCDescriptor, InMemorySigner}; use crate::types::payment::{PaymentHash, PaymentPreimage}; use crate::util::test_utils::{TestBroadcaster, TestFeeEstimator, TestLogger}; @@ -1301,11 +1301,12 @@ mod tests { fn test_broadcast_height() { let secp_ctx = Secp256k1::new(); let signer = InMemorySigner::new( - &secp_ctx, SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), + true, + SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), [41; 32], [0; 32], @@ -1338,7 +1339,7 @@ mod tests { // Use non-anchor channels so that HTLC-Timeouts are broadcast immediately instead of sent // to the user for external funding. let chan_params = ChannelTransactionParameters { - holder_pubkeys: signer.holder_channel_pubkeys.clone(), + holder_pubkeys: signer.new_pubkeys(None, &secp_ctx), holder_selected_contest_delay: 66, is_outbound_from_holder: true, counterparty_parameters: Some(CounterpartyChannelTransactionParameters { diff --git a/lightning/src/events/bump_transaction/mod.rs b/lightning/src/events/bump_transaction/mod.rs index f600397e060..399bd1f6d33 100644 --- a/lightning/src/events/bump_transaction/mod.rs +++ b/lightning/src/events/bump_transaction/mod.rs @@ -1147,7 +1147,7 @@ mod tests { ), ]), }; - let signer = KeysManager::new(&[42; 32], 42, 42); + let signer = KeysManager::new(&[42; 32], 42, 42, true); let logger = TestLogger::new(); let handler = BumpTransactionEventHandlerSync::new(&broadcaster, &source, &signer, &logger); diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 0a5e37286bc..e6a45527e3a 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -1014,11 +1014,11 @@ pub struct ChannelTransactionParameters { /// If a channel was funded with transaction A, and later spliced with transaction B, this field /// tracks the txid of transaction A. /// - /// See [`compute_funding_key_tweak`] and [`ChannelSigner::pubkeys`] for more context on how + /// See [`compute_funding_key_tweak`] and [`ChannelSigner::new_pubkeys`] for more context on how /// this may be used. /// /// [`compute_funding_key_tweak`]: crate::sign::compute_funding_key_tweak - /// [`ChannelSigner::pubkeys`]: crate::sign::ChannelSigner::pubkeys + /// [`ChannelSigner::new_pubkeys`]: crate::sign::ChannelSigner::new_pubkeys pub splice_parent_funding_txid: Option, /// This channel's type, as negotiated during channel open. For old objects where this field /// wasn't serialized, it will default to static_remote_key at deserialization. @@ -2240,8 +2240,8 @@ mod tests { let counterparty_signer = keys_provider.derive_channel_signer(keys_provider.generate_channel_keys_id(true, 1)); let per_commitment_secret = SecretKey::from_slice(&>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap(); let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); - let holder_pubkeys = signer.pubkeys(None, &secp_ctx); - let counterparty_pubkeys = counterparty_signer.pubkeys(None, &secp_ctx).clone(); + let holder_pubkeys = signer.new_pubkeys(None, &secp_ctx); + let counterparty_pubkeys = counterparty_signer.new_pubkeys(None, &secp_ctx).clone(); let channel_parameters = ChannelTransactionParameters { holder_pubkeys: holder_pubkeys.clone(), holder_selected_contest_delay: 0, diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 1ca067f43f4..4ee60142603 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -2380,7 +2380,7 @@ impl FundingScope { // Rotate the pubkeys using the prev_funding_txid as a tweak let prev_funding_txid = prev_funding.get_funding_txid(); - let holder_pubkeys = context.holder_pubkeys(prev_funding_txid); + let holder_pubkeys = context.new_holder_pubkeys(prev_funding_txid); let channel_parameters = &prev_funding.channel_transaction_parameters; let mut post_channel_transaction_parameters = ChannelTransactionParameters { @@ -3365,7 +3365,7 @@ where // TODO(dual_funding): Checks for `funding_feerate_sat_per_1000_weight`? - let pubkeys = holder_signer.pubkeys(None, &secp_ctx); + let pubkeys = holder_signer.new_pubkeys(None, &secp_ctx); let funding = FundingScope { value_to_self_msat, @@ -3603,7 +3603,7 @@ where Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get destination script".to_owned()}), }; - let pubkeys = holder_signer.pubkeys(None, &secp_ctx); + let pubkeys = holder_signer.new_pubkeys(None, &secp_ctx); let temporary_channel_id = temporary_channel_id_fn.map(|f| f(&pubkeys)) .unwrap_or_else(|| ChannelId::temporary_from_entropy_source(entropy_source)); @@ -3967,9 +3967,9 @@ where } /// Returns holder pubkeys to use for the channel. - fn holder_pubkeys(&self, prev_funding_txid: Option) -> ChannelPublicKeys { + fn new_holder_pubkeys(&self, prev_funding_txid: Option) -> ChannelPublicKeys { match &self.holder_signer { - ChannelSignerType::Ecdsa(ecdsa) => ecdsa.pubkeys(prev_funding_txid, &self.secp_ctx), + ChannelSignerType::Ecdsa(ecdsa) => ecdsa.new_pubkeys(prev_funding_txid, &self.secp_ctx), // TODO (taproot|arik) #[cfg(taproot)] _ => todo!(), @@ -11553,7 +11553,7 @@ where // Rotate the pubkeys using the prev_funding_txid as a tweak let prev_funding_txid = self.funding.get_funding_txid(); - let funding_pubkey = self.context.holder_pubkeys(prev_funding_txid).funding_pubkey; + let funding_pubkey = self.context.new_holder_pubkeys(prev_funding_txid).funding_pubkey; Ok(msgs::SpliceInit { channel_id: self.context.channel_id, @@ -15983,10 +15983,11 @@ mod tests { let secp_ctx = Secp256k1::new(); let signer = InMemorySigner::new( - &secp_ctx, SecretKey::from_slice(&>::from_hex("30ff4956bbdd3222d44cc5e8a1261dab1e07957bdac5ae88fe3261ef321f3749").unwrap()[..]).unwrap(), SecretKey::from_slice(&>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(), SecretKey::from_slice(&>::from_hex("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(), + SecretKey::from_slice(&>::from_hex("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(), + true, SecretKey::from_slice(&>::from_hex("3333333333333333333333333333333333333333333333333333333333333333").unwrap()[..]).unwrap(), SecretKey::from_slice(&>::from_hex("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(), @@ -15996,7 +15997,7 @@ mod tests { [0; 32], ); - let holder_pubkeys = signer.pubkeys(None, &secp_ctx); + let holder_pubkeys = signer.new_pubkeys(None, &secp_ctx); assert_eq!(holder_pubkeys.funding_pubkey.serialize()[..], >::from_hex("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb").unwrap()[..]); let keys_provider = Keys { signer: signer.clone() }; diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 41af9145152..3878239e2e0 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -19083,7 +19083,7 @@ pub mod bench { config.channel_handshake_config.minimum_depth = 1; let seed_a = [1u8; 32]; - let keys_manager_a = KeysManager::new(&seed_a, 42, 42); + let keys_manager_a = KeysManager::new(&seed_a, 42, 42, true); let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a, &keys_manager_a, keys_manager_a.get_peer_storage_key()); let node_a = ChannelManager::new(&fee_estimator, &chain_monitor_a, &tx_broadcaster, &router, &message_router, &logger_a, &keys_manager_a, &keys_manager_a, &keys_manager_a, config.clone(), ChainParameters { network, @@ -19093,7 +19093,7 @@ pub mod bench { let logger_b = test_utils::TestLogger::with_id("node a".to_owned()); let seed_b = [2u8; 32]; - let keys_manager_b = KeysManager::new(&seed_b, 42, 42); + let keys_manager_b = KeysManager::new(&seed_b, 42, 42, true); let chain_monitor_b = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_b, &keys_manager_b, keys_manager_b.get_peer_storage_key()); let node_b = ChannelManager::new(&fee_estimator, &chain_monitor_b, &tx_broadcaster, &router, &message_router, &logger_b, &keys_manager_b, &keys_manager_b, &keys_manager_b, config.clone(), ChainParameters { network, diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index f26ef03a74f..e838805bae4 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -4167,10 +4167,10 @@ pub fn fail_payment<'a, 'b, 'c>( } pub fn create_chanmon_cfgs(node_count: usize) -> Vec { - create_chanmon_cfgs_with_keys(node_count, None) + create_chanmon_cfgs_with_legacy_keys(node_count, None) } -pub fn create_chanmon_cfgs_with_keys( +pub fn create_chanmon_cfgs_with_legacy_keys( node_count: usize, predefined_keys_ids: Option>, ) -> Vec { let mut chan_mon_cfgs = Vec::new(); @@ -4181,7 +4181,12 @@ pub fn create_chanmon_cfgs_with_keys( let logger = test_utils::TestLogger::with_id(format!("node {}", i)); let persister = test_utils::TestPersister::new(); let seed = [i as u8; 32]; - let keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet); + let keys_manager = if predefined_keys_ids.is_some() { + // Use legacy (V1) remote_key derivation for tests using legacy key sets. + test_utils::TestKeysInterface::with_v1_remote_key_derivation(&seed, Network::Testnet) + } else { + test_utils::TestKeysInterface::new(&seed, Network::Testnet) + }; let scorer = RwLock::new(test_utils::TestScorer::new()); // Set predefined keys_id if provided diff --git a/lightning/src/ln/invoice_utils.rs b/lightning/src/ln/invoice_utils.rs index c08d4fa14c5..7c0190a23a9 100644 --- a/lightning/src/ln/invoice_utils.rs +++ b/lightning/src/ln/invoice_utils.rs @@ -1211,7 +1211,7 @@ mod test { fn make_dyn_keys_interface(seed: &[u8; 32]) -> DynKeysInterface { let cross_node_seed = [44u8; 32]; - let inner = PhantomKeysManager::new(&seed, 43, 44, &cross_node_seed); + let inner = PhantomKeysManager::new(&seed, 43, 44, &cross_node_seed, true); let dyn_inner = DynPhantomKeysInterface::new(inner); DynKeysInterface::new(Box::new(dyn_inner)) } diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index ba3312c9f15..d1d71399051 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -2301,7 +2301,7 @@ fn do_test_restored_packages_retry(check_old_monitor_retries_after_upgrade: bool let node1_key_id = <[u8; 32]>::from_hex("0000000000000000000000004D49E5DAD000D6201F116BAFD379F1D61DF161B9").unwrap(); let predefined_keys_ids = Some(vec![node0_key_id, node1_key_id]); - let chanmon_cfgs = create_chanmon_cfgs_with_keys(2, predefined_keys_ids); + let chanmon_cfgs = create_chanmon_cfgs_with_legacy_keys(2, predefined_keys_ids); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let persister; let new_chain_monitor; @@ -2451,7 +2451,9 @@ fn do_test_monitor_rebroadcast_pending_claims(anchors: bool) { if should_bump { assert!(htlc_tx_feerate > prev_htlc_tx_feerate.take().unwrap()); } else if let Some(prev_feerate) = prev_htlc_tx_feerate.take() { - assert_eq!(htlc_tx_feerate, prev_feerate); + // Feerates may fluctuate marginally based on signature size + assert!(htlc_tx_feerate >= prev_feerate - 1); + assert!(htlc_tx_feerate <= prev_feerate + 1); } prev_htlc_tx_feerate = Some(htlc_tx_feerate); Some(htlc_tx) diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 0934c6c812b..84b1338b1a7 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -663,9 +663,9 @@ mod tests { // adding an intermediate onion layer, causing the receiver to error with "final payload // provided for us as an intermediate node." let secp_ctx = Secp256k1::new(); - let bob = crate::sign::KeysManager::new(&[2; 32], 42, 42); + let bob = crate::sign::KeysManager::new(&[2; 32], 42, 42, true); let bob_pk = PublicKey::from_secret_key(&secp_ctx, &bob.get_node_secret_key()); - let charlie = crate::sign::KeysManager::new(&[3; 32], 42, 42); + let charlie = crate::sign::KeysManager::new(&[3; 32], 42, 42, true); let charlie_pk = PublicKey::from_secret_key(&secp_ctx, &charlie.get_node_secret_key()); let ( @@ -693,9 +693,9 @@ mod tests { use super::*; let secp_ctx = Secp256k1::new(); - let bob = crate::sign::KeysManager::new(&[2; 32], 42, 42); + let bob = crate::sign::KeysManager::new(&[2; 32], 42, 42, true); let bob_pk = PublicKey::from_secret_key(&secp_ctx, &bob.get_node_secret_key()); - let charlie = crate::sign::KeysManager::new(&[3; 32], 42, 42); + let charlie = crate::sign::KeysManager::new(&[3; 32], 42, 42, true); let charlie_pk = PublicKey::from_secret_key(&secp_ctx, &charlie.get_node_secret_key()); let (session_priv, total_amt_msat, cur_height, recipient_onion, preimage, payment_hash, diff --git a/lightning/src/ln/our_peer_storage.rs b/lightning/src/ln/our_peer_storage.rs index 178637430b1..ab0e9783ffa 100644 --- a/lightning/src/ln/our_peer_storage.rs +++ b/lightning/src/ln/our_peer_storage.rs @@ -37,7 +37,7 @@ use crate::prelude::*; /// use lightning::ln::our_peer_storage::DecryptedOurPeerStorage; /// use lightning::sign::{KeysManager, NodeSigner}; /// let seed = [1u8; 32]; -/// let keys_mgr = KeysManager::new(&seed, 42, 42); +/// let keys_mgr = KeysManager::new(&seed, 42, 42, true); /// let key = keys_mgr.get_peer_storage_key(); /// let decrypted_ops = DecryptedOurPeerStorage::new(vec![1, 2, 3]); /// let our_peer_storage = decrypted_ops.encrypt(&key, &[0u8; 32]); diff --git a/lightning/src/onion_message/dns_resolution.rs b/lightning/src/onion_message/dns_resolution.rs index 7961828b1b2..54eb16b5266 100644 --- a/lightning/src/onion_message/dns_resolution.rs +++ b/lightning/src/onion_message/dns_resolution.rs @@ -561,7 +561,7 @@ mod tests { #[test] #[cfg(feature = "dnssec")] fn test_expiry() { - let keys = crate::sign::KeysManager::new(&[33; 32], 0, 0); + let keys = crate::sign::KeysManager::new(&[33; 32], 0, 0, true); let resolver = OMNameResolver::new(42, 42); let name = HumanReadableName::new("user", "example.com").unwrap(); diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 890eee8859b..9a2c06bb72f 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -222,7 +222,7 @@ where /// # } /// # let seed = [42u8; 32]; /// # let time = Duration::from_secs(123456); -/// # let keys_manager = KeysManager::new(&seed, time.as_secs(), time.subsec_nanos()); +/// # let keys_manager = KeysManager::new(&seed, time.as_secs(), time.subsec_nanos(), true); /// # let logger = Arc::new(FakeLogger {}); /// # let node_secret = SecretKey::from_slice(&>::from_hex("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap(); /// # let secp_ctx = Secp256k1::new(); diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index f9db5ff4672..40d24778249 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -41,8 +41,8 @@ use crate::chain::transaction::OutPoint; use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; use crate::ln::chan_utils; use crate::ln::chan_utils::{ - get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys, - ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction, + get_counterparty_payment_script, get_revokeable_redeemscript, make_funding_redeemscript, + ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction, }; use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; @@ -56,6 +56,7 @@ use crate::ln::msgs::PartialSignatureWithNonce; use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage}; use crate::ln::script::ShutdownScript; use crate::offers::invoice::UnsignedBolt12Invoice; +use crate::types::features::ChannelTypeFeatures; use crate::types::payment::PaymentPreimage; use crate::util::async_poll::AsyncResult; use crate::util::ser::{ReadableArgs, Writeable}; @@ -140,6 +141,16 @@ pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ + pub(crate) const P2TR_KEY_PATH_WITNESS_WEIGHT: u64 = 1 /* witness items */ + 1 /* schnorr sig len */ + 64 /* schnorr sig */; +/// If a [`KeysManager`] is built with [`KeysManager::new`] with `v2_remote_key_derivation` set, +/// the script which we receive funds to on-chain when our counterparty force-closes a channel is +/// one of this many possible derivation paths. +/// +/// Keping this limited allows for scanning the chain to find lost funds if our state is destroyed, +/// while this being more than a handful provides some privacy by not constantly reusing the same +/// scripts on-chain across channels. +// Note that this MUST remain below the maximum BIP 32 derivation paths (2^31) +pub const STATIC_PAYMENT_KEY_COUNT: u16 = 1000; + /// Information about a spendable output to our "payment key". /// /// See [`SpendableOutputDescriptor::StaticPaymentOutput`] for more details on how to spend this. @@ -259,11 +270,14 @@ pub enum SpendableOutputDescriptor { /// it is an output from an old state which we broadcast (which should never happen). /// /// To derive the delayed payment key which is used to sign this input, you must pass the - /// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which corresponds to the - /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`ChannelSigner::pubkeys`]) and the provided - /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The DelayedPaymentKey can be - /// generated without the secret key using [`DelayedPaymentKey::from_basepoint`] and only the - /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`ChannelSigner::pubkeys`]. + /// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which + /// corresponds to the [`ChannelPublicKeys::delayed_payment_basepoint`] in + /// [`ChannelSigner::new_pubkeys`]) and the provided + /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to + /// [`chan_utils::derive_private_key`]. The DelayedPaymentKey can be generated without the + /// secret key using [`DelayedPaymentKey::from_basepoint`] and only the + /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in + /// [`ChannelSigner::new_pubkeys`]. /// /// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is /// used in the witness script generation), you must pass the counterparty @@ -278,7 +292,7 @@ pub enum SpendableOutputDescriptor { /// [`chan_utils::get_revokeable_redeemscript`]. DelayedPaymentOutput(DelayedPaymentOutputDescriptor), /// An output spendable exclusively by our payment key (i.e., the private key that corresponds - /// to the `payment_point` in [`ChannelSigner::pubkeys`]). The output type depends on the + /// to the `payment_point` in [`ChannelSigner::new_pubkeys`]). The output type depends on the /// channel type negotiated. /// /// On an anchor outputs channel, the witness in the spending input is: @@ -371,7 +385,7 @@ impl SpendableOutputDescriptor { if let Some(basepoint) = delayed_payment_basepoint.as_ref() { // Required to derive signing key: privkey = basepoint_secret + SHA256(per_commitment_point || basepoint) let add_tweak = basepoint.derive_add_tweak(&per_commitment_point); - let payment_key = DelayedPaymentKey(add_public_key_tweak( + let delayed_payment_key = DelayedPaymentKey(add_public_key_tweak( secp_ctx, &basepoint.to_public_key(), &add_tweak, @@ -381,7 +395,7 @@ impl SpendableOutputDescriptor { Some(get_revokeable_redeemscript( &revocation_pubkey, *to_self_delay, - &payment_key, + &delayed_payment_key, )), Some(add_tweak), ) @@ -778,14 +792,17 @@ pub trait ChannelSigner { /// and pause future signing operations until this validation completes. fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>; - /// Returns the holder's channel public keys and basepoints. + /// Returns a *new* set of holder channel public keys and basepoints. They may be the same as a + /// previous value, but are also allowed to change arbitrarily. Signing methods must still + /// support both old and new versions, but this should only be called either for new channels + /// or new splices. /// /// `splice_parent_funding_txid` can be used to compute a tweak to rotate the funding key in the /// 2-of-2 multisig script during a splice. See [`compute_funding_key_tweak`] for an example /// tweak and more details. /// /// This method is *not* asynchronous. Instead, the value must be cached locally. - fn pubkeys( + fn new_pubkeys( &self, splice_parent_funding_txid: Option, secp_ctx: &Secp256k1, ) -> ChannelPublicKeys; @@ -1084,7 +1101,7 @@ mod sealed { use bitcoin::secp256k1::{Scalar, SecretKey}; #[derive(Clone, PartialEq)] - pub struct MaybeTweakedSecretKey(SecretKey); + pub struct MaybeTweakedSecretKey(pub(super) SecretKey); impl From for MaybeTweakedSecretKey { fn from(value: SecretKey) -> Self { @@ -1140,16 +1157,20 @@ pub struct InMemorySigner { funding_key: sealed::MaybeTweakedSecretKey, /// Holder secret key for blinded revocation pubkey. pub revocation_base_key: SecretKey, - /// Holder secret key used for our balance in counterparty-broadcasted commitment transactions. - pub payment_key: SecretKey, + /// Holder secret key used for our balance in counterparty-broadcasted commitment transactions, + /// old-style derivation. + payment_key_v1: SecretKey, + /// Holder secret key used for our balance in counterparty-broadcasted commitment transactions, + /// new-style derivation. + payment_key_v2: SecretKey, + /// Which of [`Self::payment_key_v1`] and [`Self::payment_key_v2`] to use by default. + v2_remote_key_derivation: bool, /// Holder secret key used in an HTLC transaction. pub delayed_payment_base_key: SecretKey, /// Holder HTLC secret key used in commitment transaction HTLC outputs. pub htlc_base_key: SecretKey, /// Commitment seed. pub commitment_seed: [u8; 32], - /// Holder public keys and basepoints. - pub(crate) holder_channel_pubkeys: ChannelPublicKeys, /// Key derivation parameters. channel_keys_id: [u8; 32], /// A source of random bytes. @@ -1160,11 +1181,12 @@ impl PartialEq for InMemorySigner { fn eq(&self, other: &Self) -> bool { self.funding_key == other.funding_key && self.revocation_base_key == other.revocation_base_key - && self.payment_key == other.payment_key + && self.payment_key_v1 == other.payment_key_v1 + && self.payment_key_v2 == other.payment_key_v2 + && self.v2_remote_key_derivation == other.v2_remote_key_derivation && self.delayed_payment_base_key == other.delayed_payment_base_key && self.htlc_base_key == other.htlc_base_key && self.commitment_seed == other.commitment_seed - && self.holder_channel_pubkeys == other.holder_channel_pubkeys && self.channel_keys_id == other.channel_keys_id } } @@ -1174,11 +1196,12 @@ impl Clone for InMemorySigner { Self { funding_key: self.funding_key.clone(), revocation_base_key: self.revocation_base_key.clone(), - payment_key: self.payment_key.clone(), + payment_key_v1: self.payment_key_v1.clone(), + payment_key_v2: self.payment_key_v2.clone(), + v2_remote_key_derivation: self.v2_remote_key_derivation, delayed_payment_base_key: self.delayed_payment_base_key.clone(), htlc_base_key: self.htlc_base_key.clone(), commitment_seed: self.commitment_seed.clone(), - holder_channel_pubkeys: self.holder_channel_pubkeys.clone(), channel_keys_id: self.channel_keys_id, entropy_source: RandomBytes::new(self.get_secure_random_bytes()), } @@ -1186,28 +1209,43 @@ impl Clone for InMemorySigner { } impl InMemorySigner { - /// Creates a new [`InMemorySigner`]. - pub fn new( - secp_ctx: &Secp256k1, funding_key: SecretKey, revocation_base_key: SecretKey, - payment_key: SecretKey, delayed_payment_base_key: SecretKey, htlc_base_key: SecretKey, - commitment_seed: [u8; 32], channel_keys_id: [u8; 32], rand_bytes_unique_start: [u8; 32], + #[cfg(any(feature = "_test_utils", test))] + pub fn new( + funding_key: SecretKey, revocation_base_key: SecretKey, payment_key_v1: SecretKey, + payment_key_v2: SecretKey, v2_remote_key_derivation: bool, + delayed_payment_base_key: SecretKey, htlc_base_key: SecretKey, commitment_seed: [u8; 32], + channel_keys_id: [u8; 32], rand_bytes_unique_start: [u8; 32], + ) -> InMemorySigner { + InMemorySigner { + funding_key: sealed::MaybeTweakedSecretKey::from(funding_key), + revocation_base_key, + payment_key_v1, + payment_key_v2, + v2_remote_key_derivation, + delayed_payment_base_key, + htlc_base_key, + commitment_seed, + channel_keys_id, + entropy_source: RandomBytes::new(rand_bytes_unique_start), + } + } + + #[cfg(not(any(feature = "_test_utils", test)))] + fn new( + funding_key: SecretKey, revocation_base_key: SecretKey, payment_key_v1: SecretKey, + payment_key_v2: SecretKey, v2_remote_key_derivation: bool, + delayed_payment_base_key: SecretKey, htlc_base_key: SecretKey, commitment_seed: [u8; 32], + channel_keys_id: [u8; 32], rand_bytes_unique_start: [u8; 32], ) -> InMemorySigner { - let holder_channel_pubkeys = InMemorySigner::make_holder_keys( - secp_ctx, - &funding_key, - &revocation_base_key, - &payment_key, - &delayed_payment_base_key, - &htlc_base_key, - ); InMemorySigner { funding_key: sealed::MaybeTweakedSecretKey::from(funding_key), revocation_base_key, - payment_key, + payment_key_v1, + payment_key_v2, + v2_remote_key_derivation, delayed_payment_base_key, htlc_base_key, commitment_seed, - holder_channel_pubkeys, channel_keys_id, entropy_source: RandomBytes::new(rand_bytes_unique_start), } @@ -1221,22 +1259,6 @@ impl InMemorySigner { self.funding_key.with_tweak(tweak) } - fn make_holder_keys( - secp_ctx: &Secp256k1, funding_key: &SecretKey, revocation_base_key: &SecretKey, - payment_key: &SecretKey, delayed_payment_base_key: &SecretKey, htlc_base_key: &SecretKey, - ) -> ChannelPublicKeys { - let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s); - ChannelPublicKeys { - funding_pubkey: from_secret(&funding_key), - revocation_basepoint: RevocationBasepoint::from(from_secret(&revocation_base_key)), - payment_point: from_secret(&payment_key), - delayed_payment_basepoint: DelayedPaymentBasepoint::from(from_secret( - &delayed_payment_base_key, - )), - htlc_basepoint: HtlcBasepoint::from(from_secret(&htlc_base_key)), - } - } - /// Sign the single input of `spend_tx` at index `input_idx`, which spends the output described /// by `descriptor`, returning the witness stack for the input. /// @@ -1264,14 +1286,28 @@ impl InMemorySigner { return Err(()); } - let remotepubkey = bitcoin::PublicKey::new(self.holder_channel_pubkeys.payment_point); - let supports_anchors_zero_fee_htlc_tx = descriptor + let legacy_default_channel_type = ChannelTypeFeatures::only_static_remote_key(); + let channel_type_features = descriptor .channel_transaction_parameters .as_ref() - .map(|params| params.channel_type_features.supports_anchors_zero_fee_htlc_tx()) - .unwrap_or(false); + .map(|params| ¶ms.channel_type_features) + .unwrap_or(&legacy_default_channel_type); + + let payment_point_v1 = PublicKey::from_secret_key(secp_ctx, &self.payment_key_v1); + let payment_point_v2 = PublicKey::from_secret_key(secp_ctx, &self.payment_key_v2); + let spk_v1 = get_counterparty_payment_script(channel_type_features, &payment_point_v1); + let spk_v2 = get_counterparty_payment_script(channel_type_features, &payment_point_v2); - let witness_script = if supports_anchors_zero_fee_htlc_tx { + let (remotepubkey, payment_key) = if spk_v1 == descriptor.output.script_pubkey { + (bitcoin::PublicKey::new(payment_point_v1), &self.payment_key_v1) + } else { + if spk_v2 != descriptor.output.script_pubkey { + return Err(()); + } + (bitcoin::PublicKey::new(payment_point_v2), &self.payment_key_v2) + }; + + let witness_script = if channel_type_features.supports_anchors_zero_fee_htlc_tx() { chan_utils::get_to_countersigner_keyed_anchor_redeemscript(&remotepubkey.inner) } else { ScriptBuf::new_p2pkh(&remotepubkey.pubkey_hash()) @@ -1286,8 +1322,8 @@ impl InMemorySigner { ) .unwrap()[..] ); - let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self); - let payment_script = if supports_anchors_zero_fee_htlc_tx { + let remotesig = sign_with_aux_rand(secp_ctx, &sighash, payment_key, &self); + let payment_script = if channel_type_features.supports_anchors_zero_fee_htlc_tx() { witness_script.to_p2wsh() } else { ScriptBuf::new_p2wpkh(&remotepubkey.wpubkey_hash().unwrap()) @@ -1300,7 +1336,7 @@ impl InMemorySigner { let mut witness = Vec::with_capacity(2); witness.push(remotesig.serialize_der().to_vec()); witness[0].push(EcdsaSighashType::All as u8); - if supports_anchors_zero_fee_htlc_tx { + if channel_type_features.supports_anchors_zero_fee_htlc_tx() { witness.push(witness_script.to_bytes()); } else { witness.push(remotepubkey.to_bytes()); @@ -1412,10 +1448,22 @@ impl ChannelSigner for InMemorySigner { Ok(()) } - fn pubkeys( + fn new_pubkeys( &self, splice_parent_funding_txid: Option, secp_ctx: &Secp256k1, ) -> ChannelPublicKeys { - let mut pubkeys = self.holder_channel_pubkeys.clone(); + let payment_key = + if self.v2_remote_key_derivation { &self.payment_key_v2 } else { &self.payment_key_v1 }; + let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s); + let mut pubkeys = ChannelPublicKeys { + funding_pubkey: from_secret(&self.funding_key.0), + revocation_basepoint: RevocationBasepoint::from(from_secret(&self.revocation_base_key)), + payment_point: from_secret(payment_key), + delayed_payment_basepoint: DelayedPaymentBasepoint::from(from_secret( + &self.delayed_payment_base_key, + )), + htlc_basepoint: HtlcBasepoint::from(from_secret(&self.htlc_base_key)), + }; + if splice_parent_funding_txid.is_some() { pubkeys.funding_pubkey = self.funding_key(splice_parent_funding_txid).public_key(secp_ctx); @@ -1874,6 +1922,8 @@ pub struct KeysManager { destination_script: ScriptBuf, shutdown_pubkey: PublicKey, channel_master_key: Xpriv, + static_payment_key: Xpriv, + v2_remote_key_derivation: bool, channel_child_index: AtomicUsize, peer_storage_key: PeerStorageKey, receive_auth_key: ReceiveAuthKey, @@ -1905,8 +1955,16 @@ impl KeysManager { /// [`ChannelMonitor`] data, though a current copy of [`ChannelMonitor`] data is also required /// for any channel, and some on-chain during-closing funds. /// + /// If `v2_remote_key_derivation` is set, the `script_pubkey`s which receive funds on-chain when + /// our counterparty force-closes will be one of a static set of [`STATIC_PAYMENT_KEY_COUNT`]*2 + /// possible `script_pubkey`s. This only applies to new or spliced channels, however if this is + /// set you *MUST NOT* downgrade to a version of LDK prior to 0.2. + /// /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor - pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self { + pub fn new( + seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, + v2_remote_key_derivation: bool, + ) -> Self { // Constants for key derivation path indices used in this function. const NODE_SECRET_INDEX: ChildNumber = ChildNumber::Hardened { index: 0 }; const DESTINATION_SCRIPT_INDEX: ChildNumber = ChildNumber::Hardened { index: 1 }; @@ -1915,6 +1973,7 @@ impl KeysManager { const INBOUND_PAYMENT_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 5 }; const PEER_STORAGE_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 6 }; const RECEIVE_AUTH_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 7 }; + const STATIC_PAYMENT_KEY_INDEX: ChildNumber = ChildNumber::Hardened { index: 8 }; let secp_ctx = Secp256k1::new(); // Note that when we aren't serializing the key, network doesn't matter @@ -1962,6 +2021,10 @@ impl KeysManager { .expect("Your RNG is busted") .private_key; + let static_payment_key = master_key + .derive_priv(&secp_ctx, &STATIC_PAYMENT_KEY_INDEX) + .expect("Your RNG is busted"); + let mut rand_bytes_engine = Sha256::engine(); rand_bytes_engine.input(&starting_time_secs.to_be_bytes()); rand_bytes_engine.input(&starting_time_nanos.to_be_bytes()); @@ -1985,6 +2048,9 @@ impl KeysManager { channel_master_key, channel_child_index: AtomicUsize::new(0), + static_payment_key, + v2_remote_key_derivation, + entropy_source: RandomBytes::new(rand_bytes_unique_start), seed: *seed, @@ -2004,6 +2070,48 @@ impl KeysManager { self.node_secret } + /// Gets the set of possible `script_pubkey`s which can appear on chain for our + /// non-HTLC-encumbered balance if our counterparty force-closes the channel. + /// + /// Only channels opened or spliced when using a [`KeysManager`] with the + /// `v2_remote_key_derivation` argument to [`KeysManager::new`] will close to such scripts, + /// other channels will close to a randomly-generated `script_pubkey`. + pub fn possible_v2_counterparty_closed_balance_spks( + &self, secp_ctx: &Secp256k1, + ) -> Vec { + let mut res = Vec::with_capacity(usize::from(STATIC_PAYMENT_KEY_COUNT) * 2); + let static_remote_key_features = ChannelTypeFeatures::only_static_remote_key(); + let mut zero_fee_htlc_features = ChannelTypeFeatures::only_static_remote_key(); + zero_fee_htlc_features.set_anchors_zero_fee_htlc_tx_required(); + for idx in 0..STATIC_PAYMENT_KEY_COUNT { + let key = self + .static_payment_key + .derive_priv( + &self.secp_ctx, + &ChildNumber::from_hardened_idx(u32::from(idx)).expect("key space exhausted"), + ) + .expect("Your RNG is busted") + .private_key; + let pubkey = PublicKey::from_secret_key(secp_ctx, &key); + res.push(get_counterparty_payment_script(&static_remote_key_features, &pubkey)); + res.push(get_counterparty_payment_script(&zero_fee_htlc_features, &pubkey)); + } + res + } + + fn derive_payment_key_v2(&self, params: &[u8; 32]) -> SecretKey { + let mut eight_bytes = [0; 8]; + eight_bytes.copy_from_slice(¶ms[0..8]); + let idx = u64::from_le_bytes(eight_bytes) % u64::from(STATIC_PAYMENT_KEY_COUNT); + self.static_payment_key + .derive_priv( + &self.secp_ctx, + &ChildNumber::from_hardened_idx(idx as u32).expect("key space exhausted"), + ) + .expect("Your RNG is busted") + .private_key + } + /// Derive an old [`EcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters. pub fn derive_channel_keys(&self, params: &[u8; 32]) -> InMemorySigner { let chan_id = u64::from_be_bytes(params[0..8].try_into().unwrap()); @@ -2044,16 +2152,17 @@ impl KeysManager { } let funding_key = key_step!(b"funding key", commitment_seed); let revocation_base_key = key_step!(b"revocation base key", funding_key); - let payment_key = key_step!(b"payment key", revocation_base_key); - let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key); + let payment_key_v1 = key_step!(b"payment key", revocation_base_key); + let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key_v1); let htlc_base_key = key_step!(b"HTLC base key", delayed_payment_base_key); let prng_seed = self.get_secure_random_bytes(); InMemorySigner::new( - &self.secp_ctx, funding_key, revocation_base_key, - payment_key, + payment_key_v1, + self.derive_payment_key_v2(&commitment_seed), + self.v2_remote_key_derivation, delayed_payment_base_key, htlc_base_key, commitment_seed, @@ -2091,6 +2200,15 @@ impl KeysManager { let signer = self.derive_channel_keys(&descriptor.channel_keys_id); keys_cache = Some((signer, descriptor.channel_keys_id)); } + #[cfg(test)] + if self.v2_remote_key_derivation { + // In tests, we don't have to deal with upgrades from V1 sighers with + // `v2_remote_key_derivation` set, so use this opportunity to test + // `possible_v2_counterparty_closed_balance_spks`. + let possible_spks = + self.possible_v2_counterparty_closed_balance_spks(secp_ctx); + assert!(possible_spks.contains(&descriptor.output.script_pubkey)); + } let witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input( &psbt.unsigned_tx, input_idx, @@ -2457,8 +2575,8 @@ impl PhantomKeysManager { /// that is shared across all nodes that intend to participate in [phantom node payments] /// together. /// - /// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and - /// `starting_time_nanos`. + /// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, + /// `starting_time_nanos`, and `v2_remote_key_derivation`. /// /// `cross_node_seed` must be the same across all phantom payment-receiving nodes and also the /// same across restarts, or else inbound payments may fail. @@ -2466,9 +2584,14 @@ impl PhantomKeysManager { /// [phantom node payments]: PhantomKeysManager pub fn new( seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, - cross_node_seed: &[u8; 32], + cross_node_seed: &[u8; 32], v2_remote_key_derivation: bool, ) -> Self { - let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos); + let inner = KeysManager::new( + seed, + starting_time_secs, + starting_time_nanos, + v2_remote_key_derivation, + ); let (inbound_key, phantom_key) = hkdf_extract_expand_twice( b"LDK Inbound and Phantom Payment Key Expansion", cross_node_seed, @@ -2546,7 +2669,8 @@ pub mod benches { pub fn bench_get_secure_random_bytes(bench: &mut Criterion) { let seed = [0u8; 32]; let now = Duration::from_secs(genesis_block(Network::Testnet).header.time as u64); - let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_micros())); + let keys_manager = + Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_micros(), true)); let mut handles = Vec::new(); let mut stops = Vec::new(); diff --git a/lightning/src/util/dyn_signer.rs b/lightning/src/util/dyn_signer.rs index 9e8ba4aee2c..c519484a938 100644 --- a/lightning/src/util/dyn_signer.rs +++ b/lightning/src/util/dyn_signer.rs @@ -174,7 +174,7 @@ delegate!(DynSigner, ChannelSigner, holder_tx: &HolderCommitmentTransaction, preimages: Vec ) -> Result<(), ()>, - fn pubkeys(, + fn new_pubkeys(, splice_parent_funding_txid: Option, secp_ctx: &Secp256k1 ) -> ChannelPublicKeys, fn channel_keys_id(,) -> [u8; 32], diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index 4d9bf244dbe..6b1950169e8 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -221,10 +221,10 @@ impl ChannelSigner for TestChannelSigner { Ok(()) } - fn pubkeys( + fn new_pubkeys( &self, splice_parent_funding_txid: Option, secp_ctx: &Secp256k1, ) -> ChannelPublicKeys { - self.inner.pubkeys(splice_parent_funding_txid, secp_ctx) + self.inner.new_pubkeys(splice_parent_funding_txid, secp_ctx) } fn channel_keys_id(&self) -> [u8; 32] { diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index deeb3a38e9f..eb7af681b79 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -1893,7 +1893,7 @@ pub static SIGNER_FACTORY: MutGlobal> = pub trait TestSignerFactory: Send + Sync { /// Make a dynamic signer fn make_signer( - &self, seed: &[u8; 32], now: Duration, + &self, seed: &[u8; 32], now: Duration, v2_remote_key_derivation: bool, ) -> Box>; } @@ -1902,9 +1902,15 @@ struct DefaultSignerFactory(); impl TestSignerFactory for DefaultSignerFactory { fn make_signer( - &self, seed: &[u8; 32], now: Duration, + &self, seed: &[u8; 32], now: Duration, v2_remote_key_derivation: bool, ) -> Box> { - let phantom = sign::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed); + let phantom = sign::PhantomKeysManager::new( + seed, + now.as_secs(), + now.subsec_nanos(), + seed, + v2_remote_key_derivation, + ); let dphantom = DynPhantomKeysInterface::new(phantom); let backing = Box::new(dphantom) as Box>; backing @@ -1912,15 +1918,7 @@ impl TestSignerFactory for DefaultSignerFactory { } impl TestKeysInterface { - pub fn new(seed: &[u8; 32], network: Network) -> Self { - #[cfg(feature = "std")] - let factory = SIGNER_FACTORY.get(); - - #[cfg(not(feature = "std"))] - let factory = DefaultSignerFactory(); - - let now = Duration::from_secs(genesis_block(network).header.time as u64); - let backing = factory.make_signer(seed, now); + fn build(backing: Box>) -> Self { Self { backing: DynKeysInterface::new(backing), override_random_bytes: Mutex::new(None), @@ -1934,6 +1932,30 @@ impl TestKeysInterface { } } + pub fn new(seed: &[u8; 32], network: Network) -> Self { + #[cfg(feature = "std")] + let factory = SIGNER_FACTORY.get(); + + #[cfg(not(feature = "std"))] + let factory = DefaultSignerFactory(); + + let now = Duration::from_secs(genesis_block(network).header.time as u64); + let backing = factory.make_signer(seed, now, true); + Self::build(backing) + } + + pub fn with_v1_remote_key_derivation(seed: &[u8; 32], network: Network) -> Self { + #[cfg(feature = "std")] + let factory = SIGNER_FACTORY.get(); + + #[cfg(not(feature = "std"))] + let factory = DefaultSignerFactory(); + + let now = Duration::from_secs(genesis_block(network).header.time as u64); + let backing = factory.make_signer(seed, now, false); + Self::build(backing) + } + /// Sets an expectation that [`sign::SignerProvider::get_shutdown_scriptpubkey`] is /// called. pub fn expect(&self, expectation: OnGetShutdownScriptpubkey) -> &Self {