- /// Funding key is your key included in the 2-2 funding_outpoint lock. Should be provided
- /// by your ChannelKeys.
- /// Funding redeemscript is script locking funding_outpoint. This is the mutlsig script
- /// between your own funding key and your counterparty's. Currently, this is provided in
- /// ChannelKeys::sign_local_commitment() calls directly.
- /// Channel value is amount locked in funding_outpoint.
- pub fn add_local_sig<T: secp256k1::Signing>(&mut self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) {
- if self.has_local_sig() { return; }
- let sighash = hash_to_message!(&bip143::SighashComponents::new(&self.tx)
- .sighash_all(&self.tx.input[0], funding_redeemscript, channel_value_satoshis)[..]);
- let our_sig = secp_ctx.sign(&sighash, funding_key);
-
- if self.tx.input[0].witness[1].is_empty() {
- self.tx.input[0].witness[1] = our_sig.serialize_der().to_vec();
- self.tx.input[0].witness[1].push(SigHashType::All as u8);
+ /// The boolean representing the side that initiated the channel is
+ /// an input to the commitment number obscure factor computation.
+ pub fn is_outbound(&self) -> bool {
+ if self.holder_is_broadcaster { self.inner.is_outbound_from_holder } else { !self.inner.is_outbound_from_holder }
+ }
+
+ /// The funding outpoint
+ pub fn funding_outpoint(&self) -> OutPoint {
+ self.inner.funding_outpoint.unwrap().into_bitcoin_outpoint()
+ }
+}
+
+/// Information needed to build and sign a holder's commitment transaction.
+///
+/// The transaction is only signed once we are ready to broadcast.
+#[derive(Clone)]
+pub struct HolderCommitmentTransaction {
+ inner: CommitmentTransaction,
+ /// Our counterparty's signature for the transaction
+ pub counterparty_sig: Signature,
+ /// All non-dust counterparty HTLC signatures, in the order they appear in the transaction
+ pub counterparty_htlc_sigs: Vec<Signature>,
+ // Which order the signatures should go in when constructing the final commitment tx witness.
+ // The user should be able to reconstruct this themselves, so we don't bother to expose it.
+ holder_sig_first: bool,
+}
+
+impl Deref for HolderCommitmentTransaction {
+ type Target = CommitmentTransaction;
+
+ fn deref(&self) -> &Self::Target { &self.inner }
+}
+
+impl PartialEq for HolderCommitmentTransaction {
+ // We dont care whether we are signed in equality comparison
+ fn eq(&self, o: &Self) -> bool {
+ self.inner == o.inner
+ }
+}
+
+impl_writeable!(HolderCommitmentTransaction, 0, {
+ inner, counterparty_sig, counterparty_htlc_sigs, holder_sig_first
+});
+
+impl HolderCommitmentTransaction {
+ #[cfg(test)]
+ pub fn dummy() -> Self {
+ let secp_ctx = Secp256k1::new();
+ let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+ let dummy_sig = secp_ctx.sign(&secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap());
+
+ let keys = TxCreationKeys {
+ per_commitment_point: dummy_key.clone(),
+ revocation_key: dummy_key.clone(),
+ broadcaster_htlc_key: dummy_key.clone(),
+ countersignatory_htlc_key: dummy_key.clone(),
+ broadcaster_delayed_payment_key: dummy_key.clone(),
+ };
+ let channel_pubkeys = ChannelPublicKeys {
+ funding_pubkey: dummy_key.clone(),
+ revocation_basepoint: dummy_key.clone(),
+ payment_point: dummy_key.clone(),
+ delayed_payment_basepoint: dummy_key.clone(),
+ htlc_basepoint: dummy_key.clone()
+ };
+ let channel_parameters = ChannelTransactionParameters {
+ holder_pubkeys: channel_pubkeys.clone(),
+ holder_selected_contest_delay: 0,
+ is_outbound_from_holder: false,
+ counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: channel_pubkeys.clone(), selected_contest_delay: 0 }),
+ funding_outpoint: Some(chain::transaction::OutPoint { txid: Default::default(), index: 0 })
+ };
+ let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
+ let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, keys, 0, &mut htlcs_with_aux, &channel_parameters.as_counterparty_broadcastable());
+ HolderCommitmentTransaction {
+ inner,
+ counterparty_sig: dummy_sig,
+ counterparty_htlc_sigs: Vec::new(),
+ holder_sig_first: false
+ }
+ }
+
+ /// Create a new holder transaction with the given counterparty signatures.
+ /// The funding keys are used to figure out which signature should go first when building the transaction for broadcast.
+ pub fn new(commitment_tx: CommitmentTransaction, counterparty_sig: Signature, counterparty_htlc_sigs: Vec<Signature>, holder_funding_key: &PublicKey, counterparty_funding_key: &PublicKey) -> Self {
+ Self {
+ inner: commitment_tx,
+ counterparty_sig,
+ counterparty_htlc_sigs,
+ holder_sig_first: holder_funding_key.serialize()[..] < counterparty_funding_key.serialize()[..],
+ }
+ }
+
+ pub(crate) fn add_holder_sig(&self, funding_redeemscript: &Script, holder_sig: Signature) -> Transaction {
+ // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
+ let mut tx = self.inner.built.transaction.clone();
+ tx.input[0].witness.push(Vec::new());
+
+ if self.holder_sig_first {
+ tx.input[0].witness.push(holder_sig.serialize_der().to_vec());
+ tx.input[0].witness.push(self.counterparty_sig.serialize_der().to_vec());