@@ -15,18 +15,21 @@ use bitcoin::amount::Amount;
15
15
use bitcoin:: consensus:: Encodable ;
16
16
use bitcoin:: constants:: WITNESS_SCALE_FACTOR ;
17
17
use bitcoin:: policy:: MAX_STANDARD_TX_WEIGHT ;
18
+ use bitcoin:: secp256k1:: PublicKey ;
18
19
use bitcoin:: transaction:: Version ;
19
20
use bitcoin:: { OutPoint , ScriptBuf , Sequence , Transaction , TxIn , TxOut , Weight } ;
20
21
21
22
use crate :: chain:: chaininterface:: fee_for_weight;
22
23
use crate :: events:: bump_transaction:: { BASE_INPUT_WEIGHT , EMPTY_SCRIPT_SIG_WEIGHT } ;
24
+ use crate :: events:: MessageSendEvent ;
23
25
use crate :: ln:: channel:: TOTAL_BITCOIN_SUPPLY_SATOSHIS ;
24
26
use crate :: ln:: msgs;
25
27
use crate :: ln:: msgs:: SerialId ;
26
28
use crate :: ln:: types:: ChannelId ;
27
29
use crate :: sign:: { EntropySource , P2TR_KEY_PATH_WITNESS_WEIGHT , P2WPKH_WITNESS_WEIGHT } ;
28
30
use crate :: util:: ser:: TransactionU16LenLimited ;
29
31
32
+ use core:: fmt:: Display ;
30
33
use core:: ops:: Deref ;
31
34
32
35
/// The number of received `tx_add_input` messages during a negotiation at which point the
@@ -43,7 +46,7 @@ const MAX_INPUTS_OUTPUTS_COUNT: usize = 252;
43
46
44
47
/// The total weight of the common fields whose fee is paid by the initiator of the interactive
45
48
/// transaction construction protocol.
46
- const TX_COMMON_FIELDS_WEIGHT : u64 = ( 4 /* version */ + 4 /* locktime */ + 1 /* input count */ +
49
+ pub ( crate ) const TX_COMMON_FIELDS_WEIGHT : u64 = ( 4 /* version */ + 4 /* locktime */ + 1 /* input count */ +
47
50
1 /* output count */ ) * WITNESS_SCALE_FACTOR as u64 + 2 /* segwit marker + flag */ ;
48
51
49
52
// BOLT 3 - Lower bounds for input weights
@@ -108,6 +111,47 @@ pub(crate) enum AbortReason {
108
111
InvalidLowFundingOutputValue ,
109
112
}
110
113
114
+ impl AbortReason {
115
+ pub fn into_tx_abort_msg ( self , channel_id : ChannelId ) -> msgs:: TxAbort {
116
+ msgs:: TxAbort { channel_id, data : self . to_string ( ) . into_bytes ( ) }
117
+ }
118
+ }
119
+
120
+ impl Display for AbortReason {
121
+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
122
+ f. write_str ( match self {
123
+ AbortReason :: InvalidStateTransition => "State transition was invalid" ,
124
+ AbortReason :: UnexpectedCounterpartyMessage => "Unexpected message" ,
125
+ AbortReason :: ReceivedTooManyTxAddInputs => "Too many `tx_add_input`s received" ,
126
+ AbortReason :: ReceivedTooManyTxAddOutputs => "Too many `tx_add_output`s received" ,
127
+ AbortReason :: IncorrectInputSequenceValue => {
128
+ "Input has a sequence value greater than 0xFFFFFFFD"
129
+ } ,
130
+ AbortReason :: IncorrectSerialIdParity => "Parity for `serial_id` was incorrect" ,
131
+ AbortReason :: SerialIdUnknown => "The `serial_id` is unknown" ,
132
+ AbortReason :: DuplicateSerialId => "The `serial_id` already exists" ,
133
+ AbortReason :: PrevTxOutInvalid => "Invalid previous transaction output" ,
134
+ AbortReason :: ExceededMaximumSatsAllowed => {
135
+ "Output amount exceeded total bitcoin supply"
136
+ } ,
137
+ AbortReason :: ExceededNumberOfInputsOrOutputs => "Too many inputs or outputs" ,
138
+ AbortReason :: TransactionTooLarge => "Transaction weight is too large" ,
139
+ AbortReason :: BelowDustLimit => "Output amount is below the dust limit" ,
140
+ AbortReason :: InvalidOutputScript => "The output script is non-standard" ,
141
+ AbortReason :: InsufficientFees => "Insufficient fees paid" ,
142
+ AbortReason :: OutputsValueExceedsInputsValue => {
143
+ "Total value of outputs exceeds total value of inputs"
144
+ } ,
145
+ AbortReason :: InvalidTx => "The transaction is invalid" ,
146
+ AbortReason :: MissingFundingOutput => "No shared funding output found" ,
147
+ AbortReason :: DuplicateFundingOutput => "More than one funding output found" ,
148
+ AbortReason :: InvalidLowFundingOutputValue => {
149
+ "Local part of funding output value is greater than the funding output value"
150
+ } ,
151
+ } )
152
+ }
153
+ }
154
+
111
155
#[ derive( Debug , Clone , PartialEq , Eq ) ]
112
156
pub ( crate ) struct ConstructedTransaction {
113
157
holder_is_initiator : bool ,
@@ -1048,8 +1092,9 @@ impl InteractiveTxInput {
1048
1092
}
1049
1093
}
1050
1094
1051
- pub ( crate ) struct InteractiveTxConstructor {
1095
+ pub ( super ) struct InteractiveTxConstructor {
1052
1096
state_machine : StateMachine ,
1097
+ initiator_first_message : Option < InteractiveTxMessageSend > ,
1053
1098
channel_id : ChannelId ,
1054
1099
inputs_to_contribute : Vec < ( SerialId , TxIn , TransactionU16LenLimited ) > ,
1055
1100
outputs_to_contribute : Vec < ( SerialId , OutputOwned ) > ,
@@ -1062,6 +1107,39 @@ pub(crate) enum InteractiveTxMessageSend {
1062
1107
TxComplete ( msgs:: TxComplete ) ,
1063
1108
}
1064
1109
1110
+ impl InteractiveTxMessageSend {
1111
+ pub fn into_msg_send_event ( self , counterparty_node_id : PublicKey ) -> MessageSendEvent {
1112
+ match self {
1113
+ InteractiveTxMessageSend :: TxAddInput ( msg) => {
1114
+ MessageSendEvent :: SendTxAddInput { node_id : counterparty_node_id, msg }
1115
+ } ,
1116
+ InteractiveTxMessageSend :: TxAddOutput ( msg) => {
1117
+ MessageSendEvent :: SendTxAddOutput { node_id : counterparty_node_id, msg }
1118
+ } ,
1119
+ InteractiveTxMessageSend :: TxComplete ( msg) => {
1120
+ MessageSendEvent :: SendTxComplete { node_id : counterparty_node_id, msg }
1121
+ } ,
1122
+ }
1123
+ }
1124
+ }
1125
+
1126
+ pub ( super ) struct InteractiveTxMessageSendResult (
1127
+ pub Result < InteractiveTxMessageSend , msgs:: TxAbort > ,
1128
+ ) ;
1129
+
1130
+ impl InteractiveTxMessageSendResult {
1131
+ pub fn into_msg_send_event ( self , counterparty_node_id : PublicKey ) -> MessageSendEvent {
1132
+ match self . 0 {
1133
+ Ok ( interactive_tx_msg_send) => {
1134
+ interactive_tx_msg_send. into_msg_send_event ( counterparty_node_id)
1135
+ } ,
1136
+ Err ( tx_abort_msg) => {
1137
+ MessageSendEvent :: SendTxAbort { node_id : counterparty_node_id, msg : tx_abort_msg }
1138
+ } ,
1139
+ }
1140
+ }
1141
+ }
1142
+
1065
1143
// This macro executes a state machine transition based on a provided action.
1066
1144
macro_rules! do_state_transition {
1067
1145
( $self: ident, $transition: ident, $msg: expr) => { {
@@ -1094,6 +1172,22 @@ pub(crate) enum HandleTxCompleteValue {
1094
1172
NegotiationComplete ( ConstructedTransaction ) ,
1095
1173
}
1096
1174
1175
+ pub ( super ) struct HandleTxCompleteResult ( pub Result < HandleTxCompleteValue , msgs:: TxAbort > ) ;
1176
+
1177
+ pub ( super ) struct InteractiveTxConstructorArgs < ' a , ES : Deref >
1178
+ where
1179
+ ES :: Target : EntropySource ,
1180
+ {
1181
+ pub entropy_source : & ' a ES ,
1182
+ pub channel_id : ChannelId ,
1183
+ pub feerate_sat_per_kw : u32 ,
1184
+ pub is_initiator : bool ,
1185
+ pub funding_tx_locktime : AbsoluteLockTime ,
1186
+ pub inputs_to_contribute : Vec < ( TxIn , TransactionU16LenLimited ) > ,
1187
+ pub outputs_to_contribute : Vec < OutputOwned > ,
1188
+ pub expected_remote_shared_funding_output : Option < ( ScriptBuf , u64 ) > ,
1189
+ }
1190
+
1097
1191
impl InteractiveTxConstructor {
1098
1192
/// Instantiates a new `InteractiveTxConstructor`.
1099
1193
///
@@ -1103,20 +1197,24 @@ impl InteractiveTxConstructor {
1103
1197
/// and its (local) contribution from the shared output:
1104
1198
/// 0 when the whole value belongs to the remote node, or
1105
1199
/// positive if owned also by local.
1106
- /// Note: The local value cannot be larger that the actual shared output.
1200
+ /// Note: The local value cannot be larger than the actual shared output.
1107
1201
///
1108
- /// A tuple is returned containing the newly instantiate `InteractiveTxConstructor` and optionally
1109
- /// an initial wrapped `Tx_` message which the holder needs to send to the counterparty.
1110
- pub fn new < ES : Deref > (
1111
- entropy_source : & ES , channel_id : ChannelId , feerate_sat_per_kw : u32 , is_initiator : bool ,
1112
- funding_tx_locktime : AbsoluteLockTime ,
1113
- inputs_to_contribute : Vec < ( TxIn , TransactionU16LenLimited ) > ,
1114
- outputs_to_contribute : Vec < OutputOwned > ,
1115
- expected_remote_shared_funding_output : Option < ( ScriptBuf , u64 ) > ,
1116
- ) -> Result < ( Self , Option < InteractiveTxMessageSend > ) , AbortReason >
1202
+ /// If the holder is the initiator, they need to send the first message which is a `TxAddInput`
1203
+ /// message.
1204
+ pub fn new < ES : Deref > ( args : InteractiveTxConstructorArgs < ES > ) -> Result < Self , AbortReason >
1117
1205
where
1118
1206
ES :: Target : EntropySource ,
1119
1207
{
1208
+ let InteractiveTxConstructorArgs {
1209
+ entropy_source,
1210
+ channel_id,
1211
+ feerate_sat_per_kw,
1212
+ is_initiator,
1213
+ funding_tx_locktime,
1214
+ inputs_to_contribute,
1215
+ outputs_to_contribute,
1216
+ expected_remote_shared_funding_output,
1217
+ } = args;
1120
1218
// Sanity check: There can be at most one shared output, local-added or remote-added
1121
1219
let mut expected_shared_funding_output: Option < ( ScriptBuf , u64 ) > = None ;
1122
1220
for output in & outputs_to_contribute {
@@ -1175,28 +1273,27 @@ impl InteractiveTxConstructor {
1175
1273
. collect ( ) ;
1176
1274
// In the same manner and for the same rationale as the inputs above, we'll shuffle the outputs.
1177
1275
outputs_to_contribute. sort_unstable_by_key ( |( serial_id, _) | * serial_id) ;
1178
- let mut constructor =
1179
- Self { state_machine, channel_id, inputs_to_contribute, outputs_to_contribute } ;
1180
- let message_send = if is_initiator {
1181
- match constructor. maybe_send_message ( ) {
1182
- Ok ( msg_send) => Some ( msg_send) ,
1183
- Err ( _) => {
1184
- debug_assert ! (
1185
- false ,
1186
- "We should always be able to start our state machine successfully"
1187
- ) ;
1188
- None
1189
- } ,
1190
- }
1191
- } else {
1192
- None
1276
+ let mut constructor = Self {
1277
+ state_machine,
1278
+ initiator_first_message : None ,
1279
+ channel_id,
1280
+ inputs_to_contribute,
1281
+ outputs_to_contribute,
1193
1282
} ;
1194
- Ok ( ( constructor, message_send) )
1283
+ // We'll store the first message for the initiator.
1284
+ if is_initiator {
1285
+ constructor. initiator_first_message = Some ( constructor. maybe_send_message ( ) ?) ;
1286
+ }
1287
+ Ok ( constructor)
1195
1288
} else {
1196
1289
Err ( AbortReason :: MissingFundingOutput )
1197
1290
}
1198
1291
}
1199
1292
1293
+ pub fn take_initiator_first_message ( & mut self ) -> Option < InteractiveTxMessageSend > {
1294
+ self . initiator_first_message . take ( )
1295
+ }
1296
+
1200
1297
fn maybe_send_message ( & mut self ) -> Result < InteractiveTxMessageSend , AbortReason > {
1201
1298
// We first attempt to send inputs we want to add, then outputs. Once we are done sending
1202
1299
// them both, then we always send tx_complete.
@@ -1295,8 +1392,8 @@ mod tests {
1295
1392
use crate :: ln:: channel:: TOTAL_BITCOIN_SUPPLY_SATOSHIS ;
1296
1393
use crate :: ln:: interactivetxs:: {
1297
1394
generate_holder_serial_id, AbortReason , HandleTxCompleteValue , InteractiveTxConstructor ,
1298
- InteractiveTxMessageSend , MAX_INPUTS_OUTPUTS_COUNT , MAX_RECEIVED_TX_ADD_INPUT_COUNT ,
1299
- MAX_RECEIVED_TX_ADD_OUTPUT_COUNT ,
1395
+ InteractiveTxConstructorArgs , InteractiveTxMessageSend , MAX_INPUTS_OUTPUTS_COUNT ,
1396
+ MAX_RECEIVED_TX_ADD_INPUT_COUNT , MAX_RECEIVED_TX_ADD_OUTPUT_COUNT ,
1300
1397
} ;
1301
1398
use crate :: ln:: types:: ChannelId ;
1302
1399
use crate :: sign:: EntropySource ;
@@ -1395,7 +1492,7 @@ mod tests {
1395
1492
ES :: Target : EntropySource ,
1396
1493
{
1397
1494
let channel_id = ChannelId ( entropy_source. get_secure_random_bytes ( ) ) ;
1398
- let tx_locktime = AbsoluteLockTime :: from_height ( 1337 ) . unwrap ( ) ;
1495
+ let funding_tx_locktime = AbsoluteLockTime :: from_height ( 1337 ) . unwrap ( ) ;
1399
1496
1400
1497
// funding output sanity check
1401
1498
let shared_outputs_by_a: Vec < _ > =
@@ -1448,16 +1545,16 @@ mod tests {
1448
1545
}
1449
1546
}
1450
1547
1451
- let ( mut constructor_a, first_message_a ) = match InteractiveTxConstructor :: new (
1548
+ let mut constructor_a = match InteractiveTxConstructor :: new ( InteractiveTxConstructorArgs {
1452
1549
entropy_source,
1453
1550
channel_id,
1454
- TEST_FEERATE_SATS_PER_KW ,
1455
- true ,
1456
- tx_locktime ,
1457
- session. inputs_a ,
1458
- session. outputs_a . to_vec ( ) ,
1459
- session. a_expected_remote_shared_output ,
1460
- ) {
1551
+ feerate_sat_per_kw : TEST_FEERATE_SATS_PER_KW ,
1552
+ is_initiator : true ,
1553
+ funding_tx_locktime ,
1554
+ inputs_to_contribute : session. inputs_a ,
1555
+ outputs_to_contribute : session. outputs_a . to_vec ( ) ,
1556
+ expected_remote_shared_funding_output : session. a_expected_remote_shared_output ,
1557
+ } ) {
1461
1558
Ok ( r) => r,
1462
1559
Err ( abort_reason) => {
1463
1560
assert_eq ! (
@@ -1469,16 +1566,16 @@ mod tests {
1469
1566
return ;
1470
1567
} ,
1471
1568
} ;
1472
- let ( mut constructor_b, first_message_b ) = match InteractiveTxConstructor :: new (
1569
+ let mut constructor_b = match InteractiveTxConstructor :: new ( InteractiveTxConstructorArgs {
1473
1570
entropy_source,
1474
1571
channel_id,
1475
- TEST_FEERATE_SATS_PER_KW ,
1476
- false ,
1477
- tx_locktime ,
1478
- session. inputs_b ,
1479
- session. outputs_b . to_vec ( ) ,
1480
- session. b_expected_remote_shared_output ,
1481
- ) {
1572
+ feerate_sat_per_kw : TEST_FEERATE_SATS_PER_KW ,
1573
+ is_initiator : false ,
1574
+ funding_tx_locktime ,
1575
+ inputs_to_contribute : session. inputs_b ,
1576
+ outputs_to_contribute : session. outputs_b . to_vec ( ) ,
1577
+ expected_remote_shared_funding_output : session. b_expected_remote_shared_output ,
1578
+ } ) {
1482
1579
Ok ( r) => r,
1483
1580
Err ( abort_reason) => {
1484
1581
assert_eq ! (
@@ -1514,8 +1611,7 @@ mod tests {
1514
1611
}
1515
1612
} ;
1516
1613
1517
- assert ! ( first_message_b. is_none( ) ) ;
1518
- let mut message_send_a = first_message_a;
1614
+ let mut message_send_a = constructor_a. take_initiator_first_message ( ) ;
1519
1615
let mut message_send_b = None ;
1520
1616
let mut final_tx_a = None ;
1521
1617
let mut final_tx_b = None ;
0 commit comments