Skip to content

Commit e6cac08

Browse files
Fix bug in onion payment payload decode
Prior to this change, we could have failed to decode a valid payload of size >253. This is because we were decoding the length (a BigSize, big-endian) as a VarInt (little-endian). Found in #1652.
1 parent dcef41d commit e6cac08

File tree

1 file changed

+41
-10
lines changed

1 file changed

+41
-10
lines changed

lightning/src/ln/msgs.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use io_extras::read_to_end;
4242

4343
use util::events::MessageSendEventsProvider;
4444
use util::logger;
45-
use util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};
45+
use util::ser::{BigSize, LengthReadable, Readable, ReadableArgs, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};
4646

4747
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
4848

@@ -1418,16 +1418,11 @@ impl Writeable for OnionHopData {
14181418
}
14191419

14201420
impl Readable for OnionHopData {
1421-
fn read<R: Read>(mut r: &mut R) -> Result<Self, DecodeError> {
1422-
use bitcoin::consensus::encode::{Decodable, Error, VarInt};
1423-
let v: VarInt = Decodable::consensus_decode(&mut r)
1424-
.map_err(|e| match e {
1425-
Error::Io(ioe) => DecodeError::from(ioe),
1426-
_ => DecodeError::InvalidValue
1427-
})?;
1421+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
1422+
let b: BigSize = Readable::read(r)?;
14281423
const LEGACY_ONION_HOP_FLAG: u64 = 0;
1429-
let (format, amt, cltv_value) = if v.0 != LEGACY_ONION_HOP_FLAG {
1430-
let mut rd = FixedLengthReader::new(r, v.0);
1424+
let (format, amt, cltv_value) = if b.0 != LEGACY_ONION_HOP_FLAG {
1425+
let mut rd = FixedLengthReader::new(r, b.0);
14311426
let mut amt = HighZeroBytesDroppedVarInt(0u64);
14321427
let mut cltv_value = HighZeroBytesDroppedVarInt(0u32);
14331428
let mut short_id: Option<u64> = None;
@@ -2824,4 +2819,40 @@ mod tests {
28242819
assert_eq!(gossip_timestamp_filter.first_timestamp, 1590000000);
28252820
assert_eq!(gossip_timestamp_filter.timestamp_range, 0xffff_ffff);
28262821
}
2822+
2823+
#[test]
2824+
fn decode_onion_hop_data_len_as_bigsize() {
2825+
// Tests that we can decode an onion payload that is >253 bytes.
2826+
// Previously, receiving a payload of this size could've caused us to fail to decode a valid
2827+
// payload, because we were decoding the length (a BigSize, big-endian) as a VarInt
2828+
// (little-endian).
2829+
2830+
// Encode a test onion payload with a big custom TLV such that it's >253 bytes, forcing the
2831+
// payload length to be encoded over multiple bytes rather than a single u8.
2832+
let big_payload = encode_big_payload().unwrap();
2833+
let mut rd = Cursor::new(&big_payload[..]);
2834+
<msgs::OnionHopData as Readable>::read(&mut rd).unwrap();
2835+
}
2836+
// see above test, needs to be a separate method for use of the serialization macros.
2837+
fn encode_big_payload() -> Result<Vec<u8>, std::io::Error> {
2838+
use util::ser::HighZeroBytesDroppedVarInt;
2839+
let payload = msgs::OnionHopData {
2840+
format: OnionHopDataFormat::NonFinalNode {
2841+
short_channel_id: 0xdeadbeef1bad1dea,
2842+
},
2843+
amt_to_forward: 1000,
2844+
outgoing_cltv_value: 0xffffffff,
2845+
};
2846+
let mut encoded_payload = Vec::new();
2847+
let test_bytes = vec![42 as u8; 1000];
2848+
if let OnionHopDataFormat::NonFinalNode { short_channel_id } = payload.format {
2849+
encode_varint_length_prefixed_tlv!(&mut encoded_payload, {
2850+
(1, test_bytes, vec_type),
2851+
(2, HighZeroBytesDroppedVarInt(payload.amt_to_forward), required),
2852+
(4, HighZeroBytesDroppedVarInt(payload.outgoing_cltv_value), required),
2853+
(6, short_channel_id, required)
2854+
});
2855+
}
2856+
Ok(encoded_payload)
2857+
}
28272858
}

0 commit comments

Comments
 (0)