//! security-domain-separated system design, you should consider having multiple paths for
//! ChannelMonitors to get out of the HSM and onto monitoring devices.
-use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::transaction::{TxOut,Transaction};
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
-use bitcoin::secp256k1::{Secp256k1,Signature};
-use bitcoin::secp256k1::key::{SecretKey,PublicKey};
+use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
+use bitcoin::secp256k1::{SecretKey, PublicKey};
use bitcoin::secp256k1;
use ln::{PaymentHash, PaymentPreimage};
/// An update generated by the underlying Channel itself which contains some new information the
/// ChannelMonitor should be made aware of.
-#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
+#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq))]
#[derive(Clone)]
#[must_use]
pub struct ChannelMonitorUpdate {
pub(crate) payment_hash: PaymentHash,
pub(crate) payment_preimage: Option<PaymentPreimage>,
pub(crate) source: HTLCSource,
- pub(crate) onchain_value_satoshis: Option<u64>,
+ pub(crate) htlc_value_satoshis: Option<u64>,
}
impl_writeable_tlv_based!(HTLCUpdate, {
(0, payment_hash, required),
- (1, onchain_value_satoshis, option),
+ (1, htlc_value_satoshis, option),
(2, source, required),
(4, payment_preimage, option),
});
HTLCUpdate {
source: HTLCSource,
payment_hash: PaymentHash,
- onchain_value_satoshis: Option<u64>,
+ htlc_value_satoshis: Option<u64>,
/// None in the second case, above, ie when there is no relevant output in the commitment
/// transaction which appeared on chain.
- input_idx: Option<u32>,
+ commitment_tx_output_idx: Option<u32>,
},
MaturingOutput {
descriptor: SpendableOutputDescriptor,
/// * a revoked-state HTLC transaction was broadcasted, which was claimed by the revocation
/// signature.
HTLCSpendConfirmation {
- input_idx: u32,
+ commitment_tx_output_idx: u32,
/// If the claim was made by either party with a preimage, this is filled in
preimage: Option<PaymentPreimage>,
/// If the claim was made by us on an inbound HTLC against a local commitment transaction,
impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
(0, HTLCUpdate) => {
(0, source, required),
- (1, onchain_value_satoshis, option),
+ (1, htlc_value_satoshis, option),
(2, payment_hash, required),
- (3, input_idx, option),
+ (3, commitment_tx_output_idx, option),
},
(1, MaturingOutput) => {
(0, descriptor, required),
(0, on_local_output_csv, option),
},
(5, HTLCSpendConfirmation) => {
- (0, input_idx, required),
+ (0, commitment_tx_output_idx, required),
(2, preimage, option),
(4, on_to_local_output_csv, option),
},
);
-#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
+#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq))]
#[derive(Clone)]
pub(crate) enum ChannelMonitorUpdateStep {
LatestHolderCommitmentTXInfo {
},
}
+impl ChannelMonitorUpdateStep {
+ fn variant_name(&self) -> &'static str {
+ match self {
+ ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { .. } => "LatestHolderCommitmentTXInfo",
+ ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. } => "LatestCounterpartyCommitmentTXInfo",
+ ChannelMonitorUpdateStep::PaymentPreimage { .. } => "PaymentPreimage",
+ ChannelMonitorUpdateStep::CommitmentSecret { .. } => "CommitmentSecret",
+ ChannelMonitorUpdateStep::ChannelForceClosed { .. } => "ChannelForceClosed",
+ ChannelMonitorUpdateStep::ShutdownScript { .. } => "ShutdownScript",
+ }
+ }
+}
+
impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
/// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY.
#[derive(PartialEq)]
struct IrrevocablyResolvedHTLC {
- input_idx: u32,
+ commitment_tx_output_idx: u32,
/// Only set if the HTLC claim was ours using a payment preimage
payment_preimage: Option<PaymentPreimage>,
}
impl_writeable_tlv_based!(IrrevocablyResolvedHTLC, {
- (0, input_idx, required),
+ (0, commitment_tx_output_idx, required),
(2, payment_preimage, option),
});
/// Transaction outputs to watch for on-chain spends.
pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>);
-#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
-/// Used only in testing and fuzztarget to check serialization roundtrips don't change the
-/// underlying object
+#[cfg(any(test, fuzzing, feature = "_test_utils"))]
+/// Used only in testing and fuzzing to check serialization roundtrips don't change the underlying
+/// object
impl<Signer: Sign> PartialEq for ChannelMonitor<Signer> {
fn eq(&self, other: &Self) -> bool {
let inner = self.inner.lock().unwrap();
}
}
-#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
-/// Used only in testing and fuzztarget to check serialization roundtrips don't change the
-/// underlying object
+#[cfg(any(test, fuzzing, feature = "_test_utils"))]
+/// Used only in testing and fuzzing to check serialization roundtrips don't change the underlying
+/// object
impl<Signer: Sign> PartialEq for ChannelMonitorImpl<Signer> {
fn eq(&self, other: &Self) -> bool {
if self.latest_update_id != other.latest_update_id ||
macro_rules! walk_htlcs {
($holder_commitment: expr, $htlc_iter: expr) => {
for htlc in $htlc_iter {
- if let Some(htlc_input_idx) = htlc.transaction_output_index {
- if us.htlcs_resolved_on_chain.iter().any(|v| v.input_idx == htlc_input_idx) {
- assert!(us.funding_spend_confirmed.is_some());
+ if let Some(htlc_commitment_tx_output_idx) = htlc.transaction_output_index {
+ if let Some(conf_thresh) = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::MaturingOutput { descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) } = &event.event {
+ if descriptor.outpoint.index as u32 == htlc_commitment_tx_output_idx { Some(event.confirmation_threshold()) } else { None }
+ } else { None }
+ }) {
+ debug_assert!($holder_commitment);
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ confirmation_height: conf_thresh,
+ });
+ } else if us.htlcs_resolved_on_chain.iter().any(|v| v.commitment_tx_output_idx == htlc_commitment_tx_output_idx) {
+ // Funding transaction spends should be fully confirmed by the time any
+ // HTLC transactions are resolved, unless we're talking about a holder
+ // commitment tx, whose resolution is delayed until the CSV timeout is
+ // reached, even though HTLCs may be resolved after only
+ // ANTI_REORG_DELAY confirmations.
+ debug_assert!($holder_commitment || us.funding_spend_confirmed.is_some());
} else if htlc.offered == $holder_commitment {
// If the payment was outbound, check if there's an HTLCUpdate
// indicating we have spent this HTLC with a timeout, claiming it back
// and awaiting confirmations on it.
let htlc_update_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
- if let OnchainEvent::HTLCUpdate { input_idx: Some(input_idx), .. } = event.event {
- if input_idx == htlc_input_idx { Some(event.confirmation_threshold()) } else { None }
+ if let OnchainEvent::HTLCUpdate { commitment_tx_output_idx: Some(commitment_tx_output_idx), .. } = event.event {
+ if commitment_tx_output_idx == htlc_commitment_tx_output_idx {
+ Some(event.confirmation_threshold()) } else { None }
} else { None }
});
if let Some(conf_thresh) = htlc_update_pending {
// preimage, we lost funds to our counterparty! We will then continue
// to show it as ContentiousClaimable until ANTI_REORG_DELAY.
let htlc_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
- if let OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } = event.event {
- if input_idx == htlc_input_idx {
+ if let OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } = event.event {
+ if commitment_tx_output_idx == htlc_commitment_tx_output_idx {
Some((event.confirmation_threshold(), preimage.is_some()))
} else { None }
} else { None }
macro_rules! walk_htlcs {
($holder_commitment: expr, $htlc_iter: expr) => {
for (htlc, source) in $htlc_iter {
- if us.htlcs_resolved_on_chain.iter().any(|v| Some(v.input_idx) == htlc.transaction_output_index) {
+ if us.htlcs_resolved_on_chain.iter().any(|v| Some(v.commitment_tx_output_idx) == htlc.transaction_output_index) {
// We should assert that funding_spend_confirmed is_some() here, but we
// have some unit tests which violate HTLC transaction CSVs entirely and
// would fail.
// indicating we have spent this HTLC with a timeout, claiming it back
// and awaiting confirmations on it.
let htlc_update_confd = us.onchain_events_awaiting_threshold_conf.iter().any(|event| {
- if let OnchainEvent::HTLCUpdate { input_idx: Some(input_idx), .. } = event.event {
+ if let OnchainEvent::HTLCUpdate { commitment_tx_output_idx: Some(commitment_tx_output_idx), .. } = event.event {
// If the HTLC was timed out, we wait for ANTI_REORG_DELAY blocks
// before considering it "no longer pending" - this matches when we
// provide the ChannelManager an HTLC failure event.
- Some(input_idx) == htlc.transaction_output_index &&
+ Some(commitment_tx_output_idx) == htlc.transaction_output_index &&
us.best_block.height() >= event.height + ANTI_REORG_DELAY - 1
- } else if let OnchainEvent::HTLCSpendConfirmation { input_idx, .. } = event.event {
+ } else if let OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, .. } = event.event {
// If the HTLC was fulfilled with a preimage, we consider the HTLC
// immediately non-pending, matching when we provide ChannelManager
// the preimage.
- Some(input_idx) == htlc.transaction_output_index
+ Some(commitment_tx_output_idx) == htlc.transaction_output_index
} else { false }
});
if !htlc_update_confd {
event: OnchainEvent::HTLCUpdate {
source: (**source).clone(),
payment_hash: htlc.payment_hash.clone(),
- onchain_value_satoshis: Some(htlc.amount_msat / 1000),
- input_idx: None,
+ htlc_value_satoshis: Some(htlc.amount_msat / 1000),
+ commitment_tx_output_idx: None,
},
};
log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction, waiting for confirmation (at height {})",
F::Target: FeeEstimator,
L::Target: Logger,
{
+ log_info!(logger, "Applying update to monitor {}, bringing update_id from {} to {} with {} changes.",
+ log_funding_info!(self), self.latest_update_id, updates.update_id, updates.updates.len());
// ChannelMonitor updates may be applied after force close if we receive a
// preimage for a broadcasted commitment transaction HTLC output that we'd
// like to claim on-chain. If this is the case, we no longer have guaranteed
// access to the monitor's update ID, so we use a sentinel value instead.
if updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
+ assert_eq!(updates.updates.len(), 1);
match updates.updates[0] {
ChannelMonitorUpdateStep::PaymentPreimage { .. } => {},
- _ => panic!("Attempted to apply post-force-close ChannelMonitorUpdate that wasn't providing a payment preimage"),
+ _ => {
+ log_error!(logger, "Attempted to apply post-force-close ChannelMonitorUpdate of type {}", updates.updates[0].variant_name());
+ panic!("Attempted to apply post-force-close ChannelMonitorUpdate that wasn't providing a payment preimage");
+ },
}
- assert_eq!(updates.updates.len(), 1);
} else if self.latest_update_id + 1 != updates.update_id {
panic!("Attempted to apply ChannelMonitorUpdates out of order, check the update_id before passing an update to update_monitor!");
}
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);
}
// Produce actionable events from on-chain events having reached their threshold.
for entry in onchain_events_reaching_threshold_conf.drain(..) {
match entry.event {
- OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis, input_idx } => {
+ OnchainEvent::HTLCUpdate { ref source, payment_hash, htlc_value_satoshis, commitment_tx_output_idx } => {
// Check for duplicate HTLC resolutions.
#[cfg(debug_assertions)]
{
payment_hash,
payment_preimage: None,
source: source.clone(),
- onchain_value_satoshis,
+ htlc_value_satoshis,
}));
- if let Some(idx) = input_idx {
- self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx: idx, payment_preimage: None });
+ if let Some(idx) = commitment_tx_output_idx {
+ self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { commitment_tx_output_idx: idx, payment_preimage: None });
}
},
OnchainEvent::MaturingOutput { descriptor } => {
outputs: vec![descriptor]
});
},
- OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } => {
- self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx, payment_preimage: preimage });
+ OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => {
+ self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { commitment_tx_output_idx, payment_preimage: preimage });
},
OnchainEvent::FundingSpendConfirmation { .. } => {
self.funding_spend_confirmed = Some(entry.txid);
// appears to be spending the correct type (ie that the match would
// actually succeed in BIP 158/159-style filters).
if _script_pubkey.is_v0_p2wsh() {
- assert_eq!(&bitcoin::Address::p2wsh(&Script::from(input.witness.last().unwrap().clone()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
+ assert_eq!(&bitcoin::Address::p2wsh(&Script::from(input.witness.last().unwrap().to_vec()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
} else if _script_pubkey.is_v0_p2wpkh() {
assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey);
} else { panic!(); }
fn is_resolving_htlc_output<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) where L::Target: Logger {
'outer_loop: for input in &tx.input {
let mut payment_data = None;
- let revocation_sig_claim = (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && input.witness[1].len() == 33)
- || (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && input.witness[1].len() == 33);
- let accepted_preimage_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::AcceptedHTLC);
+ let witness_items = input.witness.len();
+ let htlctype = input.witness.last().map(|w| w.len()).and_then(HTLCType::scriptlen_to_htlctype);
+ let prev_last_witness_len = input.witness.second_to_last().map(|w| w.len()).unwrap_or(0);
+ let revocation_sig_claim = (witness_items == 3 && htlctype == Some(HTLCType::OfferedHTLC) && prev_last_witness_len == 33)
+ || (witness_items == 3 && htlctype == Some(HTLCType::AcceptedHTLC) && prev_last_witness_len == 33);
+ let accepted_preimage_claim = witness_items == 5 && htlctype == Some(HTLCType::AcceptedHTLC);
#[cfg(not(fuzzing))]
- let accepted_timeout_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim;
- let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && !revocation_sig_claim;
+ let accepted_timeout_claim = witness_items == 3 && htlctype == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim;
+ let offered_preimage_claim = witness_items == 3 && htlctype == Some(HTLCType::OfferedHTLC) && !revocation_sig_claim;
#[cfg(not(fuzzing))]
- let offered_timeout_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::OfferedHTLC);
+ let offered_timeout_claim = witness_items == 5 && htlctype == Some(HTLCType::OfferedHTLC);
let mut payment_preimage = PaymentPreimage([0; 32]);
if accepted_preimage_claim {
- payment_preimage.0.copy_from_slice(&input.witness[3]);
+ payment_preimage.0.copy_from_slice(input.witness.second_to_last().unwrap());
} else if offered_preimage_claim {
- payment_preimage.0.copy_from_slice(&input.witness[1]);
+ payment_preimage.0.copy_from_slice(input.witness.second_to_last().unwrap());
}
macro_rules! log_claim {
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
txid: tx.txid(), height,
event: OnchainEvent::HTLCSpendConfirmation {
- input_idx: input.previous_output.vout,
+ commitment_tx_output_idx: input.previous_output.vout,
preimage: if accepted_preimage_claim || offered_preimage_claim {
Some(payment_preimage) } else { None },
// If this is a payment to us (!outbound_htlc, above),
txid: tx.txid(),
height,
event: OnchainEvent::HTLCSpendConfirmation {
- input_idx: input.previous_output.vout,
+ commitment_tx_output_idx: input.previous_output.vout,
preimage: Some(payment_preimage),
on_to_local_output_csv: None,
},
source,
payment_preimage: Some(payment_preimage),
payment_hash,
- onchain_value_satoshis: Some(amount_msat / 1000),
+ htlc_value_satoshis: Some(amount_msat / 1000),
}));
}
} else if offered_preimage_claim {
txid: tx.txid(),
height,
event: OnchainEvent::HTLCSpendConfirmation {
- input_idx: input.previous_output.vout,
+ commitment_tx_output_idx: input.previous_output.vout,
preimage: Some(payment_preimage),
on_to_local_output_csv: None,
},
source,
payment_preimage: Some(payment_preimage),
payment_hash,
- onchain_value_satoshis: Some(amount_msat / 1000),
+ htlc_value_satoshis: Some(amount_msat / 1000),
}));
}
} else {
height,
event: OnchainEvent::HTLCUpdate {
source, payment_hash,
- onchain_value_satoshis: Some(amount_msat / 1000),
- input_idx: Some(input.previous_output.vout),
+ htlc_value_satoshis: Some(amount_msat / 1000),
+ commitment_tx_output_idx: Some(input.previous_output.vout),
},
};
log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
F::Target: FeeEstimator,
L::Target: Logger,
{
- fn block_connected(&self, block: &Block, height: u32) {
- let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
- self.0.block_connected(&block.header, &txdata, height, &*self.1, &*self.2, &*self.3);
+ fn filtered_block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
+ self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &*self.3);
}
fn block_disconnected(&self, header: &BlockHeader, height: u32) {
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
- use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, SigHashType};
+ use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, EcdsaSighashType};
use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
- use bitcoin::util::bip143;
+ use bitcoin::util::sighash;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::hex::FromHex;
use bitcoin::hash_types::{BlockHash, Txid};
use bitcoin::network::constants::Network;
- use bitcoin::secp256k1::key::{SecretKey,PublicKey};
+ use bitcoin::secp256k1::{SecretKey,PublicKey};
use bitcoin::secp256k1::Secp256k1;
use hex;
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};
use util::ser::{ReadableArgs, Writeable};
use sync::{Arc, Mutex};
use io;
+ use bitcoin::Witness;
use prelude::*;
fn do_test_funding_spend_refuses_updates(use_local_txn: bool) {
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
+ SecretKey::from_slice(&[41; 32]).unwrap(),
[41; 32],
0,
[0; 32]
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]),
transaction_output_index: Some($idx as u32),
};
let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&pubkey, 256, &pubkey) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, $opt_anchors, &pubkey, &pubkey, &pubkey) };
- let sighash = hash_to_message!(&$sighash_parts.signature_hash($idx, &redeem_script, $amount, SigHashType::All)[..]);
- 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();
+ let sighash = hash_to_message!(&$sighash_parts.segwit_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]);
+ let sig = secp_ctx.sign_ecdsa(&sighash, &privkey);
+ let mut ser_sig = sig.serialize_der().to_vec();
+ ser_sig.push(EcdsaSighashType::All as u8);
+ $sum_actual_sigs += ser_sig.len();
+ let witness = $sighash_parts.witness_mut($idx).unwrap();
+ witness.push(ser_sig);
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 {
- $sighash_parts.access_witness($idx).push(pubkey.clone().serialize().to_vec());
- } else if *$weight == WEIGHT_RECEIVED_HTLC {
- $sighash_parts.access_witness($idx).push(vec![0]);
+ witness.push(vec!(1));
+ } else if *$weight == weight_revoked_offered_htlc($opt_anchors) || *$weight == weight_revoked_received_htlc($opt_anchors) {
+ witness.push(pubkey.clone().serialize().to_vec());
+ } else if *$weight == weight_received_htlc($opt_anchors) {
+ witness.push(vec![0]);
} else {
- $sighash_parts.access_witness($idx).push(PaymentPreimage([1; 32]).0.to_vec());
+ witness.push(PaymentPreimage([1; 32]).0.to_vec());
}
- $sighash_parts.access_witness($idx).push(redeem_script.into_bytes());
- println!("witness[0] {}", $sighash_parts.access_witness($idx)[0].len());
- println!("witness[1] {}", $sighash_parts.access_witness($idx)[1].len());
- println!("witness[2] {}", $sighash_parts.access_witness($idx)[2].len());
+ witness.push(redeem_script.into_bytes());
+ let witness = witness.to_vec();
+ println!("witness[0] {}", witness[0].len());
+ println!("witness[1] {}", witness[1].len());
+ println!("witness[2] {}", witness[2].len());
}
}
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: Witness::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.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 = sighash::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.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: Witness::new(),
+ });
+ }
+ claim_tx.output.push(TxOut {
+ script_pubkey: script_pubkey.clone(),
+ value: 0,
+ });
+ let base_weight = claim_tx.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 = sighash::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.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(),
+ witness: Witness::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.weight();
+ let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
+ let mut inputs_total_weight = 2; // count segwit flags
+ {
+ let mut sighash_parts = sighash::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.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.