/// 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.
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction);
/// 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
chan_utils::build_commitment_secret(&self.commitment_seed, idx)
}
+ fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) {
+ }
+
fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
self.counterparty_funding_pubkey()
);
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx);
+
// Now that we're past error-generating stuff, update our local state:
let funding_redeemscript = self.get_funding_redeemscript();
self.counterparty_funding_pubkey()
);
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx);
let funding_redeemscript = self.get_funding_redeemscript();
let funding_txo = self.get_funding_txo().unwrap();
);
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);
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...
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),
// 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).
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());
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,
}
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,
}
use prelude::*;
use core::cmp;
use sync::{Mutex, Arc};
+#[cfg(test)] use sync::MutexGuard;
use bitcoin::blockdata::transaction::{Transaction, SigHashType};
use bitcoin::util::bip143;
/// - When signing, the holder transaction has not been revoked
/// - When revoking, the holder transaction has not been signed
/// - The holder commitment number is monotonic and without gaps
+/// - The revoked holder commitment number is monotonic and without gaps
+/// - There is at least one unrevoked holder transaction at all times
/// - The counterparty commitment number is monotonic and without gaps
/// - The pre-derived keys and pre-built transaction in CommitmentTransaction were correctly built
///
/// Eventually we will probably want to expose a variant of this which would essentially
/// be what you'd want to run on a hardware wallet.
///
+/// Note that counterparty signatures on the holder transaction are not checked, but it should
+/// be in a complete implementation.
+///
/// Note that before we do so we should ensure its serialization format has backwards- and
/// forwards-compatibility prefix/suffixes!
#[derive(Clone)]
disable_revocation_policy_check
}
}
+
+ #[cfg(test)]
+ pub fn get_enforcement_state(&self) -> MutexGuard<EnforcementState> {
+ self.state.lock().unwrap()
+ }
}
impl BaseSign for EnforcingSigner {
fn release_commitment_secret(&self, idx: u64) -> [u8; 32] {
{
let mut state = self.state.lock().unwrap();
- 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);
- state.revoked_commitment = idx;
+ assert!(idx == state.last_holder_revoked_commitment || idx == state.last_holder_revoked_commitment - 1, "can only revoke the current or next unrevoked commitment - trying {}, last revoked {}", idx, state.last_holder_revoked_commitment);
+ assert!(idx > state.last_holder_commitment, "cannot revoke the last holder commitment - attempted to revoke {} last commitment {}", idx, state.last_holder_commitment);
+ state.last_holder_revoked_commitment = idx;
}
self.inner.release_commitment_secret(idx)
}
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) {
+ let mut state = self.state.lock().unwrap();
+ let idx = holder_tx.commitment_number();
+ 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);
+ state.last_holder_commitment = idx;
+ }
+
fn pubkeys(&self) -> &ChannelPublicKeys { self.inner.pubkeys() }
fn channel_keys_id(&self) -> [u8; 32] { self.inner.channel_keys_id() }
let state = self.state.lock().unwrap();
let commitment_number = trusted_tx.commitment_number();
- if state.revoked_commitment - 1 != commitment_number && state.revoked_commitment - 2 != commitment_number {
+ if state.last_holder_revoked_commitment - 1 != commitment_number && state.last_holder_revoked_commitment - 2 != commitment_number {
if !self.disable_revocation_policy_check {
panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}",
- state.revoked_commitment, commitment_number, self.inner.commitment_seed[0])
+ state.last_holder_revoked_commitment, commitment_number, self.inner.commitment_seed[0])
}
}
/// The last counterparty commitment number we signed, backwards counting
pub last_counterparty_commitment: u64,
/// The last holder commitment number we revoked, backwards counting
- pub revoked_commitment: u64,
-
+ pub last_holder_revoked_commitment: u64,
+ /// The last validated holder commitment number, backwards counting
+ pub last_holder_commitment: u64,
}
impl EnforcementState {
pub fn new() -> Self {
EnforcementState {
last_counterparty_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
- revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
+ last_holder_revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
+ last_holder_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
}
}
}