Skip to content

anchors: Fix SigHashType and weight calculations for anchors #1229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

152 changes: 83 additions & 69 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2041,7 +2041,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone());
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), self.onchain_tx_handler.channel_transaction_parameters.opt_anchors.is_some());
let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
claimable_outpoints.push(justice_package);
}
Expand Down Expand Up @@ -3307,7 +3307,7 @@ mod tests {
use ::{check_added_monitors, check_closed_broadcast, check_closed_event, check_spends, get_local_commitment_txn, get_monitor, get_route_and_payment_hash, unwrap_send_err};
use chain::{BestBlock, Confirm};
use chain::channelmonitor::ChannelMonitor;
use chain::package::{WEIGHT_OFFERED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_RECEIVED_HTLC, WEIGHT_REVOKED_OUTPUT};
use chain::package::{weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT};
use chain::transaction::OutPoint;
use chain::keysinterface::InMemorySigner;
use ln::{PaymentPreimage, PaymentHash};
Expand Down Expand Up @@ -3564,12 +3564,11 @@ mod tests {
let secp_ctx = Secp256k1::new();
let privkey = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
let pubkey = PublicKey::from_secret_key(&secp_ctx, &privkey);
let mut sum_actual_sigs = 0;

macro_rules! sign_input {
($sighash_parts: expr, $idx: expr, $amount: expr, $weight: expr, $sum_actual_sigs: expr, $opt_anchors: expr) => {
let htlc = HTLCOutputInCommitment {
offered: if *$weight == WEIGHT_REVOKED_OFFERED_HTLC || *$weight == WEIGHT_OFFERED_HTLC { true } else { false },
offered: if *$weight == weight_revoked_offered_htlc($opt_anchors) || *$weight == weight_offered_htlc($opt_anchors) { true } else { false },
amount_msat: 0,
cltv_expiry: 2 << 16,
payment_hash: PaymentHash([1; 32]),
Expand All @@ -3580,12 +3579,12 @@ mod tests {
let sig = secp_ctx.sign(&sighash, &privkey);
$sighash_parts.access_witness($idx).push(sig.serialize_der().to_vec());
$sighash_parts.access_witness($idx)[0].push(SigHashType::All as u8);
sum_actual_sigs += $sighash_parts.access_witness($idx)[0].len();
$sum_actual_sigs += $sighash_parts.access_witness($idx)[0].len();
if *$weight == WEIGHT_REVOKED_OUTPUT {
$sighash_parts.access_witness($idx).push(vec!(1));
} else if *$weight == WEIGHT_REVOKED_OFFERED_HTLC || *$weight == WEIGHT_REVOKED_RECEIVED_HTLC {
} else if *$weight == weight_revoked_offered_htlc($opt_anchors) || *$weight == weight_revoked_received_htlc($opt_anchors) {
$sighash_parts.access_witness($idx).push(pubkey.clone().serialize().to_vec());
} else if *$weight == WEIGHT_RECEIVED_HTLC {
} else if *$weight == weight_received_htlc($opt_anchors) {
$sighash_parts.access_witness($idx).push(vec![0]);
} else {
$sighash_parts.access_witness($idx).push(PaymentPreimage([1; 32]).0.to_vec());
Expand All @@ -3601,83 +3600,98 @@ mod tests {
let txid = Txid::from_hex("56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d").unwrap();

// Justice tx with 1 to_holder, 2 revoked offered HTLCs, 1 revoked received HTLCs
let mut claim_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
for i in 0..4 {
claim_tx.input.push(TxIn {
previous_output: BitcoinOutPoint {
txid,
vout: i,
},
script_sig: Script::new(),
sequence: 0xfffffffd,
witness: Vec::new(),
for &opt_anchors in [false, true].iter() {
let mut claim_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
for i in 0..4 {
claim_tx.input.push(TxIn {
previous_output: BitcoinOutPoint {
txid,
vout: i,
},
script_sig: Script::new(),
sequence: 0xfffffffd,
witness: Vec::new(),
});
}
claim_tx.output.push(TxOut {
script_pubkey: script_pubkey.clone(),
value: 0,
});
}
claim_tx.output.push(TxOut {
script_pubkey: script_pubkey.clone(),
value: 0,
});
let base_weight = claim_tx.get_weight();
let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_RECEIVED_HTLC];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, false);
inputs_total_weight += inp;
let base_weight = claim_tx.get_weight();
let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, weight_revoked_offered_htlc(opt_anchors), weight_revoked_offered_htlc(opt_anchors), weight_revoked_received_htlc(opt_anchors)];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, opt_anchors);
inputs_total_weight += inp;
}
}
assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));
}
assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));

