Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions changelog.d/7065-guarded-string-from.changed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactor to avoid the risk of panics when creating `ClarityName`s and similar
2 changes: 1 addition & 1 deletion clarity-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ rstest = "0.17.0"

[features]
default = []
testing = []
testing = ["stacks_common/testing"]
developer-mode = ["stacks_common/developer-mode"]
slog_json = ["stacks_common/slog_json"]
rusqlite = ["stacks_common/rusqlite", "dep:rusqlite", "dep:serde_json"]
Expand Down
18 changes: 18 additions & 0 deletions clarity-types/src/tests/representations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,21 @@ fn test_contract_name_deserialization_errors(#[case] buffer: Vec<u8>, #[case] er
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), error_message);
}

/// Regression test for the issue where some `try_*` calls might panic instead of
/// returning an error as they should. See https://github.com/stacks-network/stacks-core/pull/7065
#[test]
fn test_try_functions_on_invalid_value_fail_but_do_not_panic() {
let as_str = "äß}🔥";
let as_string = as_str.to_string();

let into_result_str: Result<ClarityName, _> = as_str.try_into();
let into_result_string: Result<ClarityName, _> = as_string.clone().try_into();
let from_result_str = ClarityName::try_from(as_str);
let from_result_string = ClarityName::try_from(as_string);

assert!(into_result_str.is_err());
assert!(into_result_string.is_err());
assert!(from_result_str.is_err());
assert!(from_result_string.is_err());
}
19 changes: 13 additions & 6 deletions clarity-types/src/tests/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod signatures;
use rstest::rstest;
use stacks_common::types::StacksEpochId;

use crate::ClarityName;
use crate::errors::ClarityTypeError;
use crate::types::{
ASCIIData, BuffData, CharType, ListTypeData, MAX_VALUE_SIZE, PrincipalData,
Expand Down Expand Up @@ -117,7 +118,7 @@ fn test_constructors() {
};
let inner_value = cons().unwrap();
assert_eq!(
TupleData::from_data(vec![("a".into(), inner_value.clone())]),
TupleData::from_data(vec![(ClarityName::from_literal("a"), inner_value.clone())]),
Err(ClarityTypeError::TypeSignatureTooDeep)
);

Expand Down Expand Up @@ -159,7 +160,7 @@ fn simple_size_test() {

#[test]
fn simple_tuple_get_test() {
let t = TupleData::from_data(vec![("abc".into(), Value::Int(0))]).unwrap();
let t = TupleData::from_data(vec![(ClarityName::from_literal("abc"), Value::Int(0))]).unwrap();
assert_eq!(t.get("abc"), Ok(&Value::Int(0)));
// should error!
t.get("abcd").unwrap_err();
Expand Down Expand Up @@ -203,7 +204,10 @@ fn test_some_displays() {
assert_eq!(
&format!(
"{}",
Value::from(TupleData::from_data(vec![("a".into(), Value::Int(2))]).unwrap())
Value::from(
TupleData::from_data(vec![(ClarityName::from_literal("a"), Value::Int(2))])
.unwrap()
)
),
"(tuple (a 2))"
);
Expand Down Expand Up @@ -394,11 +398,14 @@ fn test_utf8_data_to_value_returns_clarity_types_error_invalid_utf8_encoding() {

#[test]
fn test_tuple_data_from_data_typed_returns_clarity_type_error() {
let tuple_type =
TupleTypeSignature::try_from(vec![("a".into(), TypeSignature::IntType)]).unwrap();
let tuple_type = TupleTypeSignature::try_from(vec![(
ClarityName::from_literal("a"),
TypeSignature::IntType,
)])
.unwrap();
let err = TupleData::from_data_typed(
&StacksEpochId::Epoch32,
vec![("a".into(), Value::UInt(1))],
vec![(ClarityName::from_literal("a"), Value::UInt(1))],
&tuple_type,
)
.unwrap_err();
Expand Down
30 changes: 17 additions & 13 deletions clarity-types/src/tests/types/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::types::{
ASCIIData, CharType, MAX_VALUE_SIZE, PrincipalData, QualifiedContractIdentifier, SequenceData,
StandardPrincipalData, TupleData, TypeSignature, Value,
};
use crate::{ClarityName, ContractName};

fn test_deser_ser(v: Value) {
assert_eq!(
Expand Down Expand Up @@ -182,30 +183,32 @@ fn test_string_utf8() {
fn test_tuples() {
let t_1 = Value::from(
TupleData::from_data(vec![
("a".into(), Value::Int(1)),
("b".into(), Value::Int(1)),
(ClarityName::from_literal("a"), Value::Int(1)),
(ClarityName::from_literal("b"), Value::Int(1)),
])
.unwrap(),
);
let t_0 = Value::from(
TupleData::from_data(vec![
("b".into(), Value::Int(1)),
("a".into(), Value::Int(1)),
(ClarityName::from_literal("b"), Value::Int(1)),
(ClarityName::from_literal("a"), Value::Int(1)),
])
.unwrap(),
);
let t_2 = Value::from(
TupleData::from_data(vec![
("a".into(), Value::Int(1)),
("b".into(), Value::Bool(true)),
(ClarityName::from_literal("a"), Value::Int(1)),
(ClarityName::from_literal("b"), Value::Bool(true)),
])
.unwrap(),
);
let t_3 = Value::from(TupleData::from_data(vec![("a".into(), Value::Int(1))]).unwrap());
let t_3 = Value::from(
TupleData::from_data(vec![(ClarityName::from_literal("a"), Value::Int(1))]).unwrap(),
);
let t_4 = Value::from(
TupleData::from_data(vec![
("a".into(), Value::Int(1)),
("c".into(), Value::Bool(true)),
(ClarityName::from_literal("a"), Value::Int(1)),
(ClarityName::from_literal("c"), Value::Bool(true)),
])
.unwrap(),
);
Expand Down Expand Up @@ -303,7 +306,7 @@ fn test_vectors() {
],
)
.unwrap(),
"abcd".into(),
ContractName::from_literal("abcd"),
)
.into()),
),
Expand Down Expand Up @@ -334,8 +337,8 @@ fn test_vectors() {
"0c000000020362617a0906666f6f62617203",
Ok(Value::from(
TupleData::from_data(vec![
("baz".into(), Value::none()),
("foobar".into(), Value::Bool(true)),
(ClarityName::from_literal("baz"), Value::none()),
(ClarityName::from_literal("foobar"), Value::Bool(true)),
])
.unwrap(),
)),
Expand Down Expand Up @@ -405,7 +408,8 @@ fn test_principals() {
.unwrap();
let standard_p = Value::from(issuer.clone());

let contract_identifier = QualifiedContractIdentifier::new(issuer, "foo".into());
let contract_identifier =
QualifiedContractIdentifier::new(issuer, ContractName::from_literal("foo"));
let contract_p2 = Value::from(PrincipalData::Contract(contract_identifier));

test_deser_ser(contract_p2.clone());
Expand Down
Loading
Loading