+ sequence: Sequence(if channel_type_features.supports_anchors_zero_fee_htlc_tx() { 1 } else { 0 }),
+ witness: Witness::new(),
+ }
+}
+
+pub(crate) fn build_htlc_output(
+ feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey
+) -> TxOut {
+ let weight = if htlc.offered {
+ htlc_timeout_tx_weight(channel_type_features)
+ } else {
+ htlc_success_tx_weight(channel_type_features)
+ };
+ let output_value = if channel_type_features.supports_anchors_zero_fee_htlc_tx() && !channel_type_features.supports_anchors_nonzero_fee_htlc_tx() {
+ htlc.amount_msat / 1000
+ } else {
+ let total_fee = feerate_per_kw as u64 * weight / 1000;
+ htlc.amount_msat / 1000 - total_fee
+ };
+
+ TxOut {
+ script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_v0_p2wsh(),
+ value: output_value,
+ }
+}
+
+/// Returns the witness required to satisfy and spend a HTLC input.
+pub fn build_htlc_input_witness(
+ local_sig: &Signature, remote_sig: &Signature, preimage: &Option<PaymentPreimage>,
+ redeem_script: &Script, channel_type_features: &ChannelTypeFeatures,
+) -> Witness {
+ let remote_sighash_type = if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
+ EcdsaSighashType::SinglePlusAnyoneCanPay
+ } else {
+ EcdsaSighashType::All
+ };
+
+ let mut witness = Witness::new();
+ // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
+ witness.push(vec![]);
+ witness.push_bitcoin_signature(&remote_sig.serialize_der(), remote_sighash_type);
+ witness.push_bitcoin_signature(&local_sig.serialize_der(), EcdsaSighashType::All);
+ if let Some(preimage) = preimage {
+ witness.push(preimage.0.to_vec());
+ } else {
+ // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay.
+ witness.push(vec![]);
+ }
+ witness.push(redeem_script.to_bytes());
+ witness
+}
+
+/// Pre-anchors channel type features did not use to get serialized in the following six structs:
+/// — [`ChannelTransactionParameters`]
+/// — [`CommitmentTransaction`]
+/// — [`CounterpartyOfferedHTLCOutput`]
+/// — [`CounterpartyReceivedHTLCOutput`]
+/// — [`HolderHTLCOutput`]
+/// — [`HolderFundingOutput`]
+///
+/// To ensure a forwards-compatible serialization, we use odd TLV fields. However, if new features
+/// are used that could break security, where old signers should be prevented from handling the
+/// serialized data, an optional even-field TLV will be used as a stand-in to break compatibility.
+///
+/// This method determines whether or not that option needs to be set based on the chanenl type
+/// features, and returns it.
+///
+/// [`CounterpartyOfferedHTLCOutput`]: crate::chain::package::CounterpartyOfferedHTLCOutput
+/// [`CounterpartyReceivedHTLCOutput`]: crate::chain::package::CounterpartyReceivedHTLCOutput
+/// [`HolderHTLCOutput`]: crate::chain::package::HolderHTLCOutput
+/// [`HolderFundingOutput`]: crate::chain::package::HolderFundingOutput
+pub(crate) fn legacy_deserialization_prevention_marker_for_channel_type_features(features: &ChannelTypeFeatures) -> Option<()> {
+ let mut legacy_version_bit_set = ChannelTypeFeatures::only_static_remote_key();
+ legacy_version_bit_set.set_scid_privacy_required();
+ legacy_version_bit_set.set_zero_conf_required();
+
+ if features.is_subset(&legacy_version_bit_set) {
+ None
+ } else {
+ Some(())
+ }
+}
+
+/// Gets the witnessScript for the to_remote output when anchors are enabled.
+#[inline]
+pub fn get_to_countersignatory_with_anchors_redeemscript(payment_point: &PublicKey) -> Script {
+ Builder::new()
+ .push_slice(&payment_point.serialize()[..])
+ .push_opcode(opcodes::all::OP_CHECKSIGVERIFY)
+ .push_int(1)
+ .push_opcode(opcodes::all::OP_CSV)
+ .into_script()
+}
+
+/// Gets the witnessScript for an anchor output from the funding public key.
+/// The witness in the spending input must be:
+/// <BIP 143 funding_signature>
+/// After 16 blocks of confirmation, an alternative satisfying witness could be:
+/// <>
+/// (empty vector required to satisfy compliance with MINIMALIF-standard rule)
+#[inline]
+pub fn get_anchor_redeemscript(funding_pubkey: &PublicKey) -> Script {
+ Builder::new().push_slice(&funding_pubkey.serialize()[..])
+ .push_opcode(opcodes::all::OP_CHECKSIG)
+ .push_opcode(opcodes::all::OP_IFDUP)
+ .push_opcode(opcodes::all::OP_NOTIF)
+ .push_int(16)
+ .push_opcode(opcodes::all::OP_CSV)
+ .push_opcode(opcodes::all::OP_ENDIF)
+ .into_script()
+}
+
+/// Locates the output with an anchor script paying to `funding_pubkey` within `commitment_tx`.
+pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubkey: &PublicKey) -> Option<(u32, &'a TxOut)> {
+ let anchor_script = chan_utils::get_anchor_redeemscript(funding_pubkey).to_v0_p2wsh();
+ commitment_tx.output.iter().enumerate()
+ .find(|(_, txout)| txout.script_pubkey == anchor_script)
+ .map(|(idx, txout)| (idx as u32, txout))
+}
+
+/// Returns the witness required to satisfy and spend an anchor input.
+pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness {
+ let anchor_redeem_script = chan_utils::get_anchor_redeemscript(funding_key);
+ let mut ret = Witness::new();
+ ret.push_bitcoin_signature(&funding_sig.serialize_der(), EcdsaSighashType::All);
+ ret.push(anchor_redeem_script.as_bytes());
+ ret
+}
+
+/// Per-channel data used to build transactions in conjunction with the per-commitment data (CommitmentTransaction).
+/// The fields are organized by holder/counterparty.
+///
+/// Normally, this is converted to the broadcaster/countersignatory-organized DirectedChannelTransactionParameters
+/// before use, via the as_holder_broadcastable and as_counterparty_broadcastable functions.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct ChannelTransactionParameters {
+ /// Holder public keys
+ pub holder_pubkeys: ChannelPublicKeys,
+ /// The contest delay selected by the holder, which applies to counterparty-broadcast transactions
+ pub holder_selected_contest_delay: u16,
+ /// Whether the holder is the initiator of this channel.
+ /// This is an input to the commitment number obscure factor computation.
+ pub is_outbound_from_holder: bool,
+ /// The late-bound counterparty channel transaction parameters.
+ /// These parameters are populated at the point in the protocol where the counterparty provides them.
+ pub counterparty_parameters: Option<CounterpartyChannelTransactionParameters>,
+ /// The late-bound funding outpoint
+ pub funding_outpoint: Option<chain::transaction::OutPoint>,
+ /// This channel's type, as negotiated during channel open. For old objects where this field
+ /// wasn't serialized, it will default to static_remote_key at deserialization.
+ pub channel_type_features: ChannelTypeFeatures
+}
+
+/// Late-bound per-channel counterparty data used to build transactions.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct CounterpartyChannelTransactionParameters {
+ /// Counter-party public keys
+ pub pubkeys: ChannelPublicKeys,
+ /// The contest delay selected by the counterparty, which applies to holder-broadcast transactions
+ pub selected_contest_delay: u16,
+}
+
+impl ChannelTransactionParameters {
+ /// Whether the late bound parameters are populated.
+ pub fn is_populated(&self) -> bool {
+ self.counterparty_parameters.is_some() && self.funding_outpoint.is_some()
+ }
+
+ /// Convert the holder/counterparty parameters to broadcaster/countersignatory-organized parameters,
+ /// given that the holder is the broadcaster.
+ ///
+ /// self.is_populated() must be true before calling this function.
+ pub fn as_holder_broadcastable(&self) -> DirectedChannelTransactionParameters {
+ assert!(self.is_populated(), "self.late_parameters must be set before using as_holder_broadcastable");
+ DirectedChannelTransactionParameters {
+ inner: self,
+ holder_is_broadcaster: true
+ }
+ }
+
+ /// Convert the holder/counterparty parameters to broadcaster/countersignatory-organized parameters,
+ /// given that the counterparty is the broadcaster.
+ ///
+ /// self.is_populated() must be true before calling this function.
+ pub fn as_counterparty_broadcastable(&self) -> DirectedChannelTransactionParameters {
+ assert!(self.is_populated(), "self.late_parameters must be set before using as_counterparty_broadcastable");
+ DirectedChannelTransactionParameters {
+ inner: self,
+ holder_is_broadcaster: false
+ }
+ }
+}
+
+impl_writeable_tlv_based!(CounterpartyChannelTransactionParameters, {
+ (0, pubkeys, required),
+ (2, selected_contest_delay, required),
+});
+
+impl Writeable for ChannelTransactionParameters {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ let legacy_deserialization_prevention_marker = legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features);
+ write_tlv_fields!(writer, {
+ (0, self.holder_pubkeys, required),
+ (2, self.holder_selected_contest_delay, required),
+ (4, self.is_outbound_from_holder, required),
+ (6, self.counterparty_parameters, option),
+ (8, self.funding_outpoint, option),
+ (10, legacy_deserialization_prevention_marker, option),
+ (11, self.channel_type_features, required),
+ });
+ Ok(())
+ }
+}
+
+impl Readable for ChannelTransactionParameters {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ let mut holder_pubkeys = RequiredWrapper(None);
+ let mut holder_selected_contest_delay = RequiredWrapper(None);
+ let mut is_outbound_from_holder = RequiredWrapper(None);
+ let mut counterparty_parameters = None;
+ let mut funding_outpoint = None;
+ let mut _legacy_deserialization_prevention_marker: Option<()> = None;
+ let mut channel_type_features = None;
+
+ read_tlv_fields!(reader, {
+ (0, holder_pubkeys, required),
+ (2, holder_selected_contest_delay, required),
+ (4, is_outbound_from_holder, required),
+ (6, counterparty_parameters, option),
+ (8, funding_outpoint, option),
+ (10, _legacy_deserialization_prevention_marker, option),
+ (11, channel_type_features, option),
+ });
+
+ let mut additional_features = ChannelTypeFeatures::empty();
+ additional_features.set_anchors_nonzero_fee_htlc_tx_required();
+ chain::package::verify_channel_type_features(&channel_type_features, Some(&additional_features))?;
+
+ Ok(Self {
+ holder_pubkeys: holder_pubkeys.0.unwrap(),
+ holder_selected_contest_delay: holder_selected_contest_delay.0.unwrap(),
+ is_outbound_from_holder: is_outbound_from_holder.0.unwrap(),
+ counterparty_parameters,
+ funding_outpoint,
+ channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key())
+ })
+ }
+}