// Claim tx with 1 offered HTLCs, 3 received HTLCs
claim_tx.input.clear();
sum_actual_sigs = 0;
for i in 0..4 {
for &opt_anchors in [false, true].iter() {
let mut claim_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
for i in 0..4 {
claim_tx.input.push(TxIn {
previous_output: BitcoinOutPoint {
txid,
vout: i,
},
script_sig: Script::new(),
sequence: 0xfffffffd,
witness: Vec::new(),
});
}
claim_tx.output.push(TxOut {
script_pubkey: script_pubkey.clone(),
value: 0,
});
let base_weight = claim_tx.get_weight();
let inputs_weight = vec![weight_offered_htlc(opt_anchors), weight_received_htlc(opt_anchors), weight_received_htlc(opt_anchors), weight_received_htlc(opt_anchors)];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, opt_anchors);
inputs_total_weight += inp;
}
}
assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));
}

// Justice tx with 1 revoked HTLC-Success tx output
for &opt_anchors in [false, true].iter() {
let mut claim_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
claim_tx.input.push(TxIn {
previous_output: BitcoinOutPoint {
txid,
vout: i,
vout: 0,
},
script_sig: Script::new(),
sequence: 0xfffffffd,
witness: Vec::new(),
});
}
let base_weight = claim_tx.get_weight();
let inputs_weight = vec![WEIGHT_OFFERED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_RECEIVED_HTLC];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, false);
inputs_total_weight += inp;
}
}
assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));

// Justice tx with 1 revoked HTLC-Success tx output
claim_tx.input.clear();
sum_actual_sigs = 0;
claim_tx.input.push(TxIn {
previous_output: BitcoinOutPoint {
txid,
vout: 0,
},
script_sig: Script::new(),
sequence: 0xfffffffd,
witness: Vec::new(),
});
let base_weight = claim_tx.get_weight();
let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, false);
inputs_total_weight += inp;
claim_tx.output.push(TxOut {
script_pubkey: script_pubkey.clone(),
value: 0,
});
let base_weight = claim_tx.get_weight();
let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, opt_anchors);
inputs_total_weight += inp;
}
}
assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_isg */ (73 * inputs_weight.len() - sum_actual_sigs));
}
assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_isg */ (73 * inputs_weight.len() - sum_actual_sigs));
}

// Further testing is done in the ChannelManager integration tests.
Expand Down
3 changes: 2 additions & 1 deletion lightning/src/chain/keysinterface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,8 @@ impl BaseSign for InMemorySigner {
for htlc in commitment_tx.htlcs() {
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), self.holder_selected_contest_delay(), htlc, self.opt_anchors(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys);
let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, SigHashType::All)[..]);
let htlc_sighashtype = if self.opt_anchors() { SigHashType::SinglePlusAnyoneCanPay } else { SigHashType::All };
let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype)[..]);
let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key).map_err(|_| ())?;
htlc_sigs.push(secp_ctx.sign(&htlc_sighash, &holder_htlc_key));
}
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/chain/onchaintx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
let new_timer = Some(cached_request.get_height_timer(cur_height));
if cached_request.is_malleable() {
let predicted_weight = cached_request.package_weight(&self.destination_script);
let predicted_weight = cached_request.package_weight(&self.destination_script, self.channel_transaction_parameters.opt_anchors.is_some());
if let Some((output_value, new_feerate)) =
cached_request.compute_package_output(predicted_weight, self.destination_script.dust_value().as_sat(), fee_estimator, logger) {
assert!(new_feerate != 0);
Expand Down
Loading