Skip to content

Commit 78b967f

Browse files
committed
Generate local signatures with additional randomness
Previously, our local signatures would always be deterministic, whether we'd grind for low R value signatures or not. For peers supporting SegWit, Bitcoin Core will generally use a transaction's witness-txid, as opposed to its txid, to advertise transactions. Therefore, to ensure a transaction has the best chance to propagate across node mempools in the network, each of its broadcast attempts should have a unique/distinct witness-txid, which we can achieve by introducing random nonce data when generating local signatures, such that they are no longer deterministic.
1 parent 2c5bd1c commit 78b967f

File tree

5 files changed

+56
-24
lines changed

5 files changed

+56
-24
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use bitcoin::secp256k1::ecdsa::RecoverableSignature;
3232
use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
3333

3434
use crate::util::transaction_utils;
35-
use crate::util::crypto::{hkdf_extract_expand_twice, sign};
35+
use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
3636
use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
3737
use crate::chain::transaction::OutPoint;
3838
#[cfg(anchors)]
@@ -713,7 +713,7 @@ impl InMemorySigner {
713713
let remotepubkey = self.pubkeys().payment_point;
714714
let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Testnet).script_pubkey();
715715
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
716-
let remotesig = sign(secp_ctx, &sighash, &self.payment_key);
716+
let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
717717
let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
718718

719719
if payment_script != descriptor.output.script_pubkey { return Err(()); }
@@ -749,7 +749,7 @@ impl InMemorySigner {
749749
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
750750
let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
751751
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
752-
let local_delayedsig = sign(secp_ctx, &sighash, &delayed_payment_key);
752+
let local_delayedsig = sign_with_aux_rand(secp_ctx, &sighash, &delayed_payment_key, &self);
753753
let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
754754

755755
if descriptor.output.script_pubkey != payment_script { return Err(()); }
@@ -810,7 +810,7 @@ impl EcdsaChannelSigner for InMemorySigner {
810810
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
811811

812812
let built_tx = trusted_tx.built_transaction();
813-
let commitment_sig = built_tx.sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
813+
let commitment_sig = built_tx.sign_counterparty_commitment(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
814814
let commitment_txid = built_tx.txid;
815815

816816
let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
@@ -835,9 +835,9 @@ impl EcdsaChannelSigner for InMemorySigner {
835835
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
836836
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
837837
let trusted_tx = commitment_tx.trust();
838-
let sig = trusted_tx.built_transaction().sign(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, secp_ctx);
838+
let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
839839
let channel_parameters = self.get_channel_parameters();
840-
let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), secp_ctx)?;
840+
let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
841841
Ok((sig, htlc_sigs))
842842
}
843843

@@ -846,9 +846,9 @@ impl EcdsaChannelSigner for InMemorySigner {
846846
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
847847
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
848848
let trusted_tx = commitment_tx.trust();
849-
let sig = trusted_tx.built_transaction().sign(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, secp_ctx);
849+
let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
850850
let channel_parameters = self.get_channel_parameters();
851-
let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), secp_ctx)?;
851+
let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
852852
Ok((sig, htlc_sigs))
853853
}
854854

@@ -862,7 +862,7 @@ impl EcdsaChannelSigner for InMemorySigner {
862862
};
863863
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
864864
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
865-
return Ok(sign(secp_ctx, &sighash, &revocation_key))
865+
return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
866866
}
867867

868868
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, ()> {
@@ -876,7 +876,7 @@ impl EcdsaChannelSigner for InMemorySigner {
876876
};
877877
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
878878
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
879-
return Ok(sign(secp_ctx, &sighash, &revocation_key))
879+
return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
880880
}
881881

882882
#[cfg(anchors)]
@@ -894,7 +894,7 @@ impl EcdsaChannelSigner for InMemorySigner {
894894
let our_htlc_private_key = chan_utils::derive_private_key(
895895
&secp_ctx, &per_commitment_point, &self.htlc_base_key
896896
);
897-
Ok(sign(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key))
897+
Ok(sign_with_aux_rand(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key, &self))
898898
}
899899

