Skip to content

Commit f633008

Browse files
committed
Check P2WPKH script against expected before gen'ing an output event
This fixes a bug in 3518f1f where we may generate an output event for a P2WPKH output which is not ours if the transaction has a sequence/lock_time combination which false-positives our remote tx detection. Also note that the TODO is removed as this should already be covered without issue if the client properly replays the chain on restart.
1 parent 6412380 commit f633008

File tree

1 file changed

+21
-27
lines changed

1 file changed

+21
-27
lines changed

src/ln/channelmonitor.rs

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -889,16 +889,18 @@ impl ChannelMonitor {
889889
if commitment_number >= self.get_min_seen_secret() {
890890
let secret = self.get_secret(commitment_number).unwrap();
891891
let per_commitment_key = ignore_error!(SecretKey::from_slice(&self.secp_ctx, &secret));
892-
let (revocation_pubkey, b_htlc_key) = match self.key_storage {
893-
KeyStorage::PrivMode { ref revocation_base_key, ref htlc_base_key, .. } => {
892+
let (revocation_pubkey, b_htlc_key, local_payment_key) = match self.key_storage {
893+
KeyStorage::PrivMode { ref revocation_base_key, ref htlc_base_key, ref payment_base_key, .. } => {
894894
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
895895
(ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &PublicKey::from_secret_key(&self.secp_ctx, &revocation_base_key))),
896-
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &PublicKey::from_secret_key(&self.secp_ctx, &htlc_base_key))))
896+
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &PublicKey::from_secret_key(&self.secp_ctx, &htlc_base_key))),
897+
Some(ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &payment_base_key))))
897898
},
898899
KeyStorage::SigsMode { ref revocation_base_key, ref htlc_base_key, .. } => {
899900
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
900901
(ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &revocation_base_key)),
901-
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &htlc_base_key)))
902+
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &htlc_base_key)),
903+
None)
902904
},
903905
};
904906
let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_delayed_payment_base_key.unwrap()));
@@ -910,6 +912,13 @@ impl ChannelMonitor {
910912
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key);
911913
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
912914

915+
let local_payment_p2wpkh = if let Some(payment_key) = local_payment_key {
916+
// Note that the Network here is ignored as we immediately drop the address for the
917+
// script_pubkey version.
918+
let payment_hash160 = Hash160::from_data(&PublicKey::from_secret_key(&self.secp_ctx, &payment_key).serialize());
919+
Some(Builder::new().push_opcode(opcodes::All::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script())
920+
} else { None };
921+
913922
let mut total_value = 0;
914923
let mut values = Vec::new();
915924
let mut inputs = Vec::new();
@@ -929,23 +938,12 @@ impl ChannelMonitor {
929938
htlc_idxs.push(None);
930939
values.push(outp.value);
931940
total_value += outp.value;
932-
} else if outp.script_pubkey.is_v0_p2wpkh() {
933-
match self.key_storage {
934-
KeyStorage::PrivMode { ref payment_base_key, .. } => {
935-
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
936-
if let Ok(local_key) = chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &payment_base_key) {
937-
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
938-
outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 },
939-
key: local_key,
940-
output: outp.clone(),
941-
});
942-
}
943-
}
944-
KeyStorage::SigsMode { .. } => {
945-
//TODO: we need to ensure an offline client will generate the event when it
946-
// cames back online after only the watchtower saw the transaction
947-
}
948-
}
941+
} else if Some(&outp.script_pubkey) == local_payment_p2wpkh.as_ref() {
942+
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
943+
outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 },
944+
key: local_payment_key.unwrap(),
945+
output: outp.clone(),
946+
});
949947
}
950948
}
951949

@@ -1083,7 +1081,6 @@ impl ChannelMonitor {
10831081
Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)),
10841082
};
10851083

1086-
10871084
for (idx, outp) in tx.output.iter().enumerate() {
10881085
if outp.script_pubkey.is_v0_p2wpkh() {
10891086
match self.key_storage {
@@ -1095,11 +1092,8 @@ impl ChannelMonitor {
10951092
output: outp.clone(),
10961093
});
10971094
}
1098-
}
1099-
KeyStorage::SigsMode { .. } => {
1100-
//TODO: we need to ensure an offline client will generate the event when it
1101-
// cames back online after only the watchtower saw the transaction
1102-
}
1095+
},
1096+
KeyStorage::SigsMode { .. } => {}
11031097
}
11041098
break; // Only to_remote ouput is claimable
11051099
}

0 commit comments

Comments
 (0)