Skip to content

Commit 731773c

Browse files
authored
Merge pull request #1039 from lightning-signer/2021-08-more-enforcement
Introduce EnforcementState, validate release of revocation secret
2 parents 4368b56 + 608ed12 commit 731773c

File tree

8 files changed

+180
-80
lines changed

8 files changed

+180
-80
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
4141
use lightning::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
4242
use lightning::ln::msgs::{CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
4343
use lightning::ln::script::ShutdownScript;
44-
use lightning::util::enforcing_trait_impls::{EnforcingSigner, INITIAL_REVOKED_COMMITMENT_NUMBER};
44+
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
4545
use lightning::util::errors::APIError;
4646
use lightning::util::events;
4747
use lightning::util::logger::Logger;
@@ -161,7 +161,7 @@ impl chain::Watch<EnforcingSigner> for TestChainMonitor {
161161
struct KeyProvider {
162162
node_id: u8,
163163
rand_bytes_id: atomic::AtomicU32,
164-
revoked_commitments: Mutex<HashMap<[u8;32], Arc<Mutex<u64>>>>,
164+
enforcement_states: Mutex<HashMap<[u8;32], Arc<Mutex<EnforcementState>>>>,
165165
}
166166
impl KeysInterface for KeyProvider {
167167
type Signer = EnforcingSigner;
@@ -198,7 +198,7 @@ impl KeysInterface for KeyProvider {
198198
channel_value_satoshis,
199199
[0; 32],
200200
);
201-
let revoked_commitment = self.make_revoked_commitment_cell(keys.commitment_seed);
201+
let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed);
202202
EnforcingSigner::new_with_revoked(keys, revoked_commitment, false)
203203
}
204204

@@ -213,14 +213,11 @@ impl KeysInterface for KeyProvider {
213213
let mut reader = std::io::Cursor::new(buffer);
214214

215215
let inner: InMemorySigner = Readable::read(&mut reader)?;
216-
let revoked_commitment = self.make_revoked_commitment_cell(inner.commitment_seed);
217-
218-
let last_commitment_number = Readable::read(&mut reader)?;
216+
let state = self.make_enforcement_state_cell(inner.commitment_seed);
219217

220218
Ok(EnforcingSigner {
221219
inner,
222-
last_commitment_number: Arc::new(Mutex::new(last_commitment_number)),
223-
revoked_commitment,
220+
state,
224221
disable_revocation_policy_check: false,
225222
})
226223
}
@@ -231,10 +228,10 @@ impl KeysInterface for KeyProvider {
231228
}
232229

233230
impl KeyProvider {
234-
fn make_revoked_commitment_cell(&self, commitment_seed: [u8; 32]) -> Arc<Mutex<u64>> {
235-
let mut revoked_commitments = self.revoked_commitments.lock().unwrap();
231+
fn make_enforcement_state_cell(&self, commitment_seed: [u8; 32]) -> Arc<Mutex<EnforcementState>> {
232+
let mut revoked_commitments = self.enforcement_states.lock().unwrap();
236233
if !revoked_commitments.contains_key(&commitment_seed) {
237-
revoked_commitments.insert(commitment_seed, Arc::new(Mutex::new(INITIAL_REVOKED_COMMITMENT_NUMBER)));
234+
revoked_commitments.insert(commitment_seed, Arc::new(Mutex::new(EnforcementState::new())));
238235
}
239236
let cell = revoked_commitments.get(&commitment_seed).unwrap();
240237
Arc::clone(cell)
@@ -351,7 +348,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
351348
macro_rules! make_node {
352349
($node_id: expr, $fee_estimator: expr) => { {
353350
let logger: Arc<dyn Logger> = Arc::new(test_logger::TestLogger::new($node_id.to_string(), out.clone()));
354-
let keys_manager = Arc::new(KeyProvider { node_id: $node_id, rand_bytes_id: atomic::AtomicU32::new(0), revoked_commitments: Mutex::new(HashMap::new()) });
351+
let keys_manager = Arc::new(KeyProvider { node_id: $node_id, rand_bytes_id: atomic::AtomicU32::new(0), enforcement_states: Mutex::new(HashMap::new()) });
355352
let monitor = Arc::new(TestChainMonitor::new(broadcast.clone(), logger.clone(), $fee_estimator.clone(), Arc::new(TestPersister{}), Arc::clone(&keys_manager)));
356353

357354
let mut config = UserConfig::default();

fuzz/src/full_stack.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use lightning::routing::network_graph::NetGraphMsgHandler;
4242
use lightning::util::config::UserConfig;
4343
use lightning::util::errors::APIError;
4444
use lightning::util::events::Event;
45-
use lightning::util::enforcing_trait_impls::EnforcingSigner;
45+
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
4646
use lightning::util::logger::Logger;
4747
use lightning::util::ser::Readable;
4848

@@ -315,8 +315,15 @@ impl KeysInterface for KeyProvider {
315315
(ctr >> 8*7) as u8, (ctr >> 8*6) as u8, (ctr >> 8*5) as u8, (ctr >> 8*4) as u8, (ctr >> 8*3) as u8, (ctr >> 8*2) as u8, (ctr >> 8*1) as u8, 14, (ctr >> 8*0) as u8]
316316
}
317317

318-
fn read_chan_signer(&self, data: &[u8]) -> Result<EnforcingSigner, DecodeError> {
319-
EnforcingSigner::read(&mut std::io::Cursor::new(data))
318+
fn read_chan_signer(&self, mut data: &[u8]) -> Result<EnforcingSigner, DecodeError> {
319+
let inner: InMemorySigner = Readable::read(&mut data)?;
320+
let state = Arc::new(Mutex::new(EnforcementState::new()));
321+
322+
Ok(EnforcingSigner::new_with_revoked(
323+
inner,
324+
state,
325+
false
326+
))
320327
}
321328

322329
fn sign_invoice(&self, _invoice_preimage: Vec<u8>) -> Result<RecoverableSignature, ()> {

lightning/src/chain/keysinterface.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ pub trait BaseSign {
212212
/// Note that the commitment number starts at (1 << 48) - 1 and counts backwards.
213213
// TODO: return a Result so we can signal a validation error
214214
fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
215+
/// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
216+
///
217+
/// This is required in order for the signer to make sure that releasing a commitment
218+
/// secret won't leave us without a broadcastable holder transaction.
219+
/// Policy checks should be implemented in this function, including checking the amount
220+
/// sent to us and checking the HTLCs.
221+
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) -> Result<(), ()>;
215222
/// Gets the holder's channel public keys and basepoints
216223
fn pubkeys(&self) -> &ChannelPublicKeys;
217224
/// Gets an arbitrary identifier describing the set of keys which are provided back to you in
@@ -222,9 +229,17 @@ pub trait BaseSign {
222229
/// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
223230
///
224231
/// Note that if signing fails or is rejected, the channel will be force-closed.
232+
///
233+
/// Policy checks should be implemented in this function, including checking the amount
234+
/// sent to us and checking the HTLCs.
225235
//
226236
// TODO: Document the things someone using this interface should enforce before signing.
227237
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
238+
/// Validate the counterparty's revocation.
239+
///
240+
/// This is required in order for the signer to make sure that the state has moved
241+
/// forward and it is safe to sign the next counterparty commitment.
242+
fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>;
228243

229244
/// Create a signatures for a holder's commitment transaction and its claiming HTLC transactions.
230245
/// This will only ever be called with a non-revoked commitment_tx. This will be called with the
@@ -558,6 +573,10 @@ impl BaseSign for InMemorySigner {
558573
chan_utils::build_commitment_secret(&self.commitment_seed, idx)
559574
}
560575

576+
fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) -> Result<(), ()> {
577+
Ok(())
578+
}
579+
561580
fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
562581
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
563582

@@ -584,6 +603,10 @@ impl BaseSign for InMemorySigner {
584603
Ok((commitment_sig, htlc_sigs))
585604
}
586605

606+
fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> {
607+
Ok(())
608+
}
609+
587610
fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
588611
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
589612
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);

lightning/src/ln/channel.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,9 @@ impl<Signer: Sign> Channel<Signer> {
17911791
self.counterparty_funding_pubkey()
17921792
);
17931793

1794+
self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
1795+
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
1796+
17941797
// Now that we're past error-generating stuff, update our local state:
17951798

17961799
let funding_redeemscript = self.get_funding_redeemscript();
@@ -1865,6 +1868,9 @@ impl<Signer: Sign> Channel<Signer> {
18651868
self.counterparty_funding_pubkey()
18661869
);
18671870

1871+
self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
1872+
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
1873+
18681874

18691875
let funding_redeemscript = self.get_funding_redeemscript();
18701876
let funding_txo = self.get_funding_txo().unwrap();
@@ -2502,6 +2508,8 @@ impl<Signer: Sign> Channel<Signer> {
25022508
);
25032509

25042510
let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number - 1, &self.secp_ctx);
2511+
self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
2512+
.map_err(|_| (None, ChannelError::Close("Failed to validate our commitment".to_owned())))?;
25052513
let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 1);
25062514

25072515
// Update state now that we've passed all the can-fail calls...
@@ -2738,8 +2746,10 @@ impl<Signer: Sign> Channel<Signer> {
27382746
return Err(ChannelError::Close("Peer sent revoke_and_ack after we'd started exchanging closing_signeds".to_owned()));
27392747
}
27402748

2749+
let secret = secp_check!(SecretKey::from_slice(&msg.per_commitment_secret), "Peer provided an invalid per_commitment_secret".to_owned());
2750+
27412751
if let Some(counterparty_prev_commitment_point) = self.counterparty_prev_commitment_point {
2742-
if PublicKey::from_secret_key(&self.secp_ctx, &secp_check!(SecretKey::from_slice(&msg.per_commitment_secret), "Peer provided an invalid per_commitment_secret".to_owned())) != counterparty_prev_commitment_point {
2752+
if PublicKey::from_secret_key(&self.secp_ctx, &secret) != counterparty_prev_commitment_point {
27432753
return Err(ChannelError::Close("Got a revoke commitment secret which didn't correspond to their current pubkey".to_owned()));
27442754
}
27452755
}
@@ -2761,6 +2771,11 @@ impl<Signer: Sign> Channel<Signer> {
27612771
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
27622772
}
27632773

2774+
self.holder_signer.validate_counterparty_revocation(
2775+
self.cur_counterparty_commitment_transaction_number + 1,
2776+
&secret
2777+
).map_err(|_| ChannelError::Close("Failed to validate revocation from peer".to_owned()))?;
2778+
27642779
self.commitment_secrets.provide_secret(self.cur_counterparty_commitment_transaction_number + 1, msg.per_commitment_secret)
27652780
.map_err(|_| ChannelError::Close("Previous secrets did not match new one".to_owned()))?;
27662781
self.latest_monitor_update_id += 1;

lightning/src/ln/functional_tests.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,9 @@ fn test_fee_spike_violation_fails_htlc() {
13011301
let chan_lock = nodes[0].node.channel_state.lock().unwrap();
13021302
let local_chan = chan_lock.by_id.get(&chan.2).unwrap();
13031303
let chan_signer = local_chan.get_signer();
1304+
// Make the signer believe we validated another commitment, so we can release the secret
1305+
chan_signer.get_enforcement_state().last_holder_commitment -= 1;
1306+
13041307
let pubkeys = chan_signer.pubkeys();
13051308
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
13061309
chan_signer.release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
@@ -7981,7 +7984,7 @@ fn test_counterparty_raa_skip_no_crash() {
79817984
// commitment transaction, we would have happily carried on and provided them the next
79827985
// commitment transaction based on one RAA forward. This would probably eventually have led to
79837986
// channel closure, but it would not have resulted in funds loss. Still, our
7984-
// EnforcingSigner would have paniced as it doesn't like jumps into the future. Here, we
7987+
// EnforcingSigner would have panicked as it doesn't like jumps into the future. Here, we
79857988
// check simply that the channel is closed in response to such an RAA, but don't check whether
79867989
// we decide to punish our counterparty for revoking their funds (as we don't currently
79877990
// implement that).
@@ -7992,11 +7995,19 @@ fn test_counterparty_raa_skip_no_crash() {
79927995
let channel_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
79937996

79947997
let mut guard = nodes[0].node.channel_state.lock().unwrap();
7995-
let keys = &guard.by_id.get_mut(&channel_id).unwrap().get_signer();
7998+
let keys = guard.by_id.get_mut(&channel_id).unwrap().get_signer();
7999+
79968000
const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
8001+
8002+
// Make signer believe we got a counterparty signature, so that it allows the revocation
8003+
keys.get_enforcement_state().last_holder_commitment -= 1;
79978004
let per_commitment_secret = keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER);
8005+
79988006
// Must revoke without gaps
8007+
keys.get_enforcement_state().last_holder_commitment -= 1;
79998008
keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1);
8009+
8010+
keys.get_enforcement_state().last_holder_commitment -= 1;
80008011
let next_per_commitment_point = PublicKey::from_secret_key(&Secp256k1::new(),
80018012
&SecretKey::from_slice(&keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2)).unwrap());
80028013

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

0 commit comments

Comments
 (0)