Skip to content
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

test(covenant): improve test coverage #4052

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 15 additions & 10 deletions base_layer/core/src/covenants/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::{
covenants::{
byte_codes,
covenant::Covenant,
decoder::{CovenantDecodeError, CovenentReadExt},
decoder::{CovenantDecodeError, CovenantReadExt},
encoder::CovenentWriteExt,
error::CovenantError,
fields::{OutputField, OutputFields},
Expand Down Expand Up @@ -238,7 +238,12 @@ impl Display for CovenantArg {

#[cfg(test)]
mod test {
use tari_common_types::types::Commitment;
use tari_script::script;
use tari_utilities::hex::from_hex;

use super::*;
use crate::{covenant, covenants::byte_codes::*};

mod require_x_impl {
use super::*;
Expand All @@ -260,18 +265,18 @@ mod test {
}
}

mod write_to {
use tari_common_types::types::Commitment;
use tari_script::script;
use tari_utilities::hex::from_hex;

mod write_to_and_read_from {
use super::*;
use crate::{covenant, covenants::byte_codes::*};

fn test_case(arg: CovenantArg, expected: &[u8]) {
fn test_case(argument: CovenantArg, mut data: &[u8]) {
let mut buf = Vec::new();
arg.write_to(&mut buf).unwrap();
assert_eq!(buf, expected);
argument.write_to(&mut buf).unwrap();
assert_eq!(buf, data);

let reader = &mut data;
let code = reader.read_next_byte_code().unwrap().unwrap();
let arg = CovenantArg::read_from(&mut data, code).unwrap();
assert_eq!(arg, argument);
}

#[test]
Expand Down
71 changes: 69 additions & 2 deletions base_layer/core/src/covenants/byte_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,21 @@

//---------------------------------- ARG byte codes --------------------------------------------//
pub(super) fn is_valid_arg_code(code: u8) -> bool {
(0x01..=0x09).contains(&code)
ALL_ARGS.contains(&code)
}

pub(super) const ALL_ARGS: [u8; 9] = [
ARG_HASH,
ARG_PUBLIC_KEY,
ARG_COMMITMENT,
ARG_TARI_SCRIPT,
ARG_COVENANT,
ARG_UINT,
ARG_OUTPUT_FIELD,
ARG_OUTPUT_FIELDS,
ARG_BYTES,
];

pub const ARG_HASH: u8 = 0x01;
pub const ARG_PUBLIC_KEY: u8 = 0x02;
pub const ARG_COMMITMENT: u8 = 0x03;
Expand All @@ -37,9 +50,22 @@ pub const ARG_BYTES: u8 = 0x09;
//---------------------------------- FILTER byte codes --------------------------------------------//

pub(super) fn is_valid_filter_code(code: u8) -> bool {
(0x20..=0x24).contains(&code) || (0x30..=0x34).contains(&code)
ALL_FILTERS.contains(&code)
}

pub(super) const ALL_FILTERS: [u8; 10] = [
FILTER_IDENTITY,
FILTER_AND,
FILTER_OR,
FILTER_XOR,
FILTER_NOT,
FILTER_OUTPUT_HASH_EQ,
FILTER_FIELDS_PRESERVED,
FILTER_FIELDS_HASHED_EQ,
FILTER_FIELD_EQ,
FILTER_ABSOLUTE_HEIGHT,
];

pub const FILTER_IDENTITY: u8 = 0x20;
pub const FILTER_AND: u8 = 0x21;
pub const FILTER_OR: u8 = 0x22;
Expand All @@ -63,3 +89,44 @@ pub const FIELD_FEATURES_MATURITY: u8 = 0x06;
pub const FIELD_FEATURES_UNIQUE_ID: u8 = 0x07;
pub const FIELD_FEATURES_PARENT_PUBLIC_KEY: u8 = 0x08;
pub const FIELD_FEATURES_METADATA: u8 = 0x09;

#[cfg(test)]
mod tests {
use super::*;

mod is_valid_filter_code {
use super::*;

#[test]
fn it_returns_true_for_all_filter_codes() {
ALL_FILTERS.iter().for_each(|code| {
assert!(is_valid_filter_code(*code));
});
}

#[test]
fn it_returns_false_for_all_arg_codes() {
ALL_ARGS.iter().for_each(|code| {
assert!(!is_valid_filter_code(*code));
});
}
}

mod is_valid_arg_code {
use super::*;

#[test]
fn it_returns_false_for_all_filter_codes() {
ALL_FILTERS.iter().for_each(|code| {
assert!(!is_valid_arg_code(*code));
});
}

#[test]
fn it_returns_true_for_all_arg_codes() {
ALL_ARGS.iter().for_each(|code| {
assert!(is_valid_arg_code(*code));
});
}
}
}
51 changes: 48 additions & 3 deletions base_layer/core/src/covenants/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ pub enum CovenantDecodeError {
Io(#[from] io::Error),
}

pub(super) trait CovenentReadExt: io::Read {
pub(super) trait CovenantReadExt: io::Read {
fn read_next_byte_code(&mut self) -> Result<Option<u8>, io::Error>;
fn read_variable_length_bytes(&mut self, size: usize) -> Result<Vec<u8>, io::Error>;
}

impl<R: io::Read> CovenentReadExt for R {
impl<R: io::Read> CovenantReadExt for R {
fn read_next_byte_code(&mut self) -> Result<Option<u8>, io::Error> {
let mut buf = [0u8; 1];
loop {
Expand Down Expand Up @@ -127,13 +127,41 @@ mod test {
use super::*;
use crate::{
covenant,
covenants::{arguments::CovenantArg, fields::OutputField, filters::CovenantFilter},
covenants::{
arguments::CovenantArg,
byte_codes::ARG_OUTPUT_FIELD,
fields::OutputField,
filters::CovenantFilter,
},
};

#[test]
fn it_immediately_ends_iterator_given_empty_bytes() {
let buf = &[] as &[u8; 0];
assert!(CovenantTokenDecoder::new(&mut &buf[..]).next().is_none());
assert!(CovenantTokenDecoder::new(&mut &buf[..]).next().is_none());
sdbondi marked this conversation as resolved.
Show resolved Hide resolved
}

#[test]
fn it_ends_after_an_error() {
let buf = &[0xffu8];
let mut reader = &buf[..];
let mut decoder = CovenantTokenDecoder::new(&mut reader);
assert!(matches!(decoder.next(), Some(Err(_))));
assert!(decoder.next().is_none());
}

#[test]
fn it_returns_an_error_if_arg_expected() {
let buf = &[ARG_OUTPUT_FIELD];
let mut reader = &buf[..];
let mut decoder = CovenantTokenDecoder::new(&mut reader);

assert!(matches!(
decoder.next(),
Some(Err(CovenantDecodeError::UnexpectedEof { .. }))
));
assert!(decoder.next().is_none());
}

#[test]
Expand Down Expand Up @@ -171,4 +199,21 @@ mod test {

assert!(decoder.next().is_none());
}

mod covenant_read_ext {
use super::*;

#[test]
fn it_reads_bytes_with_length_prefix() {
let data = vec![0x03u8, 0x01, 0x02, 0x03];
let bytes = CovenantReadExt::read_variable_length_bytes(&mut data.as_slice(), 3).unwrap();
assert_eq!(bytes, [1u8, 2, 3]);
}

#[test]
fn it_errors_if_len_byte_exceeds_maximum() {
let data = vec![0x02, 0x01];
CovenantReadExt::read_variable_length_bytes(&mut data.as_slice(), 1).unwrap_err();
}
}
}
60 changes: 60 additions & 0 deletions base_layer/core/src/covenants/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,63 @@ impl<W: io::Write> CovenentWriteExt for W {
Ok(1)
}
}

#[cfg(test)]
mod tests {

use super::*;
use crate::{
covenant,
covenants::{
byte_codes::{ARG_HASH, ARG_OUTPUT_FIELD, FILTER_AND, FILTER_FIELD_EQ, FILTER_IDENTITY, FILTER_OR},
OutputField,
},
};

#[test]
fn it_encodes_empty_tokens() {
let encoder = CovenantTokenEncoder::new(&[]);
let mut buf = Vec::<u8>::new();
let written = encoder.write_to(&mut buf).unwrap();
assert_eq!(buf, [] as [u8; 0]);
assert_eq!(written, 0);
}

#[test]
fn it_encodes_tokens_correctly() {
let covenant = covenant!(and(identity(), or(identity())));
let encoder = CovenantTokenEncoder::new(covenant.tokens());
let mut buf = Vec::<u8>::new();
let written = encoder.write_to(&mut buf).unwrap();
assert_eq!(buf, [FILTER_AND, FILTER_IDENTITY, FILTER_OR, FILTER_IDENTITY]);
assert_eq!(written, 4);
}

#[test]
fn it_encodes_args_correctly() {
let dummy = [0u8; 32];
let covenant = covenant!(field_eq(@field::features, @hash(dummy)));
let encoder = CovenantTokenEncoder::new(covenant.tokens());
let mut buf = Vec::<u8>::new();
let written = encoder.write_to(&mut buf).unwrap();
assert_eq!(buf[..4], [
FILTER_FIELD_EQ,
ARG_OUTPUT_FIELD,
OutputField::Features.as_byte(),
ARG_HASH
]);
assert_eq!(buf[4..], [0u8; 32]);
assert_eq!(written, 36);
}

mod covenant_write_ext {
use super::*;

#[test]
fn it_writes_a_single_byte() {
let mut buf = Vec::new();
buf.write_u8_fixed(123u8).unwrap();
assert_eq!(buf, vec![123u8]);
}
}
}
2 changes: 0 additions & 2 deletions base_layer/core/src/covenants/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,4 @@ pub enum CovenantError {
RemainingTokens,
#[error("Invalid argument for filter {filter}: {details}")]
InvalidArgument { filter: &'static str, details: String },
#[error("Unsupported argument {arg}: {details}")]
UnsupportedArgument { arg: &'static str, details: String },
}
Loading