Skip to content

Commit

Permalink
fix edge cases in assert_before parsing. Specifically, assert_before …
Browse files Browse the repository at this point in the history
…a negative value is an automatic fail. assert_before a value exceeding the max is an automatic pass
  • Loading branch information
arvidn committed Feb 14, 2023
1 parent 6736e64 commit fd60489
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 133 deletions.
248 changes: 161 additions & 87 deletions src/gen/condition_sanitizers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ pub fn parse_seconds(a: &Allocator, n: NodePtr, code: ErrorCode) -> Result<u64,
}
}

// a negative height is a failure, but exceeding the max is a no-op.
// This is used for parsing assert_before_height conditions
pub fn parse_positive_height(a: &Allocator, n: NodePtr, code: ErrorCode) -> Result<Option<u32>, ValidationErr> {
// heights are not allowed to exceed 2^32. i.e. 4 bytes
match sanitize_uint(a, n, 4, code) {
Err(ValidationErr(n, ErrorCode::NegativeAmount)) => Err(ValidationErr(n, code)),
Err(ValidationErr(_, ErrorCode::AmountExceedsMaximum)) => Ok(None),
Err(r) => Err(r),
Ok(r) => Ok(Some(u64_from_bytes(r) as u32)),
}
}

// negative seconds are a failure, exceeding the max limit is no-op.
// this is used for parsing assert_before_seconds conditions.
pub fn parse_positive_seconds(a: &Allocator, n: NodePtr, code: ErrorCode) -> Result<Option<u64>, ValidationErr> {
// seconds are not allowed to exceed 2^64. i.e. 8 bytes
match sanitize_uint(a, n, 8, code) {
Err(ValidationErr(n, ErrorCode::NegativeAmount)) => Err(ValidationErr(n, code)),
Err(ValidationErr(_, ErrorCode::AmountExceedsMaximum)) => Ok(None),
Err(r) => Err(r),
Ok(r) => Ok(Some(u64_from_bytes(r))),
}
}

pub fn sanitize_announce_msg(
a: &Allocator,
n: NodePtr,
Expand All @@ -75,6 +99,9 @@ pub fn sanitize_announce_msg(
}
}

#[cfg(test)]
use rstest::rstest;

