Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 9 additions & 12 deletions fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
use lightning::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use lightning::ln::msgs::{CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
use lightning::ln::script::ShutdownScript;
use lightning::util::enforcing_trait_impls::{EnforcingSigner, INITIAL_REVOKED_COMMITMENT_NUMBER};
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use lightning::util::errors::APIError;
use lightning::util::events;
use lightning::util::logger::Logger;
Expand Down Expand Up @@ -161,7 +161,7 @@ impl chain::Watch<EnforcingSigner> for TestChainMonitor {
struct KeyProvider {
node_id: u8,
rand_bytes_id: atomic::AtomicU32,
revoked_commitments: Mutex<HashMap<[u8;32], Arc<Mutex<u64>>>>,
enforcement_states: Mutex<HashMap<[u8;32], Arc<Mutex<EnforcementState>>>>,
}
impl KeysInterface for KeyProvider {
type Signer = EnforcingSigner;
Expand Down Expand Up @@ -198,7 +198,7 @@ impl KeysInterface for KeyProvider {
channel_value_satoshis,
[0; 32],
);
let revoked_commitment = self.make_revoked_commitment_cell(keys.commitment_seed);
let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed);
EnforcingSigner::new_with_revoked(keys, revoked_commitment, false)
}

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

let inner: InMemorySigner = Readable::read(&mut reader)?;
let revoked_commitment = self.make_revoked_commitment_cell(inner.commitment_seed);

let last_commitment_number = Readable::read(&mut reader)?;
let state = self.make_enforcement_state_cell(inner.commitment_seed);

Ok(EnforcingSigner {
inner,
last_commitment_number: Arc::new(Mutex::new(last_commitment_number)),
revoked_commitment,
state,
disable_revocation_policy_check: false,
})
}
Expand All @@ -231,10 +228,10 @@ impl KeysInterface for KeyProvider {
}

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

let mut config = UserConfig::default();
Expand Down
13 changes: 10 additions & 3 deletions fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use lightning::routing::network_graph::NetGraphMsgHandler;
use lightning::util::config::UserConfig;
use lightning::util::errors::APIError;
use lightning::util::events::Event;
use lightning::util::enforcing_trait_impls::EnforcingSigner;
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use lightning::util::logger::Logger;
use lightning::util::ser::Readable;

Expand Down Expand Up @@ -315,8 +315,15 @@ impl KeysInterface for KeyProvider {
(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]
}

