use crate::{
DescriptorLimits, MsgDescHot, ValidationError, INLINE_PAYLOAD_SIZE, INLINE_PAYLOAD_SLOT,
};
pub fn validate_descriptor(
desc: &MsgDescHot,
limits: &DescriptorLimits,
) -> Result<(), ValidationError> {
if desc.payload_len > limits.max_payload_len {
return Err(ValidationError::PayloadTooLarge {
len: desc.payload_len,
max: limits.max_payload_len,
});
}
if desc.channel_id > limits.max_channels {
return Err(ValidationError::ChannelOutOfBounds {
channel: desc.channel_id,
max: limits.max_channels,
});
}
if desc.payload_slot == INLINE_PAYLOAD_SLOT {
if desc.payload_len > INLINE_PAYLOAD_SIZE as u32 {
return Err(ValidationError::InlinePayloadTooLarge {
len: desc.payload_len,
max: INLINE_PAYLOAD_SIZE as u32,
});
}
if desc.payload_generation != 0 || desc.payload_offset != 0 {
return Err(ValidationError::InvalidInlineDescriptor);
}
} else if limits.slot_count > 0 {
if desc.payload_slot >= limits.slot_count {
return Err(ValidationError::SlotOutOfBounds {
slot: desc.payload_slot,
max: limits.slot_count,
});
}
let end = desc.payload_offset.saturating_add(desc.payload_len);
if end > limits.slot_size {
return Err(ValidationError::PayloadOutOfBounds {
offset: desc.payload_offset,
len: desc.payload_len,
slot_size: limits.slot_size,
});
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn make_desc() -> MsgDescHot {
MsgDescHot::new()
}
#[test]
fn test_valid_inline_payload() {
let mut desc = make_desc();
desc.payload_len = 10;
desc.inline_payload[..10].fill(0xAB);
let limits = DescriptorLimits::default();
assert!(validate_descriptor(&desc, &limits).is_ok());
}
#[test]
fn test_inline_payload_too_large() {
let mut desc = make_desc();
desc.payload_len = 30;
let limits = DescriptorLimits::default();
let err = validate_descriptor(&desc, &limits).unwrap_err();
assert!(matches!(err, ValidationError::InlinePayloadTooLarge { .. }));
}
#[test]
fn test_invalid_inline_descriptor() {
let mut desc = make_desc();
desc.payload_len = 10;
desc.payload_generation = 1;
let limits = DescriptorLimits::default();
let err = validate_descriptor(&desc, &limits).unwrap_err();
assert!(matches!(err, ValidationError::InvalidInlineDescriptor));
}
#[test]
fn test_payload_too_large() {
let mut desc = make_desc();
desc.payload_slot = 0;
desc.payload_len = 2 * 1024 * 1024;
let limits = DescriptorLimits::with_slots(16, 4096);
let err = validate_descriptor(&desc, &limits).unwrap_err();
assert!(matches!(err, ValidationError::PayloadTooLarge { .. }));
}
#[test]
fn test_slot_out_of_bounds() {
let mut desc = make_desc();
desc.payload_slot = 100;
desc.payload_len = 100;
let limits = DescriptorLimits::with_slots(16, 4096);
let err = validate_descriptor(&desc, &limits).unwrap_err();
assert!(matches!(err, ValidationError::SlotOutOfBounds { .. }));
}
#[test]
fn test_payload_out_of_bounds() {
let mut desc = make_desc();
desc.payload_slot = 0;
desc.payload_offset = 4000;
desc.payload_len = 200;
let limits = DescriptorLimits::with_slots(16, 4096);
let err = validate_descriptor(&desc, &limits).unwrap_err();
assert!(matches!(err, ValidationError::PayloadOutOfBounds { .. }));
}
#[test]
fn test_channel_out_of_bounds() {
let mut desc = make_desc();
desc.channel_id = 2000;
let limits = DescriptorLimits::default();
let err = validate_descriptor(&desc, &limits).unwrap_err();
assert!(matches!(err, ValidationError::ChannelOutOfBounds { .. }));
}
#[test]
fn test_valid_slot_payload() {
let mut desc = make_desc();
desc.payload_slot = 5;
desc.payload_generation = 42;
desc.payload_offset = 100;
desc.payload_len = 500;
let limits = DescriptorLimits::with_slots(16, 4096);
assert!(validate_descriptor(&desc, &limits).is_ok());
}
}