Skip to content

bolt: implement tx_signatures message codec#28

Open
harsh04044 wants to merge 2 commits intomorehouse:masterfrom
harsh04044:bolt/tx-signatures
Open

bolt: implement tx_signatures message codec#28
harsh04044 wants to merge 2 commits intomorehouse:masterfrom
harsh04044:bolt/tx-signatures

Conversation

@harsh04044
Copy link
Copy Markdown

Implements the tx_signatures codec (type 71) for BOLT 2 interactive-tx.

The witnesses field is a nested structure -- one entry per input, each containing a witness stack of variable-length items. Encoded as num_witnesses (u16) followed by num_items (u16) and length-prefixed items for each witness.

Tests cover roundtrip, empty witnesses, empty stack items, trailing bytes, wire size, and truncation at each field boundary.

Part of #5.

Comment on lines +20 to +23
/// Outer `Vec`: one entry per input.
/// Middle `Vec`: the witness stack for that input.
/// Inner `Vec<u8>`: a single witness stack item.
pub witnesses: Vec<Vec<Vec<u8>>>,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks wrong -- shouldn't there only be an outer and an inner vec (outer: witnesses, inner: witness_data)? I think it would also help readability to define a newtype for Witness in this file. Then we just have witnesses: Vec<Witness>.

/// Middle `Vec`: the witness stack for that input.
/// Inner `Vec<u8>`: a single witness stack item.
pub witnesses: Vec<Vec<Vec<u8>>>,
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also should implement the tlvs field.

Comment on lines +33 to +35
u16::try_from(self.witnesses.len()).unwrap_or(u16::MAX).write(&mut out);
for witness in &self.witnesses {
u16::try_from(witness.len()).unwrap_or(u16::MAX).write(&mut out);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than silently truncating, I think we should assert that u16::MAX is not exceeded, since that indicates a programming error on our part.

@harsh04044
Copy link
Copy Markdown
Author

Done: added the Witness newtype, TxSignaturesTlvs, and swapped the unwrap_or(u16::MAX) calls for asserts.
@morehouse

@harsh04044 harsh04044 requested a review from morehouse April 7, 2026 11:28
@Chand-ra
Copy link
Copy Markdown

Chand-ra commented Apr 7, 2026

Since we're panicking in TxSignatures::encode() now, it might be a good idea to add tests to cover the 'panic' cases as well.

let channel_id: ChannelId = WireFormat::read(&mut cursor)?;
let txid: Txid = WireFormat::read(&mut cursor)?;
let num_witnesses: u16 = WireFormat::read(&mut cursor)?;
let mut witnesses = Vec::with_capacity(num_witnesses as usize);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like recipe for an Out-Of-Memory (OOM) crash in case the fuzzer decides on a high value for num_witnesses or num_items. This would mean the WireFormat::read loop doesn't even get a chance to fail on a truncated payload.

I'd say it's better to let the vector grow dynamically:

let mut witnesses = Vec::new();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants