Skip to content

Commit 3f1c6bb

Browse files
committed
Enforce that revocation can only occur after we validated a new commitment
1 parent 22d83b6 commit 3f1c6bb

File tree

5 files changed

+53
-15
lines changed

5 files changed

+53
-15
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, idx: u64, 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, _idx: u64, _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(self.cur_holder_commitment_transaction_number, &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(self.cur_holder_commitment_transaction_number, &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(self.cur_holder_commitment_transaction_number, &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: 26 additions & 11 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,19 @@ 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, idx: u64, _holder_tx: &HolderCommitmentTransaction) {
101+
let mut state = self.state.lock().unwrap();
102+
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);
103+
state.last_holder_commitment = idx;
104+
}
105+
93106
fn pubkeys(&self) -> &ChannelPublicKeys { self.inner.pubkeys() }
94107
fn channel_keys_id(&self) -> [u8; 32] { self.inner.channel_keys_id() }
95108

@@ -99,11 +112,11 @@ impl BaseSign for EnforcingSigner {
99112
{
100113
let mut state = self.state.lock().unwrap();
101114
let actual_commitment_number = commitment_tx.commitment_number();
102-
let last_commitment_number = state.last_commitment_number.unwrap_or(actual_commitment_number);
115+
let last_commitment_number = state.last_counterparty_commitment_number.unwrap_or(actual_commitment_number);
103116
// These commitment numbers are backwards counting. We expect either the same as the previously encountered,
104117
// or the next one.
105118
assert!(last_commitment_number == actual_commitment_number || last_commitment_number - 1 == actual_commitment_number, "{} doesn't come after {}", actual_commitment_number, last_commitment_number);
106-
state.last_commitment_number = Some(cmp::min(last_commitment_number, actual_commitment_number))
119+
state.last_counterparty_commitment_number = Some(cmp::min(last_commitment_number, actual_commitment_number))
107120
}
108121

109122
Ok(self.inner.sign_counterparty_commitment(commitment_tx, secp_ctx).unwrap())
@@ -116,10 +129,10 @@ impl BaseSign for EnforcingSigner {
116129

117130
let state = self.state.lock().unwrap();
118131
let commitment_number = trusted_tx.commitment_number();
119-
if state.revoked_commitment - 1 != commitment_number && state.revoked_commitment - 2 != commitment_number {
132+
if state.last_holder_revoked_commitment - 1 != commitment_number && state.last_holder_revoked_commitment - 2 != commitment_number {
120133
if !self.disable_revocation_policy_check {
121134
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])
135+
state.last_holder_revoked_commitment, commitment_number, self.inner.commitment_seed[0])
123136
}
124137
}
125138

@@ -210,18 +223,20 @@ impl EnforcingSigner {
210223
#[derive(Clone)]
211224
pub struct EnforcementState {
212225
/// The last counterparty commitment number we signed, backwards counting
213-
pub last_commitment_number: Option<u64>,
226+
pub last_counterparty_commitment_number: Option<u64>,
214227
/// The last holder commitment number we revoked, backwards counting
215-
pub revoked_commitment: u64,
216-
228+
pub last_holder_revoked_commitment: u64,
229+
/// The last validated holder commitment number, backwards counting
230+
pub last_holder_commitment: u64,
217231
}
218232

219233
impl EnforcementState {
220234
/// Enforcement state for a new channel
221235
pub fn new() -> Self {
222236
EnforcementState {
223-
last_commitment_number: None,
224-
revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER
237+
last_counterparty_commitment_number: None,
238+
last_holder_revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
239+
last_holder_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
225240
}
226241
}
227242
}

0 commit comments

Comments
 (0)