Skip to content

Commit 262072d

Browse files
committed
Provide inbound HTLC preimages to the EcdsaChannelSigner
The VLS signer has a desire to see preimages for resolved forwarded HTLCs when they are first claimed by us, even if that claim was for the inbound edge (where claiming strictly increases our balance). Luckily, providing that information is rather trivial, which we do here. Fixes lightningdevkit#2356
1 parent 2659a23 commit 262072d

File tree

6 files changed

+44
-30
lines changed

6 files changed

+44
-30
lines changed

lightning/src/ln/channel.rs

+20-10
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,8 @@ struct CommitmentStats<'a> {
479479
htlcs_included: Vec<(HTLCOutputInCommitment, Option<&'a HTLCSource>)>, // the list of HTLCs (dust HTLCs *included*) which were not ignored when building the transaction
480480
local_balance_msat: u64, // local balance before fees but considering dust limits
481481
remote_balance_msat: u64, // remote balance before fees but considering dust limits
482-
preimages: Vec<PaymentPreimage>, // preimages for successful offered HTLCs since last commitment
482+
outbound_htlc_preimages: Vec<PaymentPreimage>, // preimages for successful offered HTLCs since last commitment
483+
inbound_htlc_preimages: Vec<PaymentPreimage>, // preimages for successful received HTLCs since last commitment
483484
}
484485

485486
/// Used when calculating whether we or the remote can afford an additional HTLC.
@@ -1393,6 +1394,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
13931394
}
13941395
}
13951396

1397+
let mut inbound_htlc_preimages: Vec<PaymentPreimage> = Vec::new();
1398+
13961399
for ref htlc in self.pending_inbound_htlcs.iter() {
13971400
let (include, state_name) = match htlc.state {
13981401
InboundHTLCState::RemoteAnnounced(_) => (!generated_by_local, "RemoteAnnounced"),
@@ -1410,7 +1413,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
14101413
match &htlc.state {
14111414
&InboundHTLCState::LocalRemoved(ref reason) => {
14121415
if generated_by_local {
1413-
if let &InboundHTLCRemovalReason::Fulfill(_) = reason {
1416+
if let &InboundHTLCRemovalReason::Fulfill(preimage) = reason {
1417+
inbound_htlc_preimages.push(preimage);
14141418
value_to_self_msat_offset += htlc.amount_msat as i64;
14151419
}
14161420
}
@@ -1420,7 +1424,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
14201424
}
14211425
}
14221426

1423-
let mut preimages: Vec<PaymentPreimage> = Vec::new();
1427+
1428+
let mut outbound_htlc_preimages: Vec<PaymentPreimage> = Vec::new();
14241429

14251430
for ref htlc in self.pending_outbound_htlcs.iter() {
14261431
let (include, state_name) = match htlc.state {
@@ -1439,7 +1444,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
14391444
};
14401445

14411446
if let Some(preimage) = preimage_opt {
1442-
preimages.push(preimage);
1447+
outbound_htlc_preimages.push(preimage);
14431448
}
14441449

14451450
if include {
@@ -1545,7 +1550,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
15451550
htlcs_included,
15461551
local_balance_msat: value_to_self_msat as u64,
15471552
remote_balance_msat: value_to_remote_msat as u64,
1548-
preimages
1553+
inbound_htlc_preimages,
1554+
outbound_htlc_preimages,
15491555
}
15501556
}
15511557

@@ -2141,7 +2147,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
21412147
let signature = match &self.holder_signer {
21422148
// TODO (taproot|arik): move match into calling method for Taproot
21432149
ChannelSignerType::Ecdsa(ecdsa) => {
2144-
ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
2150+
ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), Vec::new(), &self.secp_ctx)
21452151
.map(|(sig, _)| sig).ok()?
21462152
},
21472153
// TODO (taproot|arik)
@@ -2178,7 +2184,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
21782184
match &self.holder_signer {
21792185
// TODO (arik): move match into calling method for Taproot
21802186
ChannelSignerType::Ecdsa(ecdsa) => {
2181-
let funding_signed = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
2187+
let funding_signed = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), Vec::new(), &self.secp_ctx)
21822188
.map(|(signature, _)| msgs::FundingSigned {
21832189
channel_id: self.channel_id(),
21842190
signature,
@@ -3208,7 +3214,7 @@ impl<SP: Deref> Channel<SP> where
32083214
self.context.counterparty_funding_pubkey()
32093215
);
32103216

3211-
self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.preimages)
3217+
self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.outbound_htlc_preimages)
32123218
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
32133219

32143220
// Update state now that we've passed all the can-fail calls...
@@ -5709,8 +5715,12 @@ impl<SP: Deref> Channel<SP> where
57095715
htlcs.push(htlc);
57105716
}
57115717

