Skip to content

Commit 2f01d68

Browse files
committed
Sanity check for ChannelManager and KeysInterface
Fix build errors Create script using p2wsh for comparison Using p2wpkh for generating the payment script spendable_outputs sanity check Return err in spendable_outputs Doc updates in keysinterface
1 parent 34cdca9 commit 2f01d68

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,8 @@ impl InMemorySigner {
526526
/// described by descriptor, returning the witness stack for the input.
527527
///
528528
/// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
529-
/// or is not spending the outpoint described by `descriptor.outpoint`.
529+
/// is not spending the outpoint described by `descriptor.outpoint`,
530+
/// or if an output descriptor script_pubkey does not match the one we can spend.
530531
pub fn sign_counterparty_payment_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
531532
// TODO: We really should be taking the SigHashCache as a parameter here instead of
532533
// spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
@@ -540,6 +541,9 @@ impl InMemorySigner {
540541
let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
541542
let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
542543
let remotesig = secp_ctx.sign(&sighash, &self.payment_key);
544+
let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
545+
546+
if payment_script != descriptor.output.script_pubkey { return Err(()); }
543547

544548
let mut witness = Vec::with_capacity(2);
545549
witness.push(remotesig.serialize_der().to_vec());
@@ -552,8 +556,9 @@ impl InMemorySigner {
552556
/// described by descriptor, returning the witness stack for the input.
553557
///
554558
/// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
555-
/// is not spending the outpoint described by `descriptor.outpoint`, or does not have a
556-
/// sequence set to `descriptor.to_self_delay`.
559+
/// is not spending the outpoint described by `descriptor.outpoint`, does not have a
560+
/// sequence set to `descriptor.to_self_delay`, or if an output descriptor
561+
/// script_pubkey does not match the one we can spend.
557562
pub fn sign_dynamic_p2wsh_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
558563
// TODO: We really should be taking the SigHashCache as a parameter here instead of
559564
// spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
@@ -570,6 +575,9 @@ impl InMemorySigner {
570575
let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
571576
let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
572577
let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
578+
let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
579+
580+
if descriptor.output.script_pubkey != payment_script { return Err(()); }
573581

574582
let mut witness = Vec::with_capacity(3);
575583
witness.push(local_delayedsig.serialize_der().to_vec());
@@ -927,8 +935,9 @@ impl KeysManager {
927935
/// output to the given change destination (if sufficient change value remains). The
928936
/// transaction will have a feerate, at least, of the given value.
929937
///
930-
/// Returns `Err(())` if the output value is greater than the input value minus required fee or
931-
/// if a descriptor was duplicated.
938+
/// Returns `Err(())` if the output value is greater than the input value minus required fee,
939+
/// if a descriptor was duplicated, or if an output descriptor script_pubkey
940+
/// does not match the one we can spend.
932941
///
933942
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
934943
///
@@ -996,15 +1005,15 @@ impl KeysManager {
9961005
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
9971006
descriptor.channel_keys_id));
9981007
}
999-
spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap();
1008+
spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?;
10001009
},
10011010
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
10021011
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
10031012
keys_cache = Some((
10041013
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
10051014
descriptor.channel_keys_id));
10061015
}
1007-
spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap();
1016+
spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?;
10081017
},
10091018
SpendableOutputDescriptor::StaticOutput { ref output, .. } => {
10101019
let derivation_idx = if output.script_pubkey == self.destination_script {
@@ -1029,6 +1038,10 @@ impl KeysManager {
10291038
assert_eq!(pubkey.key, self.shutdown_pubkey);
10301039
}
10311040
let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
1041+
let payment_script = bitcoin::Address::p2wpkh(&pubkey, Network::Testnet).expect("uncompressed key found").script_pubkey();
1042+
1043+
if payment_script != output.script_pubkey { return Err(()); };
1044+
10321045
let sighash = hash_to_message!(&bip143::SigHashCache::new(&spend_tx).signature_hash(input_idx, &witness_script, output.value, SigHashType::All)[..]);
10331046
let sig = secp_ctx.sign(&sighash, &secret.private_key.key);
10341047
spend_tx.input[input_idx].witness.push(sig.serialize_der().to_vec());

lightning/src/ln/channelmanager.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6215,6 +6215,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
62156215
write_tlv_fields!(writer, {
62166216
(1, pending_outbound_payments_no_retry, required),
62176217
(3, pending_outbound_payments, required),
6218+
(5, self.our_network_pubkey, required)
62186219
});
62196220

62206221
Ok(())
@@ -6509,10 +6510,13 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
65096510
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
65106511
let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
65116512
let mut pending_outbound_payments = None;
6513+
let mut received_network_pubkey: Option<PublicKey> = None;
65126514
read_tlv_fields!(reader, {
65136515
(1, pending_outbound_payments_no_retry, option),
65146516
(3, pending_outbound_payments, option),
6517+
(5, received_network_pubkey, option)
65156518
});
6519+
65166520
if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
65176521
pending_outbound_payments = Some(pending_outbound_payments_compat);
65186522
} else if pending_outbound_payments.is_none() {
@@ -6575,6 +6579,14 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
65756579
pending_events_read.append(&mut channel_closures);
65766580
}
65776581

6582+
let our_network_pubkey = PublicKey::from_secret_key(&secp_ctx, &args.keys_manager.get_node_secret());
6583+
if let Some(network_pubkey) = received_network_pubkey {
6584+
if network_pubkey != our_network_pubkey {
6585+
log_error!(args.logger, "Key that was generated does not match the existing key.");
6586+
return Err(DecodeError::InvalidValue);
6587+
}
6588+
}
6589+
65786590
let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material();
65796591
let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
65806592
let channel_manager = ChannelManager {
@@ -6597,7 +6609,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
65976609
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
65986610

65996611
our_network_key: args.keys_manager.get_node_secret(),
6600-
our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &args.keys_manager.get_node_secret()),
6612+
our_network_pubkey,
66016613
secp_ctx,
66026614

66036615
last_node_announcement_serial: AtomicUsize::new(last_node_announcement_serial as usize),

0 commit comments

Comments
 (0)