Skip to content

Commit 19fdde2

Browse files
authored
Merge pull request #1250 from vss96/sanity_check
Sanity check for ChannelManager and KeysInterface
2 parents b1bdba5 + 2f01d68 commit 19fdde2

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)