5712-
let res = ecdsa.sign_counterparty_commitment(&commitment_stats.tx, commitment_stats.preimages, &self.context.secp_ctx)
5713-
.map_err(|_| ChannelError::Ignore("Failed to get signatures for new commitment_signed".to_owned()))?;
5718+
let res = ecdsa.sign_counterparty_commitment(
5719+
&commitment_stats.tx,
5720+
commitment_stats.inbound_htlc_preimages,
5721+
commitment_stats.outbound_htlc_preimages,
5722+
&self.context.secp_ctx,
5723+
).map_err(|_| ChannelError::Ignore("Failed to get signatures for new commitment_signed".to_owned()))?;
57145724
signature = res.0;
57155725
htlc_signatures = res.1;
57165726

lightning/src/ln/functional_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ fn test_update_fee_that_funder_cannot_afford() {
746746
&mut htlcs,
747747
&local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable()
748748
);
749-
local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
749+
local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), Vec::new(), &secp_ctx).unwrap()
750750
};
751751

752752
let commit_signed_msg = msgs::CommitmentSigned {
@@ -1493,7 +1493,7 @@ fn test_fee_spike_violation_fails_htlc() {
14931493
&mut vec![(accepted_htlc_info, ())],
14941494
&local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable()
14951495
);
1496-
local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
1496+
local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), Vec::new(), &secp_ctx).unwrap()
14971497
};
14981498