900900
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, ()> {
@@ -905,7 +905,7 @@ impl EcdsaChannelSigner for InMemorySigner {
905905
let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
906906
let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
907907
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
908-
Ok(sign(secp_ctx, &sighash, &htlc_key))
908+
Ok(sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self))
909909
}
910910

911911
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
@@ -921,7 +921,7 @@ impl EcdsaChannelSigner for InMemorySigner {
921921
let sighash = sighash::SighashCache::new(&*anchor_tx).segwit_signature_hash(
922922
input, &witness_script, ANCHOR_OUTPUT_VALUE_SATOSHI, EcdsaSighashType::All,
923923
).unwrap();
924-
Ok(sign(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key))
924+
Ok(sign_with_aux_rand(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key, &self))
925925
}
926926

927927
fn sign_channel_announcement_with_funding_key(
@@ -1273,7 +1273,7 @@ impl KeysManager {
12731273
if payment_script != output.script_pubkey { return Err(()); };
12741274

12751275
let sighash = hash_to_message!(&sighash::SighashCache::new(&spend_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
1276-
let sig = sign(secp_ctx, &sighash, &secret.private_key);
1276+
let sig = sign_with_aux_rand(secp_ctx, &sighash, &secret.private_key, &self);
12771277
let mut sig_ser = sig.serialize_der().to_vec();
12781278
sig_ser.push(EcdsaSighashType::All as u8);
12791279
spend_tx.input[input_idx].witness.push(sig_ser);

lightning/src/ln/chan_utils.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;
2121
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
2222
use bitcoin::hash_types::{Txid, PubkeyHash};
2323

24+
use crate::chain::keysinterface::EntropySource;
2425
use crate::ln::{PaymentHash, PaymentPreimage};
2526
use crate::ln::msgs::DecodeError;
2627
use crate::util::ser::{Readable, Writeable, Writer};
@@ -39,7 +40,7 @@ use crate::util::transaction_utils::sort_outputs;
3940
use crate::ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
4041
use core::ops::Deref;
4142
use crate::chain;
42-
use crate::util::crypto::sign;
43+
use crate::util::crypto::{sign, sign_with_aux_rand};
4344

4445
/// Maximum number of one-way in-flight HTLC (protocol-level value).
4546
pub const MAX_HTLCS: u16 = 483;
@@ -1081,12 +1082,20 @@ impl BuiltCommitmentTransaction {
10811082
hash_to_message!(sighash)
10821083
}
10831084

1084-
/// Sign a transaction, either because we are counter-signing the counterparty's transaction or
1085-
/// because we are about to broadcast a holder transaction.
1086-
pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
1085+
/// Signs the counterparty's commitment transaction.
1086+
pub fn sign_counterparty_commitment<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
10871087
let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
10881088
sign(secp_ctx, &sighash, funding_key)
10891089
}
1090+
1091+
/// Signs the holder commitment transaction because we are about to broadcast it.
1092+
pub fn sign_holder_commitment<T: secp256k1::Signing, ES: Deref>(
1093+
&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64,
1094+
entropy_source: &ES, secp_ctx: &Secp256k1<T>
1095+
) -> Signature where ES::Target: EntropySource {
1096+
let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
1097+
sign_with_aux_rand(secp_ctx, &sighash, funding_key, entropy_source)
1098+
}
10901099
}
10911100

10921101
/// This class tracks the per-transaction information needed to build a closing transaction and will
@@ -1563,7 +1572,10 @@ impl<'a> TrustedCommitmentTransaction<'a> {
15631572
/// The returned Vec has one entry for each HTLC, and in the same order.
15641573
///
15651574
/// This function is only valid in the holder commitment context, it always uses EcdsaSighashType::All.
1566-
pub fn get_htlc_sigs<T: secp256k1::Signing>(&self, htlc_base_key: &SecretKey, channel_parameters: &DirectedChannelTransactionParameters, secp_ctx: &Secp256k1<T>) -> Result<Vec<Signature>, ()> {
1575+
pub fn get_htlc_sigs<T: secp256k1::Signing, ES: Deref>(
1576+
&self, htlc_base_key: &SecretKey, channel_parameters: &DirectedChannelTransactionParameters,
1577+
entropy_source: &ES, secp_ctx: &Secp256k1<T>,
1578+
) -> Result<Vec<Signature>, ()> where ES::Target: EntropySource {
15671579
let inner = self.inner;
15681580
let keys = &inner.keys;
15691581
let txid = inner.built.txid;
@@ -1577,7 +1589,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
15771589
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);
15781590

15791591
let sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, this_htlc.amount_msat / 1000, EcdsaSighashType::All).unwrap()[..]);
1580-
ret.push(sign(secp_ctx, &sighash, &holder_htlc_key));
1592+
ret.push(sign_with_aux_rand(secp_ctx, &sighash, &holder_htlc_key, entropy_source));
15811593
}
15821594
Ok(ret)
15831595
}

