Skip to content

Commit 5656fdd

Browse files
authored
Merge pull request #6427 from Jiloc/chore/test-clarity-serialization-interpreter-errors
chore: add test coverage for `InterpeterError` bug in `clarity-serialization`
2 parents e16b7ef + 7760293 commit 5656fdd

File tree

3 files changed

+383
-0
lines changed

3 files changed

+383
-0
lines changed

clarity/src/vm/types/mod.rs

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,8 @@ pub fn byte_len_of_serialization(serialized: &str) -> u64 {
16991699
#[cfg(test)]
17001700
mod test {
17011701
use super::*;
1702+
use crate::vm::errors::{Error, InterpreterError, RuntimeErrorType};
1703+
17021704
#[test]
17031705
fn test_constructors() {
17041706
assert_eq!(
@@ -1922,4 +1924,343 @@ mod test {
19221924
.unwrap();
19231925
assert!(principal.is_multisig());
19241926
}
1927+
1928+
#[test]
1929+
fn test_qualified_contract_identifier_local_returns_runtime_error() {
1930+
let err = QualifiedContractIdentifier::local("1nvalid-name")
1931+
.expect_err("Unexpected qualified contract identifier");
1932+
assert_eq!(
1933+
Error::from(RuntimeErrorType::BadNameValue(
1934+
"ContractName",
1935+
"1nvalid-name".into()
1936+
)),
1937+
err,
1938+
);
1939+
}
1940+
1941+
#[rstest]
1942+
#[case::too_short("S162RK3CHJPCSSK6BM757FW", RuntimeErrorType::ParseError(
1943+
"Invalid principal literal: Expected 20 data bytes.".to_string(),
1944+
))]
1945+
#[case::too_long("S1C5H66S35CSKK6CK1C9HP8SB6CWSK4RB2CDJK8HY4", RuntimeErrorType::ParseError(
1946+
"Invalid principal literal: Expected 20 data bytes.".to_string(),
1947+
))]
1948+
#[case::invalid_c32("II2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G", RuntimeErrorType::ParseError(
1949+
"Invalid principal literal: base58ck checksum 0x1074d4f7 does not match expected 0xae29c6e0".to_string(),
1950+
))]
1951+
fn test_principal_data_parse_standard_principal_returns_runtime_error(
1952+
#[case] input: &str,
1953+
#[case] expected_err: RuntimeErrorType,
1954+
) {
1955+
let err =
1956+
PrincipalData::parse_standard_principal(input).expect_err("Unexpected principal data");
1957+
assert_eq!(Error::from(expected_err), err);
1958+
}
1959+
1960+
#[rstest]
1961+
#[case::no_dot("SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0Gcontract-name", RuntimeErrorType::ParseError(
1962+
"Invalid principal literal: expected a `.` in a qualified contract name"
1963+
.to_string(),
1964+
))]
1965+
#[case::invalid_contract_name("SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G.1nvalid-name", RuntimeErrorType::BadNameValue("ContractName", "1nvalid-name".into()))]
1966+
1967+
fn test_qualified_contract_identifier_parse_returns_interpreter_error(
1968+
#[case] input: &str,
1969+
#[case] expected_err: RuntimeErrorType,
1970+
) {
1971+
let err = QualifiedContractIdentifier::parse(input)
1972+
.expect_err("Unexpected qualified contract identifier");
1973+
assert_eq!(Error::from(expected_err), err);
1974+
}
1975+
1976+
#[rstest]
1977+
#[case::no_dot("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-traitnft-trait", RuntimeErrorType::ParseError(
1978+
"Invalid principal literal: expected a `.` in a qualified contract name"
1979+
.to_string(),
1980+
))]
1981+
#[case::invalid_contract_name("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.1nvalid-contract.valid-trait", RuntimeErrorType::BadNameValue("ContractName", "1nvalid-contract".into()))]
1982+
#[case::invalid_trait_name("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.valid-contract.1nvalid-trait", RuntimeErrorType::BadNameValue("ClarityName", "1nvalid-trait".into()))]
1983+
#[case::invalid_standard_principal("S162RK3CHJPCSSK6BM757FW.valid-contract.valid-trait", RuntimeErrorType::ParseError(
1984+
"Invalid principal literal: Expected 20 data bytes.".to_string(),
1985+
))]
1986+
fn test_trait_identifier_parse_returns_runtime_error(
1987+
#[case] input: &str,
1988+
#[case] expected_err: RuntimeErrorType,
1989+
) {
1990+
let expected_err = Error::from(expected_err);
1991+
1992+
let err = TraitIdentifier::parse(input).expect_err("Unexpected trait identifier");
1993+
assert_eq!(expected_err, err);
1994+
1995+
let err =
1996+
TraitIdentifier::parse_sugared_syntax(input).expect_err("Unexpected trait identifier");
1997+
assert_eq!(expected_err, err);
1998+
}
1999+
2000+
#[rstest]
2001+
#[case::bad_type_construction(
2002+
".valid-contract.valid-trait",
2003+
RuntimeErrorType::BadTypeConstruction
2004+
)]
2005+
#[case::forwards_parse_errors("S162RK3CHJPCSSK6BM757FW.valid-contract.valid-trait", RuntimeErrorType::ParseError(
2006+
"Invalid principal literal: Expected 20 data bytes.".to_string(),
2007+
))]
2008+
fn test_trait_identifier_parse_fully_qualified_returns_runtime_error(
2009+
#[case] input: &str,
2010+
#[case] expected_err: RuntimeErrorType,
2011+
) {
2012+
let err =
2013+
TraitIdentifier::parse_fully_qualified(input).expect_err("Unexpected trait identifier");
2014+
assert_eq!(Error::from(expected_err), err);
2015+
}
2016+
2017+
/// The returned InterpreterError is consensus-critical.
2018+
#[test]
2019+
fn test_standard_principal_data_new_returns_interpreter_error_consensus_critical() {
2020+
let result = StandardPrincipalData::new(32, [0; 20]);
2021+
let err = result.expect_err("Unexpected principal data");
2022+
2023+
assert_eq!(
2024+
Error::from(InterpreterError::Expect("Unexpected principal data".into())),
2025+
err.into(),
2026+
);
2027+
}
2028+
2029+
/// The returned InterpreterError is consensus-critical.
2030+
#[test]
2031+
fn test_sequence_data_element_at_returns_interpreter_error_consensus_critical() {
2032+
let buff = SequenceData::String(CharType::ASCII(ASCIIData { data: vec![1] }));
2033+
let err = buff.element_at(0).unwrap_err();
2034+
assert_eq!(
2035+
Error::from(InterpreterError::Expect(
2036+
"BUG: failed to initialize single-byte ASCII buffer".into()
2037+
)),
2038+
err
2039+
);
2040+
}
2041+
2042+
/// The returned InterpreterError is consensus-critical.
2043+
#[test]
2044+
fn test_ascii_data_to_value_returns_interpreter_error_consensus_critical() {
2045+
let err = ASCIIData::to_value(&1).unwrap_err();
2046+
assert_eq!(
2047+
Error::from(InterpreterError::Expect(
2048+
"ERROR: Invalid ASCII string successfully constructed".into()
2049+
)),
2050+
err
2051+
);
2052+
}
2053+
2054+
/// The returned InterpreterError is consensus-critical.
2055+
#[test]
2056+
fn test_utf8_data_to_value_returns_interpreter_error_consensus_critical() {
2057+
let err = UTF8Data::to_value(&vec![0xED, 0xA0, 0x80]).unwrap_err();
2058+
assert_eq!(
2059+
Error::from(InterpreterError::Expect(
2060+
"ERROR: Invalid UTF8 string successfully constructed".into()
2061+
)),
2062+
err
2063+
);
2064+
}
2065+
2066+
/// The returned InterpreterError is consensus-critical.
2067+
#[test]
2068+
fn test_tuple_data_from_data_typed_returns_interpreter_error_consensus_critical() {
2069+
let tuple_type =
2070+
TupleTypeSignature::try_from(vec![("a".into(), TypeSignature::IntType)]).unwrap();
2071+
let err = TupleData::from_data_typed(
2072+
&StacksEpochId::Epoch32,
2073+
vec![("a".into(), Value::UInt(1))],
2074+
&tuple_type,
2075+
)
2076+
.unwrap_err();
2077+
assert_eq!(
2078+
Error::from(InterpreterError::FailureConstructingTupleWithType),
2079+
err
2080+
);
2081+
}
2082+
2083+
#[rstest]
2084+
#[case::not_a_string(Value::none(), InterpreterError::Expect("Expected ASCII string".to_string()))]
2085+
#[case::invalid_utf8(Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data: vec![0xED, 0xA0, 0x80] }))), InterpreterError::Expect("Non UTF-8 data in string".to_string()))]
2086+
fn test_value_expect_ascii_returns_interpreter_error(
2087+
#[case] value: Value,
2088+
#[case] expected_err: InterpreterError,
2089+
) {
2090+
let err = value.expect_ascii().unwrap_err();
2091+
assert_eq!(Error::from(expected_err), err);
2092+
}
2093+
2094+
/// The returned InterpreterError is consensus-critical.
2095+
#[test]
2096+
fn test_value_expect_u128_returns_interpreter_error_consensus_critical() {
2097+
let err = Value::none().expect_u128().unwrap_err();
2098+
assert_eq!(
2099+
Error::from(InterpreterError::Expect("Expected u128".to_string())),
2100+
err
2101+
);
2102+
}
2103+
2104+
#[test]
2105+
fn test_value_expect_i128_returns_interpreter_error() {
2106+
let err = Value::none().expect_i128().unwrap_err();
2107+
assert_eq!(
2108+
Error::from(InterpreterError::Expect("Expected i128".to_string())),
2109+
err
2110+
);
2111+
}
2112+
2113+
#[rstest]
2114+
#[case::not_a_buffer(Value::none(), InterpreterError::Expect("Expected buff".to_string()))]
2115+
#[case::too_small(Value::buff_from(vec![1, 2, 3, 4]).unwrap(), InterpreterError::Expect("Unexpected buff length".to_string()))]
2116+
fn test_value_expect_buff_returns_interpreter_error(
2117+
#[case] value: Value,
2118+
#[case] expected_err: InterpreterError,
2119+
) {
2120+
let err = value.expect_buff(1).unwrap_err();
2121+
assert_eq!(Error::from(expected_err), err);
2122+
}
2123+
2124+
#[test]
2125+
fn test_value_expect_tuple_returns_interpreter_error() {
2126+
let err = Value::none().expect_tuple().unwrap_err();
2127+
assert_eq!(
2128+
Error::from(InterpreterError::Expect("Expected tuple".to_string())),
2129+
err
2130+
);
2131+
}
2132+
2133+
#[test]
2134+
fn test_value_expect_list_returns_interpreter_error() {
2135+
let err = Value::none().expect_list().unwrap_err();
2136+
assert_eq!(
2137+
Error::from(InterpreterError::Expect("Expected list".to_string())),
2138+
err
2139+
);
2140+
}
2141+
2142+
#[test]
2143+
fn test_value_expect_buff_padded_returns_interpreter_error() {
2144+
let err = Value::none().expect_buff_padded(10, 0).unwrap_err();
2145+
assert_eq!(
2146+
Error::from(InterpreterError::Expect("Expected buff".to_string())),
2147+
err
2148+
);
2149+
}
2150+
2151+
#[test]
2152+
fn test_value_expect_bool_returns_interpreter_error() {
2153+
let err = Value::none().expect_bool().unwrap_err();
2154+
assert_eq!(
2155+
Error::from(InterpreterError::Expect("Expected bool".to_string())),
2156+
err
2157+
);
2158+
}
2159+
2160+
/// The returned InterpreterError is consensus-critical.
2161+
#[test]
2162+
fn test_value_expect_optional_returns_interpreter_error_consensus_critical() {
2163+
let err = Value::okay_true().expect_optional().unwrap_err();
2164+
assert_eq!(
2165+
Error::from(InterpreterError::Expect("Expected optional".to_string())),
2166+
err
2167+
);
2168+
}
2169+
2170+
/// The returned InterpreterError is consensus-critical.
2171+
#[test]
2172+
fn test_value_expect_principal_returns_interpreter_error_consensus_critical() {
2173+
let err = Value::none().expect_principal().unwrap_err();
2174+
assert_eq!(
2175+
Error::from(InterpreterError::Expect("Expected principal".to_string())),
2176+
err
2177+
);
2178+
}
2179+
2180+
/// The returned InterpreterError is consensus-critical.
2181+
#[test]
2182+
fn test_value_expect_callable_returns_interpreter_error_consensus_critical() {
2183+
let err = Value::none().expect_callable().unwrap_err();
2184+
assert_eq!(
2185+
Error::from(InterpreterError::Expect("Expected callable".to_string())),
2186+
err
2187+
);
2188+
}
2189+
2190+
#[test]
2191+
fn test_value_expect_result_returns_interpreter_error() {
2192+
let err = Value::none().expect_result().unwrap_err();
2193+
assert_eq!(
2194+
Error::from(InterpreterError::Expect("Expected response".to_string())),
2195+
err
2196+
);
2197+
}
2198+
2199+
#[rstest]
2200+
#[case::not_a_response(Value::none(), InterpreterError::Expect("Expected response".to_string()))]
2201+
#[case::not_an_ok_response(Value::error(Value::Int(1)).unwrap(), InterpreterError::Expect("Expected ok response".to_string()))]
2202+
fn test_value_expect_result_ok_returns_interpreter_error(
2203+
#[case] value: Value,
2204+
#[case] expected_err: InterpreterError,
2205+
) {
2206+
let err = value.expect_result_ok().unwrap_err();
2207+
assert_eq!(Error::from(expected_err), err);
2208+
}
2209+
2210+
#[rstest]
2211+
#[case::not_a_response(Value::none(), InterpreterError::Expect("Expected response".to_string()))]
2212+
#[case::not_an_err_response(Value::okay_true(), InterpreterError::Expect("Expected err response".to_string()))]
2213+
fn test_value_expect_result_err_returns_interpreter_error(
2214+
#[case] value: Value,
2215+
#[case] expected_err: InterpreterError,
2216+
) {
2217+
let err = value.expect_result_err().unwrap_err();
2218+
assert_eq!(Error::from(expected_err), err);
2219+
}
2220+
2221+
/// The returned InterpreterError is consensus-critical.
2222+
#[test]
2223+
fn test_buff_data_len_returns_interpreter_error_consensus_critical() {
2224+
let err = BuffData {
2225+
data: vec![1; MAX_VALUE_SIZE as usize + 1],
2226+
}
2227+
.len()
2228+
.unwrap_err();
2229+
assert_eq!(
2230+
Error::from(InterpreterError::Expect(
2231+
"Data length should be valid".into()
2232+
)),
2233+
err
2234+
);
2235+
}
2236+
2237+
#[test]
2238+
fn test_ascii_data_len_returns_interpreter_error() {
2239+
let err = ASCIIData {
2240+
data: vec![1; MAX_VALUE_SIZE as usize + 1],
2241+
}
2242+
.len()
2243+
.unwrap_err();
2244+
assert_eq!(
2245+
Error::from(InterpreterError::Expect(
2246+
"Data length should be valid".into()
2247+
)),
2248+
err
2249+
);
2250+
}
2251+
2252+
#[test]
2253+
fn test_utf8_data_len_returns_interpreter_error() {
2254+
let err = UTF8Data {
2255+
data: vec![vec![]; MAX_VALUE_SIZE as usize + 1],
2256+
}
2257+
.len()
2258+
.unwrap_err();
2259+
assert_eq!(
2260+
Error::from(InterpreterError::Expect(
2261+
"Data length should be valid".into()
2262+
)),
2263+
err
2264+
);
2265+
}
19252266
}

