Skip to content
Merged
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
run: |
timeout 15m cargo test -p interledger-packet
timeout 15m cargo test -p interledger-packet --features strict
timeout 15m cargo test -p interledger-packet --features roundtrip-only

- name: Test with subset of features (interledger-btp)
run: |
Expand Down
4 changes: 3 additions & 1 deletion crates/interledger-packet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ edition = "2018"
repository = "https://github.com/interledger-rs/interledger-rs"

[features]
# used when fuzzing; accepts only roundtripping input
# strict adherence to the rfcs, but not taking any "roundtrip-only" shortcuts
strict = []
# used when fuzzing; accepts only roundtripping input
roundtrip-only = ["strict"]

[dependencies]
byteorder = { version = "1.3.2" }
Expand Down
2 changes: 1 addition & 1 deletion crates/interledger-packet/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ bytes = "0.5"

[dependencies.interledger-packet]
path = ".."
features = ["strict"]
features = ["roundtrip-only"]

# Prevent this from interfering with workspaces
[workspace]
Expand Down
45 changes: 1 addition & 44 deletions crates/interledger-packet/fuzz/fuzz_targets/packet.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,6 @@
#![no_main]
use bytes::BytesMut;
use libfuzzer_sys::fuzz_target;
use std::convert::{TryFrom, TryInto};

fuzz_target!(|data: &[u8]| {
use interledger_packet::{FulfillBuilder, Packet, PrepareBuilder, RejectBuilder};

if let Ok(pkt) = interledger_packet::Packet::try_from(BytesMut::from(data)) {
// try to create a corresponding builder and a new set of bytes
match pkt {
Packet::Prepare(p) => {
let other = PrepareBuilder {
amount: p.amount(),
expires_at: p.expires_at(),
destination: p.destination(),
execution_condition: p
.execution_condition()
.try_into()
.expect("wrong length slice"),
data: p.data(),
}
.build();

assert_eq!(p, other);
}
Packet::Fulfill(f) => {
let other = FulfillBuilder {
fulfillment: f.fulfillment().try_into().expect("wrong length slice"),
data: f.data(),
}
.build();

assert_eq!(f, other);
}
Packet::Reject(r) => {
let other = RejectBuilder {
code: r.code(),
message: r.message(),
triggered_by: r.triggered_by().as_ref(),
data: r.data(),
}
.build();

assert_eq!(r, other);
}
}
}
let _ = interledger_packet::lenient_packet_roundtrips(data);
});
1 change: 1 addition & 0 deletions crates/interledger-packet/src/hex.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt;

/// Slice as hex string debug formatter, doesn't require allocating a string.
#[derive(PartialEq, Eq)]
pub struct HexString<'a>(pub &'a [u8]);

impl<'a> fmt::Debug for HexString<'a> {
Expand Down
65 changes: 65 additions & 0 deletions crates/interledger-packet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,68 @@ pub use self::errors::ParseError;
pub use self::packet::MaxPacketAmountDetails;
pub use self::packet::{Fulfill, Packet, PacketType, Prepare, Reject};
pub use self::packet::{FulfillBuilder, PrepareBuilder, RejectBuilder};

#[cfg(any(fuzzing, test))]
pub fn lenient_packet_roundtrips(data: &[u8]) -> Result<(), ParseError> {
use bytes::BytesMut;
use hex::HexString;
use std::convert::{TryFrom, TryInto};

let pkt = Packet::try_from(BytesMut::from(data))?;

// try to create a corresponding builder and a new set of bytes
let other = match pkt {
Packet::Prepare(p) => {
let other = PrepareBuilder {
amount: p.amount(),
expires_at: p.expires_at(),
destination: p.destination(),
execution_condition: p
.execution_condition()
.try_into()
.expect("wrong length slice"),
data: p.data(),
}
.build();

if p == other {
// if the packet roundtripped, great, we are done
return Ok(());
}

BytesMut::from(other)
}
Packet::Fulfill(f) => {
let other = FulfillBuilder {
fulfillment: f.fulfillment().try_into().expect("wrong length slice"),
data: f.data(),
}
.build();

if f == other {
return Ok(());
}

BytesMut::from(other)
}
Packet::Reject(r) => {
let other = RejectBuilder {
code: r.code(),
message: r.message(),
triggered_by: r.triggered_by().as_ref(),
data: r.data(),
}
.build();

if r == other {
return Ok(());
}

BytesMut::from(other)
}
};

assert_eq!(HexString(data), HexString(&other[..]));

Ok(())
}
Loading