Skip to content

Commit 01c8e4f

Browse files
authored
Merge pull request #163 from ariard/claim_revoked_htlc_tx
Implement claiming of revoked HTLC transactions by ChannelMonitor
2 parents 77c2622 + 383bd90 commit 01c8e4f

File tree

3 files changed

+141
-26
lines changed

3 files changed

+141
-26
lines changed

src/ln/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ impl Channel {
570570
&PublicKey::from_secret_key(&secp_ctx, &chan_keys.delayed_payment_base_key),
571571
&chan_keys.htlc_base_key,
572572
BREAKDOWN_TIMEOUT, our_channel_monitor_claim_script);
573-
channel_monitor.set_their_htlc_base_key(&msg.htlc_basepoint);
573+
channel_monitor.set_their_base_keys(&msg.htlc_basepoint, &msg.delayed_payment_basepoint);
574574
channel_monitor.set_their_to_self_delay(msg.to_self_delay);
575575

576576
let mut chan = Channel {
@@ -1236,7 +1236,7 @@ impl Channel {
12361236
// max_accepted_htlcs too small
12371237
// dust_limit_satoshis too small
12381238

1239-
self.channel_monitor.set_their_htlc_base_key(&msg.htlc_basepoint);
1239+
self.channel_monitor.set_their_base_keys(&msg.htlc_basepoint, &msg.delayed_payment_basepoint);
12401240

12411241
self.their_dust_limit_satoshis = msg.dust_limit_satoshis;
12421242
self.their_max_htlc_value_in_flight_msat = cmp::min(msg.max_htlc_value_in_flight_msat, self.channel_value_satoshis * 1000);

src/ln/channelmanager.rs

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3093,9 +3093,11 @@ mod tests {
30933093

30943094
#[derive(PartialEq)]
30953095
enum HTLCType { NONE, TIMEOUT, SUCCESS }
3096-
fn test_txn_broadcast(node: &Node, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction), commitment_tx: Option<Transaction>, has_htlc_tx: HTLCType) -> Vec<Transaction> {
3096+
#[derive(PartialEq)]
3097+
enum PenaltyType { NONE, HTLC }
3098+
fn test_txn_broadcast(node: &Node, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction), commitment_tx: Option<Transaction>, revoked_tx: Option<Transaction>, has_htlc_tx: HTLCType, has_penalty_tx: PenaltyType) -> Vec<Transaction> {
30973099
let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
3098-
assert!(node_txn.len() >= if commitment_tx.is_some() { 0 } else { 1 } + if has_htlc_tx == HTLCType::NONE { 0 } else { 1 });
3100+
assert!(node_txn.len() >= if has_htlc_tx == HTLCType::NONE { 0 } else { 1 } + if has_penalty_tx == PenaltyType::NONE { 0 } else { 1 });
30993101

31003102
let mut res = Vec::with_capacity(2);
31013103

@@ -3111,7 +3113,9 @@ mod tests {
31113113
}
31123114
}
31133115
}
3114-
assert_eq!(res.len(), 1);
3116+
if !revoked_tx.is_some() && !(has_penalty_tx == PenaltyType::HTLC) {
3117+
assert_eq!(res.len(), 1);
3118+
}
31153119

31163120
if has_htlc_tx != HTLCType::NONE {
31173121
for tx in node_txn.iter() {
@@ -3130,6 +3134,20 @@ mod tests {
31303134
}
31313135
assert_eq!(res.len(), 2);
31323136
}
3137+
3138+
if has_penalty_tx == PenaltyType::HTLC {
3139+
let revoked_tx = revoked_tx.unwrap();
3140+
for tx in node_txn.iter() {
3141+
if tx.input.len() == 1 && tx.input[0].previous_output.txid == revoked_tx.txid() {
3142+
let mut funding_tx_map = HashMap::new();
3143+
funding_tx_map.insert(revoked_tx.txid(), revoked_tx.clone());
3144+
tx.verify(&funding_tx_map).unwrap();
3145+
res.push(tx.clone());
3146+
break;
3147+
}
3148+
}
3149+
assert_eq!(res.len(), 1);
3150+
}
31333151
node_txn.clear();
31343152
res
31353153
}
@@ -3207,10 +3225,10 @@ mod tests {
32073225
// Simple case with no pending HTLCs:
32083226
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), true);
32093227
{
3210-
let mut node_txn = test_txn_broadcast(&nodes[1], &chan_1, None, HTLCType::NONE);
3228+
let mut node_txn = test_txn_broadcast(&nodes[1], &chan_1, None, None, HTLCType::NONE, PenaltyType::NONE);
32113229
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
32123230
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn.drain(..).next().unwrap()] }, 1);
3213-
test_txn_broadcast(&nodes[0], &chan_1, None, HTLCType::NONE);
3231+
test_txn_broadcast(&nodes[0], &chan_1, None, None, HTLCType::NONE, PenaltyType::NONE);
32143232
}
32153233
get_announce_close_broadcast_events(&nodes, 0, 1);
32163234
assert_eq!(nodes[0].node.list_channels().len(), 0);
@@ -3222,10 +3240,10 @@ mod tests {
32223240
// Simple case of one pending HTLC to HTLC-Timeout
32233241
nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id(), true);
32243242
{
3225-
let mut node_txn = test_txn_broadcast(&nodes[1], &chan_2, None, HTLCType::TIMEOUT);
3243+
let mut node_txn = test_txn_broadcast(&nodes[1], &chan_2, None, None, HTLCType::TIMEOUT, PenaltyType::NONE);
32263244
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
32273245
nodes[2].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn.drain(..).next().unwrap()] }, 1);
3228-
test_txn_broadcast(&nodes[2], &chan_2, None, HTLCType::NONE);
3246+
test_txn_broadcast(&nodes[2], &chan_2, None, None, HTLCType::NONE, PenaltyType::NONE);
32293247
}
32303248
get_announce_close_broadcast_events(&nodes, 1, 2);
32313249
assert_eq!(nodes[1].node.list_channels().len(), 0);
@@ -3259,7 +3277,7 @@ mod tests {
32593277
// HTLC-Timeout and a nodes[3] claim against it (+ its own announces)
32603278
nodes[2].node.peer_disconnected(&nodes[3].node.get_our_node_id(), true);
32613279
{
3262-
let node_txn = test_txn_broadcast(&nodes[2], &chan_3, None, HTLCType::TIMEOUT);
3280+
let node_txn = test_txn_broadcast(&nodes[2], &chan_3, None, None, HTLCType::TIMEOUT, PenaltyType::NONE);
32633281

32643282
// Claim the payment on nodes[3], giving it knowledge of the preimage
32653283
claim_funds!(nodes[3], nodes[2], payment_preimage_1);
@@ -3284,9 +3302,9 @@ mod tests {
32843302
nodes[3].chain_monitor.block_connected_checked(&header, i, &Vec::new()[..], &[0; 0]);
32853303
}
32863304

3287-
let node_txn = test_txn_broadcast(&nodes[3], &chan_4, None, HTLCType::TIMEOUT);
3305+
let node_txn = test_txn_broadcast(&nodes[3], &chan_4, None, None, HTLCType::TIMEOUT, PenaltyType::NONE);
32883306

3289-
// Claim the payment on nodes[3], giving it knowledge of the preimage
3307+
// Claim the payment on nodes[4], giving it knowledge of the preimage
32903308
claim_funds!(nodes[4], nodes[3], payment_preimage_2);
32913309

32923310
header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
@@ -3296,7 +3314,7 @@ mod tests {
32963314
nodes[4].chain_monitor.block_connected_checked(&header, i, &Vec::new()[..], &[0; 0]);
32973315
}
32983316

3299-
test_txn_broadcast(&nodes[4], &chan_4, None, HTLCType::SUCCESS);
3317+
test_txn_broadcast(&nodes[4], &chan_4, None, None, HTLCType::SUCCESS, PenaltyType::NONE);
33003318

33013319
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
33023320
nodes[4].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[0].clone()] }, TEST_FINAL_CLTV - 5);
@@ -3331,15 +3349,13 @@ mod tests {
33313349
node_txn[0].verify(&funding_tx_map).unwrap();
33323350
node_txn.swap_remove(0);
33333351
}
3334-
test_txn_broadcast(&nodes[1], &chan_5, None, HTLCType::NONE);
3352+
test_txn_broadcast(&nodes[1], &chan_5, None, None, HTLCType::NONE, PenaltyType::NONE);
33353353