clarity/src/vm/types/serialization.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,7 @@ pub mod tests {
13701370
use super::super::*;
13711371
use super::SerializationError;
13721372
use crate::vm::database::{ClarityDeserializable, ClaritySerializable, RollbackWrapper};
1373+
use crate::vm::errors::{Error, InterpreterError};
13731374
use crate::vm::tests::test_clarity_versions;
13741375
use crate::vm::ClarityVersion;
13751376

@@ -2199,4 +2200,34 @@ pub mod tests {
21992200
test_bad_expectation(contract_p2, TypeSignature::BoolType);
22002201
test_bad_expectation(standard_p, TypeSignature::BoolType);
22012202
}
2203+
2204+
/// The returned InterpreterError is consensus-critical.
2205+
#[test]
2206+
fn test_serialize_to_vec_returns_interpreter_error_consensus_critical() {
2207+
let value = Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData {
2208+
data: vec![0; MAX_VALUE_SIZE as usize + 1],
2209+
})));
2210+
let err = value.serialize_to_vec().unwrap_err();
2211+
assert_eq!(
2212+
Error::from(InterpreterError::Expect(
2213+
"IOError filling byte buffer.".into()
2214+
)),
2215+
err.into()
2216+
);
2217+
}
2218+
2219+
/// The returned InterpreterError is consensus-critical.
2220+
#[test]
2221+
fn test_serialize_to_hex_returns_interpreter_error_consensus_critical() {
2222+
let value = Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData {
2223+
data: vec![0; MAX_VALUE_SIZE as usize + 1],
2224+
})));
2225+
let err = value.serialize_to_hex().unwrap_err();
2226+
assert_eq!(
2227+
Error::from(InterpreterError::Expect(
2228+
"IOError filling byte buffer.".into()
2229+
)),
2230+
err.into()
2231+
);
2232+
}
22022233
}

0 commit comments

Comments
 (0)