Skip to content

Commit 55d131a

Browse files
committed
Enforce that revocation can only occur after we validated a new commitment
1 parent 905e28f commit 55d131a

File tree

5 files changed

+50
-11
lines changed

5 files changed

+50
-11
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ pub trait BaseSign {
211211
/// Note that the commitment number starts at (1 << 48) - 1 and counts backwards.
212212
// TODO: return a Result so we can signal a validation error
213213
fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
214+
/// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
215+
///
216+
/// This is required in order for the signer to make sure that releasing a commitment
217+
/// secret won't leave us without a broadcastable holder transaction.
218+
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction);
214219
/// Gets the holder's channel public keys and basepoints
215220
fn pubkeys(&self) -> &ChannelPublicKeys;
216221
/// Gets an arbitrary identifier describing the set of keys which are provided back to you in
@@ -558,6 +563,9 @@ impl BaseSign for InMemorySigner {
558563
chan_utils::build_commitment_secret(&self.commitment_seed, idx)
559564
}
560565

566+
fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) {
567+
}
568+
561569
fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
562570
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
563571

lightning/src/ln/channel.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,8 @@ impl<Signer: Sign> Channel<Signer> {
16811681
self.counterparty_funding_pubkey()
16821682
);
16831683

1684+
self.holder_signer.validate_holder_commitment(&holder_commitment_tx);
1685+
16841686
// Now that we're past error-generating stuff, update our local state:
16851687

16861688
let funding_redeemscript = self.get_funding_redeemscript();
@@ -1754,6 +1756,7 @@ impl<Signer: Sign> Channel<Signer> {
17541756
self.counterparty_funding_pubkey()
17551757
);
17561758

1759+
self.holder_signer.validate_holder_commitment(&holder_commitment_tx);
17571760

17581761
let funding_redeemscript = self.get_funding_redeemscript();
17591762
let funding_txo = self.get_funding_txo().unwrap();
@@ -2342,6 +2345,7 @@ impl<Signer: Sign> Channel<Signer> {
23422345
);
23432346

23442347
let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number - 1, &self.secp_ctx);
2348+
self.holder_signer.validate_holder_commitment(&holder_commitment_tx);
23452349
let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 1);
23462350

23472351
// Update state now that we've passed all the can-fail calls...

lightning/src/ln/functional_tests.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,9 @@ fn test_fee_spike_violation_fails_htlc() {
16151615
let chan_lock = nodes[0].node.channel_state.lock().unwrap();
16161616
let local_chan = chan_lock.by_id.get(&chan.2).unwrap();
16171617
let chan_signer = local_chan.get_signer();
1618+
// Make the signer believe we validated another commitment, so we can release the secret
1619+
chan_signer.get_enforcement_state().last_holder_commitment -= 1;
1620+
16181621
let pubkeys = chan_signer.pubkeys();
16191622
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
16201623
chan_signer.release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
@@ -8499,7 +8502,7 @@ fn test_counterparty_raa_skip_no_crash() {
84998502
// commitment transaction, we would have happily carried on and provided them the next
85008503
// commitment transaction based on one RAA forward. This would probably eventually have led to
85018504
// channel closure, but it would not have resulted in funds loss. Still, our
8502-
// EnforcingSigner would have paniced as it doesn't like jumps into the future. Here, we
8505+
// EnforcingSigner would have panicked as it doesn't like jumps into the future. Here, we
85038506
// check simply that the channel is closed in response to such an RAA, but don't check whether
85048507
// we decide to punish our counterparty for revoking their funds (as we don't currently
85058508
// implement that).
@@ -8510,11 +8513,19 @@ fn test_counterparty_raa_skip_no_crash() {
85108513
let channel_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
85118514

85128515
let mut guard = nodes[0].node.channel_state.lock().unwrap();
8513-
let keys = &guard.by_id.get_mut(&channel_id).unwrap().get_signer();
8516+
let keys = guard.by_id.get_mut(&channel_id).unwrap().get_signer();
8517+
85148518
const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
8519+
8520+
// Make signer believe we got a counterparty signature, so that it allows the revocation
8521+
keys.get_enforcement_state().last_holder_commitment -= 1;
85158522
let per_commitment_secret = keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER);
8523+
85168524
// Must revoke without gaps
8525+
keys.get_enforcement_state().last_holder_commitment -= 1;
85178526
keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1);
8527+
8528+
keys.get_enforcement_state().last_holder_commitment -= 1;
85188529
let next_per_commitment_point = PublicKey::from_secret_key(&Secp256k1::new(),
85198530
&SecretKey::from_slice(&keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2)).unwrap());
85208531