33363354
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
3337-
let node_txn = test_txn_broadcast(&nodes[0], &chan_5, Some(revoked_local_txn[0].clone()), HTLCType::TIMEOUT);
3355+
let node_txn = test_txn_broadcast(&nodes[0], &chan_5, Some(revoked_local_txn[0].clone()), None, HTLCType::TIMEOUT, PenaltyType::NONE);
33383356
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
33393357
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
3340-
3341-
//TODO: At this point nodes[1] should claim the revoked HTLC-Timeout output, but that's
3342-
//not yet implemented in ChannelMonitor
3358+
test_txn_broadcast(&nodes[1], &chan_5, None, Some(node_txn[1].clone()), HTLCType::NONE, PenaltyType::HTLC);
33433359
}
33443360
get_announce_close_broadcast_events(&nodes, 0, 1);
33453361
assert_eq!(nodes[0].node.list_channels().len(), 0);

src/ln/channelmonitor.rs

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ pub struct ChannelMonitor {
166166
key_storage: KeyStorage,
167167
delayed_payment_base_key: PublicKey,
168168
their_htlc_base_key: Option<PublicKey>,
169+
their_delayed_payment_base_key: Option<PublicKey>,
169170
// first is the idx of the first of the two revocation points
170171
their_cur_revocation_points: Option<(u64, PublicKey, Option<PublicKey>)>,
171172

@@ -207,6 +208,7 @@ impl Clone for ChannelMonitor {
207208
key_storage: self.key_storage.clone(),
208209
delayed_payment_base_key: self.delayed_payment_base_key.clone(),
209210
their_htlc_base_key: self.their_htlc_base_key.clone(),
211+
their_delayed_payment_base_key: self.their_delayed_payment_base_key.clone(),
210212
their_cur_revocation_points: self.their_cur_revocation_points.clone(),
211213

212214
our_to_self_delay: self.our_to_self_delay,
@@ -238,6 +240,7 @@ impl PartialEq for ChannelMonitor {
238240
self.key_storage != other.key_storage ||
239241
self.delayed_payment_base_key != other.delayed_payment_base_key ||
240242
self.their_htlc_base_key != other.their_htlc_base_key ||
243+
self.their_delayed_payment_base_key != other.their_delayed_payment_base_key ||
241244
self.their_cur_revocation_points != other.their_cur_revocation_points ||
242245
self.our_to_self_delay != other.our_to_self_delay ||
243246
self.their_to_self_delay != other.their_to_self_delay ||
@@ -274,6 +277,7 @@ impl ChannelMonitor {
274277
},
275278
delayed_payment_base_key: delayed_payment_base_key.clone(),
276279
their_htlc_base_key: None,
280+
their_delayed_payment_base_key: None,
277281
their_cur_revocation_points: None,
278282

279283
our_to_self_delay: our_to_self_delay,
@@ -478,8 +482,10 @@ impl ChannelMonitor {
478482
self.funding_txo = Some(funding_info);
479483
}
480484

481-
pub(super) fn set_their_htlc_base_key(&mut self, their_htlc_base_key: &PublicKey) {
485+
/// We log these base keys at channel opening to being able to rebuild redeemscript in case of leaked revoked commit tx
486+
pub(super) fn set_their_base_keys(&mut self, their_htlc_base_key: &PublicKey, their_delayed_payment_base_key: &PublicKey) {
482487
self.their_htlc_base_key = Some(their_htlc_base_key.clone());
488+
self.their_delayed_payment_base_key = Some(their_delayed_payment_base_key.clone());
483489
}
484490

485491
pub(super) fn set_their_to_self_delay(&mut self, their_to_self_delay: u16) {
@@ -531,6 +537,7 @@ impl ChannelMonitor {
531537

532538
res.extend_from_slice(&self.delayed_payment_base_key.serialize());
533539
res.extend_from_slice(&self.their_htlc_base_key.as_ref().unwrap().serialize());
540+
res.extend_from_slice(&self.their_delayed_payment_base_key.as_ref().unwrap().serialize());
534541

535542
match self.their_cur_revocation_points {
536543
Some((idx, pubkey, second_option)) => {
@@ -705,6 +712,7 @@ impl ChannelMonitor {
705712

706713
let delayed_payment_base_key = unwrap_obj!(PublicKey::from_slice(&secp_ctx, read_bytes!(33)));
707714
let their_htlc_base_key = Some(unwrap_obj!(PublicKey::from_slice(&secp_ctx, read_bytes!(33))));
715+
let their_delayed_payment_base_key = Some(unwrap_obj!(PublicKey::from_slice(&secp_ctx, read_bytes!(33))));
708716

709717
let their_cur_revocation_points = {
710718
let first_idx = byte_utils::slice_to_be48(read_bytes!(6));
@@ -867,6 +875,7 @@ impl ChannelMonitor {
867875
key_storage,
868876
delayed_payment_base_key,
869877
their_htlc_base_key,
878+
their_delayed_payment_base_key,
870879
their_cur_revocation_points,
871880

872881
our_to_self_delay,
@@ -915,8 +924,7 @@ impl ChannelMonitor {
915924
/// Attempts to claim a remote commitment transaction's outputs using the revocation key and
916925
/// data in remote_claimable_outpoints. Will directly claim any HTLC outputs which expire at a
917926
/// height > height + CLTV_SHARED_CLAIM_BUFFER. In any case, will install monitoring for
918-
/// HTLC-Success/HTLC-Timeout transactions, and claim them using the revocation key (if
919-
/// applicable) as well.
927+
/// HTLC-Success/HTLC-Timeout transactions.
920928
fn check_spend_remote_transaction(&self, tx: &Transaction, height: u32) -> (Vec<Transaction>, (Sha256dHash, Vec<TxOut>)) {
921929
// Most secp and related errors trying to create keys means we have no hope of constructing
922930
// a spend transaction...so we return no transactions to broadcast
@@ -1196,13 +1204,97 @@ impl ChannelMonitor {
11961204
txn_to_broadcast.push(spend_tx);
11971205
}
11981206
}
1199-
} else {
1200-
//TODO: For each input check if its in our remote_commitment_txn_on_chain map!
12011207
}
12021208

12031209
(txn_to_broadcast, (commitment_txid, watch_outputs))
12041210
}
12051211

1212+
/// Attempst to claim a remote HTLC-Success/HTLC-Timeout s outputs using the revocation key
1213+
fn check_spend_remote_htlc(&self, tx: &Transaction, commitment_number: u64) -> Vec<Transaction> {
1214+
let mut txn_to_broadcast = Vec::new();
1215+
1216+
let htlc_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
1217+
1218+
macro_rules! ignore_error {
1219+
( $thing : expr ) => {
1220+
match $thing {
1221+
Ok(a) => a,
1222+
Err(_) => return txn_to_broadcast
1223+
}
1224+
};
1225+
}
1226+
1227+
let secret = self.get_secret(commitment_number).unwrap();
1228+
let per_commitment_key = ignore_error!(SecretKey::from_slice(&self.secp_ctx, &secret));
1229+
let revocation_pubkey = match self.key_storage {
1230+
KeyStorage::PrivMode { ref revocation_base_key, .. } => {
1231+
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
1232+
ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &PublicKey::from_secret_key(&self.secp_ctx, &revocation_base_key)))
1233+
},
1234+
KeyStorage::SigsMode { ref revocation_base_key, .. } => {
1235+
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
1236+
ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &revocation_base_key))
1237+
},
1238+
};
1239+
let delayed_key = match self.their_delayed_payment_base_key {
1240+
None => return txn_to_broadcast,
1241+
Some(their_delayed_payment_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &their_delayed_payment_base_key)),
1242+
};
1243+
let redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.their_to_self_delay.unwrap(), &delayed_key);
1244+
let revokeable_p2wsh = redeemscript.to_v0_p2wsh();
1245+
1246+
let mut inputs = Vec::new();
1247+
let mut amount = 0;
1248+
1249+
if tx.output[0].script_pubkey == revokeable_p2wsh { //HTLC transactions have one txin, one txout
1250+
inputs.push(TxIn {
1251+
previous_output: BitcoinOutPoint {
1252+
txid: htlc_txid,
1253+
vout: 0,
1254+
},
1255+
script_sig: Script::new(),
1256+
sequence: 0xfffffffd,
1257+
witness: Vec::new(),
1258+
});
1259+
amount = tx.output[0].value;
1260+
}
1261+
1262+
if !inputs.is_empty() {
1263+
let outputs = vec!(TxOut {
1264+
script_pubkey: self.destination_script.clone(),
1265+
value: amount, //TODO: - fee
1266+
});
1267+
1268+
let mut spend_tx = Transaction {
1269+
version: 2,
1270+
lock_time: 0,
1271+
input: inputs,
1272+
output: outputs,
1273+
};
1274+
1275+
1276+
let sighash_parts = bip143::SighashComponents::new(&spend_tx);
1277+
1278+
let sig = match self.key_storage {
1279+
KeyStorage::PrivMode { ref revocation_base_key, .. } => {
1280+
let sighash = ignore_error!(Message::from_slice(&sighash_parts.sighash_all(&spend_tx.input[0], &redeemscript, amount)[..]));
1281+
let revocation_key = ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &revocation_base_key));
1282+
self.secp_ctx.sign(&sighash, &revocation_key)
1283+
}
1284+
KeyStorage::SigsMode { .. } => {
1285+
unimplemented!();
1286+
}
1287+
};
1288+
spend_tx.input[0].witness.push(sig.serialize_der(&self.secp_ctx).to_vec());
1289+
spend_tx.input[0].witness[0].push(SigHashType::All as u8);
1290+
spend_tx.input[0].witness.push(vec!(1));
1291+
spend_tx.input[0].witness.push(redeemscript.into_bytes());
1292+
1293+
txn_to_broadcast.push(spend_tx);
1294+
}
1295+
txn_to_broadcast
1296+
}
1297+
12061298
fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx) -> Vec<Transaction> {
12071299
let mut res = Vec::with_capacity(local_tx.htlc_outputs.len());
12081300

@@ -1264,19 +1356,26 @@ impl ChannelMonitor {
12641356
fn block_connected(&self, txn_matched: &[&Transaction], height: u32, broadcaster: &BroadcasterInterface)-> Vec<(Sha256dHash, Vec<TxOut>)> {
12651357
let mut watch_outputs = Vec::new();
12661358
for tx in txn_matched {
1359+
let mut txn: Vec<Transaction> = Vec::new();
12671360
for txin in tx.input.iter() {
12681361
if self.funding_txo.is_none() || (txin.previous_output.txid == self.funding_txo.as_ref().unwrap().0.txid && txin.previous_output.vout == self.funding_txo.as_ref().unwrap().0.index as u32) {
1269-
let (mut txn, new_outputs) = self.check_spend_remote_transaction(tx, height);
1362+
let (remote_txn, new_outputs) = self.check_spend_remote_transaction(tx, height);
1363+
txn = remote_txn;
12701364
if !new_outputs.1.is_empty() {
12711365
watch_outputs.push(new_outputs);
12721366
}
12731367
if txn.is_empty() {
12741368
txn = self.check_spend_local_transaction(tx, height);
12751369
}
1276-
for tx in txn.iter() {
1277-
broadcaster.broadcast_transaction(tx);
1370+
} else {
1371+
let remote_commitment_txn_on_chain = self.remote_commitment_txn_on_chain.lock().unwrap();
1372+
for commitment_number in remote_commitment_txn_on_chain.get(&txin.previous_output.txid) {
1373+
txn = self.check_spend_remote_htlc(tx, *commitment_number);
12781374
}
12791375
}
1376+
for tx in txn.iter() {
1377+
broadcaster.broadcast_transaction(tx);
1378+
}
12801379
}
12811380
}
12821381
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {

0 commit comments

Comments
 (0)