14991499
let commit_signed_msg = msgs::CommitmentSigned {

lightning/src/sign/ecdsa.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,18 @@ pub trait EcdsaChannelSigner: ChannelSigner {
2929
/// Policy checks should be implemented in this function, including checking the amount
3030
/// sent to us and checking the HTLCs.
3131
///
32-
/// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
33-
/// A validating signer should ensure that an HTLC output is removed only when the matching
34-
/// preimage is provided, or when the value to holder is restored.
32+
/// The preimages of outbound and inbound HTLCs that were fulfilled since the last commitment
33+
/// are provided. A validating signer should ensure that an outbound HTLC output is removed
34+
/// only when the matching preimage is provided and after the corresponding inbound HTLC has
35+
/// been removed for forwarded payments.
3536
///
3637
/// Note that all the relevant preimages will be provided, but there may also be additional
3738
/// irrelevant or duplicate preimages.
3839
//
3940
// TODO: Document the things someone using this interface should enforce before signing.
4041
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction,
41-
preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>
42+
inbound_htlc_preimages: Vec<PaymentPreimage>,
43+
outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>,
4244
) -> Result<(Signature, Vec<Signature>), ()>;
4345
/// Validate the counterparty's revocation.
4446
///

lightning/src/sign/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -594,14 +594,14 @@ pub trait ChannelSigner {
594594
/// Policy checks should be implemented in this function, including checking the amount
595595
/// sent to us and checking the HTLCs.
596596
///
597-
/// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
597+
/// The preimages of outbound HTLCs that were fulfilled since the last commitment are provided.
598598
/// A validating signer should ensure that an HTLC output is removed only when the matching
599599
/// preimage is provided, or when the value to holder is restored.
600600
///
601601
/// Note that all the relevant preimages will be provided, but there may also be additional
602602
/// irrelevant or duplicate preimages.
603603
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction,
604-
preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
604+
outbound_htlc_preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
605605

606606
/// Returns the holder's channel public keys and basepoints.
607607
fn pubkeys(&self) -> &ChannelPublicKeys;
@@ -1080,7 +1080,7 @@ impl ChannelSigner for InMemorySigner {
10801080
chan_utils::build_commitment_secret(&self.commitment_seed, idx)
10811081
}
10821082

1083-
fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
1083+
fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _outbound_htlc_preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
10841084
Ok(())
10851085
}
10861086

@@ -1102,7 +1102,7 @@ impl ChannelSigner for InMemorySigner {
11021102
const MISSING_PARAMS_ERR: &'static str = "ChannelSigner::provide_channel_parameters must be called before signing operations";
11031103

11041104
impl EcdsaChannelSigner for InMemorySigner {
1105-
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
1105+
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _inbound_htlc_preimages: Vec<PaymentPreimage>, _outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
11061106
let trusted_tx = commitment_tx.trust();
11071107
let keys = trusted_tx.keys();
11081108

@@ -1254,7 +1254,7 @@ impl TaprootChannelSigner for InMemorySigner {
12541254
todo!()
12551255
}
12561256

1257-
fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction, preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<All>) -> Result<(PartialSignatureWithNonce, Vec<schnorr::Signature>), ()> {
1257+
fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec<PaymentPreimage>, outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<All>) -> Result<(PartialSignatureWithNonce, Vec<schnorr::Signature>), ()> {
12581258
todo!()
12591259
}
12601260

lightning/src/sign/taproot.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,19 @@ pub trait TaprootChannelSigner: ChannelSigner {
2727
/// Policy checks should be implemented in this function, including checking the amount
2828
/// sent to us and checking the HTLCs.
2929
///
30-
/// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
31-
/// A validating signer should ensure that an HTLC output is removed only when the matching
32-
/// preimage is provided, or when the value to holder is restored.
30+
/// The preimages of outbound and inbound HTLCs that were fulfilled since the last commitment
31+
/// are provided. A validating signer should ensure that an outbound HTLC output is removed
32+
/// only when the matching preimage is provided and after the corresponding inbound HTLC has
33+
/// been removed for forwarded payments.
3334
///
3435
/// Note that all the relevant preimages will be provided, but there may also be additional
3536
/// irrelevant or duplicate preimages.
3637
//
3738
// TODO: Document the things someone using this interface should enforce before signing.
3839
fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce,
39-
commitment_tx: &CommitmentTransaction, preimages: Vec<PaymentPreimage>,
40-
secp_ctx: &Secp256k1<secp256k1::All>,
40+
commitment_tx: &CommitmentTransaction,
41+
inbound_htlc_preimages: Vec<PaymentPreimage>,
42+
outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>,
4143
) -> Result<(PartialSignatureWithNonce, Vec<Signature>), ()>;
4244

4345
/// Creates a signature for a holder's commitment transaction.

lightning/src/util/test_channel_signer.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ impl ChannelSigner for TestChannelSigner {
138138
self.inner.release_commitment_secret(idx)
139139
}
140140

141-
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, _preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
141+
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, _outbound_htlc_preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
142142
let mut state = self.state.lock().unwrap();
143143
let idx = holder_tx.commitment_number();
144144
assert!(idx == state.last_holder_commitment || idx == state.last_holder_commitment - 1, "expecting to validate the current or next holder commitment - trying {}, current {}", idx, state.last_holder_commitment);
@@ -156,7 +156,7 @@ impl ChannelSigner for TestChannelSigner {
156156
}
157157

158158
impl EcdsaChannelSigner for TestChannelSigner {
159-
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
159+
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec<PaymentPreimage>, outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
160160
self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx);
161161

162162
{
@@ -175,7 +175,7 @@ impl EcdsaChannelSigner for TestChannelSigner {
175175
state.last_counterparty_commitment = cmp::min(last_commitment_number, actual_commitment_number)
176176
}
177177

178-
Ok(self.inner.sign_counterparty_commitment(commitment_tx, preimages, secp_ctx).unwrap())
178+
Ok(self.inner.sign_counterparty_commitment(commitment_tx, inbound_htlc_preimages, outbound_htlc_preimages, secp_ctx).unwrap())
179179
}
180180

181181
fn validate_counterparty_revocation(&self, idx: u64, _secret: &SecretKey) -> Result<(), ()> {
@@ -288,7 +288,7 @@ impl TaprootChannelSigner for TestChannelSigner {
288288
todo!()
289289
}
290290

291-
fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction, preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<All>) -> Result<(PartialSignatureWithNonce, Vec<secp256k1::schnorr::Signature>), ()> {
291+
fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec<PaymentPreimage>, outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<All>) -> Result<(PartialSignatureWithNonce, Vec<secp256k1::schnorr::Signature>), ()> {
292292
todo!()
293293
}
294294

0 commit comments

Comments
 (0)