Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions smite/src/bolt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

mod accept_channel;
mod channel_ready;
mod channel_reestablish;
mod error;
mod funding_created;
mod funding_signed;
Expand All @@ -28,6 +29,7 @@ mod wire;

pub use accept_channel::{AcceptChannel, AcceptChannelTlvs};
pub use channel_ready::{ChannelReady, ChannelReadyTlvs};
pub use channel_reestablish::ChannelReestablish;
pub use error::Error;
pub use funding_created::FundingCreated;
pub use funding_signed::FundingSigned;
Expand Down Expand Up @@ -127,6 +129,8 @@ pub mod msg_type {
pub const TX_ACK_RBF: u16 = 73;
/// `tx_abort` message (BOLT 2).
pub const TX_ABORT: u16 = 74;
/// `channel_reestablish` message (BOLT 2).
pub const CHANNEL_REESTABLISH: u16 = 136;
/// Gossip timestamp filter message (BOLT 7).
pub const GOSSIP_TIMESTAMP_FILTER: u16 = 265;
}
Expand Down Expand Up @@ -171,6 +175,8 @@ pub enum Message {
TxAckRbf(TxAckRbf),
/// `tx_abort` message (type 74).
TxAbort(TxAbort),
/// `channel_reestablish` message (type 136).
ChannelReestablish(ChannelReestablish),
/// Gossip timestamp filter message (type 265).
GossipTimestampFilter(GossipTimestampFilter),
/// Unknown message type.
Expand Down Expand Up @@ -208,6 +214,7 @@ impl Message {
Self::TxInitRbf(_) => msg_type::TX_INIT_RBF,
Self::TxAckRbf(_) => msg_type::TX_ACK_RBF,
Self::TxAbort(_) => msg_type::TX_ABORT,
Self::ChannelReestablish(_) => msg_type::CHANNEL_REESTABLISH,
Self::GossipTimestampFilter(_) => msg_type::GOSSIP_TIMESTAMP_FILTER,
Self::Unknown { msg_type, .. } => *msg_type,
}
Expand Down Expand Up @@ -237,6 +244,7 @@ impl Message {
Self::TxInitRbf(m) => out.extend(m.encode()),
Self::TxAckRbf(m) => out.extend(m.encode()),
Self::TxAbort(m) => out.extend(m.encode()),
Self::ChannelReestablish(m) => out.extend(m.encode()),
Self::GossipTimestampFilter(m) => out.extend(m.encode()),
Self::Unknown { payload, .. } => out.extend(payload),
}
Expand Down Expand Up @@ -273,6 +281,9 @@ impl Message {
msg_type::TX_INIT_RBF => Ok(Self::TxInitRbf(TxInitRbf::decode(cursor)?)),
msg_type::TX_ACK_RBF => Ok(Self::TxAckRbf(TxAckRbf::decode(cursor)?)),
msg_type::TX_ABORT => Ok(Self::TxAbort(TxAbort::decode(cursor)?)),
msg_type::CHANNEL_REESTABLISH => Ok(Self::ChannelReestablish(
ChannelReestablish::decode(cursor)?,
)),
msg_type::GOSSIP_TIMESTAMP_FILTER => Ok(Self::GossipTimestampFilter(
GossipTimestampFilter::decode(cursor)?,
)),
Expand Down Expand Up @@ -309,6 +320,7 @@ mod tests {
use secp256k1::hashes::Hash;
use secp256k1::{PublicKey, Secp256k1, SecretKey};
use types::CHAIN_HASH_SIZE;
use types::PER_COMMITMENT_SECRET_SIZE;

// Tests ordered by message type number: Warning(1), Init(16), Error(17), Ping(18), Pong(19)

Expand Down Expand Up @@ -615,6 +627,28 @@ mod tests {
assert_eq!(decoded, Message::TxAbort(tx_abort));
}

#[test]
fn message_channel_reestablish_roundtrip() {
let secp = Secp256k1::new();
let sk =
SecretKey::from_byte_array([0x11; PER_COMMITMENT_SECRET_SIZE]).expect("valid secret");
let pk = PublicKey::from_secret_key(&secp, &sk);

let channel_reestablish = ChannelReestablish {
channel_id: ChannelId::new([0xcd; CHANNEL_ID_SIZE]),
next_commitment_number: 42,
next_revocation_number: 41,
your_last_per_commitment_secret: [0xab; PER_COMMITMENT_SECRET_SIZE],
my_current_per_commitment_point: pk,
tlvs: TlvStream::new(),
};

let msg = Message::ChannelReestablish(channel_reestablish.clone());
let encoded = msg.encode();
let decoded = Message::decode(&encoded).unwrap();
assert_eq!(decoded, Message::ChannelReestablish(channel_reestablish));
}

#[test]
fn message_gossip_timestamp_filter_roundtrip() {
let chain_hash = [0x6f; 32];
Expand All @@ -637,6 +671,7 @@ mod tests {
}

#[test]
#[allow(clippy::too_many_lines)]
fn message_type_values() {
assert_eq!(
Message::Warning(Warning::all_channels("")).msg_type(),
Expand Down Expand Up @@ -722,6 +757,24 @@ mod tests {
Message::TxAbort(TxAbort::new(ChannelId::new([0; CHANNEL_ID_SIZE]), "")).msg_type(),
msg_type::TX_ABORT
);
assert_eq!(
Message::ChannelReestablish(ChannelReestablish {
channel_id: ChannelId::new([0; CHANNEL_ID_SIZE]),
next_commitment_number: 0,
next_revocation_number: 0,
your_last_per_commitment_secret: [0; PER_COMMITMENT_SECRET_SIZE],
// Taken from https://github.com/lightning/bolts/blob/master/03-transactions.md#appendix-e-key-derivation-test-vectors
my_current_per_commitment_point: PublicKey::from_slice(&[
Comment thread
theAnuragMishra marked this conversation as resolved.
0x02, 0x5f, 0x71, 0x17, 0xa7, 0x81, 0x50, 0xfe, 0x2e, 0xf9, 0x7d, 0xb7, 0xcf,
0xc8, 0x3b, 0xd5, 0x7b, 0x2e, 0x2c, 0x0d, 0x0d, 0xd2, 0x5e, 0xaf, 0x46, 0x7a,
0x4a, 0x1c, 0x2a, 0x45, 0xce, 0x14, 0x86,
])
.unwrap(),
tlvs: TlvStream::new(),
})
.msg_type(),
msg_type::CHANNEL_REESTABLISH
);
assert_eq!(
Message::GossipTimestampFilter(GossipTimestampFilter::no_gossip([0u8; 32])).msg_type(),
msg_type::GOSSIP_TIMESTAMP_FILTER
Expand Down
234 changes: 234 additions & 0 deletions smite/src/bolt/channel_reestablish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
//! BOLT 2 `channel_reestablish` message.

use super::BoltError;
use super::tlv::TlvStream;
use super::types::ChannelId;
use super::types::PER_COMMITMENT_SECRET_SIZE;
use super::wire::WireFormat;
use secp256k1::PublicKey;

/// BOLT 2 `channel_reestablish` message (type 136).
///
/// Sent after reconnecting to synchronize commitment/revocation counters and
/// reestablish a channel's state.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ChannelReestablish {
/// The channel ID.
pub channel_id: ChannelId,
/// The next commitment number expected from the peer.
pub next_commitment_number: u64,
/// The next revocation number expected from the peer.
pub next_revocation_number: u64,
/// The sender's view of the receiver's last per-commitment secret.
pub your_last_per_commitment_secret: [u8; PER_COMMITMENT_SECRET_SIZE],
/// The sender's current per-commitment point.
pub my_current_per_commitment_point: PublicKey,
/// Optional TLV extensions.
pub tlvs: TlvStream,
}
Comment on lines +15 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TLV stream missing


impl ChannelReestablish {
/// Encodes to wire format (without message type prefix).
#[must_use]
pub fn encode(&self) -> Vec<u8> {
let mut out = Vec::new();
self.channel_id.write(&mut out);
self.next_commitment_number.write(&mut out);
self.next_revocation_number.write(&mut out);
self.your_last_per_commitment_secret.write(&mut out);
self.my_current_per_commitment_point.write(&mut out);
out.extend(self.tlvs.encode());

out
}

/// Decodes from wire format (without message type prefix).
///
/// # Errors
///
/// Returns `Truncated` if the payload is too short for any fixed field or
/// `InvalidPublicKey` if the per-commitment point is invalid.
pub fn decode(payload: &[u8]) -> Result<Self, BoltError> {
let mut cursor = payload;

let channel_id = WireFormat::read(&mut cursor)?;
let next_commitment_number = WireFormat::read(&mut cursor)?;
let next_revocation_number = WireFormat::read(&mut cursor)?;
let your_last_per_commitment_secret = WireFormat::read(&mut cursor)?;
let my_current_per_commitment_point = WireFormat::read(&mut cursor)?;
let tlvs = TlvStream::decode(cursor)?;

Ok(Self {
channel_id,
next_commitment_number,
next_revocation_number,
your_last_per_commitment_secret,
my_current_per_commitment_point,
tlvs,
})
}
}

#[cfg(test)]
mod tests {
use super::super::{CHANNEL_ID_SIZE, PUBLIC_KEY_SIZE};
use super::*;
use secp256k1::{Secp256k1, SecretKey};

/// Valid `ChannelReestablish` message for testing.
fn sample_channel_reestablish() -> ChannelReestablish {
let secp = Secp256k1::new();
let sk = SecretKey::from_byte_array([0x11; 32]).expect("valid secret");
let pk = PublicKey::from_secret_key(&secp, &sk);

ChannelReestablish {
channel_id: ChannelId::new([0xaa; CHANNEL_ID_SIZE]),
next_commitment_number: 42,
next_revocation_number: 41,
your_last_per_commitment_secret: [0xbb; PER_COMMITMENT_SECRET_SIZE],
my_current_per_commitment_point: pk,
tlvs: TlvStream::new(),
}
}

#[test]
fn encode_fixed_field_size() {
let msg = sample_channel_reestablish();
let encoded = msg.encode();
// channel_id(32) + next_commitment_number(8) + next_revocation_number(8)
// + your_last_per_commitment_secret(32) + my_current_per_commitment_point(33) = 113
assert_eq!(encoded.len(), 113);
}

#[test]
fn roundtrip() {
let original = sample_channel_reestablish();
let encoded = original.encode();
let decoded = ChannelReestablish::decode(&encoded).unwrap();
assert_eq!(original, decoded);
}

#[test]
fn roundtrip_with_tlvs() {
let mut original = sample_channel_reestablish();
original.tlvs.add(3, vec![0xaa, 0xbb]);

let encoded = original.encode();
let decoded = ChannelReestablish::decode(&encoded).unwrap();
assert_eq!(original, decoded);
}

#[test]
fn decode_unknown_odd_tlv_ignored() {
let msg = sample_channel_reestablish();
let mut encoded = msg.encode();

// Append unknown odd TLV: type 5, length 2, value [0xaa, 0xbb]
encoded.extend_from_slice(&[0x05, 0x02, 0xaa, 0xbb]);

let decoded = ChannelReestablish::decode(&encoded).unwrap();
assert_eq!(decoded.tlvs.get(5), Some(&[0xaa, 0xbb][..]));
}

#[test]
fn decode_unknown_even_tlv_rejected() {
let msg = sample_channel_reestablish();
let mut encoded = msg.encode();

// Append unknown even TLV: type 4, length 1, value [0x00]
encoded.extend_from_slice(&[0x04, 0x01, 0x00]);

assert!(matches!(
ChannelReestablish::decode(&encoded),
Err(BoltError::TlvUnknownEvenType(4))
));
}

#[test]
fn decode_truncated_channel_id() {
assert_eq!(
ChannelReestablish::decode(&[0x00; 20]),
Err(BoltError::Truncated {
expected: CHANNEL_ID_SIZE,
actual: 20
})
);
}

#[test]
fn decode_truncated_next_commitment_number() {
// channel_id(32) + only 2 bytes into next_commitment_number
let mut data = vec![0xaa; CHANNEL_ID_SIZE];
data.extend_from_slice(&[0x00; 2]);
assert_eq!(
ChannelReestablish::decode(&data),
Err(BoltError::Truncated {
expected: 8,
actual: 2
})
);
}

#[test]
fn decode_truncated_next_revocation_number() {
// channel_id(32) + next_commitment_number(8) + only 3 bytes into next_revocation_number
let mut data = vec![0xaa; CHANNEL_ID_SIZE];
data.extend_from_slice(&42u64.to_be_bytes());
data.extend_from_slice(&[0x00; 3]);
assert_eq!(
ChannelReestablish::decode(&data),
Err(BoltError::Truncated {
expected: 8,
actual: 3
})
);
}

#[test]
fn decode_truncated_your_last_per_commitment_secret() {
// channel_id(32) + next_commitment_number(8) + next_revocation_number(8)
// + only 10 bytes of your_last_per_commitment_secret
let mut data = vec![0xaa; CHANNEL_ID_SIZE];
data.extend_from_slice(&42u64.to_be_bytes());
data.extend_from_slice(&41u64.to_be_bytes());
data.extend_from_slice(&[0xbb; 10]);
assert_eq!(
ChannelReestablish::decode(&data),
Err(BoltError::Truncated {
expected: PER_COMMITMENT_SECRET_SIZE,
actual: 10
})
);
}

#[test]
fn decode_truncated_my_current_per_commitment_point() {
// Fixed fields + only 10 bytes of point data
let mut data = vec![0xaa; CHANNEL_ID_SIZE];
data.extend_from_slice(&42u64.to_be_bytes());
data.extend_from_slice(&41u64.to_be_bytes());
data.extend_from_slice(&[0xbb; PER_COMMITMENT_SECRET_SIZE]);
data.extend_from_slice(&[0x02; 10]);
assert_eq!(
ChannelReestablish::decode(&data),
Err(BoltError::Truncated {
expected: PUBLIC_KEY_SIZE,
actual: 10
})
);
}

#[test]
fn decode_invalid_my_current_per_commitment_point() {
// Full length payload with invalid all-zero compressed key bytes.
let mut data = vec![0xaa; CHANNEL_ID_SIZE];
data.extend_from_slice(&42u64.to_be_bytes());
data.extend_from_slice(&41u64.to_be_bytes());
data.extend_from_slice(&[0xbb; PER_COMMITMENT_SECRET_SIZE]);
data.extend_from_slice(&[0x00; PUBLIC_KEY_SIZE]);
assert_eq!(
ChannelReestablish::decode(&data),
Err(BoltError::InvalidPublicKey([0x00; PUBLIC_KEY_SIZE]))
);
}
}
3 changes: 3 additions & 0 deletions smite/src/bolt/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub const COMPACT_SIGNATURE_SIZE: usize = 64;
/// Size of a compressed secp256k1 public key.
pub const PUBLIC_KEY_SIZE: usize = 33;

/// Size of `your_last_per_commitment_secret` in bytes.
pub const PER_COMMITMENT_SECRET_SIZE: usize = 32;

/// A 32-byte channel identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ChannelId(pub [u8; CHANNEL_ID_SIZE]);
Expand Down
Loading