lightning/src/ln/functional_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3461,7 +3461,7 @@ fn test_htlc_ignore_latest_remote_commitment() {
34613461

34623462
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
34633463
assert_eq!(node_txn.len(), 3);
3464-
assert_eq!(node_txn[0], node_txn[1]);
3464+
assert_eq!(node_txn[0].txid(), node_txn[1].txid());
34653465

34663466
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
34673467
connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[1].clone()]});
@@ -9248,7 +9248,7 @@ fn do_test_tx_confirmed_skipping_blocks_immediate_broadcast(test_height_before_t
92489248
// We should broadcast an HTLC transaction spending our funding transaction first
92499249
let spending_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
92509250
assert_eq!(spending_txn.len(), 2);
9251-
assert_eq!(spending_txn[0], node_txn[0]);
9251+
assert_eq!(spending_txn[0].txid(), node_txn[0].txid());
92529252
check_spends!(spending_txn[1], node_txn[0]);
92539253
// We should also generate a SpendableOutputs event with the to_self output (as its
92549254
// timelock is up).

lightning/src/ln/payment_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
343343
if !confirm_before_reload {
344344
let as_broadcasted_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
345345
assert_eq!(as_broadcasted_txn.len(), 1);
346-
assert_eq!(as_broadcasted_txn[0], as_commitment_tx);
346+
assert_eq!(as_broadcasted_txn[0].txid(), as_commitment_tx.txid());
347347
} else {
348348
assert!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().is_empty());
349349
}
@@ -684,7 +684,7 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co
684684
connect_blocks(&nodes[0], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
685685
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
686686
assert_eq!(node_txn.len(), 3);
687-
assert_eq!(node_txn[0], node_txn[1]);
687+
assert_eq!(node_txn[0].txid(), node_txn[1].txid());
688688
check_spends!(node_txn[1], funding_tx);
689689
check_spends!(node_txn[2], node_txn[1]);
690690
let timeout_txn = vec![node_txn[2].clone()];

lightning/src/util/crypto.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine};
33
use bitcoin::hashes::sha256::Hash as Sha256;
44
use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, ecdsa::Signature, Signing};
55

6+
use crate::chain::keysinterface::EntropySource;
7+
8+
use core::ops::Deref;
9+
610
macro_rules! hkdf_extract_expand {
711
($salt: expr, $ikm: expr) => {{
812
let mut hmac = HmacEngine::<Sha256>::new($salt);
@@ -46,3 +50,19 @@ pub fn sign<C: Signing>(ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey) -> Si
4650
let sig = ctx.sign_ecdsa(msg, sk);
4751
sig
4852
}
53+
54+
#[inline]
55+
pub fn sign_with_aux_rand<C: Signing, ES: Deref>(
56+
ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey, entropy_source: &ES
57+
) -> Signature where ES::Target: EntropySource {
58+
#[cfg(feature = "grind_signatures")]
59+
let sig = loop {
60+
let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes());
61+
if sig.serialize_compact()[0] < 0x80 {
62+
break sig;
63+
}
64+
};
65+
#[cfg(not(feature = "grind_signatures"))]
66+
let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes());
67+
sig
68+
}

0 commit comments

Comments
 (0)