for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
- let htlc_output = if htlc.offered {
- HolderHTLCOutput::build_offered(htlc.amount_msat, htlc.cltv_expiry)
+ let (htlc_output, aggregable) = if htlc.offered {
+ let htlc_output = HolderHTLCOutput::build_offered(
+ htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.opt_anchors()
+ );
+ (htlc_output, false)
+ } else {
+ let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
+ preimage.clone()
} else {
- let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
- preimage.clone()
- } else {
- // We can't build an HTLC-Success transaction without the preimage
- continue;
- };
- HolderHTLCOutput::build_accepted(payment_preimage, htlc.amount_msat)
+ // We can't build an HTLC-Success transaction without the preimage
+ continue;
};
- let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), htlc.cltv_expiry, false, conf_height);
+ let htlc_output = HolderHTLCOutput::build_accepted(
+ payment_preimage, htlc.amount_msat, self.onchain_tx_handler.opt_anchors()
+ );
+ (htlc_output, self.onchain_tx_handler.opt_anchors())
+ };
+ let htlc_package = PackageTemplate::build_package(
+ holder_tx.txid, transaction_output_index,
+ PackageSolvingData::HolderHTLCOutput(htlc_output),
+ htlc.cltv_expiry, aggregable, conf_height
+ );
claim_requests.push(htlc_package);
}
}
amount_msat: u64,
/// Defaults to 0 for HTLC-Success transactions, which have no expiry
cltv_expiry: u32,
+ opt_anchors: Option<()>,
}
impl HolderHTLCOutput {
- pub(crate) fn build_offered(amount_msat: u64, cltv_expiry: u32) -> Self {
+ pub(crate) fn build_offered(amount_msat: u64, cltv_expiry: u32, opt_anchors: bool) -> Self {
HolderHTLCOutput {
preimage: None,
amount_msat,
cltv_expiry,
+ opt_anchors: if opt_anchors { Some(()) } else { None } ,
}
}
- pub(crate) fn build_accepted(preimage: PaymentPreimage, amount_msat: u64) -> Self {
+ pub(crate) fn build_accepted(preimage: PaymentPreimage, amount_msat: u64, opt_anchors: bool) -> Self {
HolderHTLCOutput {
preimage: Some(preimage),
amount_msat,
cltv_expiry: 0,
+ opt_anchors: if opt_anchors { Some(()) } else { None } ,
}
}
+
+ fn opt_anchors(&self) -> bool {
+ self.opt_anchors.is_some()
+ }
}
impl_writeable_tlv_based!(HolderHTLCOutput, {
(0, amount_msat, required),
(2, cltv_expiry, required),
- (4, preimage, option)
+ (4, preimage, option),
+ (6, opt_anchors, option)
});
/// A struct to describe the channel output on the funding transaction.
PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.amount,
PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
- // Note: Currently, amounts of holder outputs spending witnesses aren't used
- // as we can't malleate spending package to increase their feerate. This
- // should change with the remaining anchor output patchset.
- PackageSolvingData::HolderHTLCOutput(..) => unreachable!(),
+ PackageSolvingData::HolderHTLCOutput(ref outp) => {
+ debug_assert!(outp.opt_anchors());
+ outp.amount_msat / 1000
+ },
PackageSolvingData::HolderFundingOutput(ref outp) => {
debug_assert!(outp.opt_anchors());
outp.funding_amount.unwrap()
amt
}
fn weight(&self) -> usize {
- let weight = match self {
- PackageSolvingData::RevokedOutput(ref outp) => { outp.weight as usize },
- PackageSolvingData::RevokedHTLCOutput(ref outp) => { outp.weight as usize },
- PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { weight_offered_htlc(outp.opt_anchors()) as usize },
- PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { weight_received_htlc(outp.opt_anchors()) as usize },
- // Note: Currently, weights of holder outputs spending witnesses aren't used
- // as we can't malleate spending package to increase their feerate. This
- // should change with the remaining anchor output patchset.
- PackageSolvingData::HolderHTLCOutput(..) => { unreachable!() },
- PackageSolvingData::HolderFundingOutput(..) => { unreachable!() },
- };
- weight
+ match self {
+ PackageSolvingData::RevokedOutput(ref outp) => outp.weight as usize,
+ PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.weight as usize,
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => weight_offered_htlc(outp.opt_anchors()) as usize,
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => weight_received_htlc(outp.opt_anchors()) as usize,
+ PackageSolvingData::HolderHTLCOutput(ref outp) => {
+ debug_assert!(outp.opt_anchors());
+ if outp.preimage.is_none() {
+ weight_offered_htlc(true) as usize
+ } else {
+ weight_received_htlc(true) as usize
+ }
+ },
+ // Since HolderFundingOutput maps to an untractable package that is already signed, its
+ // weight can be determined from the transaction itself.
+ PackageSolvingData::HolderFundingOutput(..) => unreachable!(),
+ }
}
fn is_compatible(&self, input: &PackageSolvingData) -> bool {
match self {
pub(crate) fn requires_external_funding(&self) -> bool {
self.inputs.iter().find(|input| match input.1 {
PackageSolvingData::HolderFundingOutput(ref outp) => outp.opt_anchors(),
+ PackageSolvingData::HolderHTLCOutput(ref outp) => outp.opt_anchors(),
_ => false,
}).is_some()
}
PackageSolvingData::RevokedHTLCOutput(..) => PackageMalleability::Malleable,
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => PackageMalleability::Malleable,
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => PackageMalleability::Malleable,
- PackageSolvingData::HolderHTLCOutput(..) => PackageMalleability::Untractable,
+ PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
+ PackageMalleability::Malleable
+ } else {
+ PackageMalleability::Untractable
+ },
PackageSolvingData::HolderFundingOutput(..) => PackageMalleability::Untractable,
};
let mut inputs = Vec::with_capacity(1);
PackageSolvingData::RevokedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { (PackageMalleability::Malleable, false) },
- PackageSolvingData::HolderHTLCOutput(..) => { (PackageMalleability::Untractable, false) },
+ PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
+ (PackageMalleability::Malleable, outp.preimage.is_some())
+ } else {
+ (PackageMalleability::Untractable, false)
+ },
PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) },
}
} else { return Err(DecodeError::InvalidValue); };
() => {
{
let preimage = PaymentPreimage([2;32]);
- PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0))
+ PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0, false))
}
}
}