fn read_chan_signer(&self, data: &[u8]) -> Result<EnforcingSigner, DecodeError> {
EnforcingSigner::read(&mut std::io::Cursor::new(data))
fn read_chan_signer(&self, mut data: &[u8]) -> Result<EnforcingSigner, DecodeError> {
let inner: InMemorySigner = Readable::read(&mut data)?;
let state = Arc::new(Mutex::new(EnforcementState::new()));

Ok(EnforcingSigner::new_with_revoked(
inner,
state,
false
))
}

fn sign_invoice(&self, _invoice_preimage: Vec<u8>) -> Result<RecoverableSignature, ()> {
Expand Down
23 changes: 23 additions & 0 deletions lightning/src/chain/keysinterface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,13 @@ pub trait BaseSign {
/// Note that the commitment number starts at (1 << 48) - 1 and counts backwards.
// TODO: return a Result so we can signal a validation error
fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
/// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
///
/// This is required in order for the signer to make sure that releasing a commitment
/// secret won't leave us without a broadcastable holder transaction.
/// Policy checks should be implemented in this function, including checking the amount
/// sent to us and checking the HTLCs.
fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) -> Result<(), ()>;
/// Gets the holder's channel public keys and basepoints
fn pubkeys(&self) -> &ChannelPublicKeys;
/// Gets an arbitrary identifier describing the set of keys which are provided back to you in
Expand All @@ -222,9 +229,17 @@ pub trait BaseSign {
/// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
///
/// Note that if signing fails or is rejected, the channel will be force-closed.
///
/// Policy checks should be implemented in this function, including checking the amount
/// sent to us and checking the HTLCs.
//
// TODO: Document the things someone using this interface should enforce before signing.
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
/// Validate the counterparty's revocation.
///
/// This is required in order for the signer to make sure that the state has moved
/// forward and it is safe to sign the next counterparty commitment.
fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>;

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

fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) -> Result<(), ()> {
Ok(())
}

fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }

Expand All @@ -584,6 +603,10 @@ impl BaseSign for InMemorySigner {
Ok((commitment_sig, htlc_sigs))
}

fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> {
Ok(())
}

fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
Expand Down
17 changes: 16 additions & 1 deletion lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@ impl<Signer: Sign> Channel<Signer> {
self.counterparty_funding_pubkey()
);

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

// Now that we're past error-generating stuff, update our local state:

let funding_redeemscript = self.get_funding_redeemscript();
Expand Down Expand Up @@ -1865,6 +1868,9 @@ impl<Signer: Sign> Channel<Signer> {
self.counterparty_funding_pubkey()
);

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


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

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

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

let secret = secp_check!(SecretKey::from_slice(&msg.per_commitment_secret), "Peer provided an invalid per_commitment_secret".to_owned());

if let Some(counterparty_prev_commitment_point) = self.counterparty_prev_commitment_point {
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 {
if PublicKey::from_secret_key(&self.secp_ctx, &secret) != counterparty_prev_commitment_point {
return Err(ChannelError::Close("Got a revoke commitment secret which didn't correspond to their current pubkey".to_owned()));
}
}
Expand All @@ -2761,6 +2771,11 @@ impl<Signer: Sign> Channel<Signer> {
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
}

self.holder_signer.validate_counterparty_revocation(
self.cur_counterparty_commitment_transaction_number + 1,
&secret
).map_err(|_| ChannelError::Close("Failed to validate revocation from peer".to_owned()))?;

self.commitment_secrets.provide_secret(self.cur_counterparty_commitment_transaction_number + 1, msg.per_commitment_secret)
.map_err(|_| ChannelError::Close("Previous secrets did not match new one".to_owned()))?;
self.latest_monitor_update_id += 1;
Expand Down
15 changes: 13 additions & 2 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,9 @@ fn test_fee_spike_violation_fails_htlc() {
let chan_lock = nodes[0].node.channel_state.lock().unwrap();
let local_chan = chan_lock.by_id.get(&chan.2).unwrap();
let chan_signer = local_chan.get_signer();
// Make the signer believe we validated another commitment, so we can release the secret
chan_signer.get_enforcement_state().last_holder_commitment -= 1;

let pubkeys = chan_signer.pubkeys();
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
chan_signer.release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
Expand Down Expand Up @@ -7981,7 +7984,7 @@ fn test_counterparty_raa_skip_no_crash() {
// commitment transaction, we would have happily carried on and provided them the next
// commitment transaction based on one RAA forward. This would probably eventually have led to
// channel closure, but it would not have resulted in funds loss. Still, our
// EnforcingSigner would have paniced as it doesn't like jumps into the future. Here, we
// EnforcingSigner would have panicked as it doesn't like jumps into the future. Here, we
// check simply that the channel is closed in response to such an RAA, but don't check whether
// we decide to punish our counterparty for revoking their funds (as we don't currently
// implement that).
Expand All @@ -7992,11 +7995,19 @@ fn test_counterparty_raa_skip_no_crash() {
let channel_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;

let mut guard = nodes[0].node.channel_state.lock().unwrap();
let keys = &guard.by_id.get_mut(&channel_id).unwrap().get_signer();
let keys = guard.by_id.get_mut(&channel_id).unwrap().get_signer();

const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;

// Make signer believe we got a counterparty signature, so that it allows the revocation
keys.get_enforcement_state().last_holder_commitment -= 1;
let per_commitment_secret = keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER);

// Must revoke without gaps
keys.get_enforcement_state().last_holder_commitment -= 1;
keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1);

keys.get_enforcement_state().last_holder_commitment -= 1;
let next_per_commitment_point = PublicKey::from_secret_key(&Secp256k1::new(),
&SecretKey::from_slice(&keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2)).unwrap());

Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ pub struct FundingCreated {
pub funding_txid: Txid,
/// The specific output index funding this channel
pub funding_output_index: u16,
/// The signature of the channel initiator (funder) on the funding transaction
/// The signature of the channel initiator (funder) on the initial commitment transaction
pub signature: Signature,
}

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

Expand Down
Loading