+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct InteractiveTxOutput {
+ serial_id: SerialId,
+ tx_out: TxOut,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct ConstructedTransaction {
+ holder_is_initiator: bool,
+
+ inputs: Vec<InteractiveTxInput>,
+ outputs: Vec<InteractiveTxOutput>,
+
+ local_inputs_value_satoshis: u64,
+ local_outputs_value_satoshis: u64,
+
+ remote_inputs_value_satoshis: u64,
+ remote_outputs_value_satoshis: u64,
+
+ lock_time: AbsoluteLockTime,
+}
+
+impl ConstructedTransaction {
+ fn new(context: NegotiationContext) -> Self {
+ let local_inputs_value_satoshis = context
+ .inputs
+ .iter()
+ .filter(|(serial_id, _)| {
+ !is_serial_id_valid_for_counterparty(context.holder_is_initiator, serial_id)
+ })
+ .fold(0u64, |value, (_, input)| value.saturating_add(input.prev_output.value));
+
+ let local_outputs_value_satoshis = context
+ .outputs
+ .iter()
+ .filter(|(serial_id, _)| {
+ !is_serial_id_valid_for_counterparty(context.holder_is_initiator, serial_id)
+ })
+ .fold(0u64, |value, (_, output)| value.saturating_add(output.tx_out.value));
+
+ Self {
+ holder_is_initiator: context.holder_is_initiator,
+
+ local_inputs_value_satoshis,
+ local_outputs_value_satoshis,
+
+ remote_inputs_value_satoshis: context.remote_inputs_value(),
+ remote_outputs_value_satoshis: context.remote_outputs_value(),
+
+ inputs: context.inputs.into_values().collect(),
+ outputs: context.outputs.into_values().collect(),
+
+ lock_time: context.tx_locktime,
+ }
+ }
+
+ pub fn weight(&self) -> Weight {
+ let inputs_weight = self.inputs.iter().fold(
+ Weight::from_wu(0),
+ |weight, InteractiveTxInput { prev_output, .. }| {
+ weight.checked_add(estimate_input_weight(prev_output)).unwrap_or(Weight::MAX)
+ },
+ );
+ let outputs_weight = self.outputs.iter().fold(
+ Weight::from_wu(0),
+ |weight, InteractiveTxOutput { tx_out, .. }| {
+ weight.checked_add(get_output_weight(&tx_out.script_pubkey)).unwrap_or(Weight::MAX)
+ },
+ );
+ Weight::from_wu(TX_COMMON_FIELDS_WEIGHT)
+ .checked_add(inputs_weight)
+ .and_then(|weight| weight.checked_add(outputs_weight))
+ .unwrap_or(Weight::MAX)
+ }
+
+ pub fn into_unsigned_tx(self) -> Transaction {
+ // Inputs and outputs must be sorted by serial_id
+ let ConstructedTransaction { mut inputs, mut outputs, .. } = self;
+
+ inputs.sort_unstable_by_key(|InteractiveTxInput { serial_id, .. }| *serial_id);
+ outputs.sort_unstable_by_key(|InteractiveTxOutput { serial_id, .. }| *serial_id);
+
+ let input: Vec<TxIn> =
+ inputs.into_iter().map(|InteractiveTxInput { input, .. }| input).collect();
+ let output: Vec<TxOut> =
+ outputs.into_iter().map(|InteractiveTxOutput { tx_out, .. }| tx_out).collect();
+
+ Transaction { version: 2, lock_time: self.lock_time, input, output }
+ }
+}
+