#[cfg(test)]
fn zero_vec(len: usize) -> Vec<u8> {
let mut ret = Vec::<u8>::new();
Expand Down Expand Up @@ -227,120 +254,167 @@ fn test_sanitize_create_coin_amount() {
}

#[cfg(test)]
fn height_tester(buf: &[u8]) -> Result<u32, ValidationErr> {
fn height_tester(buf: &[u8]) -> Result<Option<u64>, ValidationErr> {
let mut a = Allocator::new();
let n = a.new_atom(buf).unwrap();

parse_height(&mut a, n, ErrorCode::AssertHeightAbsolute)
Ok(Some(parse_height(&mut a, n, ErrorCode::AssertHeightAbsolute)? as u64))
}

#[test]
fn test_parse_height() {
// negative heights can be ignored
assert_eq!(height_tester(&[0x80]), Ok(0));
assert_eq!(height_tester(&[0xff]), Ok(0));
assert_eq!(height_tester(&[0xff, 0]), Ok(0));

// leading zeros are somtimes necessary to make values positive
assert_eq!(height_tester(&[0, 0xff]), Ok(0xff));
// but are stripped when they are redundant
assert_eq!(
height_tester(&[0, 0, 0, 0xff]).unwrap_err().1,
ErrorCode::AssertHeightAbsolute
);
assert_eq!(
height_tester(&[0, 0, 0, 0x80]).unwrap_err().1,
ErrorCode::AssertHeightAbsolute
);
assert_eq!(
height_tester(&[0, 0, 0, 0x7f]).unwrap_err().1,
ErrorCode::AssertHeightAbsolute
);
assert_eq!(
height_tester(&[0, 0, 0]).unwrap_err().1,
ErrorCode::AssertHeightAbsolute
);
assert_eq!(
height_tester(&[0]).unwrap_err().1,
ErrorCode::AssertHeightAbsolute
);

// heights aren't allowed to be > 2^32 (i.e. 5 bytes)
assert_eq!(
height_tester(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff])
.unwrap_err()
.1,
ErrorCode::AssertHeightAbsolute
);

// this is small enough though
assert_eq!(height_tester(&[0, 0xff, 0xff, 0xff, 0xff]), Ok(0xffffffff));
#[cfg(test)]
fn seconds_tester(buf: &[u8]) -> Result<Option<u64>, ValidationErr> {
let mut a = Allocator::new();
let n = a.new_atom(buf).unwrap();
Ok(Some(parse_seconds(&mut a, n, ErrorCode::AssertSecondsAbsolute)?))
}

#[cfg(test)]
fn positive_height_tester(buf: &[u8]) -> Result<Option<u64>, ValidationErr> {
let mut a = Allocator::new();
let pair = a.new_pair(a.null(), a.null()).unwrap();
assert_eq!(
parse_height(&mut a, pair, ErrorCode::AssertHeightAbsolute),
Err(ValidationErr(pair, ErrorCode::AssertHeightAbsolute))
);
let n = a.new_atom(buf).unwrap();
Ok(parse_positive_height(&mut a, n, ErrorCode::AssertBeforeHeightAbsolute)?.map(|v| v as u64))
}

#[cfg(test)]
fn seconds_tester(buf: &[u8]) -> Result<u64, ValidationErr> {
fn positive_seconds_tester(buf: &[u8]) -> Result<Option<u64>, ValidationErr> {
let mut a = Allocator::new();
let n = a.new_atom(buf).unwrap();
parse_positive_seconds(&mut a, n, ErrorCode::AssertBeforeSecondsAbsolute)
}

parse_seconds(&mut a, n, ErrorCode::AssertSecondsAbsolute)
#[cfg(test)]
#[rstest]
// == parse_height
#[case(height_tester, &[0x80], Some(0))]
// negative values are no-ops
#[case(height_tester, &[0xff], Some(0))]
#[case(height_tester, &[0xff, 0], Some(0))]
// leading zeros are sometimes necessary to make values positive
#[case(height_tester, &[0, 0xff], Some(0xff))]
// this is small enough
#[case(height_tester, &[0, 0xff, 0xff, 0xff, 0xff], Some(0xffffffff))]

// == parse_seconds
#[case(seconds_tester, &[0x80], Some(0))]
// negative values are no-ops
#[case(seconds_tester, &[0xff], Some(0))]
#[case(seconds_tester, &[0xff, 0], Some(0))]
// leading zeros are sometimes necessary to make values positive
#[case(seconds_tester, &[0, 0xff], Some(0xff))]
// this is small enough
#[case(seconds_tester, &[0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], Some(0xffffffffffffffff))]

// == parse_positive_height
// leading zeros are sometimes necessary to make values positive
#[case(positive_height_tester, &[0, 0xff], Some(0xff))]
// this is small enough
#[case(positive_height_tester, &[0, 0xff, 0xff, 0xff, 0xff], Some(0xffffffff))]
// positive heights are allowed to be > 2^32 (i.e. 5 bytes). it's a no-op
#[case(positive_height_tester, &[0x01, 0xff, 0xff, 0xff, 0xff], None)]

// == parse_positive_seconds
// leading zeros are sometimes necessary to make values positive
#[case(positive_seconds_tester, &[0, 0xff], Some(0xff))]
// this is small enough
#[case(positive_seconds_tester, &[0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], Some(0xffffffffffffffff))]
// before-seconds are allowed to be > 2^64 (i.e. 9 bytes)
#[case(positive_seconds_tester, &[0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], None)]
fn test_parse_ok(
#[case] fun: impl Fn(&[u8]) -> Result<Option<u64>, ValidationErr>,
#[case] buf: &[u8],
#[case] expected: Option<u64>
) {
println!("test case: {:?} expect: {:?}", buf, expected);
// negative heights can be ignored
assert_eq!(fun(buf).unwrap(), expected);
}

#[test]
fn test_parse_seconds() {
// negative seconds can be ignored
assert_eq!(seconds_tester(&[0x80]), Ok(0));
assert_eq!(seconds_tester(&[0xff]), Ok(0));
assert_eq!(seconds_tester(&[0xff, 0]), Ok(0));
#[cfg(test)]
#[rstest]
// == parse_height
#[case(height_tester, &[0, 0, 0, 0xff], ErrorCode::AssertHeightAbsolute)]
#[case(height_tester, &[0, 0, 0, 0x80], ErrorCode::AssertHeightAbsolute)]
#[case(height_tester, &[0, 0, 0, 0x7f], ErrorCode::AssertHeightAbsolute)]
#[case(height_tester, &[0, 0, 0], ErrorCode::AssertHeightAbsolute)]
#[case(height_tester, &[0], ErrorCode::AssertHeightAbsolute)]
// heights aren't allowed to be > 2^32 (i.e. 5 bytes)
#[case(height_tester, &[0x01, 0xff, 0xff, 0xff, 0xff], ErrorCode::AssertHeightAbsolute)]

// == parse_seconds
#[case(seconds_tester, &[0, 0, 0, 0xff], ErrorCode::AssertSecondsAbsolute)]
#[case(seconds_tester, &[0, 0, 0, 0x80], ErrorCode::AssertSecondsAbsolute)]
#[case(seconds_tester, &[0, 0, 0, 0x7f], ErrorCode::AssertSecondsAbsolute)]
#[case(seconds_tester, &[0, 0, 0], ErrorCode::AssertSecondsAbsolute)]
#[case(seconds_tester, &[0], ErrorCode::AssertSecondsAbsolute)]
// positive seconds are allowed to be > 2^64 (i.e. 9 bytes)
#[case(seconds_tester, &[0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], ErrorCode::AssertSecondsAbsolute)]

// == parse_positive_height
#[case(positive_height_tester, &[0, 0, 0, 0xff], ErrorCode::AssertBeforeHeightAbsolute)]
#[case(positive_height_tester, &[0, 0, 0, 0x80], ErrorCode::AssertBeforeHeightAbsolute)]
#[case(positive_height_tester, &[0, 0, 0, 0x7f], ErrorCode::AssertBeforeHeightAbsolute)]
#[case(positive_height_tester, &[0, 0, 0], ErrorCode::AssertBeforeHeightAbsolute)]
#[case(positive_height_tester, &[0], ErrorCode::AssertBeforeHeightAbsolute)]
// negative values are failures
#[case(positive_height_tester, &[0x80], ErrorCode::AssertBeforeHeightAbsolute)]
#[case(positive_height_tester, &[0xff], ErrorCode::AssertBeforeHeightAbsolute)]
#[case(positive_height_tester, &[0xff, 0], ErrorCode::AssertBeforeHeightAbsolute)]

// == parse_positive_seconds
#[case(positive_seconds_tester, &[0, 0, 0, 0xff], ErrorCode::AssertBeforeSecondsAbsolute)]
#[case(positive_seconds_tester, &[0, 0, 0, 0x80], ErrorCode::AssertBeforeSecondsAbsolute)]
#[case(positive_seconds_tester, &[0, 0, 0, 0x7f], ErrorCode::AssertBeforeSecondsAbsolute)]
#[case(positive_seconds_tester, &[0, 0, 0], ErrorCode::AssertBeforeSecondsAbsolute)]
#[case(positive_seconds_tester, &[0], ErrorCode::AssertBeforeSecondsAbsolute)]
// negative values are failures
#[case(positive_seconds_tester, &[0x80], ErrorCode::AssertBeforeSecondsAbsolute)]
#[case(positive_seconds_tester, &[0xff], ErrorCode::AssertBeforeSecondsAbsolute)]
#[case(positive_seconds_tester, &[0xff, 0], ErrorCode::AssertBeforeSecondsAbsolute)]
fn test_parse_fail(
#[case] fun: impl Fn(&[u8]) -> Result<Option<u64>, ValidationErr>,
#[case] buf: &[u8],
#[case] expected: ErrorCode
) {
println!("test case: {:?} expect: {:?}", buf, expected);
// negative heights can be ignored
assert_eq!(fun(buf).unwrap_err().1, expected);
}

// leading zeros are somtimes necessary to make values positive
assert_eq!(seconds_tester(&[0, 0xff]), Ok(0xff));
// but are stripped when they are redundant
assert_eq!(
seconds_tester(&[0, 0, 0, 0xff]).unwrap_err().1,
ErrorCode::AssertSecondsAbsolute
);
assert_eq!(
seconds_tester(&[0, 0, 0, 0x80]).unwrap_err().1,
ErrorCode::AssertSecondsAbsolute
);
assert_eq!(
seconds_tester(&[0, 0, 0, 0x7f]).unwrap_err().1,
ErrorCode::AssertSecondsAbsolute
);
assert_eq!(
seconds_tester(&[0, 0, 0]).unwrap_err().1,
ErrorCode::AssertSecondsAbsolute
);
#[test]
fn test_parse_height_pair() {
let mut a = Allocator::new();
let pair = a.new_pair(a.null(), a.null()).unwrap();
assert_eq!(
seconds_tester(&[0]).unwrap_err().1,
ErrorCode::AssertSecondsAbsolute
parse_height(&mut a, pair, ErrorCode::AssertHeightAbsolute),
Err(ValidationErr(pair, ErrorCode::AssertHeightAbsolute))
);
}

// seconds aren't allowed to be > 2^64 (i.e. 9 bytes)
#[test]
fn test_parse_seconds_pair() {
let mut a = Allocator::new();
let pair = a.new_pair(a.null(), a.null()).unwrap();
assert_eq!(
seconds_tester(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],)
.unwrap_err()
.1,
ErrorCode::AssertSecondsAbsolute
parse_seconds(&mut a, pair, ErrorCode::AssertSecondsAbsolute),
Err(ValidationErr(pair, ErrorCode::AssertSecondsAbsolute))
);
}

// this is small enough though
#[test]
fn test_parse_positive_height_pair() {
let mut a = Allocator::new();
let pair = a.new_pair(a.null(), a.null()).unwrap();
assert_eq!(
seconds_tester(&[0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
Ok(0xffffffffffffffff)
parse_positive_height(&mut a, pair, ErrorCode::AssertHeightAbsolute),
Err(ValidationErr(pair, ErrorCode::AssertHeightAbsolute))
);
}

#[test]
fn test_parse_positive_seconds_pair() {
let mut a = Allocator::new();
let pair = a.new_pair(a.null(), a.null()).unwrap();
assert_eq!(
parse_seconds(&mut a, pair, ErrorCode::AssertSecondsAbsolute),
parse_positive_seconds(&mut a, pair, ErrorCode::AssertSecondsAbsolute),
Err(ValidationErr(pair, ErrorCode::AssertSecondsAbsolute))
);
}
Loading

0 comments on commit fd60489

Please sign in to comment.