Skip to content

Commit 1422b71

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 41785dc commit 1422b71

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
@@ -881,16 +881,18 @@ impl ChannelMonitor {
881881
if commitment_number >= self.get_min_seen_secret() {
882882
let secret = self.get_secret(commitment_number).unwrap();
883883
let per_commitment_key = ignore_error!(SecretKey::from_slice(&self.secp_ctx, &secret));
884-
let (revocation_pubkey, b_htlc_key) = match self.key_storage {
885-
KeyStorage::PrivMode { ref revocation_base_key, ref htlc_base_key, .. } => {
884+
let (revocation_pubkey, b_htlc_key, local_payment_key) = match self.key_storage {
885+
KeyStorage::PrivMode { ref revocation_base_key, ref htlc_base_key, ref payment_base_key, .. } => {
886886
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
887887
(ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &PublicKey::from_secret_key(&self.secp_ctx, &revocation_base_key))),
888-
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &PublicKey::from_secret_key(&self.secp_ctx, &htlc_base_key))))
888+
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &PublicKey::from_secret_key(&self.secp_ctx, &htlc_base_key))),
889+
Some(ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &payment_base_key))))
889890
},
890891
KeyStorage::SigsMode { ref revocation_base_key, ref htlc_base_key, .. } => {
891892
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
892893
(ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &revocation_base_key)),
893-
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &htlc_base_key)))
894+
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &htlc_base_key)),
895+
None)
894896
},
895897
};
896898
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()));
@@ -902,6 +904,13 @@ impl ChannelMonitor {
902904
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key);
903905
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
904906

907+
let local_payment_p2wpkh = if let Some(payment_key) = local_payment_key {
908+
// Note that the Network here is ignored as we immediately drop the address for the
909+
// script_pubkey version.
910+
let payment_hash160 = Hash160::from_data(&PublicKey::from_secret_key(&self.secp_ctx, &payment_key).serialize());
911+
Some(Builder::new().push_opcode(opcodes::All::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script())
912+
} else { None };
913+
905914
let mut total_value = 0;
906915
let mut values = Vec::new();
907916
let mut inputs = Vec::new();
@@ -921,23 +930,12 @@ impl ChannelMonitor {
921930
htlc_idxs.push(None);
922931
values.push(outp.value);
923932
total_value += outp.value;
924-
} else if outp.script_pubkey.is_v0_p2wpkh() {
925-
match self.key_storage {
926-
KeyStorage::PrivMode { ref payment_base_key, .. } => {
927-
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
928-
if let Ok(local_key) = chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &payment_base_key) {
929-
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
930-
outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 },
931-
key: local_key,
932-
output: outp.clone(),
933-
});
934-
}
935-
}
936-
KeyStorage::SigsMode { .. } => {
937-
//TODO: we need to ensure an offline client will generate the event when it
938-
// cames back online after only the watchtower saw the transaction
939-
}
940-
}
933+
} else if Some(&outp.script_pubkey) == local_payment_p2wpkh.as_ref() {
934+
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
935+
outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 },
936+
key: local_payment_key.unwrap(),
937+
output: outp.clone(),
938+
});
941939
}
942940
}
943941

@@ -1075,7 +1073,6 @@ impl ChannelMonitor {
10751073
Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)),
10761074
};
10771075

1078-
10791076
for (idx, outp) in tx.output.iter().enumerate() {
10801077
if outp.script_pubkey.is_v0_p2wpkh() {
10811078
match self.key_storage {
@@ -1087,11 +1084,8 @@ impl ChannelMonitor {
10871084
output: outp.clone(),
10881085
});
10891086
}
1090-
}
1091-
KeyStorage::SigsMode { .. } => {
1092-
//TODO: we need to ensure an offline client will generate the event when it
1093-
// cames back online after only the watchtower saw the transaction
1094-
}
1087+
},
1088+
KeyStorage::SigsMode { .. } => {}
10951089
}
10961090
break; // Only to_remote ouput is claimable
10971091
}

0 commit comments

Comments
 (0)