-
Notifications
You must be signed in to change notification settings - Fork 19
smite: Implement update_fail_malformed_htlc codec
#38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ mod tx_init_rbf; | |
| mod tx_remove_input; | ||
| mod tx_remove_output; | ||
| mod types; | ||
| mod update_fail_malformed_htlc; | ||
| mod warning; | ||
| mod wire; | ||
|
|
||
|
|
@@ -47,8 +48,9 @@ pub use tx_remove_input::TxRemoveInput; | |
| pub use tx_remove_output::TxRemoveOutput; | ||
| pub use types::{ | ||
| BigSize, CHANNEL_ID_SIZE, COMPACT_SIGNATURE_SIZE, ChannelId, MAX_MESSAGE_SIZE, PUBLIC_KEY_SIZE, | ||
| TXID_SIZE, Txid, | ||
| SHA256_HASH_SIZE, TXID_SIZE, Txid, | ||
| }; | ||
| pub use update_fail_malformed_htlc::UpdateFailMalformedHtlc; | ||
| pub use warning::Warning; | ||
| pub use wire::WireFormat; | ||
|
|
||
|
|
@@ -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; | ||
| /// `update_fail_malformed_htlc` message (BOLT 2). | ||
| pub const UPDATE_FAIL_MALFORMED_HTLC: u16 = 135; | ||
| /// Gossip timestamp filter message (BOLT 7). | ||
| pub const GOSSIP_TIMESTAMP_FILTER: u16 = 265; | ||
| } | ||
|
|
@@ -171,6 +175,8 @@ pub enum Message { | |
| TxAckRbf(TxAckRbf), | ||
| /// `tx_abort` message (type 74). | ||
| TxAbort(TxAbort), | ||
| /// `update_fail_malformed_htlc` message (type 135). | ||
| UpdateFailMalformedHtlc(UpdateFailMalformedHtlc), | ||
| /// Gossip timestamp filter message (type 265). | ||
| GossipTimestampFilter(GossipTimestampFilter), | ||
| /// Unknown message type. | ||
|
|
@@ -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::UpdateFailMalformedHtlc(_) => msg_type::UPDATE_FAIL_MALFORMED_HTLC, | ||
| Self::GossipTimestampFilter(_) => msg_type::GOSSIP_TIMESTAMP_FILTER, | ||
| Self::Unknown { msg_type, .. } => *msg_type, | ||
| } | ||
|
|
@@ -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::UpdateFailMalformedHtlc(m) => out.extend(m.encode()), | ||
| Self::GossipTimestampFilter(m) => out.extend(m.encode()), | ||
| Self::Unknown { payload, .. } => out.extend(payload), | ||
| } | ||
|
|
@@ -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::UPDATE_FAIL_MALFORMED_HTLC => Ok(Self::UpdateFailMalformedHtlc( | ||
| UpdateFailMalformedHtlc::decode(cursor)?, | ||
| )), | ||
| msg_type::GOSSIP_TIMESTAMP_FILTER => Ok(Self::GossipTimestampFilter( | ||
| GossipTimestampFilter::decode(cursor)?, | ||
| )), | ||
|
|
@@ -306,7 +317,7 @@ pub fn message_with_type(msg_type: u16, payload: &[u8]) -> Vec<u8> { | |
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use secp256k1::hashes::Hash; | ||
| use secp256k1::hashes::{Hash, sha256}; | ||
| use secp256k1::{PublicKey, Secp256k1, SecretKey}; | ||
| use types::CHAIN_HASH_SIZE; | ||
|
|
||
|
|
@@ -615,6 +626,19 @@ mod tests { | |
| assert_eq!(decoded, Message::TxAbort(tx_abort)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn message_update_fail_malformed_htlc_roundtrip() { | ||
| let msg = UpdateFailMalformedHtlc { | ||
| channel_id: ChannelId::new([0x42; CHANNEL_ID_SIZE]), | ||
| id: 12345, | ||
| sha256_of_onion: sha256::Hash::from_byte_array([0xaa; SHA256_HASH_SIZE]), | ||
| failure_code: 0x8001, | ||
| }; | ||
| let encoded = Message::UpdateFailMalformedHtlc(msg.clone()).encode(); | ||
| let decoded = Message::decode(&encoded).unwrap(); | ||
| assert_eq!(decoded, Message::UpdateFailMalformedHtlc(msg)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn message_gossip_timestamp_filter_roundtrip() { | ||
| let chain_hash = [0x6f; 32]; | ||
|
|
@@ -637,6 +661,7 @@ mod tests { | |
| } | ||
|
|
||
| #[test] | ||
| #[allow(clippy::too_many_lines)] | ||
| fn message_type_values() { | ||
| assert_eq!( | ||
| Message::Warning(Warning::all_channels("")).msg_type(), | ||
|
|
@@ -722,6 +747,16 @@ mod tests { | |
| Message::TxAbort(TxAbort::new(ChannelId::new([0; CHANNEL_ID_SIZE]), "")).msg_type(), | ||
| msg_type::TX_ABORT | ||
| ); | ||
| assert_eq!( | ||
| Message::UpdateFailMalformedHtlc(UpdateFailMalformedHtlc { | ||
| channel_id: ChannelId::new([0x42; CHANNEL_ID_SIZE]), | ||
| id: 12345, | ||
| sha256_of_onion: sha256::Hash::from_byte_array([0xaa; SHA256_HASH_SIZE]), | ||
| failure_code: 0x8001, | ||
| }) | ||
| .msg_type(), | ||
| msg_type::UPDATE_FAIL_MALFORMED_HTLC | ||
| ); | ||
| assert_eq!( | ||
| Message::GossipTimestampFilter(GossipTimestampFilter::no_gossip([0u8; 32])).msg_type(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. huh looks like I didn't use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's worth it to replace usage of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about that too. But wouldn't this also apply to |
||
| msg_type::GOSSIP_TIMESTAMP_FILTER | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| //! BOLT 2 `update_fail_malformed_htlc` message. | ||
|
|
||
| use super::BoltError; | ||
| use super::types::ChannelId; | ||
| use super::wire::WireFormat; | ||
| use secp256k1::hashes::sha256; | ||
|
|
||
| /// BOLT 2 `update_fail_malformed_htlc` message (type 135). Sent | ||
| /// when a node cannot parse an incoming HTLC's onion packet. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub struct UpdateFailMalformedHtlc { | ||
| /// The channel ID. | ||
| pub channel_id: ChannelId, | ||
| /// The HTLC ID being failed. | ||
| pub id: u64, | ||
| /// Hash of the received unparsable onion. | ||
| pub sha256_of_onion: sha256::Hash, | ||
| /// The specific error code. | ||
| pub failure_code: u16, | ||
| } | ||
|
|
||
| impl UpdateFailMalformedHtlc { | ||
| /// Encodes to wire format (without message type prefix). | ||
| #[must_use] | ||
| pub fn encode(&self) -> Vec<u8> { | ||
| let mut out = Vec::new(); | ||
|
morehouse marked this conversation as resolved.
|
||
| self.channel_id.write(&mut out); | ||
| self.id.write(&mut out); | ||
| self.sha256_of_onion.write(&mut out); | ||
| self.failure_code.write(&mut out); | ||
| out | ||
| } | ||
|
|
||
| /// Decodes from wire format (without message type prefix). | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns `Truncated` if the payload is too short. | ||
| pub fn decode(payload: &[u8]) -> Result<Self, BoltError> { | ||
| let mut cursor = payload; | ||
|
|
||
| let channel_id = WireFormat::read(&mut cursor)?; | ||
| let id = WireFormat::read(&mut cursor)?; | ||
| let sha256_of_onion = WireFormat::read(&mut cursor)?; | ||
| let failure_code = WireFormat::read(&mut cursor)?; | ||
| Ok(Self { | ||
| channel_id, | ||
| id, | ||
| sha256_of_onion, | ||
| failure_code, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::super::CHANNEL_ID_SIZE; | ||
| use super::*; | ||
| use crate::bolt::SHA256_HASH_SIZE; | ||
| use secp256k1::hashes::Hash; | ||
|
|
||
| /// Valid `UpdateFailMalformedHtlc` message for testing. | ||
| fn sample_msg() -> UpdateFailMalformedHtlc { | ||
| UpdateFailMalformedHtlc { | ||
| channel_id: ChannelId::new([0x42; CHANNEL_ID_SIZE]), | ||
| id: 12345, | ||
| sha256_of_onion: sha256::Hash::from_byte_array([0xaa; SHA256_HASH_SIZE]), | ||
| failure_code: 0x8001, // BADONION bit + 1 | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn encode_fixed_field_size() { | ||
| let msg = sample_msg(); | ||
| let encoded = msg.encode(); | ||
| // channel_id(32) + id(8) + sha256_of_onion(32) + failure_code(2) = 74 | ||
| assert_eq!(encoded.len(), 74); | ||
| } | ||
|
|
||
| #[test] | ||
| fn roundtrip() { | ||
| let original = sample_msg(); | ||
| let encoded = original.encode(); | ||
| let decoded = UpdateFailMalformedHtlc::decode(&encoded).unwrap(); | ||
| assert_eq!(original, decoded); | ||
| } | ||
|
|
||
| #[test] | ||
| fn decode_truncated_channel_id() { | ||
| assert_eq!( | ||
| UpdateFailMalformedHtlc::decode(&[0x00; 20]), | ||
| Err(BoltError::Truncated { | ||
| expected: CHANNEL_ID_SIZE, | ||
| actual: 20 | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn decode_truncated_id() { | ||
| // Full channel_id (32 bytes) + only 4 bytes of id | ||
| let mut data = vec![0xaa; CHANNEL_ID_SIZE]; | ||
| data.extend_from_slice(&[0x00; 4]); | ||
| assert_eq!( | ||
| UpdateFailMalformedHtlc::decode(&data), | ||
| Err(BoltError::Truncated { | ||
| expected: 8, | ||
| actual: 4 | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn decode_truncated_sha256_of_onion() { | ||
| // Full channel_id (32 bytes) + full id (8 bytes) + only 16 bytes of sha256_of_onion | ||
| let mut data = vec![0x00u8; CHANNEL_ID_SIZE]; | ||
| data.extend_from_slice(&[0x00; 8]); | ||
| data.extend_from_slice(&[0x00; 16]); | ||
| assert_eq!( | ||
| UpdateFailMalformedHtlc::decode(&data), | ||
| Err(BoltError::Truncated { | ||
| expected: SHA256_HASH_SIZE, | ||
| actual: 16 | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn decode_truncated_failure_code() { | ||
| // Full channel_id (32) + full id (8) + full sha256_of_onion (32) = 72 | ||
| // failure_code needs 2, only give 1 | ||
| let mut data = vec![0x00u8; CHANNEL_ID_SIZE]; | ||
| data.extend_from_slice(&[0x00; 8]); | ||
| data.extend_from_slice(&[0x00; SHA256_HASH_SIZE]); | ||
| data.push(0x00); | ||
| assert_eq!( | ||
| UpdateFailMalformedHtlc::decode(&data), | ||
| Err(BoltError::Truncated { | ||
| expected: 2, | ||
| actual: 1 | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn decode_empty() { | ||
| assert_eq!( | ||
| UpdateFailMalformedHtlc::decode(&[]), | ||
| Err(BoltError::Truncated { | ||
| expected: CHANNEL_ID_SIZE, | ||
| actual: 0 | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.