lightning/src/ln/msgs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ pub struct FundingCreated {
194194
pub funding_txid: Txid,
195195
/// The specific output index funding this channel
196196
pub funding_output_index: u16,
197-
/// The signature of the channel initiator (funder) on the funding transaction
197+
/// The signature of the channel initiator (funder) on the initial commitment transaction
198198
pub signature: Signature,
199199
}
200200

@@ -203,7 +203,7 @@ pub struct FundingCreated {
203203
pub struct FundingSigned {
204204
/// The channel ID
205205
pub channel_id: [u8; 32],
206-
/// The signature of the channel acceptor (fundee) on the funding transaction
206+
/// The signature of the channel acceptor (fundee) on the initial commitment transaction
207207
pub signature: Signature,
208208
}
209209

lightning/src/util/enforcing_trait_impls.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use io;
1515
use prelude::*;
1616
use core::cmp;
1717
use sync::{Mutex, Arc};
18+
#[cfg(test)] use sync::MutexGuard;
1819

1920
use bitcoin::blockdata::transaction::{Transaction, SigHashType};
2021
use bitcoin::util::bip143;
@@ -74,6 +75,11 @@ impl EnforcingSigner {
7475
disable_revocation_policy_check
7576
}
7677
}
78+
79+
#[cfg(test)]
80+
pub fn get_enforcement_state(&self) -> MutexGuard<EnforcementState> {
81+
self.state.lock().unwrap()
82+
}
7783
}
7884

7985
impl BaseSign for EnforcingSigner {
@@ -84,12 +90,20 @@ impl BaseSign for EnforcingSigner {
8490
fn release_commitment_secret(&self, idx: u64) -> [u8; 32] {
8591
{
8692
let mut state = self.state.lock().unwrap();
87-
assert!(idx == state.revoked_commitment || idx == state.revoked_commitment - 1, "can only revoke the current or next unrevoked commitment - trying {}, revoked {}", idx, state.revoked_commitment);
88-
state.revoked_commitment = idx;
93+
assert!(idx == state.last_holder_revoked_commitment || idx == state.last_holder_revoked_commitment - 1, "can only revoke the current or next unrevoked commitment - trying {}, revoked {}", idx, state.last_holder_revoked_commitment);
94+
assert!(idx > state.last_holder_commitment, "cannot revoke the last holder commitment - revoking {} last commit {}", idx, state.last_holder_commitment);
95+
state.last_holder_revoked_commitment = idx;
8996
}
9097
self.inner.release_commitment_secret(idx)
9198
}
9299

100+
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) {
101+
let mut state = self.state.lock().unwrap();
102+
let idx = holder_tx.commitment_number();
103+
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);
104+
state.last_holder_commitment = idx;
105+
}
106+
93107
fn pubkeys(&self) -> &ChannelPublicKeys { self.inner.pubkeys() }
94108
fn channel_keys_id(&self) -> [u8; 32] { self.inner.channel_keys_id() }
95109

@@ -116,10 +130,10 @@ impl BaseSign for EnforcingSigner {
116130

117131
let state = self.state.lock().unwrap();
118132
let commitment_number = trusted_tx.commitment_number();
119-
if state.revoked_commitment - 1 != commitment_number && state.revoked_commitment - 2 != commitment_number {
133+
if state.last_holder_revoked_commitment - 1 != commitment_number && state.last_holder_revoked_commitment - 2 != commitment_number {
120134
if !self.disable_revocation_policy_check {
121135
panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}",
122-
state.revoked_commitment, commitment_number, self.inner.commitment_seed[0])
136+
state.last_holder_revoked_commitment, commitment_number, self.inner.commitment_seed[0])
123137
}
124138
}
125139

@@ -212,16 +226,18 @@ pub struct EnforcementState {
212226
/// The last counterparty commitment number we signed, backwards counting
213227
pub last_counterparty_commitment: Option<u64>,
214228
/// The last holder commitment number we revoked, backwards counting
215-
pub revoked_commitment: u64,
216-
229+
pub last_holder_revoked_commitment: u64,
230+
/// The last validated holder commitment number, backwards counting
231+
pub last_holder_commitment: u64,
217232
}
218233

219234
impl EnforcementState {
220235
/// Enforcement state for a new channel
221236
pub fn new() -> Self {
222237
EnforcementState {
223238
last_counterparty_commitment: None,
224-
revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
239+
last_holder_revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
240+
last_holder_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
225241
}
226242
}
227243
}

0 commit comments

Comments
 (0)