Skip to content

Commit ba2d867

Browse files
committed
Extend BaseSign with HTLC output signing support for external claims
1 parent 7269fa2 commit ba2d867

File tree

3 files changed

+87
-21
lines changed

3 files changed

+87
-21
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,17 @@ pub trait BaseSign {
324324
/// (which is committed to in the BIP 143 signatures).
325325
fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
326326

327+
#[cfg(anchors)]
328+
/// Computes the signature for a commitment transaction's HTLC output used as an input within
329+
/// `htlc_tx`, which spends the commitment transaction, at index `input`.
330+
/// Note that this should only be used to sign HTLC transactions from channels supporting anchor
331+
/// outputs after all additional inputs/outputs have been added to the transaction.
332+
fn sign_holder_htlc_transaction(
333+
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,
334+
htlc: &HTLCOutputInCommitment, counterparty_base_htlc_key: &PublicKey,
335+
counterparty_base_revocation_key: &PublicKey, secp_ctx: &Secp256k1<secp256k1::All>
336+
) -> Result<Signature, ()>;
337+
327338
/// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment
328339
/// transaction, either offered or received.
329340
///
@@ -671,7 +682,6 @@ impl InMemorySigner {
671682
witness.push(witness_script.clone().into_bytes());
672683
Ok(witness)
673684
}
674-
675685
}
676686

677687
impl BaseSign for InMemorySigner {
@@ -767,6 +777,36 @@ impl BaseSign for InMemorySigner {
767777
return Ok(sign(secp_ctx, &sighash, &revocation_key))
768778
}
769779

780+
#[cfg(anchors)]
781+
fn sign_holder_htlc_transaction(
782+
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,
783+
htlc: &HTLCOutputInCommitment, counterparty_base_htlc_key: &PublicKey,
784+
counterparty_base_revocation_key: &PublicKey, secp_ctx: &Secp256k1<secp256k1::All>
785+
) -> Result<Signature, ()> {
786+
let per_commitment_point = self.get_per_commitment_point(per_commitment_number, &secp_ctx);
787+
let witness_script = {
788+
let our_htlc_key = chan_utils::derive_public_key(
789+
secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint
790+
).map_err(|_| ())?;
791+
let counterparty_htlc_key = chan_utils::derive_public_revocation_key(
792+
secp_ctx, &per_commitment_point, counterparty_base_htlc_key
793+
).map_err(|_| ())?;
794+
let revocation_key = chan_utils::derive_public_revocation_key(
795+
secp_ctx, &per_commitment_point, counterparty_base_revocation_key
796+
).map_err(|_| ())?;
797+
chan_utils::get_htlc_redeemscript_with_explicit_keys(
798+
&htlc, true /* opt_anchors */, &our_htlc_key, &counterparty_htlc_key, &revocation_key,
799+
)
800+
};
801+
let sighash = &sighash::SighashCache::new(&*htlc_tx).segwit_signature_hash(
802+
input, &witness_script, htlc.amount_msat / 1000, EcdsaSighashType::All
803+
).map_err(|_| ())?;
804+
let our_htlc_private_key = chan_utils::derive_private_key(
805+
&secp_ctx, &per_commitment_point, &self.htlc_base_key
806+
).map_err(|_| ())?;
807+
Ok(sign(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key))
808+
}
809+
770810
fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
771811
if let Ok(htlc_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key) {
772812
let witness_script = if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) {

lightning/src/ln/chan_utils.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,37 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte
698698
}
699699
}
700700

