Skip to content

Commit 6779d74

Browse files
committed
Added tx_complete handling to state machine
1 parent 6ffc4b3 commit 6779d74

File tree

1 file changed

+79
-3
lines changed

1 file changed

+79
-3
lines changed

lightning/src/ln/interactivetxs.rs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ const MAX_RECEIVED_TX_ADD_INPUT_COUNT: u16 = 4096;
2121
/// The number of received `tx_add_output` messages during a negotiation at which point the
2222
/// negotiation MUST be failed.
2323
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+
2432
const MAX_MONEY: u64 = 2_100_000_000_000_000;
2533

2634
type SerialId = u64;
@@ -42,6 +50,9 @@ pub(crate) enum AbortReason {
4250
DuplicateSerialId,
4351
PrevTxOutInvalid,
4452
ExceededMaximumSatsAllowed,
53+
ExceededNumberOfInputsOrOutputs,
54+
InvalidTransactionState,
55+
TransactionTooLarge,
4556
}
4657

4758
// Interactive Transaction Construction negotiation
@@ -306,6 +317,49 @@ impl<S> InteractiveTxStateMachine<S>
306317
todo!();
307318
}
308319

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+
309363
fn is_valid_counterparty_serial_id(&self, serial_id: SerialId) -> bool {
310364
// A received `SerialId`'s parity must match the role of the counterparty.
311365
self.context.holder_is_initiator == !serial_id.is_valid_for_initiator()
@@ -321,11 +375,33 @@ impl InteractiveTxStateMachine<TheirTxComplete> {
321375
}
322376
}
323377

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> {
326390
InteractiveTxStateMachine {
327391
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+
})
329405
}
330406
}
331407
}

0 commit comments

Comments
 (0)