@@ -21,6 +21,14 @@ const MAX_RECEIVED_TX_ADD_INPUT_COUNT: u16 = 4096;
21
21
/// The number of received `tx_add_output` messages during a negotiation at which point the
22
22
/// negotiation MUST be failed.
23
23
const MAX_RECEIVED_TX_ADD_OUTPUT_COUNT : u16 = 4096 ;
24
+
25
+ /// The number of inputs or outputs that the state machine can have, before it MUST fail the
26
+ /// negotiation.
27
+ const MAX_INPUTS_OUTPUTS_COUNT : usize = 252 ;
28
+
29
+ /// Maximum weight of bitcoin transaction
30
+ const MAX_STANDARD_TX_WEIGHT : usize = 400_000 ;
31
+
24
32
const MAX_MONEY : u64 = 2_100_000_000_000_000 ;
25
33
26
34
type SerialId = u64 ;
@@ -42,6 +50,9 @@ pub(crate) enum AbortReason {
42
50
DuplicateSerialId ,
43
51
PrevTxOutInvalid ,
44
52
ExceededMaximumSatsAllowed ,
53
+ ExceededNumberOfInputsOrOutputs ,
54
+ InvalidTransactionState ,
55
+ TransactionTooLarge ,
45
56
}
46
57
47
58
// Interactive Transaction Construction negotiation
@@ -306,6 +317,49 @@ impl<S> InteractiveTxStateMachine<S>
306
317
todo ! ( ) ;
307
318
}
308
319
320
+ fn is_current_transaction_state_able_to_complete ( & self ) -> Result < ( ) , AbortReason > {
321
+ let tx_to_validate = Transaction {
322
+ version : self . context . base_tx . version ,
323
+ lock_time : self . context . base_tx . lock_time ,
324
+ input : self . context . inputs . values ( ) . cloned ( ) . collect ( ) ,
325
+ output : self . context . outputs . values ( ) . cloned ( ) . collect ( ) ,
326
+ } ;
327
+
328
+ // The receiving node:
329
+ // MUST fail the negotiation if:
330
+
331
+ // TODO: Verify this is the correct way to do this.
332
+ // - the peer's total input satoshis is less than their outputs
333
+ let get_output = |outpoint : & OutPoint | {
334
+ if outpoint. txid == tx_to_validate. txid ( ) {
335
+ return tx_to_validate. output . get ( outpoint. vout as usize ) . cloned ( )
336
+ } else {
337
+ None
338
+ }
339
+ } ;
340
+ if let Err ( _) = tx_to_validate. verify ( get_output) {
341
+ return Err ( AbortReason :: InvalidTransactionState )
342
+ } ;
343
+
344
+ // - there are more than 252 inputs
345
+ // - there are more than 252 outputs
346
+ if self . context . inputs . len ( ) > MAX_INPUTS_OUTPUTS_COUNT ||
347
+ self . context . outputs . len ( ) > MAX_INPUTS_OUTPUTS_COUNT {
348
+ return Err ( AbortReason :: ExceededNumberOfInputsOrOutputs )
349
+ }
350
+
351
+ if tx_to_validate. weight ( ) > MAX_STANDARD_TX_WEIGHT {
352
+ return Err ( AbortReason :: TransactionTooLarge )
353
+ }
354
+
355
+ // TODO: Need to figure out how to do this
356
+ // if is the non-initiator:
357
+ // - the initiator's fees do not cover the common fields (version, segwit marker + flag,
358
+ // input count, output count, locktime)
359
+
360
+ return Ok ( ( ) )
361
+ }
362
+
309
363
fn is_valid_counterparty_serial_id ( & self , serial_id : SerialId ) -> bool {
310
364
// A received `SerialId`'s parity must match the role of the counterparty.
311
365
self . context . holder_is_initiator == !serial_id. is_valid_for_initiator ( )
@@ -321,11 +375,33 @@ impl InteractiveTxStateMachine<TheirTxComplete> {
321
375
}
322
376
}
323
377
324
- impl InteractiveTxStateMachine < OurTxComplete > {
325
- fn receive_tx_complete ( self ) -> InteractiveTxStateMachine < NegotiationComplete > {
378
+ impl InteractiveTxStateMachine < Negotiating > {
379
+ fn receive_tx_complete ( self ) -> Result < InteractiveTxStateMachine < TheirTxComplete > , InteractiveTxStateMachine < NegotiationAborted > > {
380
+ match self . is_current_transaction_state_able_to_complete ( ) {
381
+ Err ( e) => Err ( InteractiveTxStateMachine { context : self . context , state : NegotiationAborted ( e) } ) ,
382
+ _ => Ok ( InteractiveTxStateMachine {
383
+ context : self . context ,
384
+ state : TheirTxComplete { }
385
+ } )
386
+ }
387
+ }
388
+
389
+ fn send_tx_complete ( self ) -> InteractiveTxStateMachine < OurTxComplete > {
326
390
InteractiveTxStateMachine {
327
391
context : self . context ,
328
- state : NegotiationComplete { }
392
+ state : OurTxComplete { }
393
+ }
394
+ }
395
+ }
396
+
397
+ impl InteractiveTxStateMachine < OurTxComplete > {
398
+ fn receive_tx_complete ( self ) -> Result < InteractiveTxStateMachine < NegotiationComplete > , InteractiveTxStateMachine < NegotiationAborted > > {
399
+ match self . is_current_transaction_state_able_to_complete ( ) {
400
+ Err ( e) => Err ( InteractiveTxStateMachine { context : self . context , state : NegotiationAborted ( e) } ) ,
401
+ _ => Ok ( InteractiveTxStateMachine {
402
+ context : self . context ,
403
+ state : NegotiationComplete { }
404
+ } )
329
405
}
330
406
}
331
407
}
0 commit comments