701+
/// Returns the witness required to satisfy and spend a HTLC input.
702+
pub fn build_htlc_input_witness(
703+
local_sig: &Signature, remote_sig: &Signature, preimage: &Option<PaymentPreimage>,
704+
redeem_script: &Script, opt_anchors: bool,
705+
) -> Witness {
706+
let remote_sighash_type = if opt_anchors {
707+
EcdsaSighashType::SinglePlusAnyoneCanPay
708+
} else {
709+
EcdsaSighashType::All
710+
};
711+
let mut remote_sig = remote_sig.serialize_der().to_vec();
712+
remote_sig.push(remote_sighash_type as u8);
713+
714+
let mut local_sig = local_sig.serialize_der().to_vec();
715+
local_sig.push(EcdsaSighashType::All as u8);
716+
717+
let mut witness_vec = Vec::with_capacity(5);
718+
// First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
719+
witness_vec.push(vec![]);
720+
witness_vec.push(remote_sig);
721+
witness_vec.push(local_sig);
722+
if let Some(preimage) = preimage {
723+
witness_vec.push(preimage.0.to_vec());
724+
} else {
725+
// Due to BIP146 (MINIMALIF) this must be a zero-length element to relay.
726+
witness_vec.push(vec![]);
727+
}
728+
witness_vec.push(redeem_script.to_bytes());
729+
Witness::from_vec(witness_vec)
730+
}
731+
701732
/// Gets the witnessScript for the to_remote output when anchors are enabled.
702733
#[inline]
703734
pub(crate) fn get_to_countersignatory_with_anchors_redeemscript(payment_point: &PublicKey) -> Script {
@@ -1518,26 +1549,9 @@ impl<'a> TrustedCommitmentTransaction<'a> {
15181549

15191550
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, self.opt_anchors(), &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
15201551

1521-
let sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
1522-
1523-
// First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
1524-
htlc_tx.input[0].witness.push(Vec::new());
1525-
1526-
let mut cp_sig_ser = counterparty_signature.serialize_der().to_vec();
1527-
cp_sig_ser.push(sighashtype as u8);
1528-
htlc_tx.input[0].witness.push(cp_sig_ser);
1529-
let mut holder_sig_ser = signature.serialize_der().to_vec();
1530-
holder_sig_ser.push(EcdsaSighashType::All as u8);
1531-
htlc_tx.input[0].witness.push(holder_sig_ser);
1532-
1533-
if this_htlc.offered {
1534-
// Due to BIP146 (MINIMALIF) this must be a zero-length element to relay.
1535-
htlc_tx.input[0].witness.push(Vec::new());
1536-
} else {
1537-
htlc_tx.input[0].witness.push(preimage.unwrap().0.to_vec());
1538-
}
1539-
1540-
htlc_tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
1552+
htlc_tx.input[0].witness = chan_utils::build_htlc_input_witness(
1553+
signature, counterparty_signature, preimage, &htlc_redeemscript, self.opt_anchors(),
1554+
);
15411555
htlc_tx
15421556
}
15431557
}

lightning/src/util/enforcing_trait_impls.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,18 @@ impl BaseSign for EnforcingSigner {
190190
Ok(self.inner.sign_justice_revoked_htlc(justice_tx, input, amount, per_commitment_key, htlc, secp_ctx).unwrap())
191191
}
192192

193+
#[cfg(anchors)]
194+
fn sign_holder_htlc_transaction(
195+
&self, htlc_tx: &Transaction, input: usize, per_commitment_number: u64,
196+
htlc: &HTLCOutputInCommitment, counterparty_base_htlc_key: &PublicKey,
197+
counterparty_base_revocation_key: &PublicKey, secp_ctx: &Secp256k1<secp256k1::All>
198+
) -> Result<Signature, ()> {
199+
Ok(self.inner.sign_holder_htlc_transaction(
200+
htlc_tx, input, per_commitment_number, htlc, counterparty_base_htlc_key,
201+
counterparty_base_revocation_key, secp_ctx
202+
).unwrap())
203+
}
204+
193205
fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
194206
Ok(self.inner.sign_counterparty_htlc_transaction(htlc_tx, input, amount, per_commitment_point, htlc, secp_ctx).unwrap())
195207
}

0 commit comments

Comments
 (0)